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.

ellipse.tsx 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { v4 as uuid } from 'uuid'
  2. import * as vec from 'utils/vec'
  3. import { EllipseShape, ShapeType } from 'types'
  4. import { registerShapeUtils } from './index'
  5. import { boundsContained, getRotatedEllipseBounds } from 'utils/bounds'
  6. import { intersectEllipseBounds } from 'utils/intersections'
  7. import { pointInEllipse } from 'utils/hitTests'
  8. import {
  9. getBoundsFromPoints,
  10. getRotatedCorners,
  11. rotateBounds,
  12. translateBounds,
  13. } from 'utils/utils'
  14. import { defaultStyle, getShapeStyle } from 'lib/shape-styles'
  15. const ellipse = registerShapeUtils<EllipseShape>({
  16. boundsCache: new WeakMap([]),
  17. create(props) {
  18. return {
  19. id: uuid(),
  20. type: ShapeType.Ellipse,
  21. isGenerated: false,
  22. name: 'Ellipse',
  23. parentId: 'page0',
  24. childIndex: 0,
  25. point: [0, 0],
  26. radiusX: 1,
  27. radiusY: 1,
  28. rotation: 0,
  29. isAspectRatioLocked: false,
  30. isLocked: false,
  31. isHidden: false,
  32. style: defaultStyle,
  33. ...props,
  34. }
  35. },
  36. render({ id, radiusX, radiusY, style }) {
  37. const styles = getShapeStyle(style)
  38. return (
  39. <ellipse
  40. id={id}
  41. cx={radiusX}
  42. cy={radiusY}
  43. rx={Math.max(0, radiusX - Number(styles.strokeWidth) / 2)}
  44. ry={Math.max(0, radiusY - Number(styles.strokeWidth) / 2)}
  45. />
  46. )
  47. },
  48. applyStyles(shape, style) {
  49. Object.assign(shape.style, style)
  50. return this
  51. },
  52. getBounds(shape) {
  53. if (!this.boundsCache.has(shape)) {
  54. const { radiusX, radiusY } = shape
  55. const bounds = {
  56. minX: 0,
  57. minY: 0,
  58. maxX: radiusX * 2,
  59. maxY: radiusY * 2,
  60. width: radiusX * 2,
  61. height: radiusY * 2,
  62. }
  63. this.boundsCache.set(shape, bounds)
  64. }
  65. return translateBounds(this.boundsCache.get(shape), shape.point)
  66. },
  67. getRotatedBounds(shape) {
  68. return getRotatedEllipseBounds(
  69. shape.point[0],
  70. shape.point[1],
  71. shape.radiusX,
  72. shape.radiusY,
  73. shape.rotation
  74. )
  75. },
  76. getCenter(shape) {
  77. return [shape.point[0] + shape.radiusX, shape.point[1] + shape.radiusY]
  78. },
  79. hitTest(shape, point) {
  80. return pointInEllipse(
  81. point,
  82. vec.add(shape.point, [shape.radiusX, shape.radiusY]),
  83. shape.radiusX,
  84. shape.radiusY,
  85. shape.rotation
  86. )
  87. },
  88. hitTestBounds(this, shape, brushBounds) {
  89. const shapeBounds = this.getBounds(shape)
  90. return (
  91. boundsContained(shapeBounds, brushBounds) ||
  92. intersectEllipseBounds(
  93. vec.add(shape.point, [shape.radiusX, shape.radiusY]),
  94. shape.radiusX,
  95. shape.radiusY,
  96. brushBounds,
  97. shape.rotation
  98. ).length > 0
  99. )
  100. },
  101. rotateTo(shape, rotation) {
  102. shape.rotation = rotation
  103. return this
  104. },
  105. translateTo(shape, point) {
  106. shape.point = vec.toPrecision(point)
  107. return this
  108. },
  109. transform(shape, bounds, { scaleX, scaleY, initialShape }) {
  110. // TODO: Locked aspect ratio transform
  111. shape.point = [bounds.minX, bounds.minY]
  112. shape.radiusX = bounds.width / 2
  113. shape.radiusY = bounds.height / 2
  114. shape.rotation =
  115. (scaleX < 0 && scaleY >= 0) || (scaleY < 0 && scaleX >= 0)
  116. ? -initialShape.rotation
  117. : initialShape.rotation
  118. return this
  119. },
  120. transformSingle(shape, bounds, info) {
  121. return this.transform(shape, bounds, info)
  122. },
  123. setProperty(shape, prop, value) {
  124. shape[prop] = value
  125. return this
  126. },
  127. canTransform: true,
  128. canChangeAspectRatio: true,
  129. canStyleFill: true,
  130. })
  131. export default ellipse