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.

circle.tsx 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import { v4 as uuid } from "uuid"
  2. import * as vec from "utils/vec"
  3. import { CircleShape, ShapeType, TransformCorner, TransformEdge } from "types"
  4. import { createShape } from "./index"
  5. import { boundsContained } from "utils/bounds"
  6. import { intersectCircleBounds } from "utils/intersections"
  7. import { pointInCircle } from "utils/hitTests"
  8. const circle = createShape<CircleShape>({
  9. boundsCache: new WeakMap([]),
  10. create(props) {
  11. return {
  12. id: uuid(),
  13. type: ShapeType.Circle,
  14. isGenerated: false,
  15. name: "Circle",
  16. parentId: "page0",
  17. childIndex: 0,
  18. point: [0, 0],
  19. rotation: 0,
  20. radius: 20,
  21. style: {},
  22. ...props,
  23. }
  24. },
  25. render({ id, radius }) {
  26. return <circle id={id} cx={radius} cy={radius} r={radius} />
  27. },
  28. getBounds(shape) {
  29. if (this.boundsCache.has(shape)) {
  30. return this.boundsCache.get(shape)
  31. }
  32. const {
  33. point: [x, y],
  34. radius,
  35. } = shape
  36. const bounds = {
  37. minX: x,
  38. maxX: x + radius * 2,
  39. minY: y,
  40. maxY: y + radius * 2,
  41. width: radius * 2,
  42. height: radius * 2,
  43. }
  44. this.boundsCache.set(shape, bounds)
  45. return bounds
  46. },
  47. hitTest(shape, point) {
  48. return pointInCircle(
  49. point,
  50. vec.addScalar(shape.point, shape.radius),
  51. shape.radius
  52. )
  53. },
  54. hitTestBounds(shape, bounds) {
  55. const shapeBounds = this.getBounds(shape)
  56. return (
  57. boundsContained(shapeBounds, bounds) ||
  58. intersectCircleBounds(
  59. vec.addScalar(shape.point, shape.radius),
  60. shape.radius,
  61. bounds
  62. ).length > 0
  63. )
  64. },
  65. rotate(shape) {
  66. return shape
  67. },
  68. translate(shape, delta) {
  69. shape.point = vec.add(shape.point, delta)
  70. return shape
  71. },
  72. scale(shape, scale) {
  73. return shape
  74. },
  75. stretch(shape, scaleX, scaleY) {
  76. return shape
  77. },
  78. transform(shape, bounds, { anchor }) {
  79. // Set the new corner or position depending on the anchor
  80. switch (anchor) {
  81. case TransformCorner.TopLeft: {
  82. shape.radius = Math.min(bounds.width, bounds.height) / 2
  83. shape.point = [
  84. bounds.maxX - shape.radius * 2,
  85. bounds.maxY - shape.radius * 2,
  86. ]
  87. break
  88. }
  89. case TransformCorner.TopRight: {
  90. shape.radius = Math.min(bounds.width, bounds.height) / 2
  91. shape.point = [bounds.minX, bounds.maxY - shape.radius * 2]
  92. break
  93. }
  94. case TransformCorner.BottomRight: {
  95. shape.radius = Math.min(bounds.width, bounds.height) / 2
  96. shape.point = [bounds.minX, bounds.minY]
  97. break
  98. }
  99. case TransformCorner.BottomLeft: {
  100. shape.radius = Math.min(bounds.width, bounds.height) / 2
  101. shape.point = [bounds.maxX - shape.radius * 2, bounds.minY]
  102. break
  103. }
  104. case TransformEdge.Top: {
  105. shape.radius = bounds.height / 2
  106. shape.point = [
  107. bounds.minX + (bounds.width / 2 - shape.radius),
  108. bounds.minY,
  109. ]
  110. break
  111. }
  112. case TransformEdge.Right: {
  113. shape.radius = bounds.width / 2
  114. shape.point = [
  115. bounds.maxX - shape.radius * 2,
  116. bounds.minY + (bounds.height / 2 - shape.radius),
  117. ]
  118. break
  119. }
  120. case TransformEdge.Bottom: {
  121. shape.radius = bounds.height / 2
  122. shape.point = [
  123. bounds.minX + (bounds.width / 2 - shape.radius),
  124. bounds.maxY - shape.radius * 2,
  125. ]
  126. break
  127. }
  128. case TransformEdge.Left: {
  129. shape.radius = bounds.width / 2
  130. shape.point = [
  131. bounds.minX,
  132. bounds.minY + (bounds.height / 2 - shape.radius),
  133. ]
  134. break
  135. }
  136. }
  137. return shape
  138. },
  139. canTransform: true,
  140. })
  141. export default circle