@tldraw/editor 4.3.0-canary.e52fa5385f86 → 4.3.0-canary.ea88b223b83a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/dist-cjs/index.d.ts +428 -120
  2. package/dist-cjs/index.js +5 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/components/ErrorBoundary.js.map +1 -1
  5. package/dist-cjs/lib/components/GeometryDebuggingView.js +1 -17
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  7. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +3 -3
  8. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  9. package/dist-cjs/lib/constants.js +1 -3
  10. package/dist-cjs/lib/constants.js.map +2 -2
  11. package/dist-cjs/lib/editor/Editor.js +268 -274
  12. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  13. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +18 -17
  14. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
  15. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +12 -3
  16. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  17. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -1
  18. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  19. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +5 -6
  20. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  21. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +591 -0
  22. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +7 -0
  23. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +1 -1
  24. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  25. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -22
  26. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  27. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +31 -23
  28. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  29. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js +1 -1
  30. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js.map +2 -2
  31. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -3
  32. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  33. package/dist-cjs/lib/exports/parseCss.js +1 -1
  34. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  35. package/dist-cjs/lib/hooks/useEvent.js +1 -1
  36. package/dist-cjs/lib/hooks/useEvent.js.map +2 -2
  37. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  38. package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
  39. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  40. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
  41. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  42. package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
  43. package/dist-cjs/lib/hooks/useStateAttribute.js +4 -1
  44. package/dist-cjs/lib/hooks/useStateAttribute.js.map +2 -2
  45. package/dist-cjs/lib/hooks/useTransform.js.map +1 -1
  46. package/dist-cjs/lib/hooks/useZoomCss.js +4 -8
  47. package/dist-cjs/lib/hooks/useZoomCss.js.map +2 -2
  48. package/dist-cjs/lib/options.js +6 -1
  49. package/dist-cjs/lib/options.js.map +2 -2
  50. package/dist-cjs/lib/primitives/Box.js +3 -0
  51. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  52. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +1 -0
  53. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  54. package/dist-cjs/lib/utils/rotation.js +1 -1
  55. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  56. package/dist-cjs/version.js +3 -3
  57. package/dist-cjs/version.js.map +1 -1
  58. package/dist-esm/index.d.mts +428 -120
  59. package/dist-esm/index.mjs +5 -1
  60. package/dist-esm/index.mjs.map +2 -2
  61. package/dist-esm/lib/components/ErrorBoundary.mjs.map +1 -1
  62. package/dist-esm/lib/components/GeometryDebuggingView.mjs +1 -17
  63. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  64. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +3 -3
  65. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  66. package/dist-esm/lib/constants.mjs +1 -3
  67. package/dist-esm/lib/constants.mjs.map +2 -2
  68. package/dist-esm/lib/editor/Editor.mjs +269 -277
  69. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  70. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +18 -17
  71. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
  72. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +13 -4
  73. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  74. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -1
  75. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  76. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +5 -6
  77. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  78. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +573 -0
  79. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +7 -0
  80. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +1 -1
  81. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  82. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -22
  83. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  84. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +31 -23
  85. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  86. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs +1 -1
  87. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs.map +2 -2
  88. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -3
  89. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  90. package/dist-esm/lib/exports/parseCss.mjs +1 -1
  91. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  92. package/dist-esm/lib/hooks/useEvent.mjs +1 -1
  93. package/dist-esm/lib/hooks/useEvent.mjs.map +2 -2
  94. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  95. package/dist-esm/lib/hooks/useGestureEvents.mjs +1 -1
  96. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  97. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  98. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  99. package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
  100. package/dist-esm/lib/hooks/useStateAttribute.mjs +4 -1
  101. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +2 -2
  102. package/dist-esm/lib/hooks/useTransform.mjs.map +1 -1
  103. package/dist-esm/lib/hooks/useZoomCss.mjs +4 -8
  104. package/dist-esm/lib/hooks/useZoomCss.mjs.map +2 -2
  105. package/dist-esm/lib/options.mjs +6 -1
  106. package/dist-esm/lib/options.mjs.map +2 -2
  107. package/dist-esm/lib/primitives/Box.mjs +3 -0
  108. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  109. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +1 -0
  110. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  111. package/dist-esm/lib/utils/rotation.mjs +1 -1
  112. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  113. package/dist-esm/version.mjs +3 -3
  114. package/dist-esm/version.mjs.map +1 -1
  115. package/editor.css +14 -12
  116. package/package.json +18 -16
  117. package/src/index.ts +3 -0
  118. package/src/lib/components/ErrorBoundary.tsx +1 -1
  119. package/src/lib/components/GeometryDebuggingView.tsx +1 -19
  120. package/src/lib/components/default-components/DefaultCanvas.tsx +3 -3
  121. package/src/lib/config/TLUserPreferences.test.ts +40 -0
  122. package/src/lib/constants.ts +0 -2
  123. package/src/lib/editor/Editor.test.ts +140 -0
  124. package/src/lib/editor/Editor.ts +349 -320
  125. package/src/lib/editor/derivations/notVisibleShapes.ts +37 -23
  126. package/src/lib/editor/derivations/parentsToChildren.ts +18 -7
  127. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +17 -31
  128. package/src/lib/editor/managers/ClickManager/ClickManager.ts +1 -1
  129. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +129 -79
  130. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +10 -6
  131. package/src/lib/editor/managers/InputsManager/InputsManager.ts +566 -0
  132. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +0 -4
  133. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +12 -0
  134. package/src/lib/editor/managers/SnapManager/SnapManager.ts +1 -1
  135. package/src/lib/editor/managers/TickManager/TickManager.test.ts +40 -107
  136. package/src/lib/editor/managers/TickManager/TickManager.ts +2 -32
  137. package/src/lib/editor/shapes/ShapeUtil.ts +67 -24
  138. package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
  139. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +3 -3
  140. package/src/lib/exports/parseCss.test.ts +1 -0
  141. package/src/lib/exports/parseCss.ts +1 -1
  142. package/src/lib/hooks/useEvent.tsx +1 -1
  143. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
  144. package/src/lib/hooks/useGestureEvents.ts +2 -2
  145. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +1 -1
  146. package/src/lib/hooks/usePassThroughWheelEvents.ts +1 -1
  147. package/src/lib/hooks/useScreenBounds.ts +1 -1
  148. package/src/lib/hooks/useStateAttribute.ts +4 -1
  149. package/src/lib/hooks/useTransform.ts +1 -1
  150. package/src/lib/hooks/useZoomCss.ts +3 -8
  151. package/src/lib/options.ts +32 -0
  152. package/src/lib/primitives/Box.ts +9 -0
  153. package/src/lib/primitives/geometry/Geometry2d.ts +1 -0
  154. package/src/lib/utils/rotation.ts +1 -1
  155. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/primitives/geometry/Geometry2d.ts"],
