Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

rotate-session.ts 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import { Data, ShapeType } from 'types'
  2. import * as vec from 'utils/vec'
  3. import BaseSession from './base-session'
  4. import commands from 'state/commands'
  5. import { current } from 'immer'
  6. import {
  7. clampToRotationToSegments,
  8. getBoundsCenter,
  9. getCommonBounds,
  10. getPage,
  11. getSelectedShapes,
  12. getRotatedBounds,
  13. getShapeBounds,
  14. updateParents,
  15. getDocumentBranch,
  16. } from 'utils/utils'
  17. import { getShapeUtils } from 'lib/shape-utils'
  18. const PI2 = Math.PI * 2
  19. export default class RotateSession extends BaseSession {
  20. delta = [0, 0]
  21. origin: number[]
  22. snapshot: RotateSnapshot
  23. prev = 0
  24. constructor(data: Data, point: number[]) {
  25. super(data)
  26. this.origin = point
  27. this.snapshot = getRotateSnapshot(data)
  28. }
  29. update(data: Data, point: number[], isLocked: boolean) {
  30. const { commonBoundsCenter, initialShapes } = this.snapshot
  31. const page = getPage(data)
  32. const a1 = vec.angle(commonBoundsCenter, this.origin)
  33. const a2 = vec.angle(commonBoundsCenter, point)
  34. let rot = a2 - a1
  35. const delta = rot - this.prev
  36. this.prev = rot
  37. if (isLocked) {
  38. rot = clampToRotationToSegments(rot, 24)
  39. }
  40. data.boundsRotation = (PI2 + (this.snapshot.boundsRotation + rot)) % PI2
  41. for (let { id, center, offset, rotation } of initialShapes) {
  42. const shape = page.shapes[id]
  43. const nextRotation =
  44. PI2 +
  45. ((isLocked
  46. ? clampToRotationToSegments(rotation + rot, 24)
  47. : rotation + rot) %
  48. PI2)
  49. const nextPoint = vec.sub(
  50. vec.rotWith(center, commonBoundsCenter, rot),
  51. offset
  52. )
  53. getShapeUtils(shape)
  54. .rotateTo(shape, nextRotation, delta)
  55. .translateTo(shape, nextPoint)
  56. }
  57. updateParents(
  58. data,
  59. initialShapes.map((s) => s.id)
  60. )
  61. }
  62. cancel(data: Data) {
  63. const { currentPageId, initialShapes } = this.snapshot
  64. const page = getPage(data, currentPageId)
  65. for (let { id, point, rotation } of initialShapes) {
  66. const shape = page.shapes[id]
  67. getShapeUtils(shape)
  68. .rotateTo(shape, rotation, rotation - shape.rotation)
  69. .translateTo(shape, point)
  70. }
  71. updateParents(
  72. data,
  73. initialShapes.map((s) => s.id)
  74. )
  75. }
  76. complete(data: Data) {
  77. if (!this.snapshot.hasUnlockedShapes) return
  78. commands.rotate(data, this.snapshot, getRotateSnapshot(data))
  79. }
  80. }
  81. export function getRotateSnapshot(data: Data) {
  82. const cData = current(data)
  83. const page = getPage(cData)
  84. const initialShapes = Array.from(cData.selectedIds.values())
  85. .flatMap((id) => getDocumentBranch(cData, id).map((id) => page.shapes[id]))
  86. .filter((shape) => !shape.isLocked)
  87. const hasUnlockedShapes = initialShapes.length > 0
  88. const shapesBounds = Object.fromEntries(
  89. initialShapes.map((shape) => [shape.id, getShapeBounds(shape)])
  90. )
  91. const bounds = getCommonBounds(...Object.values(shapesBounds))
  92. const commonBoundsCenter = getBoundsCenter(bounds)
  93. return {
  94. hasUnlockedShapes,
  95. currentPageId: data.currentPageId,
  96. boundsRotation: data.boundsRotation,
  97. commonBoundsCenter,
  98. initialShapes: initialShapes
  99. .filter((shape) => shape.type !== ShapeType.Group)
  100. .map((shape) => {
  101. const bounds = shapesBounds[shape.id]
  102. const center = getBoundsCenter(bounds)
  103. const offset = vec.sub(center, shape.point)
  104. const rotationOffset = vec.sub(
  105. center,
  106. getBoundsCenter(getRotatedBounds(shape))
  107. )
  108. return {
  109. id: shape.id,
  110. point: shape.point,
  111. rotation: shape.rotation,
  112. offset,
  113. rotationOffset,
  114. center,
  115. }
  116. }),
  117. }
  118. }
  119. export type RotateSnapshot = ReturnType<typeof getRotateSnapshot>