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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { Bounds, BoundsSnapshot, Shape, Shapes, ShapeType } from "types"
  2. import circle from "./circle"
  3. import dot from "./dot"
  4. import polyline from "./polyline"
  5. import rectangle from "./rectangle"
  6. import ellipse from "./ellipse"
  7. import line from "./line"
  8. import ray from "./ray"
  9. /*
  10. Shape Utiliies
  11. A shape utility is an object containing utility methods for each type of shape
  12. in the application. While shapes may be very different, each shape must support
  13. a common set of utility methods, such as hit tests or translations, that
  14. Operations throughout the app will call these utility methods
  15. when performing tests (such as hit tests) or mutations, such as translations.
  16. */
  17. export interface ShapeUtility<K extends Shape> {
  18. // A cache for the computed bounds of this kind of shape.
  19. boundsCache: WeakMap<K, Bounds>
  20. // Create a new shape.
  21. create(props: Partial<K>): K
  22. // Get the bounds of the a shape.
  23. getBounds(this: ShapeUtility<K>, shape: K): Bounds
  24. // Test whether a point lies within a shape.
  25. hitTest(this: ShapeUtility<K>, shape: K, test: number[]): boolean
  26. // Test whether bounds collide with or contain a shape.
  27. hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
  28. // Apply a rotation to a shape.
  29. rotate(this: ShapeUtility<K>, shape: K): K
  30. // Apply a translation to a shape.
  31. translate(this: ShapeUtility<K>, shape: K, delta: number[]): K
  32. // Transform to fit a new bounding box.
  33. transform(this: ShapeUtility<K>, shape: K, bounds: Bounds): K
  34. // Apply a scale to a shape.
  35. scale(this: ShapeUtility<K>, shape: K, scale: number): K
  36. // Apply a stretch to a shape.
  37. stretch(this: ShapeUtility<K>, shape: K, scaleX: number, scaleY: number): K
  38. // Render a shape to JSX.
  39. render(this: ShapeUtility<K>, shape: K): JSX.Element
  40. }
  41. // A mapping of shape types to shape utilities.
  42. const shapeUtilityMap: { [key in ShapeType]: ShapeUtility<Shapes[key]> } = {
  43. [ShapeType.Circle]: circle,
  44. [ShapeType.Dot]: dot,
  45. [ShapeType.Polyline]: polyline,
  46. [ShapeType.Rectangle]: rectangle,
  47. [ShapeType.Ellipse]: ellipse,
  48. [ShapeType.Line]: line,
  49. [ShapeType.Ray]: ray,
  50. }
  51. /**
  52. * A helper to retrieve a shape utility based on a shape object.
  53. * @param shape
  54. * @returns
  55. */
  56. export function getShapeUtils(shape: Shape): ShapeUtility<typeof shape> {
  57. return shapeUtilityMap[shape.type]
  58. }
  59. /**
  60. * A factory of shape utilities, with typing enforced.
  61. * @param shape
  62. * @returns
  63. */
  64. export function createShape<T extends Shape>(
  65. shape: ShapeUtility<T>
  66. ): ShapeUtility<T> {
  67. return Object.freeze(shape)
  68. }
  69. export default shapeUtilityMap