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.

index.tsx 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import {
  2. Bounds,
  3. BoundsSnapshot,
  4. Shape,
  5. Shapes,
  6. ShapeType,
  7. Corner,
  8. Edge,
  9. ShapeByType,
  10. } from "types"
  11. import circle from "./circle"
  12. import dot from "./dot"
  13. import polyline from "./polyline"
  14. import rectangle from "./rectangle"
  15. import ellipse from "./ellipse"
  16. import line from "./line"
  17. import ray from "./ray"
  18. /*
  19. Shape Utiliies
  20. A shape utility is an object containing utility methods for each type of shape
  21. in the application. While shapes may be very different, each shape must support
  22. a common set of utility methods, such as hit tests or translations, that
  23. Operations throughout the app will call these utility methods
  24. when performing tests (such as hit tests) or mutations, such as translations.
  25. */
  26. export interface ShapeUtility<K extends Readonly<Shape>> {
  27. // A cache for the computed bounds of this kind of shape.
  28. boundsCache: WeakMap<K, Bounds>
  29. // Whether to show transform controls when this shape is selected.
  30. canTransform: boolean
  31. // Whether the shape's aspect ratio can change
  32. canChangeAspectRatio: boolean
  33. // Create a new shape.
  34. create(props: Partial<K>): K
  35. // Apply a translation to a shape.
  36. translate(this: ShapeUtility<K>, shape: K, delta: number[]): ShapeUtility<K>
  37. // Apply a rotation to a shape.
  38. rotate(this: ShapeUtility<K>, shape: K, rotation: number): ShapeUtility<K>
  39. // Transform to fit a new bounding box when more than one shape is selected.
  40. transform(
  41. this: ShapeUtility<K>,
  42. shape: K,
  43. bounds: Bounds,
  44. info: {
  45. type: Edge | Corner
  46. initialShape: K
  47. scaleX: number
  48. scaleY: number
  49. transformOrigin: number[]
  50. }
  51. ): ShapeUtility<K>
  52. // Transform a single shape to fit a new bounding box.
  53. transformSingle(
  54. this: ShapeUtility<K>,
  55. shape: K,
  56. bounds: Bounds,
  57. info: {
  58. type: Edge | Corner
  59. initialShape: K
  60. scaleX: number
  61. scaleY: number
  62. transformOrigin: number[]
  63. }
  64. ): ShapeUtility<K>
  65. // Move a shape to a new parent.
  66. setParent(this: ShapeUtility<K>, shape: K, parentId: string): ShapeUtility<K>
  67. // Change the child index of a shape
  68. setChildIndex(
  69. this: ShapeUtility<K>,
  70. shape: K,
  71. childIndex: number
  72. ): ShapeUtility<K>
  73. // Render a shape to JSX.
  74. render(this: ShapeUtility<K>, shape: K): JSX.Element
  75. // Get the bounds of the a shape.
  76. getBounds(this: ShapeUtility<K>, shape: K): Bounds
  77. // Get the routated bounds of the a shape.
  78. getRotatedBounds(this: ShapeUtility<K>, shape: K): Bounds
  79. // Get the center of the shape
  80. getCenter(this: ShapeUtility<K>, shape: K): number[]
  81. // Test whether a point lies within a shape.
  82. hitTest(this: ShapeUtility<K>, shape: K, test: number[]): boolean
  83. // Test whether bounds collide with or contain a shape.
  84. hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
  85. }
  86. // A mapping of shape types to shape utilities.
  87. const shapeUtilityMap: Record<ShapeType, ShapeUtility<Shape>> = {
  88. [ShapeType.Circle]: circle,
  89. [ShapeType.Dot]: dot,
  90. [ShapeType.Polyline]: polyline,
  91. [ShapeType.Rectangle]: rectangle,
  92. [ShapeType.Ellipse]: ellipse,
  93. [ShapeType.Line]: line,
  94. [ShapeType.Ray]: ray,
  95. }
  96. /**
  97. * A helper to retrieve a shape utility based on a shape object.
  98. * @param shape
  99. * @returns
  100. */
  101. export function getShapeUtils<T extends Shape>(shape: T): ShapeUtility<T> {
  102. return shapeUtilityMap[shape.type] as ShapeUtility<T>
  103. }
  104. /**
  105. * A factory of shape utilities, with typing enforced.
  106. * @param shape
  107. * @returns
  108. */
  109. export function registerShapeUtils<T extends Shape>(
  110. shape: ShapeUtility<T>
  111. ): ShapeUtility<T> {
  112. return Object.freeze(shape)
  113. }
  114. export function createShape<T extends ShapeType>(
  115. type: T,
  116. props: Partial<ShapeByType<T>>
  117. ) {
  118. return shapeUtilityMap[type].create(props) as ShapeByType<T>
  119. }
  120. export default shapeUtilityMap