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 2.5KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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. getBoundsCenter,
  8. getCommonBounds,
  9. getPage,
  10. getSelectedShapes,
  11. getShapeBounds,
  12. } from "utils/utils"
  13. const PI2 = Math.PI * 2
  14. export default class RotateSession extends BaseSession {
  15. delta = [0, 0]
  16. origin: number[]
  17. snapshot: RotateSnapshot
  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) {
  24. const { boundsCenter, shapes } = this.snapshot
  25. const page = getPage(data)
  26. const a1 = vec.angle(boundsCenter, this.origin)
  27. const a2 = vec.angle(boundsCenter, point)
  28. let rot = (PI2 + (a2 - a1)) % PI2
  29. if (isLocked) {
  30. rot = Math.floor((rot + Math.PI / 8) / (Math.PI / 4)) * (Math.PI / 4)
  31. }
  32. data.boundsRotation = (PI2 + (this.snapshot.boundsRotation + rot)) % PI2
  33. for (let { id, center, offset, rotation } of shapes) {
  34. const shape = page.shapes[id]
  35. shape.rotation = (PI2 + (rotation + rot)) % PI2
  36. const newCenter = vec.rotWith(center, boundsCenter, rot % PI2)
  37. shape.point = vec.sub(newCenter, offset)
  38. }
  39. }
  40. cancel(data: Data) {
  41. const page = getPage(data, this.snapshot.currentPageId)
  42. for (let { id, point, rotation } of this.snapshot.shapes) {
  43. const shape = page.shapes[id]
  44. shape.rotation = rotation
  45. shape.point = point
  46. }
  47. }
  48. complete(data: Data) {
  49. commands.rotate(data, this.snapshot, getRotateSnapshot(data))
  50. }
  51. }
  52. export function getRotateSnapshot(data: Data) {
  53. const shapes = getSelectedShapes(current(data))
  54. // A mapping of selected shapes and their bounds
  55. const shapesBounds = Object.fromEntries(
  56. shapes.map((shape) => [shape.id, getShapeBounds(shape)])
  57. )
  58. // The common (exterior) bounds of the selected shapes
  59. const bounds = getCommonBounds(...Object.values(shapesBounds))
  60. const boundsCenter = getBoundsCenter(bounds)
  61. return {
  62. boundsCenter,
  63. currentPageId: data.currentPageId,
  64. boundsRotation: data.boundsRotation,
  65. shapes: shapes.map(({ id, point, rotation }) => {
  66. const bounds = shapesBounds[id]
  67. const offset = [bounds.width / 2, bounds.height / 2]
  68. const center = getBoundsCenter(bounds)
  69. return {
  70. id,
  71. point,
  72. rotation,
  73. offset,
  74. center,
  75. }
  76. }),
  77. }
  78. }
  79. export type RotateSnapshot = ReturnType<typeof getRotateSnapshot>