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.

draw.tsx 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { v4 as uuid } from "uuid"
  2. import * as vec from "utils/vec"
  3. import { DrawShape, ShapeType } from "types"
  4. import { registerShapeUtils } from "./index"
  5. import { intersectPolylineBounds } from "utils/intersections"
  6. import { boundsContainPolygon } from "utils/bounds"
  7. import { getBoundsFromPoints, translateBounds } from "utils/utils"
  8. const draw = registerShapeUtils<DrawShape>({
  9. boundsCache: new WeakMap([]),
  10. create(props) {
  11. return {
  12. id: uuid(),
  13. type: ShapeType.Draw,
  14. isGenerated: false,
  15. name: "Draw",
  16. parentId: "page0",
  17. childIndex: 0,
  18. point: [0, 0],
  19. points: [[0, 0]],
  20. rotation: 0,
  21. ...props,
  22. style: {
  23. strokeWidth: 2,
  24. strokeLinecap: "round",
  25. strokeLinejoin: "round",
  26. ...props.style,
  27. fill: "transparent",
  28. },
  29. }
  30. },
  31. render({ id, points }) {
  32. return <polyline id={id} points={points.toString()} />
  33. },
  34. applyStyles(shape, style) {
  35. Object.assign(shape.style, style)
  36. shape.style.fill = "transparent"
  37. return this
  38. },
  39. getBounds(shape) {
  40. if (!this.boundsCache.has(shape)) {
  41. const bounds = getBoundsFromPoints(shape.points)
  42. this.boundsCache.set(shape, bounds)
  43. }
  44. return translateBounds(this.boundsCache.get(shape), shape.point)
  45. },
  46. getRotatedBounds(shape) {
  47. return this.getBounds(shape)
  48. },
  49. getCenter(shape) {
  50. const bounds = this.getBounds(shape)
  51. return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2]
  52. },
  53. hitTest(shape, point) {
  54. let pt = vec.sub(point, shape.point)
  55. let prev = shape.points[0]
  56. for (let i = 1; i < shape.points.length; i++) {
  57. let curr = shape.points[i]
  58. if (vec.distanceToLineSegment(prev, curr, pt) < 4) {
  59. return true
  60. }
  61. prev = curr
  62. }
  63. return false
  64. },
  65. hitTestBounds(this, shape, brushBounds) {
  66. const b = this.getBounds(shape)
  67. const center = [b.minX + b.width / 2, b.minY + b.height / 2]
  68. const rotatedCorners = [
  69. [b.minX, b.minY],
  70. [b.maxX, b.minY],
  71. [b.maxX, b.maxY],
  72. [b.minX, b.maxY],
  73. ].map((point) => vec.rotWith(point, center, shape.rotation))
  74. return (
  75. boundsContainPolygon(brushBounds, rotatedCorners) ||
  76. intersectPolylineBounds(
  77. shape.points.map((point) => vec.add(point, shape.point)),
  78. brushBounds
  79. ).length > 0
  80. )
  81. },
  82. rotateTo(shape, rotation) {
  83. shape.rotation = rotation
  84. return this
  85. },
  86. translateTo(shape, point) {
  87. shape.point = point
  88. return this
  89. },
  90. transform(shape, bounds, { initialShape, scaleX, scaleY }) {
  91. const initialShapeBounds = this.boundsCache.get(initialShape)
  92. shape.points = initialShape.points.map(([x, y]) => {
  93. return [
  94. bounds.width *
  95. (scaleX < 0
  96. ? 1 - x / initialShapeBounds.width
  97. : x / initialShapeBounds.width),
  98. bounds.height *
  99. (scaleY < 0
  100. ? 1 - y / initialShapeBounds.height
  101. : y / initialShapeBounds.height),
  102. ]
  103. })
  104. const newBounds = getBoundsFromPoints(shape.points)
  105. shape.point = vec.sub(
  106. [bounds.minX, bounds.minY],
  107. [newBounds.minX, newBounds.minY]
  108. )
  109. return this
  110. },
  111. transformSingle(shape, bounds, info) {
  112. this.transform(shape, bounds, info)
  113. return this
  114. },
  115. setParent(shape, parentId) {
  116. shape.parentId = parentId
  117. return this
  118. },
  119. setChildIndex(shape, childIndex) {
  120. shape.childIndex = childIndex
  121. return this
  122. },
  123. setPoints(shape, points) {
  124. // const bounds = getBoundsFromPoints(points)
  125. // const corner = [bounds.minX, bounds.minY]
  126. // const nudged = points.map((point) => vec.sub(point, corner))
  127. // this.boundsCache.set(shape, translategetBoundsFromPoints(nudged))
  128. // shape.point = vec.add(shape.point, corner)
  129. shape.points = points
  130. return this
  131. },
  132. canTransform: true,
  133. canChangeAspectRatio: true,
  134. })
  135. export default draw