You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rotate-session.ts 3.4KB

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