@tldraw/editor 4.1.0-canary.4197e7ab8600 → 4.1.0-canary.432d7b79aa6b

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.
@@ -134,14 +134,68 @@ class HandleSnaps {
134
134
  if (nearestPointOnOutline) return nearestPointOnOutline;
135
135
  return null;
136
136
  }
137
+ getHandleSnapData({
138
+ handle,
139
+ currentShapeId
140
+ }) {
141
+ const snapThreshold = this.manager.getSnapThreshold();
142
+ const currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId));
143
+ const handleInPageSpace = currentShapeTransform.applyToPoint(handle);
144
+ let nearestXSnap = null;
145
+ let nearestYSnap = null;
146
+ let minOffsetX = snapThreshold;
147
+ let minOffsetY = snapThreshold;
148
+ for (const snapPoint of this.iterateSnapPointsInPageSpace(currentShapeId, handle)) {
149
+ const offsetX = Math.abs(handleInPageSpace.x - snapPoint.x);
150
+ const offsetY = Math.abs(handleInPageSpace.y - snapPoint.y);
151
+ if (offsetX < minOffsetX) {
152
+ minOffsetX = offsetX;
153
+ nearestXSnap = snapPoint;
154
+ }
155
+ if (offsetY < minOffsetY) {
156
+ minOffsetY = offsetY;
157
+ nearestYSnap = snapPoint;
158
+ }
159
+ }
160
+ if (!nearestXSnap && !nearestYSnap) {
161
+ return null;
162
+ }
163
+ const nudge = new Vec(
164
+ nearestXSnap ? nearestXSnap.x - handleInPageSpace.x : 0,
165
+ nearestYSnap ? nearestYSnap.y - handleInPageSpace.y : 0
166
+ );
167
+ const snappedHandle = Vec.Add(handleInPageSpace, nudge);
168
+ const snaps = [];
169
+ if (nearestXSnap) {
170
+ const snappedHandleOnX = new Vec(nearestXSnap.x, snappedHandle.y);
171
+ snaps.push({
172
+ id: uniqueId(),
173
+ type: "points",
174
+ points: [nearestXSnap, snappedHandleOnX]
175
+ });
176
+ }
177
+ if (nearestYSnap) {
178
+ const snappedHandleOnY = new Vec(snappedHandle.x, nearestYSnap.y);
179
+ snaps.push({
180
+ id: uniqueId(),
181
+ type: "points",
182
+ points: [nearestYSnap, snappedHandleOnY]
183
+ });
184
+ }
185
+ return { snaps, nudge };
186
+ }
137
187
  snapHandle({
138
188
  currentShapeId,
139
189
  handle
140
190
  }) {
141
191
  const currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId));
142
192
  const handleInPageSpace = currentShapeTransform.applyToPoint(handle);
