{
  "version": 3,
  "sources": ["../../../../../src/lib/editor/managers/SnapManager/HandleSnaps.ts"],
  "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 type { Editor } from '../../Editor'\nimport type { 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"],
  "mappings": "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",
  "names": []
}
