您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

rotate-session.ts 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { Data } 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. } from 'utils/utils'
  15. import { getShapeUtils } from 'lib/shape-utils'
  16. const PI2 = Math.PI * 2
  17. export default class RotateSession extends BaseSession {
  18. delta = [0, 0]
  19. origin: number[]
  20. snapshot: RotateSnapshot
  21. constructor(data: Data, point: number[]) {
  22. super(data)
  23. this.origin = point
  24. this.snapshot = getRotateSnapshot(data)
  25. }
  26. update(data: Data, point: number[], isLocked: boolean) {
  27. const { commonBoundsCenter, initialShapes } = this.snapshot
  28. const page = getPage(data)
  29. const a1 = vec.angle(commonBoundsCenter, this.origin)
  30. const a2 = vec.angle(commonBoundsCenter, point)
  31. let rot = a2 - a1
  32. if (isLocked) {
  33. rot = clampToRotationToSegments(rot, 24)
  34. }
  35. data.boundsRotation = (PI2 + (this.snapshot.boundsRotation + rot)) % PI2
  36. for (let { id, center, offset, rotation } of initialShapes) {
  37. const shape = page.shapes[id]
  38. // const rotationOffset = vec.sub(
  39. // getBoundsCenter(getShapeBounds(shape)),
  40. // getBoundsCenter(getRotatedBounds(shape))
  41. // )
  42. const nextRotation = isLocked
  43. ? clampToRotationToSegments(rotation + rot, 24)
  44. : rotation + rot
  45. const nextPoint = vec.sub(
  46. vec.rotWith(center, commonBoundsCenter, rot),
  47. offset
  48. )
  49. getShapeUtils(shape)
  50. .rotateTo(shape, (PI2 + nextRotation) % PI2)
  51. .translateTo(shape, nextPoint)
  52. }
  53. }
  54. cancel(data: Data) {
  55. const page = getPage(data, this.snapshot.currentPageId)
  56. for (let { id, point, rotation } of this.snapshot.initialShapes) {
  57. const shape = page.shapes[id]
  58. getShapeUtils(shape).rotateTo(shape, rotation).translateTo(shape, point)
  59. }
  60. }
  61. complete(data: Data) {
  62. if (!this.snapshot.hasUnlockedShapes) return
  63. commands.rotate(data, this.snapshot, getRotateSnapshot(data))
  64. }
  65. }
  66. export function getRotateSnapshot(data: Data) {
  67. const initialShapes = getSelectedShapes(current(data)).filter(
  68. (shape) => !shape.isLocked
  69. )
  70. const hasUnlockedShapes = initialShapes.length > 0
  71. const shapesBounds = Object.fromEntries(
  72. initialShapes.map((shape) => [shape.id, getShapeBounds(shape)])
  73. )
  74. const bounds = getCommonBounds(...Object.values(shapesBounds))
  75. const commonBoundsCenter = getBoundsCenter(bounds)
  76. return {
  77. hasUnlockedShapes,
  78. currentPageId: data.currentPageId,
  79. boundsRotation: data.boundsRotation,
  80. commonBoundsCenter,
  81. initialShapes: initialShapes.map((shape) => {
  82. const bounds = shapesBounds[shape.id]
  83. const center = getBoundsCenter(bounds)
  84. const offset = vec.sub(center, shape.point)
  85. const rotationOffset = vec.sub(
  86. center,
  87. getBoundsCenter(getRotatedBounds(shape))
  88. )
  89. return {
  90. id: shape.id,
  91. point: shape.point,
  92. rotation: shape.rotation,
  93. offset,
  94. rotationOffset,
  95. center,
  96. }
  97. }),
  98. }
  99. }
  100. export type RotateSnapshot = ReturnType<typeof getRotateSnapshot>