143
- const snapPosition = this.getHandleSnapPosition({ currentShapeId, handle, handleInPageSpace });
144
- if (snapPosition) {
193
+ const snapType = handle.canSnap ? "point" : handle.snapType;
194
+ if (snapType === "point") {
195
+ const snapPosition = this.getHandleSnapPosition({ currentShapeId, handle, handleInPageSpace });
196
+ if (!snapPosition) {
197
+ return null;
198
+ }
145
199
  this.manager.setIndicators([
146
200
  {
147
201
  id: uniqueId(),
@@ -151,6 +205,17 @@ class HandleSnaps {
151
205
  ]);
152
206
  return { nudge: Vec.Sub(snapPosition, handleInPageSpace) };
153
207
  }
208
+ if (snapType === "align") {
209
+ const snapData = this.getHandleSnapData({
210
+ handle,
211
+ currentShapeId
212
+ });
213
+ if (!snapData) {
214
+ return null;
215
+ }
216
+ this.manager.setIndicators(snapData.snaps);
217
+ return { nudge: snapData.nudge };
218
+ }
154
219
  return null;
155
220
  }
156
221
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/editor/managers/SnapManager/HandleSnaps.ts"],
4
- "sourcesContent": ["import { computed } from '@tldraw/state'\nimport { TLHandle, TLShape, TLShapeId, VecModel } from '@tldraw/tlschema'\nimport { assertExists, uniqueId } from '@tldraw/utils'\nimport { Vec } from '../../../primitives/Vec'\nimport { Geometry2d } from '../../../primitives/geometry/Geometry2d'\nimport { Editor } from '../../Editor'\nimport { SnapData, SnapManager } from './SnapManager'\n\n/**\n * When dragging a handle, users can snap the handle to key geometry on other nearby shapes.\n * Customize how handles snap to a shape by returning this from\n * {@link ShapeUtil.getHandleSnapGeometry}.\n *\n * Any co-ordinates here should be in the shape's local space.\n *\n * @public\n */\nexport interface HandleSnapGeometry {\n\t/**\n\t * A `Geometry2d` that describe the outline of the shape that the handle will snap to - fills\n\t * are ignored. By default, this is the same geometry returned by {@link ShapeUtil.getGeometry}.\n\t * Set this to `null` to disable handle snapping to this shape's outline.\n\t */\n\toutline?: Geometry2d | null\n\t/**\n\t * Key points on the shape that the handle will snap to. For example, the corners of a\n\t * rectangle, or the centroid of a triangle. By default, no points are used.\n\t */\n\tpoints?: VecModel[]\n\t/**\n\t * By default, handles can't snap to their own shape because moving the handle might change the\n\t * snapping location which can cause feedback loops. You can override this by returning a\n\t * version of `outline` that won't be affected by the current handle's position to use for\n\t * self-snapping.\n\t */\n\tgetSelfSnapOutline?(handle: TLHandle): Geometry2d | null\n\t/**\n\t * By default, handles can't snap to their own shape because moving the handle might change the\n\t * snapping location which can cause feedback loops. You can override this by returning a\n\t * version of `points` that won't be affected by the current handle's position to use for\n\t * self-snapping.\n\t */\n\tgetSelfSnapPoints?(handle: TLHandle): VecModel[]\n}\n\nconst defaultGetSelfSnapOutline = () => null\nconst defaultGetSelfSnapPoints = () => []\n/** @public */\nexport class HandleSnaps {\n\treadonly editor: Editor\n\tconstructor(readonly manager: SnapManager) {\n\t\tthis.editor = manager.editor\n\t}\n\n\t@computed private getSnapGeometryCache() {\n\t\tconst { editor } = this\n\t\treturn editor.store.createComputedCache('handle snap geometry', (shape: TLShape) => {\n\t\t\tconst snapGeometry = editor.getShapeUtil(shape).getHandleSnapGeometry(shape)\n\t\t\tconst getSelfSnapOutline = snapGeometry.getSelfSnapOutline\n\t\t\t\t? snapGeometry.getSelfSnapOutline.bind(snapGeometry)\n\t\t\t\t: defaultGetSelfSnapOutline\n\t\t\tconst getSelfSnapPoints = snapGeometry.getSelfSnapPoints\n\t\t\t\t? snapGeometry.getSelfSnapPoints.bind(snapGeometry)\n\t\t\t\t: defaultGetSelfSnapPoints\n\n\t\t\treturn {\n\t\t\t\toutline:\n\t\t\t\t\tsnapGeometry.outline === undefined\n\t\t\t\t\t\t? editor.getShapeGeometry(shape)\n\t\t\t\t\t\t: snapGeometry.outline,\n\n\t\t\t\tpoints: snapGeometry.points ?? [],\n\t\t\t\tgetSelfSnapOutline,\n\t\t\t\tgetSelfSnapPoints,\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate *iterateSnapPointsInPageSpace(currentShapeId: TLShapeId, currentHandle: TLHandle) {\n\t\tconst selfSnapPoints = this.getSnapGeometryCache()\n\t\t\t.get(currentShapeId)\n\t\t\t?.getSelfSnapPoints(currentHandle)\n\t\tif (selfSnapPoints && selfSnapPoints.length) {\n\t\t\tconst shapePageTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))\n\t\t\tfor (const point of selfSnapPoints) {\n\t\t\t\tyield shapePageTransform.applyToPoint(point)\n\t\t\t}\n\t\t}\n\n\t\tfor (const shapeId of this.manager.getSnappableShapes()) {\n\t\t\tif (shapeId === currentShapeId) continue\n\t\t\tconst snapPoints = this.getSnapGeometryCache().get(shapeId)?.points\n\t\t\tif (!snapPoints || !snapPoints.length) continue\n\n\t\t\tconst shapePageTransform = assertExists(this.editor.getShapePageTransform(shapeId))\n\t\t\tfor (const point of snapPoints) {\n\t\t\t\tyield shapePageTransform.applyToPoint(point)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate *iterateSnapOutlines(currentShapeId: TLShapeId, currentHandle: TLHandle) {\n\t\tconst selfSnapOutline = this.getSnapGeometryCache()\n\t\t\t.get(currentShapeId)\n\t\t\t?.getSelfSnapOutline(currentHandle)\n\t\tif (selfSnapOutline) {\n\t\t\tyield { shapeId: currentShapeId, outline: selfSnapOutline }\n\t\t}\n\n\t\tfor (const shapeId of this.manager.getSnappableShapes()) {\n\t\t\tif (shapeId === currentShapeId) continue\n\n\t\t\tconst snapOutline = this.getSnapGeometryCache().get(shapeId)?.outline\n\t\t\tif (!snapOutline) continue\n\n\t\t\tyield { shapeId, outline: snapOutline }\n\t\t}\n\t}\n\n\tprivate getHandleSnapPosition({\n\t\tcurrentShapeId,\n\t\thandle,\n\t\thandleInPageSpace,\n\t}: {\n\t\tcurrentShapeId: TLShapeId\n\t\thandle: TLHandle\n\t\thandleInPageSpace: Vec\n\t}): Vec | null {\n\t\tconst snapThreshold = this.manager.getSnapThreshold()\n\n\t\t// We snap to two different parts of the shape's handle snap geometry:\n\t\t// 1. The `points`. These are handles or other key points that we want to snap to with a\n\t\t// higher priority than the normal outline snapping.\n\t\t// 2. The `outline`. This describes the outline of the shape, and we just snap to the\n\t\t// nearest point on that outline.\n\n\t\t// Start with the points:\n\t\tlet minDistanceForSnapPoint = snapThreshold\n\t\tlet nearestSnapPoint: Vec | null = null\n\t\tfor (const snapPoint of this.iterateSnapPointsInPageSpace(currentShapeId, handle)) {\n\t\t\tif (Vec.DistMin(handleInPageSpace, snapPoint, minDistanceForSnapPoint)) {\n\t\t\t\tminDistanceForSnapPoint = Vec.Dist(handleInPageSpace, snapPoint)\n\t\t\t\tnearestSnapPoint = snapPoint\n\t\t\t}\n\t\t}\n\n\t\t// if we found a snap point, return it - we don't need to check the outlines because points\n\t\t// have a higher priority\n\t\tif (nearestSnapPoint) return nearestSnapPoint\n\n\t\tlet minDistanceForOutline = snapThreshold\n\t\tlet nearestPointOnOutline: Vec | null = null\n\n\t\tfor (const { shapeId, outline } of this.iterateSnapOutlines(currentShapeId, handle)) {\n\t\t\tconst shapePageTransform = assertExists(this.editor.getShapePageTransform(shapeId))\n\t\t\tconst pointInShapeSpace = this.editor.getPointInShapeSpace(shapeId, handleInPageSpace)\n\n\t\t\tconst nearestShapePointInShapeSpace = outline.nearestPoint(pointInShapeSpace)\n\t\t\tconst nearestInPageSpace = shapePageTransform.applyToPoint(nearestShapePointInShapeSpace)\n\n\t\t\tif (Vec.DistMin(handleInPageSpace, nearestInPageSpace, minDistanceForOutline)) {\n\t\t\t\tminDistanceForOutline = Vec.Dist(handleInPageSpace, nearestInPageSpace)\n\t\t\t\tnearestPointOnOutline = nearestInPageSpace\n\t\t\t}\n\t\t}\n\n\t\t// if we found a point on the outline, return it\n\t\tif (nearestPointOnOutline) return nearestPointOnOutline\n\n\t\t// if not, there's no nearby snap point\n\t\treturn null\n\t}\n\n\tsnapHandle({\n\t\tcurrentShapeId,\n\t\thandle,\n\t}: {\n\t\tcurrentShapeId: TLShapeId\n\t\thandle: TLHandle\n\t}): SnapData | null {\n\t\tconst currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))\n\t\tconst handleInPageSpace = currentShapeTransform.applyToPoint(handle)\n\t\tconst snapPosition = this.getHandleSnapPosition({ currentShapeId, handle, handleInPageSpace })\n\n\t\t// If we found a point, display snap lines, and return the nudge\n\t\tif (snapPosition) {\n\t\t\tthis.manager.setIndicators([\n\t\t\t\t{\n\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\ttype: 'points',\n\t\t\t\t\tpoints: [snapPosition],\n\t\t\t\t},\n\t\t\t])\n\n\t\t\treturn { nudge: Vec.Sub(snapPosition, handleInPageSpace) }\n\t\t}\n\n\t\treturn null\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { computed } from '@tldraw/state'\nimport { TLHandle, TLShape, TLShapeId, VecModel } from '@tldraw/tlschema'\nimport { assertExists, uniqueId } from '@tldraw/utils'\nimport { Vec } from '../../../primitives/Vec'\nimport { Geometry2d } from '../../../primitives/geometry/Geometry2d'\nimport { Editor } from '../../Editor'\nimport { PointsSnapIndicator, SnapData, SnapManager } from './SnapManager'\n\n/**\n * When dragging a handle, users can snap the handle to key geometry on other nearby shapes.\n * Customize how handles snap to a shape by returning this from\n * {@link ShapeUtil.getHandleSnapGeometry}.\n *\n * Any co-ordinates here should be in the shape's local space.\n *\n * @public\n */\nexport interface HandleSnapGeometry {\n\t/**\n\t * A `Geometry2d` that describe the outline of the shape that the handle will snap to - fills\n\t * are ignored. By default, this is the same geometry returned by {@link ShapeUtil.getGeometry}.\n\t * Set this to `null` to disable handle snapping to this shape's outline.\n\t */\n\toutline?: Geometry2d | null\n\t/**\n\t * Key points on the shape that the handle will snap to. For example, the corners of a\n\t * rectangle, or the centroid of a triangle. By default, no points are used.\n\t */\n\tpoints?: VecModel[]\n\t/**\n\t * By default, handles can't snap to their own shape because moving the handle might change the\n\t * snapping location which can cause feedback loops. You can override this by returning a\n\t * version of `outline` that won't be affected by the current handle's position to use for\n\t * self-snapping.\n\t */\n\tgetSelfSnapOutline?(handle: TLHandle): Geometry2d | null\n\t/**\n\t * By default, handles can't snap to their own shape because moving the handle might change the\n\t * snapping location which can cause feedback loops. You can override this by returning a\n\t * version of `points` that won't be affected by the current handle's position to use for\n\t * self-snapping.\n\t */\n\tgetSelfSnapPoints?(handle: TLHandle): VecModel[]\n}\n\ninterface AlignPointsSnap {\n\tsnaps: PointsSnapIndicator[]\n\tnudge: Vec\n}\n\nconst defaultGetSelfSnapOutline = () => null\nconst defaultGetSelfSnapPoints = () => []\n/** @public */\nexport class HandleSnaps {\n\treadonly editor: Editor\n\tconstructor(readonly manager: SnapManager) {\n\t\tthis.editor = manager.editor\n\t}\n\n\t@computed private getSnapGeometryCache() {\n\t\tconst { editor } = this\n\t\treturn editor.store.createComputedCache('handle snap geometry', (shape: TLShape) => {\n\t\t\tconst snapGeometry = editor.getShapeUtil(shape).getHandleSnapGeometry(shape)\n\t\t\tconst getSelfSnapOutline = snapGeometry.getSelfSnapOutline\n\t\t\t\t? snapGeometry.getSelfSnapOutline.bind(snapGeometry)\n\t\t\t\t: defaultGetSelfSnapOutline\n\t\t\tconst getSelfSnapPoints = snapGeometry.getSelfSnapPoints\n\t\t\t\t? snapGeometry.getSelfSnapPoints.bind(snapGeometry)\n\t\t\t\t: defaultGetSelfSnapPoints\n\n\t\t\treturn {\n\t\t\t\toutline:\n\t\t\t\t\tsnapGeometry.outline === undefined\n\t\t\t\t\t\t? editor.getShapeGeometry(shape)\n\t\t\t\t\t\t: snapGeometry.outline,\n\n\t\t\t\tpoints: snapGeometry.points ?? [],\n\t\t\t\tgetSelfSnapOutline,\n\t\t\t\tgetSelfSnapPoints,\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate *iterateSnapPointsInPageSpace(currentShapeId: TLShapeId, currentHandle: TLHandle) {\n\t\tconst selfSnapPoints = this.getSnapGeometryCache()\n\t\t\t.get(currentShapeId)\n\t\t\t?.getSelfSnapPoints(currentHandle)\n\t\tif (selfSnapPoints && selfSnapPoints.length) {\n\t\t\tconst shapePageTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))\n\t\t\tfor (const point of selfSnapPoints) {\n\t\t\t\tyield shapePageTransform.applyToPoint(point)\n\t\t\t}\n\t\t}\n\n\t\tfor (const shapeId of this.manager.getSnappableShapes()) {\n\t\t\tif (shapeId === currentShapeId) continue\n\t\t\tconst snapPoints = this.getSnapGeometryCache().get(shapeId)?.points\n\t\t\tif (!snapPoints || !snapPoints.length) continue\n\n\t\t\tconst shapePageTransform = assertExists(this.editor.getShapePageTransform(shapeId))\n\t\t\tfor (const point of snapPoints) {\n\t\t\t\tyield shapePageTransform.applyToPoint(point)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate *iterateSnapOutlines(currentShapeId: TLShapeId, currentHandle: TLHandle) {\n\t\tconst selfSnapOutline = this.getSnapGeometryCache()\n\t\t\t.get(currentShapeId)\n\t\t\t?.getSelfSnapOutline(currentHandle)\n\t\tif (selfSnapOutline) {\n\t\t\tyield { shapeId: currentShapeId, outline: selfSnapOutline }\n\t\t}\n\n\t\tfor (const shapeId of this.manager.getSnappableShapes()) {\n\t\t\tif (shapeId === currentShapeId) continue\n\n\t\t\tconst snapOutline = this.getSnapGeometryCache().get(shapeId)?.outline\n\t\t\tif (!snapOutline) continue\n\n\t\t\tyield { shapeId, outline: snapOutline }\n\t\t}\n\t}\n\n\tprivate getHandleSnapPosition({\n\t\tcurrentShapeId,\n\t\thandle,\n\t\thandleInPageSpace,\n\t}: {\n\t\tcurrentShapeId: TLShapeId\n\t\thandle: TLHandle\n\t\thandleInPageSpace: Vec\n\t}): Vec | null {\n\t\tconst snapThreshold = this.manager.getSnapThreshold()\n\n\t\t// We snap to two different parts of the shape's handle snap geometry:\n\t\t// 1. The `points`. These are handles or other key points that we want to snap to with a\n\t\t// higher priority than the normal outline snapping.\n\t\t// 2. The `outline`. This describes the outline of the shape, and we just snap to the\n\t\t// nearest point on that outline.\n\n\t\t// Start with the points:\n\t\tlet minDistanceForSnapPoint = snapThreshold\n\t\tlet nearestSnapPoint: Vec | null = null\n\t\tfor (const snapPoint of this.iterateSnapPointsInPageSpace(currentShapeId, handle)) {\n\t\t\tif (Vec.DistMin(handleInPageSpace, snapPoint, minDistanceForSnapPoint)) {\n\t\t\t\tminDistanceForSnapPoint = Vec.Dist(handleInPageSpace, snapPoint)\n\t\t\t\tnearestSnapPoint = snapPoint\n\t\t\t}\n\t\t}\n\n\t\t// if we found a snap point, return it - we don't need to check the outlines because points\n\t\t// have a higher priority\n\t\tif (nearestSnapPoint) return nearestSnapPoint\n\n\t\tlet minDistanceForOutline = snapThreshold\n\t\tlet nearestPointOnOutline: Vec | null = null\n\n\t\tfor (const { shapeId, outline } of this.iterateSnapOutlines(currentShapeId, handle)) {\n\t\t\tconst shapePageTransform = assertExists(this.editor.getShapePageTransform(shapeId))\n\t\t\tconst pointInShapeSpace = this.editor.getPointInShapeSpace(shapeId, handleInPageSpace)\n\n\t\t\tconst nearestShapePointInShapeSpace = outline.nearestPoint(pointInShapeSpace)\n\t\t\tconst nearestInPageSpace = shapePageTransform.applyToPoint(nearestShapePointInShapeSpace)\n\n\t\t\tif (Vec.DistMin(handleInPageSpace, nearestInPageSpace, minDistanceForOutline)) {\n\t\t\t\tminDistanceForOutline = Vec.Dist(handleInPageSpace, nearestInPageSpace)\n\t\t\t\tnearestPointOnOutline = nearestInPageSpace\n\t\t\t}\n\t\t}\n\n\t\t// if we found a point on the outline, return it\n\t\tif (nearestPointOnOutline) return nearestPointOnOutline\n\n\t\t// if not, there's no nearby snap point\n\t\treturn null\n\t}\n\n\tprivate getHandleSnapData({\n\t\thandle,\n\t\tcurrentShapeId,\n\t}: {\n\t\thandle: TLHandle\n\t\tcurrentShapeId: TLShapeId\n\t}): AlignPointsSnap | null {\n\t\tconst snapThreshold = this.manager.getSnapThreshold()\n\t\tconst currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))\n\t\tconst handleInPageSpace = currentShapeTransform.applyToPoint(handle)\n\n\t\tlet nearestXSnap: Vec | null = null\n\t\tlet nearestYSnap: Vec | null = null\n\t\tlet minOffsetX = snapThreshold\n\t\tlet minOffsetY = snapThreshold\n\n\t\tfor (const snapPoint of this.iterateSnapPointsInPageSpace(currentShapeId, handle)) {\n\t\t\tconst offsetX = Math.abs(handleInPageSpace.x - snapPoint.x)\n\t\t\tconst offsetY = Math.abs(handleInPageSpace.y - snapPoint.y)\n\t\t\tif (offsetX < minOffsetX) {\n\t\t\t\tminOffsetX = offsetX\n\t\t\t\tnearestXSnap = snapPoint\n\t\t\t}\n\t\t\tif (offsetY < minOffsetY) {\n\t\t\t\tminOffsetY = offsetY\n\t\t\t\tnearestYSnap = snapPoint\n\t\t\t}\n\t\t}\n\n\t\tif (!nearestXSnap && !nearestYSnap) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst nudge = new Vec(\n\t\t\tnearestXSnap ? nearestXSnap.x - handleInPageSpace.x : 0,\n\t\t\tnearestYSnap ? nearestYSnap.y - handleInPageSpace.y : 0\n\t\t)\n\n\t\tconst snappedHandle = Vec.Add(handleInPageSpace, nudge)\n\t\tconst snaps: PointsSnapIndicator[] = []\n\n\t\tif (nearestXSnap) {\n\t\t\tconst snappedHandleOnX = new Vec(nearestXSnap.x, snappedHandle.y)\n\t\t\tsnaps.push({\n\t\t\t\tid: uniqueId(),\n\t\t\t\ttype: 'points',\n\t\t\t\tpoints: [nearestXSnap, snappedHandleOnX],\n\t\t\t})\n\t\t}\n\t\tif (nearestYSnap) {\n\t\t\tconst snappedHandleOnY = new Vec(snappedHandle.x, nearestYSnap.y)\n\t\t\tsnaps.push({\n\t\t\t\tid: uniqueId(),\n\t\t\t\ttype: 'points',\n\t\t\t\tpoints: [nearestYSnap, snappedHandleOnY],\n\t\t\t})\n\t\t}\n\n\t\treturn { snaps, nudge }\n\t}\n\n\tsnapHandle({\n\t\tcurrentShapeId,\n\t\thandle,\n\t}: {\n\t\tcurrentShapeId: TLShapeId\n\t\thandle: TLHandle\n\t}): SnapData | null {\n\t\tconst currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))\n\t\tconst handleInPageSpace = currentShapeTransform.applyToPoint(handle)\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tconst snapType = handle.canSnap ? 'point' : handle.snapType\n\n\t\tif (snapType === 'point') {\n\t\t\tconst snapPosition = this.getHandleSnapPosition({ currentShapeId, handle, handleInPageSpace })\n\n\t\t\tif (!snapPosition) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tthis.manager.setIndicators([\n\t\t\t\t{\n\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\ttype: 'points',\n\t\t\t\t\tpoints: [snapPosition],\n\t\t\t\t},\n\t\t\t])\n\n\t\t\treturn { nudge: Vec.Sub(snapPosition, handleInPageSpace) }\n\t\t}\n\n\t\tif (snapType === 'align') {\n\t\t\tconst snapData = this.getHandleSnapData({\n\t\t\t\thandle,\n\t\t\t\tcurrentShapeId,\n\t\t\t})\n\n\t\t\tif (!snapData) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tthis.manager.setIndicators(snapData.snaps)\n\n\t\t\treturn { nudge: snapData.nudge }\n\t\t}\n\n\t\treturn null\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -66,6 +66,8 @@ class Geometry2d {
66
66
  distanceToLineSegment(A, B, filters) {
67
67
  if (Vec.Equals(A, B)) return this.distanceToPoint(A, false, filters);
68
68
  const { vertices } = this;
69
+ if (vertices.length === 0) throw Error("nearest point not found");
70
+ if (vertices.length === 1) return Vec.Dist(A, vertices[0]);
69
71
  let nearest;
70
72
  let dist = Infinity;
71
73
  let d, p, q;
@@ -108,6 +110,8 @@ class Geometry2d {
108
110
  */
109
111
  interpolateAlongEdge(t, _filters) {
110
112
  const { vertices } = this;
113
+ if (vertices.length === 0) return new Vec(0, 0);
114
+ if (vertices.length === 1) return vertices[0];
111
115
  if (t <= 0) return vertices[0];
112
116
  const distanceToTravel = t * this.length;
113
117
  let distanceTraveled = 0;
@@ -137,6 +141,7 @@ class Geometry2d {
137
141
  let closestSegment = null;
138
142
  let closestDistance = Infinity;
139
143
  let distanceTraveled = 0;
144
+ if (vertices.length === 0 || vertices.length === 1) return 0;
140
145
  for (let i = 0; i < (this.isClosed ? vertices.length : vertices.length - 1); i++) {
141
146
  const curr = vertices[i];
142
147
  const next = vertices[(i + 1) % vertices.length];
@@ -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\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 (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\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\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,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
  }
@@ -1,8 +1,8 @@
1
- const version = "4.1.0-canary.4197e7ab8600";
1
+ const version = "4.1.0-canary.432d7b79aa6b";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2025-10-06T10:07:52.201Z",
5
- patch: "2025-10-06T10:07:52.201Z"
4
+ minor: "2025-10-08T12:04:52.202Z",
5
+ patch: "2025-10-08T12:04:52.202Z"
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.1.0-canary.4197e7ab8600'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-06T10:07:52.201Z',\n\tpatch: '2025-10-06T10:07:52.201Z',\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.1.0-canary.432d7b79aa6b'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-08T12:04:52.202Z',\n\tpatch: '2025-10-08T12:04:52.202Z',\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/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.1.0-canary.4197e7ab8600",
4
+ "version": "4.1.0-canary.432d7b79aa6b",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -50,12 +50,12 @@
50
50
  "@tiptap/core": "^2.9.1",
51
51
  "@tiptap/pm": "^2.9.1",
52
52
  "@tiptap/react": "^2.9.1",
53
- "@tldraw/state": "4.1.0-canary.4197e7ab8600",
54
- "@tldraw/state-react": "4.1.0-canary.4197e7ab8600",
55
- "@tldraw/store": "4.1.0-canary.4197e7ab8600",
56
- "@tldraw/tlschema": "4.1.0-canary.4197e7ab8600",
57
- "@tldraw/utils": "4.1.0-canary.4197e7ab8600",
58
- "@tldraw/validate": "4.1.0-canary.4197e7ab8600",
53
+ "@tldraw/state": "4.1.0-canary.432d7b79aa6b",
54
+ "@tldraw/state-react": "4.1.0-canary.432d7b79aa6b",
55
+ "@tldraw/store": "4.1.0-canary.432d7b79aa6b",
56
+ "@tldraw/tlschema": "4.1.0-canary.432d7b79aa6b",
57
+ "@tldraw/utils": "4.1.0-canary.432d7b79aa6b",
58
+ "@tldraw/validate": "4.1.0-canary.432d7b79aa6b",
59
59
  "@types/core-js": "^2.5.8",
60
60
  "@use-gesture/react": "^10.3.1",
61
61
  "classnames": "^2.5.1",
@@ -69,6 +69,7 @@ import {
69
69
  JsonObject,
70
70
  PerformanceTracker,
71
71
  Result,
72
+ ZERO_INDEX_KEY,
72
73
  annotateError,
73
74
  assert,
74
75
  assertExists,
@@ -5663,7 +5664,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5663
5664
  const children = this._parentIdsToChildIds.get()[parentId]
5664
5665
 
5665
5666
  if (!children || children.length === 0) {
5666
- return 'a1' as IndexKey
5667
+ return getIndexAbove(ZERO_INDEX_KEY)
5667
5668
  }
5668
5669
  const shape = this.getShape(children[children.length - 1])!
5669
5670
  return getIndexAbove(shape.index)
@@ -4,7 +4,7 @@ import { assertExists, uniqueId } from '@tldraw/utils'
4
4
  import { Vec } from '../../../primitives/Vec'
5
5
  import { Geometry2d } from '../../../primitives/geometry/Geometry2d'
6
6
  import { Editor } from '../../Editor'
7
- import { SnapData, SnapManager } from './SnapManager'
7
+ import { PointsSnapIndicator, SnapData, SnapManager } from './SnapManager'
8
8
 
9
9
  /**
10
10
  * When dragging a handle, users can snap the handle to key geometry on other nearby shapes.
@@ -43,6 +43,11 @@ export interface HandleSnapGeometry {
43
43
  getSelfSnapPoints?(handle: TLHandle): VecModel[]
44
44
  }
45
45
 
46
+ interface AlignPointsSnap {
47
+ snaps: PointsSnapIndicator[]
48
+ nudge: Vec
49
+ }
50
+
46
51
  const defaultGetSelfSnapOutline = () => null
47
52
  const defaultGetSelfSnapPoints = () => []
48
53
  /** @public */
@@ -171,6 +176,67 @@ export class HandleSnaps {
171
176
  return null
172
177
  }
173
178
 
179
+ private getHandleSnapData({
180
+ handle,
181
+ currentShapeId,
182
+ }: {
183
+ handle: TLHandle
184
+ currentShapeId: TLShapeId
185
+ }): AlignPointsSnap | null {
186
+ const snapThreshold = this.manager.getSnapThreshold()
187
+ const currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))
188
+ const handleInPageSpace = currentShapeTransform.applyToPoint(handle)
189
+
190
+ let nearestXSnap: Vec | null = null
191
+ let nearestYSnap: Vec | null = null
192
+ let minOffsetX = snapThreshold
193
+ let minOffsetY = snapThreshold
194
+
195
+ for (const snapPoint of this.iterateSnapPointsInPageSpace(currentShapeId, handle)) {
196
+ const offsetX = Math.abs(handleInPageSpace.x - snapPoint.x)
197
+ const offsetY = Math.abs(handleInPageSpace.y - snapPoint.y)
198
+ if (offsetX < minOffsetX) {
199
+ minOffsetX = offsetX
200
+ nearestXSnap = snapPoint
201
+ }
202
+ if (offsetY < minOffsetY) {
203
+ minOffsetY = offsetY
204
+ nearestYSnap = snapPoint
205
+ }
206
+ }
207
+
208
+ if (!nearestXSnap && !nearestYSnap) {
209
+ return null
210
+ }
211
+
212
+ const nudge = new Vec(
213
+ nearestXSnap ? nearestXSnap.x - handleInPageSpace.x : 0,
214
+ nearestYSnap ? nearestYSnap.y - handleInPageSpace.y : 0
215
+ )
216
+
217
+ const snappedHandle = Vec.Add(handleInPageSpace, nudge)
218
+ const snaps: PointsSnapIndicator[] = []
219
+
220
+ if (nearestXSnap) {
221
+ const snappedHandleOnX = new Vec(nearestXSnap.x, snappedHandle.y)
222
+ snaps.push({
223
+ id: uniqueId(),
224
+ type: 'points',
225
+ points: [nearestXSnap, snappedHandleOnX],
226
+ })
227
+ }
228
+ if (nearestYSnap) {
229
+ const snappedHandleOnY = new Vec(snappedHandle.x, nearestYSnap.y)
230
+ snaps.push({
231
+ id: uniqueId(),
232
+ type: 'points',
233
+ points: [nearestYSnap, snappedHandleOnY],
234
+ })
235
+ }
236
+
237
+ return { snaps, nudge }
238
+ }
239
+
174
240
  snapHandle({
175
241
  currentShapeId,
176
242
  handle,
@@ -180,10 +246,16 @@ export class HandleSnaps {
180
246
  }): SnapData | null {
181
247
  const currentShapeTransform = assertExists(this.editor.getShapePageTransform(currentShapeId))
182
248
  const handleInPageSpace = currentShapeTransform.applyToPoint(handle)
183
- const snapPosition = this.getHandleSnapPosition({ currentShapeId, handle, handleInPageSpace })
249
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
250
+ const snapType = handle.canSnap ? 'point' : handle.snapType
251
+
252
+ if (snapType === 'point') {
253
+ const snapPosition = this.getHandleSnapPosition({ currentShapeId, handle, handleInPageSpace })
254
+
255
+ if (!snapPosition) {
256
+ return null
257
+ }
184
258
 
185
- // If we found a point, display snap lines, and return the nudge
186
- if (snapPosition) {
187
259
  this.manager.setIndicators([
188
260
  {
189
261
  id: uniqueId(),
@@ -195,6 +267,21 @@ export class HandleSnaps {
195
267
  return { nudge: Vec.Sub(snapPosition, handleInPageSpace) }
196
268
  }
197
269
 
270
+ if (snapType === 'align') {
271
+ const snapData = this.getHandleSnapData({
272
+ handle,
273
+ currentShapeId,
274
+ })
275
+
276
+ if (!snapData) {
277
+ return null
278
+ }
279
+
280
+ this.manager.setIndicators(snapData.snaps)
281
+
282
+ return { nudge: snapData.nudge }
283
+ }
284
+
198
285
  return null
199
286
  }
200
287
  }
@@ -120,6 +120,8 @@ export abstract class Geometry2d {
120
120
  distanceToLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {
121
121
  if (Vec.Equals(A, B)) return this.distanceToPoint(A, false, filters)
122
122
  const { vertices } = this
123
+ if (vertices.length === 0) throw Error('nearest point not found')
124
+ if (vertices.length === 1) return Vec.Dist(A, vertices[0])
123
125
  let nearest: Vec | undefined
124
126
  let dist = Infinity
125
127
  let d: number, p: Vec, q: Vec
@@ -175,6 +177,8 @@ export abstract class Geometry2d {
175
177
  interpolateAlongEdge(t: number, _filters?: Geometry2dFilters): Vec {
176
178
  const { vertices } = this
177
179
 
180
+ if (vertices.length === 0) return new Vec(0, 0)
181
+ if (vertices.length === 1) return vertices[0]
178
182
  if (t <= 0) return vertices[0]
179
183
 
180
184
  const distanceToTravel = t * this.length
@@ -209,6 +213,8 @@ export abstract class Geometry2d {
209
213
  let closestDistance = Infinity
210
214
  let distanceTraveled = 0
211
215
 
216
+ if (vertices.length === 0 || vertices.length === 1) return 0
217
+
212
218
  for (let i = 0; i < (this.isClosed ? vertices.length : vertices.length - 1); i++) {
213
219
  const curr = vertices[i]
214
220
  const next = vertices[(i + 1) % vertices.length]
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '4.1.0-canary.4197e7ab8600'
4
+ export const version = '4.1.0-canary.432d7b79aa6b'
5
5
  export const publishDates = {
6
6
  major: '2025-09-18T14:39:22.803Z',
7
- minor: '2025-10-06T10:07:52.201Z',
8
- patch: '2025-10-06T10:07:52.201Z',
7
+ minor: '2025-10-08T12:04:52.202Z',
8
+ patch: '2025-10-08T12:04:52.202Z',
9
9
  }