UNPKG

tldraw

Version:

A tiny little drawing editor.

256 lines (255 loc) • 9.95 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var arrowTargetState_exports = {}; __export(arrowTargetState_exports, { clearArrowTargetState: () => clearArrowTargetState, getArrowTargetState: () => getArrowTargetState, updateArrowTargetState: () => updateArrowTargetState }); module.exports = __toCommonJS(arrowTargetState_exports); var import_editor = require("@tldraw/editor"); var import_definitions = require("./elbow/definitions"); const arrowTargetStore = new import_editor.WeakCache(); function getArrowTargetAtom(editor) { return arrowTargetStore.get(editor, () => (0, import_editor.atom)("arrowTarget", null)); } function getArrowTargetState(editor) { return getArrowTargetAtom(editor).get(); } function clearArrowTargetState(editor) { getArrowTargetAtom(editor).set(null); } function updateArrowTargetState({ editor, pointInPageSpace, arrow, isPrecise, currentBinding, oppositeBinding }) { const util = editor.getShapeUtil("arrow"); if (util.options.shouldIgnoreTargets(editor)) { getArrowTargetAtom(editor).set(null); return null; } const arrowKind = arrow ? arrow.props.kind : editor.getStyleForNextShape(import_editor.ArrowShapeKindStyle); const target = editor.getShapeAtPoint(pointInPageSpace, { hitInside: true, hitFrameInside: true, margin: arrowKind === "elbow" ? 8 : [8, 0], filter: (targetShape) => { return !targetShape.isLocked && editor.canBindShapes({ fromShape: arrow ?? targetFilterFallback, toShape: targetShape, binding: "arrow" }); } }); if (!target) { getArrowTargetAtom(editor).set(null); return null; } const targetGeometryInTargetSpace = editor.getShapeGeometry(target); const targetBoundsInTargetSpace = import_editor.Box.ZeroFix(targetGeometryInTargetSpace.bounds); const targetCenterInTargetSpace = targetGeometryInTargetSpace.center; const targetTransform = editor.getShapePageTransform(target); const pointInTargetSpace = editor.getPointInShapeSpace(target, pointInPageSpace); const castDistance = Math.max( targetGeometryInTargetSpace.bounds.width, targetGeometryInTargetSpace.bounds.height ); const handlesInPageSpace = (0, import_editor.mapObjectMapValues)(import_definitions.ElbowArrowSideDeltas, (side, delta) => { const axis = import_definitions.ElbowArrowAxes[import_definitions.ElbowArrowSideAxes[side]]; const farPoint = import_editor.Vec.Mul(delta, castDistance).add(targetCenterInTargetSpace); let isEnabled = false; let handlePointInTargetSpace = axis.v( targetBoundsInTargetSpace[side], targetBoundsInTargetSpace[axis.crossMid] ); let furthestDistance = 0; const intersections = targetGeometryInTargetSpace.intersectLineSegment( targetCenterInTargetSpace, farPoint, import_editor.Geometry2dFilters.EXCLUDE_NON_STANDARD ); for (const intersection of intersections) { const distance = import_editor.Vec.Dist2(intersection, targetCenterInTargetSpace); if (distance > furthestDistance) { furthestDistance = distance; handlePointInTargetSpace = intersection; isEnabled = targetGeometryInTargetSpace.isClosed; } } const handlePointInPageSpace = targetTransform.applyToPoint(handlePointInTargetSpace); return { point: handlePointInPageSpace, isEnabled, far: targetTransform.applyToPoint(farPoint) }; }); const zoomLevel = editor.getZoomLevel(); const minDistScaled = util.options.minElbowHandleDistance / zoomLevel; const targetCenterInPageSpace = targetTransform.applyToPoint(targetCenterInTargetSpace); for (const side of (0, import_editor.objectMapKeys)(handlesInPageSpace)) { const handle = handlesInPageSpace[side]; if (import_editor.Vec.DistMin(handle.point, targetCenterInPageSpace, minDistScaled)) { handle.isEnabled = false; } } let precise = isPrecise; if (!precise) { if (!currentBinding || currentBinding && target.id !== currentBinding.toId) { precise = editor.inputs.getPointerVelocity().len() < 0.5; } } if (!isPrecise) { if (!targetGeometryInTargetSpace.isClosed) { precise = true; } if (oppositeBinding && target.id === oppositeBinding.toId && oppositeBinding.props.isPrecise) { precise = true; } } const isExact = util.options.shouldBeExact(editor, precise); if (isExact) precise = true; const shouldSnapCenter = !isExact && precise && targetGeometryInTargetSpace.isClosed; const shouldSnapEdges = !isExact && (precise && arrowKind === "elbow" || !targetGeometryInTargetSpace.isClosed); const shouldSnapEdgePoints = !isExact && precise && arrowKind === "elbow" && targetGeometryInTargetSpace.isClosed; const shouldSnapNone = precise && (targetGeometryInTargetSpace.isClosed || isExact); const shouldSnapCenterAxis = !isExact && precise && arrowKind === "elbow" && targetGeometryInTargetSpace.isClosed; let snap = "none"; let anchorInPageSpace = pointInPageSpace; if (!shouldSnapNone) { snap = "center"; anchorInPageSpace = targetCenterInPageSpace; } if (shouldSnapEdges) { const snapDistance = shouldSnapNone ? calculateSnapDistance( editor, targetBoundsInTargetSpace, util.options.elbowArrowEdgeSnapDistance ) : Infinity; const nearestPointOnEdgeInTargetSpace = targetGeometryInTargetSpace.nearestPoint( pointInTargetSpace, { includeLabels: false, includeInternal: false } ); const nearestPointOnEdgeInPageSpace = targetTransform.applyToPoint( nearestPointOnEdgeInTargetSpace ); const distance = import_editor.Vec.Dist(nearestPointOnEdgeInPageSpace, pointInPageSpace); if (distance < snapDistance) { snap = "edge"; anchorInPageSpace = nearestPointOnEdgeInPageSpace; } } if (shouldSnapCenterAxis) { const snapDistance = calculateSnapDistance( editor, targetBoundsInTargetSpace, util.options.elbowArrowAxisSnapDistance ); const distanceFromXAxis = import_editor.Vec.DistanceToLineSegment( handlesInPageSpace.left.far, handlesInPageSpace.right.far, pointInPageSpace ); const distanceFromYAxis = import_editor.Vec.DistanceToLineSegment( handlesInPageSpace.top.far, handlesInPageSpace.bottom.far, pointInPageSpace ); const snapAxis = distanceFromXAxis < distanceFromYAxis && distanceFromXAxis < snapDistance ? "x" : distanceFromYAxis < snapDistance ? "y" : null; if (snapAxis) { const axis = import_definitions.ElbowArrowAxes[snapAxis]; const loDist2 = import_editor.Vec.Dist2(handlesInPageSpace[axis.loEdge].far, pointInPageSpace); const hiDist2 = import_editor.Vec.Dist2(handlesInPageSpace[axis.hiEdge].far, pointInPageSpace); const side = loDist2 < hiDist2 ? axis.loEdge : axis.hiEdge; if (handlesInPageSpace[side].isEnabled) { snap = "edge-point"; anchorInPageSpace = handlesInPageSpace[side].point; } } } if (shouldSnapEdgePoints) { const snapDistance = calculateSnapDistance( editor, targetBoundsInTargetSpace, util.options.elbowArrowPointSnapDistance ); let closestSide = null; let closestDistance = Infinity; for (const [side, handle] of (0, import_editor.objectMapEntries)(handlesInPageSpace)) { if (!handle.isEnabled) continue; const distance = import_editor.Vec.Dist(handle.point, pointInPageSpace); if (distance < snapDistance && distance < closestDistance) { closestDistance = distance; closestSide = side; } } if (closestSide) { snap = "edge-point"; anchorInPageSpace = handlesInPageSpace[closestSide].point; } } if (shouldSnapCenter) { const snapDistance = calculateSnapDistance( editor, targetBoundsInTargetSpace, arrowKind === "elbow" ? util.options.elbowArrowCenterSnapDistance : util.options.arcArrowCenterSnapDistance ); if (import_editor.Vec.Dist(pointInTargetSpace, targetBoundsInTargetSpace.center) < snapDistance) { snap = "center"; anchorInPageSpace = targetCenterInPageSpace; } } const snapPointInTargetSpace = editor.getPointInShapeSpace(target, anchorInPageSpace); const normalizedAnchor = { x: (0, import_editor.invLerp)( targetBoundsInTargetSpace.minX, targetBoundsInTargetSpace.maxX, snapPointInTargetSpace.x ), y: (0, import_editor.invLerp)( targetBoundsInTargetSpace.minY, targetBoundsInTargetSpace.maxY, snapPointInTargetSpace.y ) }; const result = { target, arrowKind, handlesInPageSpace, centerInPageSpace: targetCenterInPageSpace, anchorInPageSpace, isExact, isPrecise: precise, snap, normalizedAnchor }; getArrowTargetAtom(editor).set(result); return result; } const targetFilterFallback = { type: "arrow" }; function calculateSnapDistance(editor, targetBoundsInTargetSpace, idealSnapDistance) { return (0, import_editor.clamp)( Math.min(targetBoundsInTargetSpace.width, targetBoundsInTargetSpace.height) * 0.15, 4, idealSnapDistance ) / editor.getZoomLevel(); } //# sourceMappingURL=arrowTargetState.js.map