4
- "sourcesContent": ["import { assert, invLerp } from '@tldraw/utils'\nimport { Box } from '../Box'\nimport { Mat, MatModel } from '../Mat'\nimport { Vec, VecLike } from '../Vec'\nimport {\n\tintersectCirclePolygon,\n\tintersectCirclePolyline,\n\tintersectLineSegmentPolygon,\n\tintersectLineSegmentPolyline,\n\tintersectPolys,\n\tlinesIntersect,\n\tpolygonIntersectsPolyline,\n\tpolygonsIntersect,\n} from '../intersect'\nimport { approximately, pointInPolygon } from '../utils'\n\n/**\n * Filter geometry within a group.\n *\n * Filters are ignored when called directly on primitive geometries, but can be used to narrow down\n * the results of an operation on `Group2d` geometries.\n *\n * @public\n */\nexport interface Geometry2dFilters {\n\treadonly includeLabels?: boolean\n\treadonly includeInternal?: boolean\n}\n\n/** @public */\nexport const Geometry2dFilters: {\n\tEXCLUDE_NON_STANDARD: Geometry2dFilters\n\tINCLUDE_ALL: Geometry2dFilters\n\tEXCLUDE_LABELS: Geometry2dFilters\n\tEXCLUDE_INTERNAL: Geometry2dFilters\n} = {\n\tEXCLUDE_NON_STANDARD: {\n\t\tincludeLabels: false,\n\t\tincludeInternal: false,\n\t},\n\tINCLUDE_ALL: { includeLabels: true, includeInternal: true },\n\tEXCLUDE_LABELS: { includeLabels: false, includeInternal: true },\n\tEXCLUDE_INTERNAL: { includeLabels: true, includeInternal: false },\n}\n\n/** @public */\nexport interface TransformedGeometry2dOptions {\n\tisLabel?: boolean\n\tisEmptyLabel?: boolean\n\tisInternal?: boolean\n\tdebugColor?: string\n\tignore?: boolean\n\texcludeFromShapeBounds?: boolean\n}\n\n/** @public */\nexport interface Geometry2dOptions extends TransformedGeometry2dOptions {\n\tisFilled: boolean\n\tisClosed: boolean\n}\n\n/** @public */\nexport abstract class Geometry2d {\n\t// todo: consider making accessors for these too, so that they can be overridden in subclasses by geometries with more complex logic\n\tisFilled = false\n\tisClosed = true\n\tisLabel = false\n\tisEmptyLabel = false\n\tisInternal = false\n\texcludeFromShapeBounds = false\n\tdebugColor?: string\n\tignore?: boolean\n\n\tconstructor(opts: Geometry2dOptions) {\n\t\tconst {\n\t\t\tisLabel = false,\n\t\t\tisEmptyLabel = false,\n\t\t\tisInternal = false,\n\t\t\texcludeFromShapeBounds = false,\n\t\t} = opts\n\t\tthis.isFilled = opts.isFilled\n\t\tthis.isClosed = opts.isClosed\n\t\tthis.debugColor = opts.debugColor\n\t\tthis.ignore = opts.ignore\n\t\tthis.isLabel = isLabel\n\t\tthis.isEmptyLabel = isEmptyLabel\n\t\tthis.isInternal = isInternal\n\t\tthis.excludeFromShapeBounds = excludeFromShapeBounds\n\t}\n\n\tisExcludedByFilter(filters?: Geometry2dFilters) {\n\t\tif (!filters) return false\n\t\tif (this.isLabel && !filters.includeLabels) return true\n\t\tif (this.isInternal && !filters.includeInternal) return true\n\t\treturn false\n\t}\n\n\tabstract getVertices(filters: Geometry2dFilters): Vec[]\n\n\tabstract nearestPoint(point: VecLike, _filters?: Geometry2dFilters): Vec\n\n\thitTestPoint(point: VecLike, margin = 0, hitInside = false, _filters?: Geometry2dFilters) {\n\t\t// First check whether the point is inside\n\t\tif (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {\n\t\t\treturn true\n\t\t}\n\t\t// Then check whether the distance is within the margin\n\t\treturn Vec.Dist2(point, this.nearestPoint(point)) <= margin * margin\n\t}\n\n\tdistanceToPoint(point: VecLike, hitInside = false, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tVec.Dist(point, this.nearestPoint(point, filters)) *\n\t\t\t(this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)\n\t\t\t\t? -1\n\t\t\t\t: 1)\n\t\t)\n\t}\n\n\tdistanceToLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\tif (Vec.Equals(A, B)) return this.distanceToPoint(A, false, filters)\n\t\tconst { vertices } = this\n\t\tif (vertices.length === 0) throw Error('nearest point not found')\n\t\tif (vertices.length === 1) return Vec.Dist(A, vertices[0])\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet d: number, p: Vec, q: Vec\n\t\tconst nextLimit = this.isClosed ? vertices.length : vertices.length - 1\n\t\tfor (let i = 0; i < vertices.length; i++) {\n\t\t\tp = vertices[i]\n\t\t\tif (i < nextLimit) {\n\t\t\t\tconst next = vertices[(i + 1) % vertices.length]\n\t\t\t\tif (linesIntersect(A, B, p, next)) return 0\n\t\t\t}\n\t\t\tq = Vec.NearestPointOnLineSegment(A, B, p, true)\n\t\t\td = Vec.Dist2(p, q)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = q\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn this.isClosed && this.isFilled && pointInPolygon(nearest, this.vertices) ? -dist : dist\n\t}\n\n\thitTestLineSegment(A: VecLike, B: VecLike, distance = 0, filters?: Geometry2dFilters): boolean {\n\t\treturn this.distanceToLineSegment(A, B, filters) <= distance\n\t}\n\n\tintersectLineSegment(A: VecLike, B: VecLike, _filters?: Geometry2dFilters): VecLike[] {\n\t\tconst intersections = this.isClosed\n\t\t\t? intersectLineSegmentPolygon(A, B, this.vertices)\n\t\t\t: intersectLineSegmentPolyline(A, B, this.vertices)\n\n\t\treturn intersections ?? []\n\t}\n\n\tintersectCircle(center: VecLike, radius: number, _filters?: Geometry2dFilters): VecLike[] {\n\t\tconst intersections = this.isClosed\n\t\t\t? intersectCirclePolygon(center, radius, this.vertices)\n\t\t\t: intersectCirclePolyline(center, radius, this.vertices)\n\n\t\treturn intersections ?? []\n\t}\n\n\tintersectPolygon(polygon: VecLike[], _filters?: Geometry2dFilters): VecLike[] {\n\t\treturn intersectPolys(polygon, this.vertices, true, this.isClosed)\n\t}\n\n\tintersectPolyline(polyline: VecLike[], _filters?: Geometry2dFilters): VecLike[] {\n\t\treturn intersectPolys(polyline, this.vertices, false, this.isClosed)\n\t}\n\n\t/**\n\t * Find a point along the edge of the geometry that is a fraction `t` along the entire way round.\n\t */\n\tinterpolateAlongEdge(t: number, _filters?: Geometry2dFilters): Vec {\n\t\tconst { vertices } = this\n\n\t\tif (vertices.length === 0) return new Vec(0, 0)\n\t\tif (vertices.length === 1) return vertices[0]\n\t\tif (t <= 0) return vertices[0]\n\n\t\tconst distanceToTravel = t * this.length\n\t\tlet distanceTraveled = 0\n\n\t\tfor (let i = 0; i < (this.isClosed ? vertices.length : vertices.length - 1); i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % vertices.length]\n\t\t\tconst dist = Vec.Dist(curr, next)\n\t\t\tconst newDistanceTraveled = distanceTraveled + dist\n\t\t\tif (newDistanceTraveled >= distanceToTravel) {\n\t\t\t\tconst p = Vec.Lrp(\n\t\t\t\t\tcurr,\n\t\t\t\t\tnext,\n\t\t\t\t\tinvLerp(distanceTraveled, newDistanceTraveled, distanceToTravel)\n\t\t\t\t)\n\t\t\t\treturn p\n\t\t\t}\n\t\t\tdistanceTraveled = newDistanceTraveled\n\t\t}\n\n\t\treturn this.isClosed ? vertices[0] : vertices[vertices.length - 1]\n\t}\n\n\t/**\n\t * Take `point`, find the closest point to it on the edge of the geometry, and return how far\n\t * along the edge it is as a fraction of the total length.\n\t */\n\tuninterpolateAlongEdge(point: VecLike, _filters?: Geometry2dFilters): number {\n\t\tconst { vertices, length } = this\n\t\tlet closestSegment = null\n\t\tlet closestDistance = Infinity\n\t\tlet distanceTraveled = 0\n\n\t\tif (vertices.length === 0 || vertices.length === 1) return 0\n\n\t\tfor (let i = 0; i < (this.isClosed ? vertices.length : vertices.length - 1); i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % vertices.length]\n\n\t\t\tconst nearestPoint = Vec.NearestPointOnLineSegment(curr, next, point, true)\n\t\t\tconst distance = Vec.Dist(nearestPoint, point)\n\n\t\t\tif (distance < closestDistance) {\n\t\t\t\tclosestDistance = distance\n\t\t\t\tclosestSegment = {\n\t\t\t\t\tstart: curr,\n\t\t\t\t\tend: next,\n\t\t\t\t\tnearestPoint,\n\t\t\t\t\tdistanceToStart: distanceTraveled,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdistanceTraveled += Vec.Dist(curr, next)\n\t\t}\n\n\t\tassert(closestSegment)\n\n\t\tconst distanceAlongRoute =\n\t\t\tclosestSegment.distanceToStart + Vec.Dist(closestSegment.start, closestSegment.nearestPoint)\n\n\t\treturn distanceAlongRoute / length\n\t}\n\n\tisPointInBounds(point: VecLike, margin = 0) {\n\t\tconst { bounds } = this\n\t\treturn !(\n\t\t\tpoint.x < bounds.minX - margin ||\n\t\t\tpoint.y < bounds.minY - margin ||\n\t\t\tpoint.x > bounds.maxX + margin ||\n\t\t\tpoint.y > bounds.maxY + margin\n\t\t)\n\t}\n\n\toverlapsPolygon(_polygon: VecLike[]): boolean {\n\t\tconst polygon = _polygon.map((v) => Vec.From(v))\n\n\t\t// Otherwise, check if the geometry itself overlaps the polygon\n\t\tconst { vertices, center, isFilled, isEmptyLabel, isClosed } = this\n\n\t\t// We'll do things in order of cheapest to most expensive checks\n\n\t\t// Skip empty labels\n\t\tif (isEmptyLabel) return false\n\n\t\t// If any of the geometry's vertices are inside the polygon, it's inside\n\t\tif (vertices.some((v) => pointInPolygon(v, polygon))) {\n\t\t\treturn true\n\t\t}\n\n\t\t// If the geometry is filled and closed and its center is inside the polygon, it's inside\n\t\tif (isClosed) {\n\t\t\tif (isFilled) {\n\t\t\t\t// If closed and filled, check if the center is inside the polygon\n\t\t\t\tif (pointInPolygon(center, polygon)) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\t// ..then, slightly more expensive check, see the geometry covers the entire polygon but not its center\n\t\t\t\tif (polygon.every((v) => pointInPolygon(v, vertices))) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If any the geometry's vertices intersect the edge of the polygon, it's inside.\n\t\t\t// for example when a rotated rectangle is moved over the corner of a parent rectangle\n\t\t\t// If the geometry is closed, intersect as a polygon\n\t\t\tif (polygonsIntersect(polygon, vertices)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} else {\n\t\t\t// If the geometry is not closed, intersect as a polyline\n\t\t\tif (polygonIntersectsPolyline(polygon, vertices)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\t// If none of the above checks passed, the geometry is outside the polygon\n\t\treturn false\n\t}\n\n\ttransform(transform: MatModel, opts?: TransformedGeometry2dOptions): Geometry2d {\n\t\treturn new TransformedGeometry2d(this, transform, opts)\n\t}\n\n\tprivate _vertices: Vec[] | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget vertices(): Vec[] {\n\t\tif (!this._vertices) {\n\t\t\tthis._vertices = this.getVertices(Geometry2dFilters.EXCLUDE_LABELS)\n\t\t}\n\n\t\treturn this._vertices\n\t}\n\n\tgetBoundsVertices(): Vec[] {\n\t\tif (this.excludeFromShapeBounds) return []\n\t\treturn this.vertices\n\t}\n\n\tprivate _boundsVertices: Vec[] | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget boundsVertices(): Vec[] {\n\t\tif (!this._boundsVertices) {\n\t\t\tthis._boundsVertices = this.getBoundsVertices()\n\t\t}\n\t\treturn this._boundsVertices\n\t}\n\n\tgetBounds() {\n\t\treturn Box.FromPoints(this.boundsVertices)\n\t}\n\n\tprivate _bounds: Box | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget bounds(): Box {\n\t\tif (!this._bounds) {\n\t\t\tthis._bounds = this.getBounds()\n\t\t}\n\t\treturn this._bounds\n\t}\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget center() {\n\t\treturn this.bounds.center\n\t}\n\n\tprivate _area: number | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget area() {\n\t\tif (!this._area) {\n\t\t\tthis._area = this.getArea()\n\t\t}\n\t\treturn this._area\n\t}\n\n\tgetArea() {\n\t\tif (!this.isClosed) {\n\t\t\treturn 0\n\t\t}\n\t\tconst { vertices } = this\n\t\tlet area = 0\n\t\tfor (let i = 0, n = vertices.length; i < n; i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % n]\n\t\t\tarea += curr.x * next.y - next.x * curr.y\n\t\t}\n\t\treturn area / 2\n\t}\n\n\ttoSimpleSvgPath() {\n\t\tlet path = ''\n\n\t\tconst { vertices } = this\n\t\tconst n = vertices.length\n\n\t\tif (n === 0) return path\n\n\t\tpath += `M${vertices[0].x},${vertices[0].y}`\n\n\t\tfor (let i = 1; i < n; i++) {\n\t\t\tpath += `L${vertices[i].x},${vertices[i].y}`\n\t\t}\n\n\t\tif (this.isClosed) {\n\t\t\tpath += 'Z'\n\t\t}\n\n\t\treturn path\n\t}\n\n\tprivate _length?: number\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget length() {\n\t\tif (this._length) return this._length\n\t\tthis._length = this.getLength(Geometry2dFilters.EXCLUDE_LABELS)\n\t\treturn this._length\n\t}\n\n\tgetLength(_filters?: Geometry2dFilters) {\n\t\tconst vertices = this.getVertices(_filters ?? Geometry2dFilters.EXCLUDE_LABELS)\n\t\tif (vertices.length === 0) return 0\n\t\tlet prev = vertices[0]\n\t\tlet length = 0\n\t\tfor (let i = 1; i < vertices.length; i++) {\n\t\t\tconst next = vertices[i]\n\t\t\tlength += Vec.Dist(prev, next)\n\t\t\tprev = next\n\t\t}\n\t\tif (this.isClosed) {\n\t\t\tlength += Vec.Dist(vertices[vertices.length - 1], vertices[0])\n\t\t}\n\t\treturn length\n\t}\n\n\tabstract getSvgPathData(first: boolean): string\n}\n\n// =================================================================================================\n// Because Geometry2d.transform depends on TransformedGeometry2d, we need to define it here instead\n// of in its own files. This prevents a circular import error.\n// =================================================================================================\n\n/** @public */\nexport class TransformedGeometry2d extends Geometry2d {\n\tprivate readonly inverse: MatModel\n\tprivate readonly decomposed\n\n\tconstructor(\n\t\tprivate readonly geometry: Geometry2d,\n\t\tprivate readonly matrix: MatModel,\n\t\topts?: TransformedGeometry2dOptions\n\t) {\n\t\tsuper(geometry)\n\t\tthis.inverse = Mat.Inverse(matrix)\n\t\tthis.decomposed = Mat.Decompose(matrix)\n\n\t\tif (opts) {\n\t\t\tif (opts.isLabel != null) this.isLabel = opts.isLabel\n\t\t\tif (opts.isInternal != null) this.isInternal = opts.isInternal\n\t\t\tif (opts.debugColor != null) this.debugColor = opts.debugColor\n\t\t\tif (opts.ignore != null) this.ignore = opts.ignore\n\t\t}\n\n\t\tassert(\n\t\t\tapproximately(this.decomposed.scaleX, this.decomposed.scaleY),\n\t\t\t'non-uniform scaling is not yet supported'\n\t\t)\n\t}\n\n\tgetVertices(filters: Geometry2dFilters): Vec[] {\n\t\treturn this.geometry.getVertices(filters).map((v) => Mat.applyToPoint(this.matrix, v))\n\t}\n\n\tgetBoundsVertices(): Vec[] {\n\t\treturn this.geometry.getBoundsVertices().map((v) => Mat.applyToPoint(this.matrix, v))\n\t}\n\n\tnearestPoint(point: VecLike, filters?: Geometry2dFilters): Vec {\n\t\treturn Mat.applyToPoint(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.nearestPoint(Mat.applyToPoint(this.inverse, point), filters)\n\t\t)\n\t}\n\n\toverride hitTestPoint(\n\t\tpoint: VecLike,\n\t\tmargin = 0,\n\t\thitInside?: boolean,\n\t\tfilters?: Geometry2dFilters\n\t): boolean {\n\t\treturn this.geometry.hitTestPoint(\n\t\t\tMat.applyToPoint(this.inverse, point),\n\t\t\tmargin / this.decomposed.scaleX,\n\t\t\thitInside,\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride distanceToPoint(point: VecLike, hitInside = false, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tthis.geometry.distanceToPoint(Mat.applyToPoint(this.inverse, point), hitInside, filters) *\n\t\t\tthis.decomposed.scaleX\n\t\t)\n\t}\n\n\toverride distanceToLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tthis.geometry.distanceToLineSegment(\n\t\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\t\tfilters\n\t\t\t) * this.decomposed.scaleX\n\t\t)\n\t}\n\n\toverride hitTestLineSegment(\n\t\tA: VecLike,\n\t\tB: VecLike,\n\t\tdistance = 0,\n\t\tfilters?: Geometry2dFilters\n\t): boolean {\n\t\treturn this.geometry.hitTestLineSegment(\n\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\tdistance / this.decomposed.scaleX,\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectLineSegment(\n\t\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\t\tfilters\n\t\t\t)\n\t\t)\n\t}\n\n\toverride intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectCircle(\n\t\t\t\tMat.applyToPoint(this.inverse, center),\n\t\t\t\tradius / this.decomposed.scaleX,\n\t\t\t\tfilters\n\t\t\t)\n\t\t)\n\t}\n\n\toverride intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectPolygon(Mat.applyToPoints(this.inverse, polygon), filters)\n\t\t)\n\t}\n\n\toverride intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectPolyline(Mat.applyToPoints(this.inverse, polyline), filters)\n\t\t)\n\t}\n\n\toverride transform(transform: MatModel, opts?: TransformedGeometry2dOptions): Geometry2d {\n\t\treturn new TransformedGeometry2d(this.geometry, Mat.Multiply(transform, this.matrix), {\n\t\t\tisLabel: opts?.isLabel ?? this.isLabel,\n\t\t\tisInternal: opts?.isInternal ?? this.isInternal,\n\t\t\tdebugColor: opts?.debugColor ?? this.debugColor,\n\t\t\tignore: opts?.ignore ?? this.ignore,\n\t\t})\n\t}\n\n\tgetSvgPathData(): string {\n\t\tthrow new Error('Cannot get SVG path data for transformed geometry.')\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { assert, invLerp } from '@tldraw/utils'\nimport { Box } from '../Box'\nimport { Mat, MatModel } from '../Mat'\nimport { Vec, VecLike } from '../Vec'\nimport {\n\tintersectCirclePolygon,\n\tintersectCirclePolyline,\n\tintersectLineSegmentPolygon,\n\tintersectLineSegmentPolyline,\n\tintersectPolys,\n\tlinesIntersect,\n\tpolygonIntersectsPolyline,\n\tpolygonsIntersect,\n} from '../intersect'\nimport { approximately, pointInPolygon } from '../utils'\n\n/**\n * Filter geometry within a group.\n *\n * Filters are ignored when called directly on primitive geometries, but can be used to narrow down\n * the results of an operation on `Group2d` geometries.\n *\n * @public\n */\nexport interface Geometry2dFilters {\n\treadonly includeLabels?: boolean\n\treadonly includeInternal?: boolean\n}\n\n/** @public */\nexport const Geometry2dFilters: {\n\tEXCLUDE_NON_STANDARD: Geometry2dFilters\n\tINCLUDE_ALL: Geometry2dFilters\n\tEXCLUDE_LABELS: Geometry2dFilters\n\tEXCLUDE_INTERNAL: Geometry2dFilters\n} = {\n\tEXCLUDE_NON_STANDARD: {\n\t\tincludeLabels: false,\n\t\tincludeInternal: false,\n\t},\n\tINCLUDE_ALL: { includeLabels: true, includeInternal: true },\n\tEXCLUDE_LABELS: { includeLabels: false, includeInternal: true },\n\tEXCLUDE_INTERNAL: { includeLabels: true, includeInternal: false },\n}\n\n/** @public */\nexport interface TransformedGeometry2dOptions {\n\tisLabel?: boolean\n\tisEmptyLabel?: boolean\n\tisInternal?: boolean\n\tdebugColor?: string\n\tignore?: boolean\n\texcludeFromShapeBounds?: boolean\n}\n\n/** @public */\nexport interface Geometry2dOptions extends TransformedGeometry2dOptions {\n\tisFilled: boolean\n\tisClosed: boolean\n}\n\n/** @public */\nexport abstract class Geometry2d {\n\t// todo: consider making accessors for these too, so that they can be overridden in subclasses by geometries with more complex logic\n\tisFilled = false\n\tisClosed = true\n\tisLabel = false\n\tisEmptyLabel = false\n\tisInternal = false\n\texcludeFromShapeBounds = false\n\tdebugColor?: string\n\tignore?: boolean\n\n\tconstructor(opts: Geometry2dOptions) {\n\t\tconst {\n\t\t\tisLabel = false,\n\t\t\tisEmptyLabel = false,\n\t\t\tisInternal = false,\n\t\t\texcludeFromShapeBounds = false,\n\t\t} = opts\n\t\tthis.isFilled = opts.isFilled\n\t\tthis.isClosed = opts.isClosed\n\t\tthis.debugColor = opts.debugColor\n\t\tthis.ignore = opts.ignore\n\t\tthis.isLabel = isLabel\n\t\tthis.isEmptyLabel = isEmptyLabel\n\t\tthis.isInternal = isInternal\n\t\tthis.excludeFromShapeBounds = excludeFromShapeBounds\n\t}\n\n\tisExcludedByFilter(filters?: Geometry2dFilters) {\n\t\tif (!filters) return false\n\t\tif (this.isLabel && !filters.includeLabels) return true\n\t\tif (this.isInternal && !filters.includeInternal) return true\n\t\treturn false\n\t}\n\n\tabstract getVertices(filters: Geometry2dFilters): Vec[]\n\n\tabstract nearestPoint(point: VecLike, _filters?: Geometry2dFilters): Vec\n\n\thitTestPoint(point: VecLike, margin = 0, hitInside = false, _filters?: Geometry2dFilters) {\n\t\t// First check whether the point is inside\n\t\tif (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {\n\t\t\treturn true\n\t\t}\n\t\t// Then check whether the distance is within the margin\n\t\treturn Vec.Dist2(point, this.nearestPoint(point)) <= margin * margin\n\t}\n\n\tdistanceToPoint(point: VecLike, hitInside = false, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tVec.Dist(point, this.nearestPoint(point, filters)) *\n\t\t\t(this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)\n\t\t\t\t? -1\n\t\t\t\t: 1)\n\t\t)\n\t}\n\n\tdistanceToLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\tif (Vec.Equals(A, B)) return this.distanceToPoint(A, false, filters)\n\t\tconst { vertices } = this\n\t\tif (vertices.length === 0) throw Error('nearest point not found')\n\t\tif (vertices.length === 1) return Vec.Dist(A, vertices[0])\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet d: number, p: Vec, q: Vec\n\t\tconst nextLimit = this.isClosed ? vertices.length : vertices.length - 1\n\t\tfor (let i = 0; i < vertices.length; i++) {\n\t\t\tp = vertices[i]\n\t\t\tif (i < nextLimit) {\n\t\t\t\tconst next = vertices[(i + 1) % vertices.length]\n\t\t\t\tif (linesIntersect(A, B, p, next)) return 0\n\t\t\t}\n\t\t\tq = Vec.NearestPointOnLineSegment(A, B, p, true)\n\t\t\td = Vec.Dist2(p, q)\n\t\t\tif (d < dist) {\n\t\t\t\tdist = d\n\t\t\t\tnearest = q\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\tdist = Math.sqrt(dist) // return the actual distance, not the squared distance\n\t\treturn this.isClosed && this.isFilled && pointInPolygon(nearest, this.vertices) ? -dist : dist\n\t}\n\n\thitTestLineSegment(A: VecLike, B: VecLike, distance = 0, filters?: Geometry2dFilters): boolean {\n\t\treturn this.distanceToLineSegment(A, B, filters) <= distance\n\t}\n\n\tintersectLineSegment(A: VecLike, B: VecLike, _filters?: Geometry2dFilters): VecLike[] {\n\t\tconst intersections = this.isClosed\n\t\t\t? intersectLineSegmentPolygon(A, B, this.vertices)\n\t\t\t: intersectLineSegmentPolyline(A, B, this.vertices)\n\n\t\treturn intersections ?? []\n\t}\n\n\tintersectCircle(center: VecLike, radius: number, _filters?: Geometry2dFilters): VecLike[] {\n\t\tconst intersections = this.isClosed\n\t\t\t? intersectCirclePolygon(center, radius, this.vertices)\n\t\t\t: intersectCirclePolyline(center, radius, this.vertices)\n\n\t\treturn intersections ?? []\n\t}\n\n\tintersectPolygon(polygon: VecLike[], _filters?: Geometry2dFilters): VecLike[] {\n\t\treturn intersectPolys(polygon, this.vertices, true, this.isClosed)\n\t}\n\n\tintersectPolyline(polyline: VecLike[], _filters?: Geometry2dFilters): VecLike[] {\n\t\treturn intersectPolys(polyline, this.vertices, false, this.isClosed)\n\t}\n\n\t/**\n\t * Find a point along the edge of the geometry that is a fraction `t` along the entire way round.\n\t */\n\tinterpolateAlongEdge(t: number, _filters?: Geometry2dFilters): Vec {\n\t\tconst { vertices } = this\n\n\t\tif (vertices.length === 0) return new Vec(0, 0)\n\t\tif (vertices.length === 1) return vertices[0]\n\t\tif (t <= 0) return vertices[0]\n\n\t\tconst distanceToTravel = t * this.length\n\t\tlet distanceTraveled = 0\n\n\t\tfor (let i = 0; i < (this.isClosed ? vertices.length : vertices.length - 1); i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % vertices.length]\n\t\t\tconst dist = Vec.Dist(curr, next)\n\t\t\tconst newDistanceTraveled = distanceTraveled + dist\n\t\t\tif (newDistanceTraveled >= distanceToTravel) {\n\t\t\t\tconst p = Vec.Lrp(\n\t\t\t\t\tcurr,\n\t\t\t\t\tnext,\n\t\t\t\t\tinvLerp(distanceTraveled, newDistanceTraveled, distanceToTravel)\n\t\t\t\t)\n\t\t\t\treturn p\n\t\t\t}\n\t\t\tdistanceTraveled = newDistanceTraveled\n\t\t}\n\n\t\treturn this.isClosed ? vertices[0] : vertices[vertices.length - 1]\n\t}\n\n\t/**\n\t * Take `point`, find the closest point to it on the edge of the geometry, and return how far\n\t * along the edge it is as a fraction of the total length.\n\t */\n\tuninterpolateAlongEdge(point: VecLike, _filters?: Geometry2dFilters): number {\n\t\tconst { vertices, length } = this\n\t\tlet closestSegment = null\n\t\tlet closestDistance = Infinity\n\t\tlet distanceTraveled = 0\n\n\t\tif (vertices.length === 0 || vertices.length === 1) return 0\n\n\t\tfor (let i = 0; i < (this.isClosed ? vertices.length : vertices.length - 1); i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % vertices.length]\n\n\t\t\tconst nearestPoint = Vec.NearestPointOnLineSegment(curr, next, point, true)\n\t\t\tconst distance = Vec.Dist(nearestPoint, point)\n\n\t\t\tif (distance < closestDistance) {\n\t\t\t\tclosestDistance = distance\n\t\t\t\tclosestSegment = {\n\t\t\t\t\tstart: curr,\n\t\t\t\t\tend: next,\n\t\t\t\t\tnearestPoint,\n\t\t\t\t\tdistanceToStart: distanceTraveled,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdistanceTraveled += Vec.Dist(curr, next)\n\t\t}\n\n\t\tassert(closestSegment)\n\n\t\tconst distanceAlongRoute =\n\t\t\tclosestSegment.distanceToStart + Vec.Dist(closestSegment.start, closestSegment.nearestPoint)\n\n\t\treturn distanceAlongRoute / length\n\t}\n\n\tisPointInBounds(point: VecLike, margin = 0) {\n\t\tconst { bounds } = this\n\t\treturn !(\n\t\t\tpoint.x < bounds.minX - margin ||\n\t\t\tpoint.y < bounds.minY - margin ||\n\t\t\tpoint.x > bounds.maxX + margin ||\n\t\t\tpoint.y > bounds.maxY + margin\n\t\t)\n\t}\n\n\toverlapsPolygon(_polygon: VecLike[]): boolean {\n\t\tconst polygon = _polygon.map((v) => Vec.From(v))\n\n\t\t// Otherwise, check if the geometry itself overlaps the polygon\n\t\tconst { vertices, center, isFilled, isEmptyLabel, isClosed } = this\n\n\t\t// We'll do things in order of cheapest to most expensive checks\n\n\t\t// Skip empty labels\n\t\tif (isEmptyLabel) return false\n\n\t\t// If any of the geometry's vertices are inside the polygon, it's inside\n\t\tif (vertices.some((v) => pointInPolygon(v, polygon))) {\n\t\t\treturn true\n\t\t}\n\n\t\t// If the geometry is filled and closed and its center is inside the polygon, it's inside\n\t\tif (isClosed) {\n\t\t\tif (isFilled) {\n\t\t\t\t// If closed and filled, check if the center is inside the polygon\n\t\t\t\tif (pointInPolygon(center, polygon)) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\n\t\t\t\t// ..then, slightly more expensive check, see the geometry covers the entire polygon but not its center\n\t\t\t\tif (polygon.every((v) => pointInPolygon(v, vertices))) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If any the geometry's vertices intersect the edge of the polygon, it's inside.\n\t\t\t// for example when a rotated rectangle is moved over the corner of a parent rectangle\n\t\t\t// If the geometry is closed, intersect as a polygon\n\t\t\tif (polygonsIntersect(polygon, vertices)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} else {\n\t\t\t// If the geometry is not closed, intersect as a polyline\n\t\t\tif (polygonIntersectsPolyline(polygon, vertices)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\t// If none of the above checks passed, the geometry is outside the polygon\n\t\treturn false\n\t}\n\n\ttransform(transform: MatModel, opts?: TransformedGeometry2dOptions): Geometry2d {\n\t\treturn new TransformedGeometry2d(this, transform, opts)\n\t}\n\n\tprivate _vertices: Vec[] | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget vertices(): Vec[] {\n\t\tif (!this._vertices) {\n\t\t\tthis._vertices = this.getVertices(Geometry2dFilters.EXCLUDE_LABELS)\n\t\t}\n\n\t\treturn this._vertices\n\t}\n\n\tgetBoundsVertices(): Vec[] {\n\t\tif (this.excludeFromShapeBounds) return []\n\t\treturn this.vertices\n\t}\n\n\tprivate _boundsVertices: Vec[] | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget boundsVertices(): Vec[] {\n\t\tif (!this._boundsVertices) {\n\t\t\tthis._boundsVertices = this.getBoundsVertices()\n\t\t}\n\t\treturn this._boundsVertices\n\t}\n\n\tgetBounds() {\n\t\treturn Box.FromPoints(this.boundsVertices)\n\t}\n\n\tprivate _bounds: Box | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget bounds(): Box {\n\t\tif (!this._bounds) {\n\t\t\tthis._bounds = this.getBounds()\n\t\t}\n\t\treturn this._bounds\n\t}\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget center() {\n\t\treturn this.bounds.center\n\t}\n\n\tprivate _area: number | undefined\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget area() {\n\t\tif (!this._area) {\n\t\t\tthis._area = this.getArea()\n\t\t}\n\t\treturn this._area\n\t}\n\n\tgetArea() {\n\t\tif (!this.isClosed) {\n\t\t\treturn 0\n\t\t}\n\t\tconst { vertices } = this\n\t\tlet area = 0\n\t\tfor (let i = 0, n = vertices.length; i < n; i++) {\n\t\t\tconst curr = vertices[i]\n\t\t\tconst next = vertices[(i + 1) % n]\n\t\t\tarea += curr.x * next.y - next.x * curr.y\n\t\t}\n\t\treturn area / 2\n\t}\n\n\ttoSimpleSvgPath() {\n\t\tlet path = ''\n\n\t\tconst { vertices } = this\n\t\tconst n = vertices.length\n\n\t\tif (n === 0) return path\n\n\t\tpath += `M${vertices[0].x},${vertices[0].y}`\n\n\t\tfor (let i = 1; i < n; i++) {\n\t\t\tpath += `L${vertices[i].x},${vertices[i].y}`\n\t\t}\n\n\t\tif (this.isClosed) {\n\t\t\tpath += 'Z'\n\t\t}\n\n\t\treturn path\n\t}\n\n\tprivate _length?: number\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget length() {\n\t\tif (this._length) return this._length\n\t\tthis._length = this.getLength(Geometry2dFilters.EXCLUDE_LABELS)\n\t\treturn this._length\n\t}\n\n\tgetLength(_filters?: Geometry2dFilters) {\n\t\tconst vertices = this.getVertices(_filters ?? Geometry2dFilters.EXCLUDE_LABELS)\n\t\tif (vertices.length === 0) return 0\n\t\tlet prev = vertices[0]\n\t\tlet length = 0\n\t\tfor (let i = 1; i < vertices.length; i++) {\n\t\t\tconst next = vertices[i]\n\t\t\tlength += Vec.Dist(prev, next)\n\t\t\tprev = next\n\t\t}\n\t\tif (this.isClosed) {\n\t\t\tlength += Vec.Dist(vertices[vertices.length - 1], vertices[0])\n\t\t}\n\t\treturn length\n\t}\n\n\tabstract getSvgPathData(first: boolean): string\n}\n\n// =================================================================================================\n// Because Geometry2d.transform depends on TransformedGeometry2d, we need to define it here instead\n// of in its own files. This prevents a circular import error.\n// =================================================================================================\n\n/** @public */\nexport class TransformedGeometry2d extends Geometry2d {\n\tprivate readonly inverse: MatModel\n\tprivate readonly decomposed\n\n\tconstructor(\n\t\tprivate readonly geometry: Geometry2d,\n\t\tprivate readonly matrix: MatModel,\n\t\topts?: TransformedGeometry2dOptions\n\t) {\n\t\tsuper(geometry)\n\t\tthis.inverse = Mat.Inverse(matrix)\n\t\tthis.decomposed = Mat.Decompose(matrix)\n\n\t\tif (opts) {\n\t\t\tif (opts.isLabel != null) this.isLabel = opts.isLabel\n\t\t\tif (opts.isInternal != null) this.isInternal = opts.isInternal\n\t\t\tif (opts.debugColor != null) this.debugColor = opts.debugColor\n\t\t\tif (opts.ignore != null) this.ignore = opts.ignore\n\t\t}\n\n\t\tassert(\n\t\t\tapproximately(this.decomposed.scaleX, this.decomposed.scaleY),\n\t\t\t'non-uniform scaling is not yet supported'\n\t\t)\n\t}\n\n\tgetVertices(filters: Geometry2dFilters): Vec[] {\n\t\treturn this.geometry.getVertices(filters).map((v) => Mat.applyToPoint(this.matrix, v))\n\t}\n\n\tgetBoundsVertices(): Vec[] {\n\t\treturn this.geometry.getBoundsVertices().map((v) => Mat.applyToPoint(this.matrix, v))\n\t}\n\n\tnearestPoint(point: VecLike, filters?: Geometry2dFilters): Vec {\n\t\treturn Mat.applyToPoint(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.nearestPoint(Mat.applyToPoint(this.inverse, point), filters)\n\t\t)\n\t}\n\n\toverride hitTestPoint(\n\t\tpoint: VecLike,\n\t\tmargin = 0,\n\t\thitInside?: boolean,\n\t\tfilters?: Geometry2dFilters\n\t): boolean {\n\t\treturn this.geometry.hitTestPoint(\n\t\t\tMat.applyToPoint(this.inverse, point),\n\t\t\tmargin / this.decomposed.scaleX,\n\t\t\thitInside,\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride distanceToPoint(point: VecLike, hitInside = false, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tthis.geometry.distanceToPoint(Mat.applyToPoint(this.inverse, point), hitInside, filters) *\n\t\t\tthis.decomposed.scaleX\n\t\t)\n\t}\n\n\toverride distanceToLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\treturn (\n\t\t\tthis.geometry.distanceToLineSegment(\n\t\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\t\tfilters\n\t\t\t) * this.decomposed.scaleX\n\t\t)\n\t}\n\n\toverride hitTestLineSegment(\n\t\tA: VecLike,\n\t\tB: VecLike,\n\t\tdistance = 0,\n\t\tfilters?: Geometry2dFilters\n\t): boolean {\n\t\treturn this.geometry.hitTestLineSegment(\n\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\tdistance / this.decomposed.scaleX,\n\t\t\tfilters\n\t\t)\n\t}\n\n\toverride intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectLineSegment(\n\t\t\t\tMat.applyToPoint(this.inverse, A),\n\t\t\t\tMat.applyToPoint(this.inverse, B),\n\t\t\t\tfilters\n\t\t\t)\n\t\t)\n\t}\n\n\toverride intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectCircle(\n\t\t\t\tMat.applyToPoint(this.inverse, center),\n\t\t\t\tradius / this.decomposed.scaleX,\n\t\t\t\tfilters\n\t\t\t)\n\t\t)\n\t}\n\n\toverride intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectPolygon(Mat.applyToPoints(this.inverse, polygon), filters)\n\t\t)\n\t}\n\n\toverride intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters): VecLike[] {\n\t\treturn Mat.applyToPoints(\n\t\t\tthis.matrix,\n\t\t\tthis.geometry.intersectPolyline(Mat.applyToPoints(this.inverse, polyline), filters)\n\t\t)\n\t}\n\n\toverride transform(transform: MatModel, opts?: TransformedGeometry2dOptions): Geometry2d {\n\t\treturn new TransformedGeometry2d(this.geometry, Mat.Multiply(transform, this.matrix), {\n\t\t\tisLabel: opts?.isLabel ?? this.isLabel,\n\t\t\tisInternal: opts?.isInternal ?? this.isInternal,\n\t\t\tdebugColor: opts?.debugColor ?? this.debugColor,\n\t\t\tignore: opts?.ignore ?? this.ignore,\n\t\t})\n\t}\n\n\tgetSvgPathData(): string {\n\t\tthrow new Error('Cannot get SVG path data for transformed geometry.')\n\t}\n}\n"],
5
+ "mappings": "AAAA,SAAS,QAAQ,eAAe;AAChC,SAAS,WAAW;AACpB,SAAS,WAAqB;AAC9B,SAAS,WAAoB;AAC7B;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,eAAe,sBAAsB;AAgBvC,MAAM,oBAKT;AAAA,EACH,sBAAsB;AAAA,IACrB,eAAe;AAAA,IACf,iBAAiB;AAAA,EAClB;AAAA,EACA,aAAa,EAAE,eAAe,MAAM,iBAAiB,KAAK;AAAA,EAC1D,gBAAgB,EAAE,eAAe,OAAO,iBAAiB,KAAK;AAAA,EAC9D,kBAAkB,EAAE,eAAe,MAAM,iBAAiB,MAAM;AACjE;AAmBO,MAAe,WAAW;AAAA;AAAA,EAEhC,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA,EACf,aAAa;AAAA,EACb,yBAAyB;AAAA,EACzB;AAAA,EACA;AAAA,EAEA,YAAY,MAAyB;AACpC,UAAM;AAAA,MACL,UAAU;AAAA,MACV,eAAe;AAAA,MACf,aAAa;AAAA,MACb,yBAAyB;AAAA,IAC1B,IAAI;AACJ,SAAK,WAAW,KAAK;AACrB,SAAK,WAAW,KAAK;AACrB,SAAK,aAAa,KAAK;AACvB,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU;AACf,SAAK,eAAe;AACpB,SAAK,aAAa;AAClB,SAAK,yBAAyB;AAAA,EAC/B;AAAA,EAEA,mBAAmB,SAA6B;AAC/C,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,KAAK,WAAW,CAAC,QAAQ,cAAe,QAAO;AACnD,QAAI,KAAK,cAAc,CAAC,QAAQ,gBAAiB,QAAO;AACxD,WAAO;AAAA,EACR;AAAA,EAMA,aAAa,OAAgB,SAAS,GAAG,YAAY,OAAO,UAA8B;AAEzF,QAAI,KAAK,aAAa,KAAK,YAAY,cAAc,eAAe,OAAO,KAAK,QAAQ,GAAG;AAC1F,aAAO;AAAA,IACR;AAEA,WAAO,IAAI,MAAM,OAAO,KAAK,aAAa,KAAK,CAAC,KAAK,SAAS;AAAA,EAC/D;AAAA,EAEA,gBAAgB,OAAgB,YAAY,OAAO,SAA6B;AAC/E,WACC,IAAI,KAAK,OAAO,KAAK,aAAa,OAAO,OAAO,CAAC,KAChD,KAAK,aAAa,KAAK,YAAY,cAAc,eAAe,OAAO,KAAK,QAAQ,IAClF,KACA;AAAA,EAEL;AAAA,EAEA,sBAAsB,GAAY,GAAY,SAA6B;AAC1E,QAAI,IAAI,OAAO,GAAG,CAAC,EAAG,QAAO,KAAK,gBAAgB,GAAG,OAAO,OAAO;AACnE,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,SAAS,WAAW,EAAG,OAAM,MAAM,yBAAyB;AAChE,QAAI,SAAS,WAAW,EAAG,QAAO,IAAI,KAAK,GAAG,SAAS,CAAC,CAAC;AACzD,QAAI;AACJ,QAAI,OAAO;AACX,QAAI,GAAW,GAAQ;AACvB,UAAM,YAAY,KAAK,WAAW,SAAS,SAAS,SAAS,SAAS;AACtE,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,UAAI,SAAS,CAAC;AACd,UAAI,IAAI,WAAW;AAClB,cAAM,OAAO,UAAU,IAAI,KAAK,SAAS,MAAM;AAC/C,YAAI,eAAe,GAAG,GAAG,GAAG,IAAI,EAAG,QAAO;AAAA,MAC3C;AACA,UAAI,IAAI,0BAA0B,GAAG,GAAG,GAAG,IAAI;AAC/C,UAAI,IAAI,MAAM,GAAG,CAAC;AAClB,UAAI,IAAI,MAAM;AACb,eAAO;AACP,kBAAU;AAAA,MACX;AAAA,IACD;AACA,QAAI,CAAC,QAAS,OAAM,MAAM,yBAAyB;AACnD,WAAO,KAAK,KAAK,IAAI;AACrB,WAAO,KAAK,YAAY,KAAK,YAAY,eAAe,SAAS,KAAK,QAAQ,IAAI,CAAC,OAAO;AAAA,EAC3F;AAAA,EAEA,mBAAmB,GAAY,GAAY,WAAW,GAAG,SAAsC;AAC9F,WAAO,KAAK,sBAAsB,GAAG,GAAG,OAAO,KAAK;AAAA,EACrD;AAAA,EAEA,qBAAqB,GAAY,GAAY,UAAyC;AACrF,UAAM,gBAAgB,KAAK,WACxB,4BAA4B,GAAG,GAAG,KAAK,QAAQ,IAC/C,6BAA6B,GAAG,GAAG,KAAK,QAAQ;AAEnD,WAAO,iBAAiB,CAAC;AAAA,EAC1B;AAAA,EAEA,gBAAgB,QAAiB,QAAgB,UAAyC;AACzF,UAAM,gBAAgB,KAAK,WACxB,uBAAuB,QAAQ,QAAQ,KAAK,QAAQ,IACpD,wBAAwB,QAAQ,QAAQ,KAAK,QAAQ;AAExD,WAAO,iBAAiB,CAAC;AAAA,EAC1B;AAAA,EAEA,iBAAiB,SAAoB,UAAyC;AAC7E,WAAO,eAAe,SAAS,KAAK,UAAU,MAAM,KAAK,QAAQ;AAAA,EAClE;AAAA,EAEA,kBAAkB,UAAqB,UAAyC;AAC/E,WAAO,eAAe,UAAU,KAAK,UAAU,OAAO,KAAK,QAAQ;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,GAAW,UAAmC;AAClE,UAAM,EAAE,SAAS,IAAI;AAErB,QAAI,SAAS,WAAW,EAAG,QAAO,IAAI,IAAI,GAAG,CAAC;AAC9C,QAAI,SAAS,WAAW,EAAG,QAAO,SAAS,CAAC;AAC5C,QAAI,KAAK,EAAG,QAAO,SAAS,CAAC;AAE7B,UAAM,mBAAmB,IAAI,KAAK;AAClC,QAAI,mBAAmB;AAEvB,aAAS,IAAI,GAAG,KAAK,KAAK,WAAW,SAAS,SAAS,SAAS,SAAS,IAAI,KAAK;AACjF,YAAM,OAAO,SAAS,CAAC;AACvB,YAAM,OAAO,UAAU,IAAI,KAAK,SAAS,MAAM;AAC/C,YAAM,OAAO,IAAI,KAAK,MAAM,IAAI;AAChC,YAAM,sBAAsB,mBAAmB;AAC/C,UAAI,uBAAuB,kBAAkB;AAC5C,cAAM,IAAI,IAAI;AAAA,UACb;AAAA,UACA;AAAA,UACA,QAAQ,kBAAkB,qBAAqB,gBAAgB;AAAA,QAChE;AACA,eAAO;AAAA,MACR;AACA,yBAAmB;AAAA,IACpB;AAEA,WAAO,KAAK,WAAW,SAAS,CAAC,IAAI,SAAS,SAAS,SAAS,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,uBAAuB,OAAgB,UAAsC;AAC5E,UAAM,EAAE,UAAU,OAAO,IAAI;AAC7B,QAAI,iBAAiB;AACrB,QAAI,kBAAkB;AACtB,QAAI,mBAAmB;AAEvB,QAAI,SAAS,WAAW,KAAK,SAAS,WAAW,EAAG,QAAO;AAE3D,aAAS,IAAI,GAAG,KAAK,KAAK,WAAW,SAAS,SAAS,SAAS,SAAS,IAAI,KAAK;AACjF,YAAM,OAAO,SAAS,CAAC;AACvB,YAAM,OAAO,UAAU,IAAI,KAAK,SAAS,MAAM;AAE/C,YAAM,eAAe,IAAI,0BAA0B,MAAM,MAAM,OAAO,IAAI;AAC1E,YAAM,WAAW,IAAI,KAAK,cAAc,KAAK;AAE7C,UAAI,WAAW,iBAAiB;AAC/B,0BAAkB;AAClB,yBAAiB;AAAA,UAChB,OAAO;AAAA,UACP,KAAK;AAAA,UACL;AAAA,UACA,iBAAiB;AAAA,QAClB;AAAA,MACD;AAEA,0BAAoB,IAAI,KAAK,MAAM,IAAI;AAAA,IACxC;AAEA,WAAO,cAAc;AAErB,UAAM,qBACL,eAAe,kBAAkB,IAAI,KAAK,eAAe,OAAO,eAAe,YAAY;AAE5F,WAAO,qBAAqB;AAAA,EAC7B;AAAA,EAEA,gBAAgB,OAAgB,SAAS,GAAG;AAC3C,UAAM,EAAE,OAAO,IAAI;AACnB,WAAO,EACN,MAAM,IAAI,OAAO,OAAO,UACxB,MAAM,IAAI,OAAO,OAAO,UACxB,MAAM,IAAI,OAAO,OAAO,UACxB,MAAM,IAAI,OAAO,OAAO;AAAA,EAE1B;AAAA,EAEA,gBAAgB,UAA8B;AAC7C,UAAM,UAAU,SAAS,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AAG/C,UAAM,EAAE,UAAU,QAAQ,UAAU,cAAc,SAAS,IAAI;AAK/D,QAAI,aAAc,QAAO;AAGzB,QAAI,SAAS,KAAK,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG;AACrD,aAAO;AAAA,IACR;AAGA,QAAI,UAAU;AACb,UAAI,UAAU;AAEb,YAAI,eAAe,QAAQ,OAAO,GAAG;AACpC,iBAAO;AAAA,QACR;AAGA,YAAI,QAAQ,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,GAAG;AACtD,iBAAO;AAAA,QACR;AAAA,MACD;AAKA,UAAI,kBAAkB,SAAS,QAAQ,GAAG;AACzC,eAAO;AAAA,MACR;AAAA,IACD,OAAO;AAEN,UAAI,0BAA0B,SAAS,QAAQ,GAAG;AACjD,eAAO;AAAA,MACR;AAAA,IACD;AAGA,WAAO;AAAA,EACR;AAAA,EAEA,UAAU,WAAqB,MAAiD;AAC/E,WAAO,IAAI,sBAAsB,MAAM,WAAW,IAAI;AAAA,EACvD;AAAA,EAEQ;AAAA;AAAA,EAGR,IAAI,WAAkB;AACrB,QAAI,CAAC,KAAK,WAAW;AACpB,WAAK,YAAY,KAAK,YAAY,kBAAkB,cAAc;AAAA,IACnE;AAEA,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,oBAA2B;AAC1B,QAAI,KAAK,uBAAwB,QAAO,CAAC;AACzC,WAAO,KAAK;AAAA,EACb;AAAA,EAEQ;AAAA;AAAA,EAGR,IAAI,iBAAwB;AAC3B,QAAI,CAAC,KAAK,iBAAiB;AAC1B,WAAK,kBAAkB,KAAK,kBAAkB;AAAA,IAC/C;AACA,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,YAAY;AACX,WAAO,IAAI,WAAW,KAAK,cAAc;AAAA,EAC1C;AAAA,EAEQ;AAAA;AAAA,EAGR,IAAI,SAAc;AACjB,QAAI,CAAC,KAAK,SAAS;AAClB,WAAK,UAAU,KAAK,UAAU;AAAA,IAC/B;AACA,WAAO,KAAK;AAAA,EACb;AAAA;AAAA,EAGA,IAAI,SAAS;AACZ,WAAO,KAAK,OAAO;AAAA,EACpB;AAAA,EAEQ;AAAA;AAAA,EAGR,IAAI,OAAO;AACV,QAAI,CAAC,KAAK,OAAO;AAChB,WAAK,QAAQ,KAAK,QAAQ;AAAA,IAC3B;AACA,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,UAAU;AACT,QAAI,CAAC,KAAK,UAAU;AACnB,aAAO;AAAA,IACR;AACA,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AAChD,YAAM,OAAO,SAAS,CAAC;AACvB,YAAM,OAAO,UAAU,IAAI,KAAK,CAAC;AACjC,cAAQ,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK;AAAA,IACzC;AACA,WAAO,OAAO;AAAA,EACf;AAAA,EAEA,kBAAkB;AACjB,QAAI,OAAO;AAEX,UAAM,EAAE,SAAS,IAAI;AACrB,UAAM,IAAI,SAAS;AAEnB,QAAI,MAAM,EAAG,QAAO;AAEpB,YAAQ,IAAI,SAAS,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC;AAE1C,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC3B,cAAQ,IAAI,SAAS,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC;AAAA,IAC3C;AAEA,QAAI,KAAK,UAAU;AAClB,cAAQ;AAAA,IACT;AAEA,WAAO;AAAA,EACR;AAAA,EAEQ;AAAA;AAAA,EAGR,IAAI,SAAS;AACZ,QAAI,KAAK,QAAS,QAAO,KAAK;AAC9B,SAAK,UAAU,KAAK,UAAU,kBAAkB,cAAc;AAC9D,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,UAAU,UAA8B;AACvC,UAAM,WAAW,KAAK,YAAY,YAAY,kBAAkB,cAAc;AAC9E,QAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAI,OAAO,SAAS,CAAC;AACrB,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,YAAM,OAAO,SAAS,CAAC;AACvB,gBAAU,IAAI,KAAK,MAAM,IAAI;AAC7B,aAAO;AAAA,IACR;AACA,QAAI,KAAK,UAAU;AAClB,gBAAU,IAAI,KAAK,SAAS,SAAS,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC;AAAA,IAC9D;AACA,WAAO;AAAA,EACR;AAGD;AAQO,MAAM,8BAA8B,WAAW;AAAA,EAIrD,YACkB,UACA,QACjB,MACC;AACD,UAAM,QAAQ;AAJG;AACA;AAIjB,SAAK,UAAU,IAAI,QAAQ,MAAM;AACjC,SAAK,aAAa,IAAI,UAAU,MAAM;AAEtC,QAAI,MAAM;AACT,UAAI,KAAK,WAAW,KAAM,MAAK,UAAU,KAAK;AAC9C,UAAI,KAAK,cAAc,KAAM,MAAK,aAAa,KAAK;AACpD,UAAI,KAAK,cAAc,KAAM,MAAK,aAAa,KAAK;AACpD,UAAI,KAAK,UAAU,KAAM,MAAK,SAAS,KAAK;AAAA,IAC7C;AAEA;AAAA,MACC,cAAc,KAAK,WAAW,QAAQ,KAAK,WAAW,MAAM;AAAA,MAC5D;AAAA,IACD;AAAA,EACD;AAAA,EAvBiB;AAAA,EACA;AAAA,EAwBjB,YAAY,SAAmC;AAC9C,WAAO,KAAK,SAAS,YAAY,OAAO,EAAE,IAAI,CAAC,MAAM,IAAI,aAAa,KAAK,QAAQ,CAAC,CAAC;AAAA,EACtF;AAAA,EAEA,oBAA2B;AAC1B,WAAO,KAAK,SAAS,kBAAkB,EAAE,IAAI,CAAC,MAAM,IAAI,aAAa,KAAK,QAAQ,CAAC,CAAC;AAAA,EACrF;AAAA,EAEA,aAAa,OAAgB,SAAkC;AAC9D,WAAO,IAAI;AAAA,MACV,KAAK;AAAA,MACL,KAAK,SAAS,aAAa,IAAI,aAAa,KAAK,SAAS,KAAK,GAAG,OAAO;AAAA,IAC1E;AAAA,EACD;AAAA,EAES,aACR,OACA,SAAS,GACT,WACA,SACU;AACV,WAAO,KAAK,SAAS;AAAA,MACpB,IAAI,aAAa,KAAK,SAAS,KAAK;AAAA,MACpC,SAAS,KAAK,WAAW;AAAA,MACzB;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAAA,EAES,gBAAgB,OAAgB,YAAY,OAAO,SAA6B;AACxF,WACC,KAAK,SAAS,gBAAgB,IAAI,aAAa,KAAK,SAAS,KAAK,GAAG,WAAW,OAAO,IACvF,KAAK,WAAW;AAAA,EAElB;AAAA,EAES,sBAAsB,GAAY,GAAY,SAA6B;AACnF,WACC,KAAK,SAAS;AAAA,MACb,IAAI,aAAa,KAAK,SAAS,CAAC;AAAA,MAChC,IAAI,aAAa,KAAK,SAAS,CAAC;AAAA,MAChC;AAAA,IACD,IAAI,KAAK,WAAW;AAAA,EAEtB;AAAA,EAES,mBACR,GACA,GACA,WAAW,GACX,SACU;AACV,WAAO,KAAK,SAAS;AAAA,MACpB,IAAI,aAAa,KAAK,SAAS,CAAC;AAAA,MAChC,IAAI,aAAa,KAAK,SAAS,CAAC;AAAA,MAChC,WAAW,KAAK,WAAW;AAAA,MAC3B;AAAA,IACD;AAAA,EACD;AAAA,EAES,qBAAqB,GAAY,GAAY,SAA6B;AAClF,WAAO,IAAI;AAAA,MACV,KAAK;AAAA,MACL,KAAK,SAAS;AAAA,QACb,IAAI,aAAa,KAAK,SAAS,CAAC;AAAA,QAChC,IAAI,aAAa,KAAK,SAAS,CAAC;AAAA,QAChC;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EAES,gBAAgB,QAAiB,QAAgB,SAA6B;AACtF,WAAO,IAAI;AAAA,MACV,KAAK;AAAA,MACL,KAAK,SAAS;AAAA,QACb,IAAI,aAAa,KAAK,SAAS,MAAM;AAAA,QACrC,SAAS,KAAK,WAAW;AAAA,QACzB;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EAES,iBAAiB,SAAoB,SAAwC;AACrF,WAAO,IAAI;AAAA,MACV,KAAK;AAAA,MACL,KAAK,SAAS,iBAAiB,IAAI,cAAc,KAAK,SAAS,OAAO,GAAG,OAAO;AAAA,IACjF;AAAA,EACD;AAAA,EAES,kBAAkB,UAAqB,SAAwC;AACvF,WAAO,IAAI;AAAA,MACV,KAAK;AAAA,MACL,KAAK,SAAS,kBAAkB,IAAI,cAAc,KAAK,SAAS,QAAQ,GAAG,OAAO;AAAA,IACnF;AAAA,EACD;AAAA,EAES,UAAU,WAAqB,MAAiD;AACxF,WAAO,IAAI,sBAAsB,KAAK,UAAU,IAAI,SAAS,WAAW,KAAK,MAAM,GAAG;AAAA,MACrF,SAAS,MAAM,WAAW,KAAK;AAAA,MAC/B,YAAY,MAAM,cAAc,KAAK;AAAA,MACrC,YAAY,MAAM,cAAc,KAAK;AAAA,MACrC,QAAQ,MAAM,UAAU,KAAK;AAAA,IAC9B,CAAC;AAAA,EACF;AAAA,EAEA,iBAAyB;AACxB,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACrE;AACD;",
6
6
  "names": []
