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.6KB

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