Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

transform-session.ts 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import { Data, Edge, Corner } 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 { getShapeUtils } from "lib/shape-utils"
  7. import {
  8. getBoundsCenter,
  9. getBoundsFromPoints,
  10. getCommonBounds,
  11. getPage,
  12. getRelativeTransformedBoundingBox,
  13. getShapes,
  14. getTransformedBoundingBox,
  15. } from "utils/utils"
  16. export default class TransformSession extends BaseSession {
  17. scaleX = 1
  18. scaleY = 1
  19. transformType: Edge | Corner | "center"
  20. origin: number[]
  21. snapshot: TransformSnapshot
  22. constructor(
  23. data: Data,
  24. transformType: Corner | Edge | "center",
  25. point: number[]
  26. ) {
  27. super(data)
  28. this.origin = point
  29. this.transformType = transformType
  30. this.snapshot = getTransformSnapshot(data, transformType)
  31. }
  32. update(data: Data, point: number[], isAspectRatioLocked = false) {
  33. const { transformType } = this
  34. const { selectedIds, shapeBounds, initialBounds } = this.snapshot
  35. const { shapes } = getPage(data)
  36. const newBoundingBox = getTransformedBoundingBox(
  37. initialBounds,
  38. transformType,
  39. vec.vec(this.origin, point),
  40. data.boundsRotation,
  41. isAspectRatioLocked
  42. )
  43. this.scaleX = newBoundingBox.scaleX
  44. this.scaleY = newBoundingBox.scaleY
  45. // Now work backward to calculate a new bounding box for each of the shapes.
  46. selectedIds.forEach((id) => {
  47. const { initialShape, initialShapeBounds, transformOrigin } =
  48. shapeBounds[id]
  49. const newShapeBounds = getRelativeTransformedBoundingBox(
  50. newBoundingBox,
  51. initialBounds,
  52. initialShapeBounds,
  53. this.scaleX < 0,
  54. this.scaleY < 0
  55. )
  56. const shape = shapes[id]
  57. // const transformOrigins = {
  58. // [Edge.Top]: [0.5, 1],
  59. // [Edge.Right]: [0, 0.5],
  60. // [Edge.Bottom]: [0.5, 0],
  61. // [Edge.Left]: [1, 0.5],
  62. // [Corner.TopLeft]: [1, 1],
  63. // [Corner.TopRight]: [0, 1],
  64. // [Corner.BottomLeft]: [1, 0],
  65. // [Corner.BottomRight]: [0, 0],
  66. // }
  67. // const origin = transformOrigins[this.transformType]
  68. getShapeUtils(shape).transform(shape, newShapeBounds, {
  69. type: this.transformType,
  70. initialShape,
  71. scaleX: this.scaleX,
  72. scaleY: this.scaleY,
  73. transformOrigin,
  74. })
  75. })
  76. }
  77. cancel(data: Data) {
  78. const { currentPageId, selectedIds, shapeBounds } = this.snapshot
  79. const page = getPage(data, currentPageId)
  80. selectedIds.forEach((id) => {
  81. const shape = page.shapes[id]
  82. const { initialShape, initialShapeBounds, transformOrigin } =
  83. shapeBounds[id]
  84. getShapeUtils(shape).transform(shape, initialShapeBounds, {
  85. type: this.transformType,
  86. initialShape,
  87. scaleX: 1,
  88. scaleY: 1,
  89. transformOrigin,
  90. })
  91. })
  92. }
  93. complete(data: Data) {
  94. commands.transform(
  95. data,
  96. this.snapshot,
  97. getTransformSnapshot(data, this.transformType),
  98. this.scaleX,
  99. this.scaleY
  100. )
  101. }
  102. }
  103. export function getTransformSnapshot(
  104. data: Data,
  105. transformType: Edge | Corner | "center"
  106. ) {
  107. const {
  108. document: { pages },
  109. selectedIds,
  110. currentPageId,
  111. } = current(data)
  112. const pageShapes = pages[currentPageId].shapes
  113. // A mapping of selected shapes and their bounds
  114. const shapesBounds = Object.fromEntries(
  115. Array.from(selectedIds.values()).map((id) => {
  116. const shape = pageShapes[id]
  117. return [shape.id, getShapeUtils(shape).getBounds(shape)]
  118. })
  119. )
  120. const boundsArr = Object.values(shapesBounds)
  121. // The common (exterior) bounds of the selected shapes
  122. const bounds = getCommonBounds(...boundsArr)
  123. const initialInnerBounds = getBoundsFromPoints(boundsArr.map(getBoundsCenter))
  124. // Return a mapping of shapes to bounds together with the relative
  125. // positions of the shape's bounds within the common bounds shape.
  126. return {
  127. type: transformType,
  128. currentPageId,
  129. selectedIds: new Set(selectedIds),
  130. initialBounds: bounds,
  131. shapeBounds: Object.fromEntries(
  132. Array.from(selectedIds.values()).map((id) => {
  133. const initialShapeBounds = shapesBounds[id]
  134. const ic = getBoundsCenter(initialShapeBounds)
  135. let ix = (ic[0] - initialInnerBounds.minX) / initialInnerBounds.width
  136. let iy = (ic[1] - initialInnerBounds.minY) / initialInnerBounds.height
  137. return [
  138. id,
  139. {
  140. initialShape: pageShapes[id],
  141. initialShapeBounds,
  142. transformOrigin: [ix, iy],
  143. },
  144. ]
  145. })
  146. ),
  147. }
  148. }
  149. export type TransformSnapshot = ReturnType<typeof getTransformSnapshot>