7
7
  }
@@ -16,7 +16,7 @@ function getRotationSnapshot({
16
16
  const initialPageCenter = rotatedPageBounds.center.clone().rotWith(rotatedPageBounds.point, rotation);
17
17
  return {
18
18
  initialPageCenter,
19
- initialCursorAngle: initialPageCenter.angle(editor.inputs.originPagePoint),
19
+ initialCursorAngle: initialPageCenter.angle(editor.inputs.getOriginPagePoint()),
20
20
  initialShapesRotation: rotation,
21
21
  shapeSnapshots: shapes.map((shape) => ({
22
22
  shape,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/rotation.ts"],
4
- "sourcesContent": ["import { isShapeId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'\nimport { compact } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { Mat } from '../primitives/Mat'\nimport { canonicalizeRotation } from '../primitives/utils'\nimport { Vec, VecLike } from '../primitives/Vec'\n\n/** @internal */\nexport function getRotationSnapshot({\n\teditor,\n\tids,\n}: {\n\teditor: Editor\n\tids: TLShapeId[]\n}): TLRotationSnapshot | null {\n\tconst shapes = compact(ids.map((id) => editor.getShape(id)))\n\tconst rotation = editor.getShapesSharedRotation(ids)\n\tconst rotatedPageBounds = editor.getShapesRotatedPageBounds(ids)\n\n\t// todo: this assumes we're rotating the selected shapes\n\t// if we try to rotate shapes that aren't selected, this\n\t// will produce the wrong results\n\n\t// Return null if there are no selected shapes\n\tif (!rotatedPageBounds) {\n\t\treturn null\n\t}\n\n\tconst initialPageCenter = rotatedPageBounds.center\n\t\t.clone()\n\t\t.rotWith(rotatedPageBounds.point, rotation)\n\n\treturn {\n\t\tinitialPageCenter,\n\t\tinitialCursorAngle: initialPageCenter.angle(editor.inputs.originPagePoint),\n\t\tinitialShapesRotation: rotation,\n\t\tshapeSnapshots: shapes.map((shape) => ({\n\t\t\tshape,\n\t\t\tinitialPagePoint: editor.getShapePageTransform(shape.id)!.point(),\n\t\t})),\n\t}\n}\n\n/**\n * @internal\n **/\nexport interface TLRotationSnapshot {\n\tinitialPageCenter: Vec\n\tinitialCursorAngle: number\n\tinitialShapesRotation: number\n\tshapeSnapshots: {\n\t\tshape: TLShape\n\t\tinitialPagePoint: Vec\n\t}[]\n}\n\n/** @internal */\nexport function applyRotationToSnapshotShapes({\n\tdelta,\n\teditor,\n\tsnapshot,\n\tstage,\n\tcenterOverride,\n}: {\n\tdelta: number\n\tsnapshot: TLRotationSnapshot\n\teditor: Editor\n\tstage: 'start' | 'update' | 'end' | 'one-off'\n\tcenterOverride?: VecLike\n}) {\n\tconst { initialPageCenter, shapeSnapshots } = snapshot\n\n\teditor.updateShapes(\n\t\tshapeSnapshots.map(({ shape, initialPagePoint }) => {\n\t\t\t// We need to both rotate each shape individually and rotate the shapes\n\t\t\t// around the pivot point (the average center of all rotating shapes.)\n\n\t\t\tconst parentTransform = isShapeId(shape.parentId)\n\t\t\t\t? editor.getShapePageTransform(shape.parentId)!\n\t\t\t\t: Mat.Identity()\n\n\t\t\tconst newPagePoint = Vec.RotWith(initialPagePoint, centerOverride ?? initialPageCenter, delta)\n\n\t\t\tconst newLocalPoint = Mat.applyToPoint(\n\t\t\t\t// use the current parent transform in case it has moved/resized since the start\n\t\t\t\t// (e.g. if rotating a shape at the edge of a group)\n\t\t\t\tMat.Inverse(parentTransform),\n\t\t\t\tnewPagePoint\n\t\t\t)\n\t\t\tconst newRotation = canonicalizeRotation(shape.rotation + delta)\n\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tx: newLocalPoint.x,\n\t\t\t\ty: newLocalPoint.y,\n\t\t\t\trotation: newRotation,\n\t\t\t}\n\t\t})\n\t)\n\n\t// Handle change\n\n\tconst changes: TLShapePartial[] = []\n\n\tshapeSnapshots.forEach(({ shape }) => {\n\t\tconst current = editor.getShape(shape.id)\n\t\tif (!current) return\n\t\tconst util = editor.getShapeUtil(shape)\n\n\t\tif (stage === 'start' || stage === 'one-off') {\n\t\t\tconst changeStart = util.onRotateStart?.(shape)\n\t\t\tif (changeStart) changes.push(changeStart)\n\t\t}\n\n\t\tconst changeUpdate = util.onRotate?.(shape, current)\n\t\tif (changeUpdate) changes.push(changeUpdate)\n\n\t\tif (stage === 'end' || stage === 'one-off') {\n\t\t\tconst changeEnd = util.onRotateEnd?.(shape, current)\n\t\t\tif (changeEnd) changes.push(changeEnd)\n\t\t}\n\t})\n\n\tif (changes.length > 0) {\n\t\teditor.updateShapes(changes)\n\t}\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAqD;AAC9D,SAAS,eAAe;AAExB,SAAS,WAAW;AACpB,SAAS,4BAA4B;AACrC,SAAS,WAAoB;AAGtB,SAAS,oBAAoB;AAAA,EACnC;AAAA,EACA;AACD,GAG8B;AAC7B,QAAM,SAAS,QAAQ,IAAI,IAAI,CAAC,OAAO,OAAO,SAAS,EAAE,CAAC,CAAC;AAC3D,QAAM,WAAW,OAAO,wBAAwB,GAAG;AACnD,QAAM,oBAAoB,OAAO,2BAA2B,GAAG;AAO/D,MAAI,CAAC,mBAAmB;AACvB,WAAO;AAAA,EACR;AAEA,QAAM,oBAAoB,kBAAkB,OAC1C,MAAM,EACN,QAAQ,kBAAkB,OAAO,QAAQ;AAE3C,SAAO;AAAA,IACN;AAAA,IACA,oBAAoB,kBAAkB,MAAM,OAAO,OAAO,eAAe;AAAA,IACzE,uBAAuB;AAAA,IACvB,gBAAgB,OAAO,IAAI,CAAC,WAAW;AAAA,MACtC;AAAA,MACA,kBAAkB,OAAO,sBAAsB,MAAM,EAAE,EAAG,MAAM;AAAA,IACjE,EAAE;AAAA,EACH;AACD;AAgBO,SAAS,8BAA8B;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAMG;AACF,QAAM,EAAE,mBAAmB,eAAe,IAAI;AAE9C,SAAO;AAAA,IACN,eAAe,IAAI,CAAC,EAAE,OAAO,iBAAiB,MAAM;AAInD,YAAM,kBAAkB,UAAU,MAAM,QAAQ,IAC7C,OAAO,sBAAsB,MAAM,QAAQ,IAC3C,IAAI,SAAS;AAEhB,YAAM,eAAe,IAAI,QAAQ,kBAAkB,kBAAkB,mBAAmB,KAAK;AAE7F,YAAM,gBAAgB,IAAI;AAAA;AAAA;AAAA,QAGzB,IAAI,QAAQ,eAAe;AAAA,QAC3B;AAAA,MACD;AACA,YAAM,cAAc,qBAAqB,MAAM,WAAW,KAAK;AAE/D,aAAO;AAAA,QACN,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,GAAG,cAAc;AAAA,QACjB,GAAG,cAAc;AAAA,QACjB,UAAU;AAAA,MACX;AAAA,IACD,CAAC;AAAA,EACF;AAIA,QAAM,UAA4B,CAAC;AAEnC,iBAAe,QAAQ,CAAC,EAAE,MAAM,MAAM;AACrC,UAAM,UAAU,OAAO,SAAS,MAAM,EAAE;AACxC,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,OAAO,aAAa,KAAK;AAEtC,QAAI,UAAU,WAAW,UAAU,WAAW;AAC7C,YAAM,cAAc,KAAK,gBAAgB,KAAK;AAC9C,UAAI,YAAa,SAAQ,KAAK,WAAW;AAAA,IAC1C;AAEA,UAAM,eAAe,KAAK,WAAW,OAAO,OAAO;AACnD,QAAI,aAAc,SAAQ,KAAK,YAAY;AAE3C,QAAI,UAAU,SAAS,UAAU,WAAW;AAC3C,YAAM,YAAY,KAAK,cAAc,OAAO,OAAO;AACnD,UAAI,UAAW,SAAQ,KAAK,SAAS;AAAA,IACtC;AAAA,EACD,CAAC;AAED,MAAI,QAAQ,SAAS,GAAG;AACvB,WAAO,aAAa,OAAO;AAAA,EAC5B;AACD;",
4
+ "sourcesContent": ["import { isShapeId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'\nimport { compact } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { Mat } from '../primitives/Mat'\nimport { canonicalizeRotation } from '../primitives/utils'\nimport { Vec, VecLike } from '../primitives/Vec'\n\n/** @internal */\nexport function getRotationSnapshot({\n\teditor,\n\tids,\n}: {\n\teditor: Editor\n\tids: TLShapeId[]\n}): TLRotationSnapshot | null {\n\tconst shapes = compact(ids.map((id) => editor.getShape(id)))\n\tconst rotation = editor.getShapesSharedRotation(ids)\n\tconst rotatedPageBounds = editor.getShapesRotatedPageBounds(ids)\n\n\t// todo: this assumes we're rotating the selected shapes\n\t// if we try to rotate shapes that aren't selected, this\n\t// will produce the wrong results\n\n\t// Return null if there are no selected shapes\n\tif (!rotatedPageBounds) {\n\t\treturn null\n\t}\n\n\tconst initialPageCenter = rotatedPageBounds.center\n\t\t.clone()\n\t\t.rotWith(rotatedPageBounds.point, rotation)\n\n\treturn {\n\t\tinitialPageCenter,\n\t\tinitialCursorAngle: initialPageCenter.angle(editor.inputs.getOriginPagePoint()),\n\t\tinitialShapesRotation: rotation,\n\t\tshapeSnapshots: shapes.map((shape) => ({\n\t\t\tshape,\n\t\t\tinitialPagePoint: editor.getShapePageTransform(shape.id)!.point(),\n\t\t})),\n\t}\n}\n\n/**\n * @internal\n **/\nexport interface TLRotationSnapshot {\n\tinitialPageCenter: Vec\n\tinitialCursorAngle: number\n\tinitialShapesRotation: number\n\tshapeSnapshots: {\n\t\tshape: TLShape\n\t\tinitialPagePoint: Vec\n\t}[]\n}\n\n/** @internal */\nexport function applyRotationToSnapshotShapes({\n\tdelta,\n\teditor,\n\tsnapshot,\n\tstage,\n\tcenterOverride,\n}: {\n\tdelta: number\n\tsnapshot: TLRotationSnapshot\n\teditor: Editor\n\tstage: 'start' | 'update' | 'end' | 'one-off'\n\tcenterOverride?: VecLike\n}) {\n\tconst { initialPageCenter, shapeSnapshots } = snapshot\n\n\teditor.updateShapes(\n\t\tshapeSnapshots.map(({ shape, initialPagePoint }) => {\n\t\t\t// We need to both rotate each shape individually and rotate the shapes\n\t\t\t// around the pivot point (the average center of all rotating shapes.)\n\n\t\t\tconst parentTransform = isShapeId(shape.parentId)\n\t\t\t\t? editor.getShapePageTransform(shape.parentId)!\n\t\t\t\t: Mat.Identity()\n\n\t\t\tconst newPagePoint = Vec.RotWith(initialPagePoint, centerOverride ?? initialPageCenter, delta)\n\n\t\t\tconst newLocalPoint = Mat.applyToPoint(\n\t\t\t\t// use the current parent transform in case it has moved/resized since the start\n\t\t\t\t// (e.g. if rotating a shape at the edge of a group)\n\t\t\t\tMat.Inverse(parentTransform),\n\t\t\t\tnewPagePoint\n\t\t\t)\n\t\t\tconst newRotation = canonicalizeRotation(shape.rotation + delta)\n\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tx: newLocalPoint.x,\n\t\t\t\ty: newLocalPoint.y,\n\t\t\t\trotation: newRotation,\n\t\t\t}\n\t\t})\n\t)\n\n\t// Handle change\n\n\tconst changes: TLShapePartial[] = []\n\n\tshapeSnapshots.forEach(({ shape }) => {\n\t\tconst current = editor.getShape(shape.id)\n\t\tif (!current) return\n\t\tconst util = editor.getShapeUtil(shape)\n\n\t\tif (stage === 'start' || stage === 'one-off') {\n\t\t\tconst changeStart = util.onRotateStart?.(shape)\n\t\t\tif (changeStart) changes.push(changeStart)\n\t\t}\n\n\t\tconst changeUpdate = util.onRotate?.(shape, current)\n\t\tif (changeUpdate) changes.push(changeUpdate)\n\n\t\tif (stage === 'end' || stage === 'one-off') {\n\t\t\tconst changeEnd = util.onRotateEnd?.(shape, current)\n\t\t\tif (changeEnd) changes.push(changeEnd)\n\t\t}\n\t})\n\n\tif (changes.length > 0) {\n\t\teditor.updateShapes(changes)\n\t}\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAqD;AAC9D,SAAS,eAAe;AAExB,SAAS,WAAW;AACpB,SAAS,4BAA4B;AACrC,SAAS,WAAoB;AAGtB,SAAS,oBAAoB;AAAA,EACnC;AAAA,EACA;AACD,GAG8B;AAC7B,QAAM,SAAS,QAAQ,IAAI,IAAI,CAAC,OAAO,OAAO,SAAS,EAAE,CAAC,CAAC;AAC3D,QAAM,WAAW,OAAO,wBAAwB,GAAG;AACnD,QAAM,oBAAoB,OAAO,2BAA2B,GAAG;AAO/D,MAAI,CAAC,mBAAmB;AACvB,WAAO;AAAA,EACR;AAEA,QAAM,oBAAoB,kBAAkB,OAC1C,MAAM,EACN,QAAQ,kBAAkB,OAAO,QAAQ;AAE3C,SAAO;AAAA,IACN;AAAA,IACA,oBAAoB,kBAAkB,MAAM,OAAO,OAAO,mBAAmB,CAAC;AAAA,IAC9E,uBAAuB;AAAA,IACvB,gBAAgB,OAAO,IAAI,CAAC,WAAW;AAAA,MACtC;AAAA,MACA,kBAAkB,OAAO,sBAAsB,MAAM,EAAE,EAAG,MAAM;AAAA,IACjE,EAAE;AAAA,EACH;AACD;AAgBO,SAAS,8BAA8B;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAMG;AACF,QAAM,EAAE,mBAAmB,eAAe,IAAI;AAE9C,SAAO;AAAA,IACN,eAAe,IAAI,CAAC,EAAE,OAAO,iBAAiB,MAAM;AAInD,YAAM,kBAAkB,UAAU,MAAM,QAAQ,IAC7C,OAAO,sBAAsB,MAAM,QAAQ,IAC3C,IAAI,SAAS;AAEhB,YAAM,eAAe,IAAI,QAAQ,kBAAkB,kBAAkB,mBAAmB,KAAK;AAE7F,YAAM,gBAAgB,IAAI;AAAA;AAAA;AAAA,QAGzB,IAAI,QAAQ,eAAe;AAAA,QAC3B;AAAA,MACD;AACA,YAAM,cAAc,qBAAqB,MAAM,WAAW,KAAK;AAE/D,aAAO;AAAA,QACN,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,GAAG,cAAc;AAAA,QACjB,GAAG,cAAc;AAAA,QACjB,UAAU;AAAA,MACX;AAAA,IACD,CAAC;AAAA,EACF;AAIA,QAAM,UAA4B,CAAC;AAEnC,iBAAe,QAAQ,CAAC,EAAE,MAAM,MAAM;AACrC,UAAM,UAAU,OAAO,SAAS,MAAM,EAAE;AACxC,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,OAAO,aAAa,KAAK;AAEtC,QAAI,UAAU,WAAW,UAAU,WAAW;AAC7C,YAAM,cAAc,KAAK,gBAAgB,KAAK;AAC9C,UAAI,YAAa,SAAQ,KAAK,WAAW;AAAA,IAC1C;AAEA,UAAM,eAAe,KAAK,WAAW,OAAO,OAAO;AACnD,QAAI,aAAc,SAAQ,KAAK,YAAY;AAE3C,QAAI,UAAU,SAAS,UAAU,WAAW;AAC3C,YAAM,YAAY,KAAK,cAAc,OAAO,OAAO;AACnD,UAAI,UAAW,SAAQ,KAAK,SAAS;AAAA,IACtC;AAAA,EACD,CAAC;AAED,MAAI,QAAQ,SAAS,GAAG;AACvB,WAAO,aAAa,OAAO;AAAA,EAC5B;AACD;",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "4.3.0-canary.e52fa5385f86";
1
+ const version = "4.3.0-canary.ea88b223b83a";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2025-12-05T21:53:17.454Z",
5
- patch: "2025-12-05T21:53:17.454Z"
4
+ minor: "2026-01-07T11:49:21.408Z",
5
+ patch: "2026-01-07T11:49:21.408Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.e52fa5385f86'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-05T21:53:17.454Z',\n\tpatch: '2025-12-05T21:53:17.454Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.ea88b223b83a'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-01-07T11:49:21.408Z',\n\tpatch: '2026-01-07T11:49:21.408Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/editor.css CHANGED
@@ -607,7 +607,6 @@ input,
607
607
  pointer-events: all;
608
608
  white-space: pre-wrap;
609
609
  overflow-wrap: break-word;
610
- text-shadow: var(--tl-text-outline);
611
610
  }
612
611
 
613
612
  .tl-text-wrapper[data-font='draw'] {
@@ -709,7 +708,7 @@ input,
709
708
  resize: none;
710
709
  border: none;
711
710
  user-select: none;
712
- contain: style paint;
711
+ contain: layout style paint;
713
712
  /* N.B. This property, while discouraged ("intended for Document Type Definition (DTD) designers") is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs. */
714
713
  unicode-bidi: plaintext;
715
714
  -webkit-user-select: none;
@@ -770,7 +769,6 @@ input,
770
769
  justify-content: center;
771
770
  align-items: center;
772
771
  color: var(--tl-color-text);
773
- text-shadow: var(--tl-text-outline);
774
772
  line-height: inherit;
775
773
  position: absolute;
776
774
  inset: 0px;
@@ -970,6 +968,14 @@ input,
970
968
  display: block;
971
969
  }
972
970
 
971
+ .tl-text__outline {
972
+ text-shadow: var(--tl-text-outline);
973
+ }
974
+
975
+ .tl-text__no-outline {
976
+ text-shadow: none;
977
+ }
978
+
973
979
  /* --------------------- Loading -------------------- */
974
980
 
975
981
  .tl-loading {
@@ -1134,14 +1140,12 @@ input,
1134
1140
  fill: none;
1135
1141
  }
1136
1142
 
1137
- @media (pointer: coarse) {
1138
- .tl-handle__bg:active {
1139
- fill: var(--tl-color-selection-fill);
1140
- }
1143
+ .tl-container[data-coarse='true'] .tl-handle__bg:active {
1144
+ fill: var(--tl-color-selection-fill);
1145
+ }
1141
1146
 
1142
- .tl-handle__create {
1143
- opacity: 1;
1144
- }
1147
+ .tl-container[data-coarse='true'] .tl-handle__create {
1148
+ opacity: 1;
1145
1149
  }
1146
1150
 
1147
1151
  .tl-rotate-corner:not(:hover),
@@ -1217,7 +1221,6 @@ input,
1217
1221
  align-items: center;
1218
1222
  text-align: center;
1219
1223
  color: var(--tl-color-text);
1220
- text-shadow: var(--tl-text-outline);
1221
1224
  }
1222
1225
 
1223
1226
  .tl-shape[data-shape-type='arrow'] .tl-text-label__inner {
@@ -1446,7 +1449,6 @@ input,
1446
1449
  }
1447
1450
 
1448
1451
  .tl-note__container > .tl-text-label {
1449
- text-shadow: none;
1450
1452
  color: currentColor;
1451
1453
  }
1452
1454
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "tldraw infinite canvas SDK (editor).",
4
- "version": "4.3.0-canary.e52fa5385f86",
4
+ "version": "4.3.0-canary.ea88b223b83a",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -47,15 +47,15 @@
47
47
  "context": "yarn run -T tsx ../../internal/scripts/context.ts"
48
48
  },
49
49
  "dependencies": {
50
- "@tiptap/core": "^3.6.2",
51
- "@tiptap/pm": "^3.6.2",
52
- "@tiptap/react": "^3.6.2",
53
- "@tldraw/state": "4.3.0-canary.e52fa5385f86",
54
- "@tldraw/state-react": "4.3.0-canary.e52fa5385f86",
55
- "@tldraw/store": "4.3.0-canary.e52fa5385f86",
56
- "@tldraw/tlschema": "4.3.0-canary.e52fa5385f86",
57
- "@tldraw/utils": "4.3.0-canary.e52fa5385f86",
58
- "@tldraw/validate": "4.3.0-canary.e52fa5385f86",
50
+ "@tiptap/core": "^3.12.1",
51
+ "@tiptap/pm": "^3.12.1",
52
+ "@tiptap/react": "^3.12.1",
53
+ "@tldraw/state": "4.3.0-canary.ea88b223b83a",
54
+ "@tldraw/state-react": "4.3.0-canary.ea88b223b83a",
55
+ "@tldraw/store": "4.3.0-canary.ea88b223b83a",
56
+ "@tldraw/tlschema": "4.3.0-canary.ea88b223b83a",
57
+ "@tldraw/utils": "4.3.0-canary.ea88b223b83a",
58
+ "@tldraw/validate": "4.3.0-canary.ea88b223b83a",
59
59
  "@types/core-js": "^2.5.8",
60
60
  "@use-gesture/react": "^10.3.1",
61
61
  "classnames": "^2.5.1",
@@ -65,20 +65,22 @@
65
65
  "is-plain-object": "^5.0.0"
66
66
  },
67
67
  "peerDependencies": {
68
- "react": "^18.2.0 || ^19.0.0",
69
- "react-dom": "^18.2.0 || ^19.0.0"
68
+ "react": "^18.2.0 || ^19.2.1",
69
+ "react-dom": "^18.2.0 || ^19.2.1"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@peculiar/webcrypto": "^1.5.0",
73
- "@testing-library/react": "^15.0.7",
73
+ "@testing-library/dom": "^10.0.0",
74
+ "@testing-library/react": "^16.0.0",
74
75
  "@types/benchmark": "^2.1.5",
75
- "@types/react": "^18.3.18",
76
+ "@types/react": "^19.2.7",
77
+ "@types/react-dom": "^19.2.3",
76
78
  "@types/wicg-file-system-access": "^2020.9.8",
77
79
  "benchmark": "^2.1.4",
78
80
  "fake-indexeddb": "^4.0.2",
79
81
  "lazyrepo": "0.0.0-alpha.27",
80
- "react": "^18.3.1",
81
- "react-dom": "^18.3.1",
82
+ "react": "^19.2.1",
83
+ "react-dom": "^19.2.1",
82
84
  "resize-observer-polyfill": "^1.5.1",
83
85
  "vitest": "^3.2.4"
84
86
  },
package/src/index.ts CHANGED
@@ -146,6 +146,7 @@ export {
146
146
  type TLFontFaceSource,
147
147
  } from './lib/editor/managers/FontManager/FontManager'
148
148
  export { HistoryManager } from './lib/editor/managers/HistoryManager/HistoryManager'
149
+ export { InputsManager } from './lib/editor/managers/InputsManager/InputsManager'
149
150
  export {
150
151
  ScribbleManager,
151
152
  type ScribbleItem,
@@ -168,6 +169,7 @@ export {
168
169
  type TLMeasureTextOpts,
169
170
  type TLMeasureTextSpanOpts,
170
171
  } from './lib/editor/managers/TextManager/TextManager'
172
+ export { TickManager } from './lib/editor/managers/TickManager/TickManager'
171
173
  export { UserPreferencesManager } from './lib/editor/managers/UserPreferencesManager/UserPreferencesManager'
172
174
  export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/editor/shapes/BaseBoxShapeUtil'
173
175
  export { GroupShapeUtil } from './lib/editor/shapes/group/GroupShapeUtil'
@@ -178,6 +180,7 @@ export {
178
180
  type TLDragShapesOutInfo,
179
181
  type TLDragShapesOverInfo,
180
182
  type TLDropShapesOverInfo,
183
+ type TLEditStartInfo,
181
184
  type TLGeometryOpts,
182
185
  type TLHandleDragInfo,
183
186
  type TLResizeInfo,
@@ -12,7 +12,7 @@ const initialState = { error: null }
12
12
 
13
13
  /** @public */
14
14
  export class ErrorBoundary extends React.Component<
15
- React.PropsWithRef<React.PropsWithChildren<TLErrorBoundaryProps>>,
15
+ React.PropsWithChildren<TLErrorBoundaryProps>,
16
16
  { error: Error | null }
17
17
  > {
18
18
  static getDerivedStateFromError(error: Error) {
@@ -1,23 +1,9 @@
1
1
  import { track } from '@tldraw/state-react'
2
2
  import { modulate } from '@tldraw/utils'
3
- import { useEffect, useState } from 'react'
4
3
  import { useEditor } from '../hooks/useEditor'
5
4
  import { Geometry2d } from '../primitives/geometry/Geometry2d'
6
5
  import { Group2d } from '../primitives/geometry/Group2d'
7
6
 
8
- function useTick(isEnabled = true) {
9
- const [_, setTick] = useState(0)
10
- const editor = useEditor()
11
- useEffect(() => {
12
- if (!isEnabled) return
13
- const update = () => setTick((tick) => tick + 1)
14
- editor.on('tick', update)
15
- return () => {
16
- editor.off('tick', update)
17
- }
18
- }, [editor, isEnabled])
19
- }
20
-
21
7
  export const GeometryDebuggingView = track(function GeometryDebuggingView({
22
8
  showStroke = true,
23
9
  showVertices = true,
@@ -29,13 +15,9 @@ export const GeometryDebuggingView = track(function GeometryDebuggingView({
29
15
  }) {
30
16
  const editor = useEditor()
31
17
 
32
- useTick(showClosestPointOnOutline)
33
-
34
18
  const zoomLevel = editor.getZoomLevel()
35
19
  const renderingShapes = editor.getRenderingShapes()
36
- const {
37
- inputs: { currentPagePoint },
38
- } = editor
20
+ const currentPagePoint = editor.inputs.getCurrentPagePoint()
39
21
 
40
22
  return (
41
23
  <svg
@@ -210,7 +210,7 @@ function GridWrapper() {
210
210
  function ScribbleWrapper() {
211
211
  const editor = useEditor()
212
212
  const scribbles = useValue('scribbles', () => editor.getInstanceState().scribbles, [editor])
213
- const zoomLevel = useValue('zoomLevel', () => editor.getZoomLevel(), [editor])
213
+ const zoomLevel = useValue('zoomLevel', () => editor.getEfficientZoomLevel(), [editor])
214
214
  const { Scribble } = useEditorComponents()
215
215
 
216
216
  if (!(Scribble && scribbles.length)) return null
@@ -243,7 +243,7 @@ function ZoomBrushWrapper() {
243
243
  function SnapIndicatorWrapper() {
244
244
  const editor = useEditor()
245
245
  const lines = useValue('snapLines', () => editor.snaps.getIndicators(), [editor])
246
- const zoomLevel = useValue('zoomLevel', () => editor.getZoomLevel(), [editor])
246
+ const zoomLevel = useValue('zoomLevel', () => editor.getEfficientZoomLevel(), [editor])
247
247
  const { SnapIndicator } = useEditorComponents()
248
248
 
249
249
  if (!(SnapIndicator && lines.length > 0)) return null
@@ -284,7 +284,7 @@ function HandlesWrapperInner({ shapeId }: { shapeId: TLShapeId }) {
284
284
  const editor = useEditor()
285
285
  const { Handles } = useEditorComponents()
286
286
 
287
- const zoomLevel = useValue('zoomLevel', () => editor.getZoomLevel(), [editor])
287
+ const zoomLevel = useValue('zoomLevel', () => editor.getEfficientZoomLevel(), [editor])
288
288
 
289
289
  const isCoarse = useValue('coarse pointer', () => editor.getInstanceState().isCoarsePointer, [
290
290
  editor,
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { defaultUserPreferences, userTypeValidator } from './TLUserPreferences'
3
+
4
+ describe('TLUserPreferences consistency', () => {
5
+ // When adding a new user preference, add it to this list AND update:
6
+ // 1. TLUserPreferences interface
7
+ // 2. userTypeValidator
8
+ // 3. defaultUserPreferences
9
+ // 4. Versions enum and migrateSnapshot()
10
+ const interfaceKeys = [
11
+ 'name',
12
+ 'color',
13
+ 'locale',
14
+ 'animationSpeed',
15
+ 'areKeyboardShortcutsEnabled',
16
+ 'edgeScrollSpeed',
17
+ 'colorScheme',
18
+ 'isSnapMode',
19
+ 'isWrapMode',
20
+ 'isDynamicSizeMode',
21
+ 'isPasteAtCursorMode',
22
+ 'enhancedA11yMode',
23
+ 'inputMode',
24
+ ] as const
25
+
26
+ it('defaultUserPreferences contains all TLUserPreferences keys (except id)', () => {
27
+ const defaultKeys = Object.keys(defaultUserPreferences).sort()
28
+ const expected = [...interfaceKeys].sort()
29
+
30
+ expect(defaultKeys).toEqual(expected)
31
+ })
32
+
33
+ it('userTypeValidator validates all TLUserPreferences keys', () => {
34
+ // Access the internal config property to check which keys the validator covers
35
+ const validatorKeys = Object.keys((userTypeValidator as any).config).sort()
36
+ const expected = ['id', ...interfaceKeys].sort()
37
+
38
+ expect(validatorKeys).toEqual(expected)
39
+ })
40
+ })
@@ -31,5 +31,3 @@ export const LEFT_MOUSE_BUTTON = 0
31
31
  export const RIGHT_MOUSE_BUTTON = 2
32
32
  export const MIDDLE_MOUSE_BUTTON = 1
33
33
  export const STYLUS_ERASER_BUTTON = 5
34
-
35
- export const ZOOM_TO_FIT_PADDING = 128
@@ -925,6 +925,146 @@ describe('replaceExternalContent', () => {
925
925
  })
926
926
  })
927
927
 
928
+ describe('dispatch event emission', () => {
929
+ let testEditor: Editor
930
+
931
+ beforeEach(() => {
932
+ testEditor = new Editor({
933
+ shapeUtils: [CustomShape],
934
+ bindingUtils: [],
935
+ tools: [],
936
+ store: createTLStore({ shapeUtils: [CustomShape], bindingUtils: [] }),
937
+ getContainer: () => document.body,
938
+ })
939
+ // Ensure camera is unlocked so events are processed
940
+ testEditor.setCameraOptions({ isLocked: false })
941
+ })
942
+
943
+ it('emits wheel events through the event emitter', () => {
944
+ const eventHandler = vi.fn()
945
+ testEditor.on('event', eventHandler)
946
+
947
+ const wheelEvent = {
948
+ type: 'wheel' as const,
949
+ name: 'wheel' as const,
950
+ delta: { x: 0, y: 10, z: 0 },
951
+ point: { x: 100, y: 100, z: 1 },
952
+ shiftKey: false,
953
+ altKey: false,
954
+ ctrlKey: false,
955
+ metaKey: false,
956
+ accelKey: false,
957
+ }
958
+
959
+ testEditor.dispatch(wheelEvent)
960
+ // Wheel events are batched for the next tick, so emit a tick to flush them
961
+ testEditor.emit('tick', 16)
962
+
963
+ expect(eventHandler).toHaveBeenCalledWith(wheelEvent)
964
+ })
965
+
966
+ it('emits pinch_start events through the event emitter', () => {
967
+ const eventHandler = vi.fn()
968
+ testEditor.on('event', eventHandler)
969
+
970
+ const pinchStartEvent = {
971
+ type: 'pinch' as const,
972
+ name: 'pinch_start' as const,
973
+ point: { x: 100, y: 100, z: 1 },
974
+ delta: { x: 0, y: 0, z: 0 },
975
+ shiftKey: false,
976
+ altKey: false,
977
+ ctrlKey: false,
978
+ metaKey: false,
979
+ accelKey: false,
980
+ }
981
+
982
+ testEditor.dispatch(pinchStartEvent)
983
+ // Pinch events are batched for the next tick, so emit a tick to flush them
984
+ testEditor.emit('tick', 16)
985
+
986
+ expect(eventHandler).toHaveBeenCalledWith(pinchStartEvent)
987
+ })
988
+
989
+ it('emits pinch events through the event emitter', () => {
990
+ const eventHandler = vi.fn()
991
+ testEditor.on('event', eventHandler)
992
+
993
+ // First dispatch pinch_start to set isPinching
994
+ const pinchStartEvent = {
995
+ type: 'pinch' as const,
996
+ name: 'pinch_start' as const,
997
+ point: { x: 100, y: 100, z: 1 },
998
+ delta: { x: 0, y: 0, z: 0 },
999
+ shiftKey: false,
1000
+ altKey: false,
1001
+ ctrlKey: false,
1002
+ metaKey: false,
1003
+ accelKey: false,
1004
+ }
1005
+ testEditor.dispatch(pinchStartEvent)
1006
+ testEditor.emit('tick', 16)
1007
+
1008
+ eventHandler.mockClear()
1009
+
1010
+ const pinchEvent = {
1011
+ type: 'pinch' as const,
1012
+ name: 'pinch' as const,
1013
+ point: { x: 100, y: 100, z: 1.5 },
1014
+ delta: { x: 10, y: 10, z: 0 },
1015
+ shiftKey: false,
1016
+ altKey: false,
1017
+ ctrlKey: false,
1018
+ metaKey: false,
1019
+ accelKey: false,
1020
+ }
1021
+
1022
+ testEditor.dispatch(pinchEvent)
1023
+ testEditor.emit('tick', 16)
1024
+
1025
+ expect(eventHandler).toHaveBeenCalledWith(pinchEvent)
1026
+ })
1027
+
1028
+ it('emits pinch_end events through the event emitter', () => {
1029
+ const eventHandler = vi.fn()
1030
+ testEditor.on('event', eventHandler)
1031
+
1032
+ // First dispatch pinch_start to set isPinching
1033
+ const pinchStartEvent = {
1034
+ type: 'pinch' as const,
1035
+ name: 'pinch_start' as const,
1036
+ point: { x: 100, y: 100, z: 1 },
1037
+ delta: { x: 0, y: 0, z: 0 },
1038
+ shiftKey: false,
1039
+ altKey: false,
1040
+ ctrlKey: false,
1041
+ metaKey: false,
1042
+ accelKey: false,
1043
+ }
1044
+ testEditor.dispatch(pinchStartEvent)
1045
+ testEditor.emit('tick', 16)
1046
+
1047
+ eventHandler.mockClear()
1048
+
1049
+ const pinchEndEvent = {
1050
+ type: 'pinch' as const,
1051
+ name: 'pinch_end' as const,
1052
+ point: { x: 100, y: 100, z: 1.5 },
1053
+ delta: { x: 0, y: 0, z: 0 },
1054
+ shiftKey: false,
1055
+ altKey: false,
1056
+ ctrlKey: false,
1057
+ metaKey: false,
1058
+ accelKey: false,
1059
+ }
1060
+
1061
+ testEditor.dispatch(pinchEndEvent)
1062
+ testEditor.emit('tick', 16)
1063
+
1064
+ expect(eventHandler).toHaveBeenCalledWith(pinchEndEvent)
1065
+ })
1066
+ })
1067
+
928
1068
  describe('setTool', () => {
929
1069
  class CustomToolA extends StateNode {
930
1070
  static override id = 'custom-tool-a'