@weasel-js/core 1.9.3 → 1.9.4
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.
- package/CHANGELOG.md +356 -0
- package/README.md +2 -2
- package/dist/WeaselProvider.js +5 -5
- package/dist/affordances/composeAffordanceLayer.js +10 -20
- package/dist/affordances/composeAffordanceLayer.js.map +1 -1
- package/dist/affordances/cornerResize.js +25 -17
- package/dist/affordances/cornerResize.js.map +1 -1
- package/dist/affordances/hitAffordanceRegions.js +27 -20
- package/dist/affordances/hitAffordanceRegions.js.map +1 -1
- package/dist/affordances/rotationHandle.js +18 -18
- package/dist/affordances/rotationHandle.js.map +1 -1
- package/dist/animation/behaviors/momentum.js +93 -55
- package/dist/animation/behaviors/momentum.js.map +1 -1
- package/dist/animation/loop.js +10 -12
- package/dist/animation/loop.js.map +1 -1
- package/dist/animation/reflow.js +8 -8
- package/dist/animation/reflow.js.map +1 -1
- package/dist/animation/stagger.js +64 -56
- package/dist/animation/stagger.js.map +1 -1
- package/dist/animation/timeline/booking.js +62 -54
- package/dist/animation/timeline/booking.js.map +1 -1
- package/dist/animation/timeline/createTimeline.js +85 -76
- package/dist/animation/timeline/createTimeline.js.map +1 -1
- package/dist/animation/timeline/sampleTrack.js +13 -5
- package/dist/animation/timeline/sampleTrack.js.map +1 -1
- package/dist/animation/useAnimator.js +45 -35
- package/dist/animation/useAnimator.js.map +1 -1
- package/dist/animation/wrappers/animateLifecycle.js +31 -24
- package/dist/animation/wrappers/animateLifecycle.js.map +1 -1
- package/dist/animation/wrappers/animateOnSetPose.js +28 -26
- package/dist/animation/wrappers/animateOnSetPose.js.map +1 -1
- package/dist/animation/wrappers/instantPose.js +13 -0
- package/dist/animation/wrappers/instantPose.js.map +1 -0
- package/dist/{poseDescriptor-D-OH-tu5.d.ts → autoPoseDescriptor-Ct7yqivU.d.ts} +28 -3
- package/dist/barrel/chrome.js +16 -0
- package/dist/barrel/input.js +19 -0
- package/dist/barrel/interactions.js +42 -0
- package/dist/barrel/paint.js +18 -0
- package/dist/barrel/paths.js +19 -0
- package/dist/barrel/renderer.js +20 -0
- package/dist/barrel/routing.js +51 -0
- package/dist/barrel/text.js +18 -0
- package/dist/barrel/viewport.js +52 -0
- package/dist/builtins-DPcdghtB.d.ts +1223 -0
- package/dist/canvas/Canvas.js +363 -448
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +164 -165
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/CursorCoordsHud.js +37 -32
- package/dist/canvas/CursorCoordsHud.js.map +1 -1
- package/dist/canvas/DrawCanvas.js +49 -43
- package/dist/canvas/DrawCanvas.js.map +1 -1
- package/dist/canvas/MinimapCanvas.js +26 -25
- package/dist/canvas/MinimapCanvas.js.map +1 -1
- package/dist/canvas/NodeShape.js +59 -377
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/PickHud.js +50 -47
- package/dist/canvas/PickHud.js.map +1 -1
- package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js +167 -0
- package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js.map +1 -0
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +13 -13
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +67 -0
- package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -0
- package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js +16 -0
- package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js.map +1 -0
- package/dist/canvas/SceneCanvas/coordTrace.js +10 -0
- package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -0
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +112 -70
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +9 -8
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/features.js +1 -0
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/isDev.js +12 -0
- package/dist/canvas/SceneCanvas/isDev.js.map +1 -0
- package/dist/canvas/SceneCanvas/poseGeometry.js +29 -28
- package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
- package/dist/canvas/SceneCanvas/poseRegistrars.js +13 -0
- package/dist/canvas/SceneCanvas/poseRegistrars.js.map +1 -0
- package/dist/canvas/SceneCanvas/props.js +38 -0
- package/dist/canvas/SceneCanvas/props.js.map +1 -0
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js +21 -62
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +46 -48
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +33 -33
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +534 -788
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SceneViewCanvas.js +45 -43
- package/dist/canvas/SceneViewCanvas.js.map +1 -1
- package/dist/canvas/buildSceneLayer.js +91 -0
- package/dist/canvas/buildSceneLayer.js.map +1 -0
- package/dist/canvas/buildSceneTree.js +16 -19
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/canvasRenderer.js +68 -52
- package/dist/canvas/canvasRenderer.js.map +1 -1
- package/dist/canvas/deps/changes.js +13 -0
- package/dist/canvas/deps/changes.js.map +1 -0
- package/dist/canvas/deps/editAnchors.js +28 -28
- package/dist/canvas/deps/insert.js +119 -91
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/slice.js +46 -45
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/deps/textEdit.js +10 -9
- package/dist/canvas/deps/textEdit.js.map +1 -1
- package/dist/canvas/derivedPath.js +46 -23
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/getNodeAtPoint.js +7 -7
- package/dist/canvas/getNodeAtPoint.js.map +1 -1
- package/dist/canvas/hud.module.js +1 -1
- package/dist/canvas/hud.module.js.map +1 -1
- package/dist/canvas/kitRequirements.js +2 -2
- package/dist/canvas/kitRequirements.js.map +1 -1
- package/dist/canvas/layerOrder.js +13 -6
- package/dist/canvas/layerOrder.js.map +1 -1
- package/dist/canvas/minimapMath.js +12 -10
- package/dist/canvas/minimapMath.js.map +1 -1
- package/dist/canvas/nodeShapes/derivedPainter.js +34 -0
- package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/imagePainter.js +87 -0
- package/dist/canvas/nodeShapes/imagePainter.js.map +1 -0
- package/dist/canvas/nodeShapes/memo.js +9 -0
- package/dist/canvas/nodeShapes/memo.js.map +1 -0
- package/dist/canvas/nodeShapes/nodePaint.js +57 -0
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -0
- package/dist/canvas/nodeShapes/pathPainter.js +38 -0
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/poseBox.js +20 -0
- package/dist/canvas/nodeShapes/poseBox.js.map +1 -0
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +38 -0
- package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -0
- package/dist/canvas/nodeShapes/shapePainter.js +72 -0
- package/dist/canvas/nodeShapes/shapePainter.js.map +1 -0
- package/dist/canvas/nodeShapes/textPainter.js +55 -0
- package/dist/canvas/nodeShapes/textPainter.js.map +1 -0
- package/dist/canvas/overlayRoleStrokes.js.map +1 -1
- package/dist/canvas/paintCull.js +10 -11
- package/dist/canvas/paintCull.js.map +1 -1
- package/dist/canvas/pickWalk.js +52 -63
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/poseRotation.js +11 -23
- package/dist/canvas/poseRotation.js.map +1 -1
- package/dist/canvas/renderDebugSnapshot.js +5 -5
- package/dist/canvas/renderSceneToPixels.js +3 -1
- package/dist/canvas/renderSceneToPixels.js.map +1 -1
- package/dist/canvas/sceneAdapter.js +66 -148
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +112 -0
- package/dist/canvas/sceneClipboard.js.map +1 -0
- package/dist/canvas/sceneSlotWiring.js +4 -4
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +39 -40
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/useCanvasRenderer.js +14 -6
- package/dist/canvas/useCanvasRenderer.js.map +1 -1
- package/dist/canvas/useCanvasView.js +58 -0
- package/dist/canvas/useCanvasView.js.map +1 -0
- package/dist/canvas/useFrameLoop.js +35 -26
- package/dist/canvas/useFrameLoop.js.map +1 -1
- package/dist/canvas/useSceneLayerList.js +21 -0
- package/dist/canvas/useSceneLayerList.js.map +1 -0
- package/dist/clipboard.d.ts +10 -6
- package/dist/clone.d.ts +4 -3
- package/dist/core/adapters/arrayAdapter.js +48 -39
- package/dist/core/adapters/arrayAdapter.js.map +1 -1
- package/dist/core/device/index.js +5 -4
- package/dist/core/device/useDeviceProfile.js +38 -44
- package/dist/core/device/useDeviceProfile.js.map +1 -1
- package/dist/core/device/watchDevicePixelRatio.js +17 -0
- package/dist/core/device/watchDevicePixelRatio.js.map +1 -0
- package/dist/core/fillInPoseFrame.js.map +1 -1
- package/dist/core/geometry/poseDescriptor.js +21 -19
- package/dist/core/geometry/poseDescriptor.js.map +1 -1
- package/dist/core/geometry/poseFrame.js +44 -0
- package/dist/core/geometry/poseFrame.js.map +1 -0
- package/dist/core/geometry/poseRotation.js.map +1 -1
- package/dist/core/geometry/regionHit.js +9 -10
- package/dist/core/geometry/regionHit.js.map +1 -1
- package/dist/core/geometry/unionBounds.js +31 -29
- package/dist/core/geometry/unionBounds.js.map +1 -1
- package/dist/core/isPlainObject.js +2 -11
- package/dist/core/ops/arrange.js +1 -0
- package/dist/core/ops/arrange.js.map +1 -1
- package/dist/core/ops/create.js +10 -5
- package/dist/core/ops/create.js.map +1 -1
- package/dist/core/ops/delete.js +12 -24
- package/dist/core/ops/delete.js.map +1 -1
- package/dist/core/ops/reorder/index.js +43 -40
- package/dist/core/ops/reorder/index.js.map +1 -1
- package/dist/core/ops/reorder/setChildOrder.js +34 -0
- package/dist/core/ops/reorder/setChildOrder.js.map +1 -0
- package/dist/core/paintKinds.js +5 -5
- package/dist/core/paintKinds.js.map +1 -1
- package/dist/core/scene/NodeProperties.js.map +1 -1
- package/dist/core/scene/appliedLog.js +37 -0
- package/dist/core/scene/appliedLog.js.map +1 -0
- package/dist/core/scene/arrivalWindow.js +101 -0
- package/dist/core/scene/arrivalWindow.js.map +1 -0
- package/dist/core/scene/derivedPath.js.map +1 -1
- package/dist/core/scene/historyFacade.js +84 -0
- package/dist/core/scene/historyFacade.js.map +1 -0
- package/dist/core/scene/kitOps.js +173 -0
- package/dist/core/scene/kitOps.js.map +1 -0
- package/dist/core/scene/layerStack.js +38 -0
- package/dist/core/scene/layerStack.js.map +1 -0
- package/dist/core/scene/lockGuard.js +37 -0
- package/dist/core/scene/lockGuard.js.map +1 -0
- package/dist/core/scene/nodeFnFields.js +5 -6
- package/dist/core/scene/nodeFnFields.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +45 -19
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/nodeStore.js +126 -0
- package/dist/core/scene/nodeStore.js.map +1 -0
- package/dist/core/scene/pickability.js +6 -0
- package/dist/core/scene/pickability.js.map +1 -0
- package/dist/core/scene/poseFeed.js +38 -38
- package/dist/core/scene/poseFeed.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +34 -38
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/renderOrder.js +84 -0
- package/dist/core/scene/renderOrder.js.map +1 -0
- package/dist/core/scene/scene.js +289 -866
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/serialization.js +68 -0
- package/dist/core/scene/serialization.js.map +1 -0
- package/dist/core/scene/treeOrder.js +13 -0
- package/dist/core/scene/treeOrder.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/viewport/meanScale.js +1 -1
- package/dist/core/viewport/meanScale.js.map +1 -1
- package/dist/core/viewport/view.js +12 -12
- package/dist/core/viewport/view.js.map +1 -1
- package/dist/core/viewport/zoomBounds.js +10 -8
- package/dist/core/viewport/zoomBounds.js.map +1 -1
- package/dist/{cornerHandles-BCrDp_mH.d.ts → cornerHandles-CtCb8a3G.d.ts} +1 -1
- package/dist/debug/createDebugOverlayLayer.js +14 -14
- package/dist/debug/createDebugOverlayLayer.js.map +1 -1
- package/dist/debug/flag.js +2 -41
- package/dist/features/chrome-caps/useHoverTracking.js +22 -17
- package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
- package/dist/features/focus/gateLayer.js +3 -4
- package/dist/features/focus/gateLayer.js.map +1 -1
- package/dist/features/images/imageCache.js +19 -9
- package/dist/features/images/imageCache.js.map +1 -1
- package/dist/features/ingestion/contentHandlers.js +14 -11
- package/dist/features/ingestion/contentHandlers.js.map +1 -1
- package/dist/features/ingestion/index.js +1 -1
- package/dist/features/ingestion/registerKitHandlers.js +7 -7
- package/dist/features/ingestion/weaselJsonHandler.js +17 -14
- package/dist/features/ingestion/weaselJsonHandler.js.map +1 -1
- package/dist/features/meshPaint/lazyKind.js +201 -35
- package/dist/features/meshPaint/lazyKind.js.map +1 -1
- package/dist/features/meshPaint/meshPaint.js +82 -79
- package/dist/features/meshPaint/meshPaint.js.map +1 -1
- package/dist/features/minimap/createMinimapContribution.js +42 -42
- package/dist/features/minimap/layers.js +44 -62
- package/dist/features/minimap/layers.js.map +1 -1
- package/dist/features/parallax/createParallaxPlane.js +10 -14
- package/dist/features/parallax/createParallaxPlane.js.map +1 -1
- package/dist/features/paths/anchorEdits.js +19 -8
- package/dist/features/paths/anchorEdits.js.map +1 -1
- package/dist/features/paths/anchors.js +31 -26
- package/dist/features/paths/anchors.js.map +1 -1
- package/dist/features/paths/booleans.js +2 -2
- package/dist/features/paths/index.js +13 -11
- package/dist/features/paths/markerCommands.js +37 -36
- package/dist/features/paths/markerCommands.js.map +1 -1
- package/dist/features/paths/pathInWorld.js +19 -19
- package/dist/features/paths/pathInWorld.js.map +1 -1
- package/dist/features/paths/pathLayer.js.map +1 -1
- package/dist/features/paths/strokeToPath.js +40 -0
- package/dist/features/paths/strokeToPath.js.map +1 -0
- package/dist/features/paths/tessellate/stroke.js.map +1 -1
- package/dist/features/patterns/commandPaint.js +18 -0
- package/dist/features/patterns/commandPaint.js.map +1 -0
- package/dist/features/patterns/index.js +5 -17
- package/dist/features/patterns/index.js.map +1 -1
- package/dist/features/patterns/rasterizeTile.js +17 -0
- package/dist/features/patterns/rasterizeTile.js.map +1 -0
- package/dist/features/patterns/resolveSpec.js +52 -44
- package/dist/features/patterns/resolveSpec.js.map +1 -1
- package/dist/features/poseRun/usePoseRun.js +8 -3
- package/dist/features/poseRun/usePoseRun.js.map +1 -1
- package/dist/features/selection/SelectionContext.js +5 -0
- package/dist/features/selection/SelectionContext.js.map +1 -1
- package/dist/features/selection/overlay.js +18 -18
- package/dist/features/selection/overlay.js.map +1 -1
- package/dist/features/text/domOffsets.js +82 -0
- package/dist/features/text/domOffsets.js.map +1 -0
- package/dist/features/text/domRuns.js +103 -131
- package/dist/features/text/domRuns.js.map +1 -1
- package/dist/features/text/hitTest.js +5 -2
- package/dist/features/text/hitTest.js.map +1 -1
- package/dist/features/text/isTextData.js +8 -0
- package/dist/features/text/isTextData.js.map +1 -0
- package/dist/features/text/overlayDirection.js +15 -0
- package/dist/features/text/overlayDirection.js.map +1 -0
- package/dist/features/text/runs/rangeStyle.js +32 -34
- package/dist/features/text/runs/rangeStyle.js.map +1 -1
- package/dist/features/text/textCommand.js +34 -33
- package/dist/features/text/textCommand.js.map +1 -1
- package/dist/features/text/textToPath.js +40 -40
- package/dist/features/text/textToPath.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +76 -82
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/features/text/useTextEdit.js +184 -160
- package/dist/features/text/useTextEdit.js.map +1 -1
- package/dist/features/tiling/createTiledLayer.js +24 -22
- package/dist/features/tiling/createTiledLayer.js.map +1 -1
- package/dist/index.d.ts +11033 -7749
- package/dist/index.js +316 -309
- package/dist/input/keyState.js +42 -43
- package/dist/input/keyState.js.map +1 -1
- package/dist/input/useKeyModifiers.js +11 -0
- package/dist/input/useKeyModifiers.js.map +1 -0
- package/dist/interactions/actions/align/align.js +8 -8
- package/dist/interactions/actions/clipboard/clipboardOps.js +34 -32
- package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
- package/dist/interactions/actions/clipboard/materializePaste.js +10 -7
- package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
- package/dist/interactions/actions/clipboard/wireFormat.js.map +1 -1
- package/dist/interactions/actions/clone/behaviors/cloneByAltDrag.js.map +1 -1
- package/dist/interactions/actions/defaultCommitAdapter.js +20 -18
- package/dist/interactions/actions/defaultCommitAdapter.js.map +1 -1
- package/dist/interactions/actions/defaults/align.js +9 -10
- package/dist/interactions/actions/defaults/align.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +59 -81
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/areaSelect.js +35 -38
- package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
- package/dist/interactions/actions/defaults/clipboard.js +26 -10
- package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
- package/dist/interactions/actions/defaults/clone.js +44 -60
- package/dist/interactions/actions/defaults/clone.js.map +1 -1
- package/dist/interactions/actions/defaults/copySubtree.js +40 -0
- package/dist/interactions/actions/defaults/copySubtree.js.map +1 -0
- package/dist/interactions/actions/defaults/distribute.js +14 -13
- package/dist/interactions/actions/defaults/distribute.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +17 -57
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/editAnchors.js +18 -18
- package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/flip.js +52 -46
- package/dist/interactions/actions/defaults/flip.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +43 -48
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +35 -33
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/insert.js +50 -51
- package/dist/interactions/actions/defaults/insert.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -7
- package/dist/interactions/actions/defaults/lassoSelect.js +67 -70
- package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
- package/dist/interactions/actions/defaults/move.js +267 -249
- package/dist/interactions/actions/defaults/move.js.map +1 -1
- package/dist/interactions/actions/defaults/nudge.js +40 -68
- package/dist/interactions/actions/defaults/nudge.js.map +1 -1
- package/dist/interactions/actions/defaults/nudgeSteps.js +37 -0
- package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -0
- package/dist/interactions/actions/defaults/reorder.js +2 -0
- package/dist/interactions/actions/defaults/reorder.js.map +1 -1
- package/dist/interactions/actions/defaults/resize.js +104 -98
- package/dist/interactions/actions/defaults/resize.js.map +1 -1
- package/dist/interactions/actions/defaults/rotate.js +111 -110
- package/dist/interactions/actions/defaults/rotate.js.map +1 -1
- package/dist/interactions/actions/defaults/setFill.js +7 -7
- package/dist/interactions/actions/defaults/setFillOpacity.js +4 -4
- package/dist/interactions/actions/defaults/setStroke.js +12 -12
- package/dist/interactions/actions/defaults/setStrokeOpacity.js +6 -6
- package/dist/interactions/actions/defaults/sliceOps.js +19 -19
- package/dist/interactions/actions/defaults/viewportDragPan.js +37 -26
- package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/geometry.js +28 -23
- package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
- package/dist/interactions/actions/flip/helpers.js +30 -17
- package/dist/interactions/actions/flip/helpers.js.map +1 -1
- package/dist/interactions/actions/hitSelection.js +8 -0
- package/dist/interactions/actions/hitSelection.js.map +1 -0
- package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js +14 -18
- package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js +5 -5
- package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +32 -21
- package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
- package/dist/interactions/actions/move/gestureAdapter.js +32 -15
- package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
- package/dist/interactions/actions/move/landedDescendants.js +33 -0
- package/dist/interactions/actions/move/landedDescendants.js.map +1 -0
- package/dist/interactions/actions/outlines/createOutlines.js +18 -18
- package/dist/interactions/actions/poseDescriptorDep.js +16 -4
- package/dist/interactions/actions/poseDescriptorDep.js.map +1 -1
- package/dist/interactions/actions/poseFrame.js +11 -6
- package/dist/interactions/actions/poseFrame.js.map +1 -1
- package/dist/interactions/actions/previewOverrides.js +5 -5
- package/dist/interactions/actions/previewOverrides.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +69 -0
- package/dist/interactions/actions/selectionSubtree.js.map +1 -0
- package/dist/interactions/actions/useStandardActions.js +66 -62
- package/dist/interactions/actions/useStandardActions.js.map +1 -1
- package/dist/interactions/gestures/dragGesture/dragGesture.js +4 -7
- package/dist/interactions/gestures/dragGesture/dragGesture.js.map +1 -1
- package/dist/interactions/gestures/dragGesture/dragPointOf.js +23 -0
- package/dist/interactions/gestures/dragGesture/dragPointOf.js.map +1 -0
- package/dist/interactions/gestures/handleDrag/handleDrag.js +21 -26
- package/dist/interactions/gestures/handleDrag/handleDrag.js.map +1 -1
- package/dist/interactions/gestures/pointerDrag/pointerDrag.js +8 -6
- package/dist/interactions/gestures/pointerDrag/pointerDrag.js.map +1 -1
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +8 -4
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
- package/dist/{math-Drrw4ewZ.d.ts → math-J-ACHyEg.d.ts} +1 -2
- package/dist/math.d.ts +2 -3
- package/dist/move.d.ts +12 -11
- package/dist/{originProjection-DEklpDc_.d.ts → originProjection-gHnOup4e.d.ts} +1 -1
- package/dist/{registerProgram-BMZRDgrh.d.ts → registerProgram-T9S_PfAX.d.ts} +18 -7
- package/dist/renderer/DrawCommand.js.map +1 -1
- package/dist/renderer/WeaselRenderer.js +75 -73
- package/dist/renderer/WeaselRenderer.js.map +1 -1
- package/dist/renderer/cache/GLImageCache.js +15 -5
- package/dist/renderer/cache/GLImageCache.js.map +1 -1
- package/dist/renderer/cache/GLMeshCache.js +23 -23
- package/dist/renderer/cache/GLMeshCache.js.map +1 -1
- package/dist/renderer/cache/GLTextureCache.js +18 -1
- package/dist/renderer/cache/GLTextureCache.js.map +1 -1
- package/dist/renderer/cache/cache.js.map +1 -1
- package/dist/renderer/cache/markerCommandCache.js.map +1 -1
- package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
- package/dist/renderer/cullDrawCommands.js +11 -11
- package/dist/renderer/draw/batch.js +133 -0
- package/dist/renderer/draw/batch.js.map +1 -0
- package/dist/renderer/draw/clip.js +89 -0
- package/dist/renderer/draw/clip.js.map +1 -0
- package/dist/renderer/draw/customShader.js +230 -0
- package/dist/renderer/draw/customShader.js.map +1 -0
- package/dist/renderer/draw/drawTriangles.js +8 -0
- package/dist/renderer/draw/drawTriangles.js.map +1 -0
- package/dist/renderer/draw/effects.js +28 -0
- package/dist/renderer/draw/effects.js.map +1 -0
- package/dist/renderer/draw/flushBatch.js +35 -0
- package/dist/renderer/draw/flushBatch.js.map +1 -0
- package/dist/renderer/draw/gradient.js +90 -0
- package/dist/renderer/draw/gradient.js.map +1 -0
- package/dist/renderer/draw/image.js +38 -0
- package/dist/renderer/draw/image.js.map +1 -0
- package/dist/renderer/draw/mesh.js +16 -0
- package/dist/renderer/draw/mesh.js.map +1 -0
- package/dist/renderer/draw/paintBind.js +70 -0
- package/dist/renderer/draw/paintBind.js.map +1 -0
- package/dist/renderer/draw/pathPaint.js +142 -0
- package/dist/renderer/draw/pathPaint.js.map +1 -0
- package/dist/renderer/draw/scratchPass.js +83 -0
- package/dist/renderer/draw/scratchPass.js.map +1 -0
- package/dist/renderer/draw/stencil.js +28 -0
- package/dist/renderer/draw/stencil.js.map +1 -0
- package/dist/renderer/draw/text.js +167 -0
- package/dist/renderer/draw/text.js.map +1 -0
- package/dist/renderer/draw/uniforms.js +77 -0
- package/dist/renderer/draw/uniforms.js.map +1 -0
- package/dist/renderer/draw.js +60 -1116
- package/dist/renderer/draw.js.map +1 -1
- package/dist/renderer/effects/EffectTargets.js +3 -0
- package/dist/renderer/effects/EffectTargets.js.map +1 -1
- package/dist/renderer/effects/composite.js +7 -5
- package/dist/renderer/effects/composite.js.map +1 -1
- package/dist/renderer/index.js +18 -17
- package/dist/renderer/math/color.js +63 -83
- package/dist/renderer/math/color.js.map +1 -1
- package/dist/renderer/math/mat3.js +15 -11
- package/dist/renderer/math/mat3.js.map +1 -1
- package/dist/renderer/math/viewToMat3.js +1 -1
- package/dist/renderer/math/viewToMat3.js.map +1 -1
- package/dist/renderer/shaders/ShaderProgram.js +13 -13
- package/dist/renderer/shaders/ShaderProgram.js.map +1 -1
- package/dist/renderer/shaders/glyphPaint.js +3 -3
- package/dist/renderer/shaders/glyphPaint.js.map +1 -1
- package/dist/renderer/shaders/gradFill.js +2 -1
- package/dist/renderer/shaders/gradFill.js.map +1 -1
- package/dist/renderer/shaders/pathFill.js +2 -1
- package/dist/renderer/shaders/pathFill.js.map +1 -1
- package/dist/renderer/shaders/patternFill.js +2 -1
- package/dist/renderer/shaders/patternFill.js.map +1 -1
- package/dist/renderer/shaders/registerProgram.js.map +1 -1
- package/dist/renderer/strokeLengths.js.map +1 -1
- package/dist/renderer/textures/registerTexture.js +25 -5
- package/dist/renderer/textures/registerTexture.js.map +1 -1
- package/dist/renderer.d.ts +16 -15
- package/dist/renderer.js +17 -16
- package/dist/resize.d.ts +5 -6
- package/dist/resize.js +5 -5
- package/dist/style.css +1 -1
- package/dist/test-hook/install.js +12 -6
- package/dist/test-hook/install.js.map +1 -1
- package/dist/{test-seams-CuIxe0xY.d.ts → test-seams-DM2B87-g.d.ts} +4 -4
- package/dist/test-seams.d.ts +2 -2
- package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
- package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
- package/dist/tools/builtin/hand/useHandTool.js +1 -0
- package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
- package/dist/tools/builtin/lasso/useLassoTool.js +11 -11
- package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
- package/dist/tools/builtin/line/useLineTool.js +1 -0
- package/dist/tools/builtin/line/useLineTool.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +153 -103
- package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
- package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
- package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js +13 -8
- package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
- package/dist/tools/builtin/rect/useRectTool.js +1 -0
- package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
- package/dist/tools/builtin/select/selectionContributions.js +24 -23
- package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +51 -48
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/shared/pointerFollower.js +17 -0
- package/dist/tools/builtin/shared/pointerFollower.js.map +1 -0
- package/dist/tools/builtin/slice/useSliceTool.js +49 -54
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +22 -16
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/builtin/text/useTextTool.js +1 -0
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/tools/useKeybindings.js +14 -30
- package/dist/tools/useKeybindings.js.map +1 -1
- package/dist/{types-B_9g6hMM.d.ts → types-B4iJRHC3.d.ts} +5 -1
- package/dist/{types-A37hYkeR.d.ts → types-k-5Oqh-U.d.ts} +31 -7
- package/dist/util/color.js +3 -3
- package/dist/util/color.js.map +1 -1
- package/dist/util/paint.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +20 -18
- package/dist/autoPoseDescriptor-vCdt55xm.d.ts +0 -27
- package/dist/builtins-C-VgUlWI.d.ts +0 -4514
- package/dist/core/isPlainObject.js.map +0 -1
- package/dist/debug/flag.js.map +0 -1
- package/dist/interactions/actions/defaults/freshNodeId.js +0 -10
- package/dist/interactions/actions/defaults/freshNodeId.js.map +0 -1
- package/dist/renderer/shaders/imageFill.js +0 -14
- package/dist/renderer/shaders/imageFill.js.map +0 -1
- package/dist/tools/prefs.js +0 -62
- package/dist/tools/prefs.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layers.js","names":["viewIdOf","data","createIndicatorLayer","opts","viewId","r","root","size","
|
|
1
|
+
{"version":3,"file":"layers.js","names":["viewIdOf","data","createIndicatorLayer","opts","viewId","r","root","size","computeIndicatorCommand","style","view","ARM","GAP","THICK","HALO","CROSSHAIR_HALO","crosshairRects","k","unitsPerPx","arm","gap","t","h","bars","x","y","b","createLinkedCursorLayer","p","pointer","here","views","rect","c","accent","color","halo"],"sources":["../../../src/features/minimap/layers.ts"],"sourcesContent":["/**\n * The minimap's chrome: the main camera's visible rect, drawn in the minimap,\n * and the linked crosshair, drawn in every view the pointer is not over.\n */\nimport type { RenderLayer } from 'core/layers/render';\nimport type { View } from 'core/viewport/view';\nimport type { DrawCommand } from '../../renderer/DrawCommand';\nimport type { ViewApi } from 'interactions/actions/depSchema';\nimport type { PointerWorldPos } from 'features/pointer/PointerContext';\nimport { computeIndicatorCommand, type IndicatorStyle } from '../../canvas/minimapMath';\n\n/** What a layer's `data` carries for this feature: the view it draws for. */\nfunction viewIdOf(data: unknown): string | null {\n return (data as { viewId?: string | null } | null)?.viewId ?? null;\n}\n\n/**\n * The main camera's visible world rect, painted only in view `viewId`. Its\n * stroke and dash are screen pixels whatever the minimap's scale.\n */\nexport function createIndicatorLayer(opts: {\n viewId: string;\n root: () => ViewApi | undefined;\n style?: IndicatorStyle;\n}): RenderLayer<unknown> {\n const { viewId, root, style = {} } = opts;\n return {\n id: `${viewId}.indicator`,\n label: 'Minimap indicator',\n draw: (data, view: View): DrawCommand[] => {\n if (viewIdOf(data) !== viewId) return [];\n const r = root();\n const size = r?.hostSize?.();\n if (!r || !size) return [];\n return [computeIndicatorCommand(r.get(), size, style, view)];\n },\n };\n}\n\n/** Screen-pixel geometry of the crosshair: arm length, the gap at its center,\n * line thickness, and the dark halo that keeps it legible over any content. */\nconst ARM = 9;\nconst GAP = 3;\nconst THICK = 2;\nconst HALO = 1;\n/** The halo's color: dark enough to separate the accent from any content. */\nexport const CROSSHAIR_HALO = 'rgba(0, 0, 0, 0.6)';\n\n/** An axis-aligned rect, `{ x, y, w, h }`. */\nexport interface CrosshairRect { x: number; y: number; w: number; h: number }\n\n/**\n * The linked crosshair at (`x`, `y`) as rects in the caller's units, where one\n * screen pixel is `unitsPerPx` of them: four halo rects to paint first, then\n * four bars. A painter with no weasel renderer — a 2D canvas — draws the same\n * crosshair from these.\n */\nexport function crosshairRects(\n x: number, y: number, unitsPerPx: number,\n): { halo: CrosshairRect[]; bars: CrosshairRect[] } {\n const k = unitsPerPx;\n const arm = ARM * k, gap = GAP * k, t = THICK * k, h = HALO * k;\n const bars: CrosshairRect[] = [\n { x: x - gap - arm, y: y - t / 2, w: arm, h: t },\n { x: x + gap, y: y - t / 2, w: arm, h: t },\n { x: x - t / 2, y: y - gap - arm, w: t, h: arm },\n { x: x - t / 2, y: y + gap, w: t, h: arm },\n ];\n const halo = bars.map((b) => ({ x: b.x - h, y: b.y - h, w: b.w + 2 * h, h: b.h + 2 * h }));\n return { halo, bars };\n}\n\n/**\n * A crosshair at the pointer's world point, in each of `views` the pointer is\n * not over — the other side of a linked cursor. A fixed screen size in every\n * view. `views` omitted paints in every view.\n */\nexport function createLinkedCursorLayer(opts: {\n id: string;\n pointer: () => PointerWorldPos;\n color: () => string;\n views?: readonly (string | null)[];\n}): RenderLayer<unknown> {\n const { id, pointer, color, views } = opts;\n return {\n id,\n label: 'Linked cursor',\n draw: (data, view: View): DrawCommand[] => {\n const p = pointer();\n const here = viewIdOf(data);\n if (!p || p.viewId === here || (views && !views.includes(here))) return [];\n const { halo, bars } = crosshairRects(p.worldX, p.worldY, 1 / view.scale.x);\n const rect = (r: CrosshairRect, c: string): DrawCommand => ({\n kind: 'path',\n path: { kind: 'rect', x: r.x, y: r.y, width: r.w, height: r.h },\n fill: { fill: 'solid', color: c },\n });\n const accent = color();\n return [\n ...halo.map((r) => rect(r, CROSSHAIR_HALO)),\n ...bars.map((r) => rect(r, accent)),\n ];\n },\n };\n}\n"],"mappings":";;AAYA,SAASA,EAAS,GAA8B;CAC9C,OAAQC,GAA4C,UAAU;AAChE;AAMA,SAAgBC,EAAqB,GAIZ;CACvB,IAAM,EAAE,WAAQ,SAAM,WAAQ,CAAC,MAAMC;CACrC,OAAO;EACL,IAAI,GAAGC,EAAO;EACd,OAAO;EACP,OAAO,GAAM,MAA8B;GACzC,IAAIJ,EAASC,CAAI,MAAMG,GAAQ,OAAO,CAAC;GACvC,IAAMC,IAAIC,EAAK,GACTC,IAAOF,GAAG,WAAW;GAE3B,OADI,CAACA,KAAK,CAACE,IAAa,CAAC,IAClB,CAACC,EAAwBH,EAAE,IAAI,GAAGE,GAAME,GAAOC,CAAI,CAAC;EAC7D;CACF;AACF;AAIA,IAAMC,IAAM,GACNC,IAAM,GACNC,IAAQ,GACRC,IAAO,GAEAC,IAAiB;AAW9B,SAAgBC,EACd,GAAW,GAAW,GAC4B;CAClD,IAAMC,IAAIC,GACJC,IAAMR,IAAMM,GAAGG,IAAMR,IAAMK,GAAGI,IAAIR,IAAQI,GAAGK,IAAIR,IAAOG,GACxDM,IAAwB;EAC5B;GAAE,GAAGC,IAAIJ,IAAMD;GAAK,GAAGM,IAAIJ,IAAI;GAAG,GAAGF;GAAK,GAAGE;EAAE;EAC/C;GAAE,GAAGG,IAAIJ;GAAK,GAAGK,IAAIJ,IAAI;GAAG,GAAGF;GAAK,GAAGE;EAAE;EACzC;GAAE,GAAGG,IAAIH,IAAI;GAAG,GAAGI,IAAIL,IAAMD;GAAK,GAAGE;GAAG,GAAGF;EAAI;EAC/C;GAAE,GAAGK,IAAIH,IAAI;GAAG,GAAGI,IAAIL;GAAK,GAAGC;GAAG,GAAGF;EAAI;CAC3C;CAEA,OAAO;EAAE,MADII,EAAK,KAAK,OAAO;GAAE,GAAGG,EAAE,IAAIJ;GAAG,GAAGI,EAAE,IAAIJ;GAAG,GAAGI,EAAE,IAAI,IAAIJ;GAAG,GAAGI,EAAE,IAAI,IAAIJ;EAAE,EAC9E;EAAM;CAAK;AACtB;AAOA,SAAgBK,EAAwB,GAKf;CACvB,IAAM,EAAE,OAAI,YAAS,UAAO,aAAUxB;CACtC,OAAO;EACL;EACA,OAAO;EACP,OAAO,GAAM,MAA8B;GACzC,IAAMyB,IAAIC,EAAQ,GACZC,IAAO9B,EAASC,CAAI;GAC1B,IAAI,CAAC2B,KAAKA,EAAE,WAAWE,KAASC,KAAS,CAACA,EAAM,SAASD,CAAI,GAAI,OAAO,CAAC;GACzE,IAAM,EAAE,SAAM,YAASd,EAAeY,EAAE,QAAQA,EAAE,QAAQ,IAAIlB,EAAK,MAAM,CAAC,GACpEsB,KAAQ,GAAkB,OAA4B;IAC1D,MAAM;IACN,MAAM;KAAE,MAAM;KAAQ,GAAG3B,EAAE;KAAG,GAAGA,EAAE;KAAG,OAAOA,EAAE;KAAG,QAAQA,EAAE;IAAE;IAC9D,MAAM;KAAE,MAAM;KAAS,OAAO4B;IAAE;GAClC,IACMC,IAASC,EAAM;GACrB,OAAO,CACL,GAAGC,EAAK,KAAK,MAAMJ,EAAK3B,GAAGU,CAAc,CAAC,GAC1C,GAAGQ,EAAK,KAAK,MAAMS,EAAK3B,GAAG6B,CAAM,CAAC,CACpC;EACF;CACF;AACF"}
|
|
@@ -1,26 +1,22 @@
|
|
|
1
|
+
import { createEmitter as e } from "@weasel-js/registry";
|
|
1
2
|
//#region src/features/parallax/createParallaxPlane.ts
|
|
2
|
-
function
|
|
3
|
-
let
|
|
3
|
+
function t(t) {
|
|
4
|
+
let n = { ...t }, r = e();
|
|
4
5
|
return {
|
|
5
|
-
get: () =>
|
|
6
|
+
get: () => n,
|
|
6
7
|
set(e) {
|
|
7
|
-
|
|
8
|
-
...
|
|
8
|
+
n = {
|
|
9
|
+
...n,
|
|
9
10
|
...e
|
|
10
|
-
};
|
|
11
|
-
for (let e of [...n]) e();
|
|
11
|
+
}, r.emit();
|
|
12
12
|
},
|
|
13
|
-
subscribe
|
|
14
|
-
return n.add(e), () => {
|
|
15
|
-
n.delete(e);
|
|
16
|
-
};
|
|
17
|
-
}
|
|
13
|
+
subscribe: r.subscribe
|
|
18
14
|
};
|
|
19
15
|
}
|
|
20
|
-
function
|
|
16
|
+
function n(e) {
|
|
21
17
|
return typeof e.get == "function";
|
|
22
18
|
}
|
|
23
19
|
//#endregion
|
|
24
|
-
export {
|
|
20
|
+
export { t as createParallaxPlane, n as isParallaxSource };
|
|
25
21
|
|
|
26
22
|
//# sourceMappingURL=createParallaxPlane.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createParallaxPlane.js","names":["createParallaxPlane","current","initial","listeners","
|
|
1
|
+
{"version":3,"file":"createParallaxPlane.js","names":["createParallaxPlane","current","initial","listeners","createEmitter","patch","isParallaxSource","p"],"sources":["../../../src/features/parallax/createParallaxPlane.ts"],"sourcesContent":["import { createEmitter } from '@weasel-js/registry';\nimport type { RedrawSource } from '../../core/layers/render';\nimport type { ParallaxOpts } from '../../core/viewport/parallax';\n\n/** Parallax opts that can change after a layer is built. `get` is read on\n * every draw, and a notification repaints whatever draws through it. */\nexport interface ParallaxSource extends RedrawSource {\n get(): ParallaxOpts;\n}\n\n/** A {@link ParallaxSource} you write to — what an animator's `onTick` drives\n * to tween a plane's `pan`, `zoom` or `anchor`. */\nexport interface ParallaxPlane extends ParallaxSource {\n /** Merge `patch` over the current opts and notify. */\n set(patch: Partial<ParallaxOpts>): void;\n}\n\n/** A {@link ParallaxPlane} starting at `initial`. Pass it as a parallax\n * layer's `parallax` and write to it to move the plane. */\nexport function createParallaxPlane(initial: ParallaxOpts): ParallaxPlane {\n let current: ParallaxOpts = { ...initial };\n const listeners = createEmitter();\n return {\n get: () => current,\n set(patch) {\n current = { ...current, ...patch };\n listeners.emit();\n },\n subscribe: listeners.subscribe,\n };\n}\n\n/** Whether `p` is a live source rather than a fixed set of opts. */\nexport function isParallaxSource(p: ParallaxOpts | ParallaxSource): p is ParallaxSource {\n return typeof (p as ParallaxSource).get === 'function';\n}\n"],"mappings":";;AAmBA,SAAgBA,EAAoB,GAAsC;CACxE,IAAIC,IAAwB,EAAE,GAAGC,EAAQ,GACnCC,IAAYC,EAAc;CAChC,OAAO;EACL,WAAWH;EACX,IAAI,GAAO;GAET,AADA,IAAU;IAAE,GAAGA;IAAS,GAAGI;GAAM,GACjCF,EAAU,KAAK;EACjB;EACA,WAAWA,EAAU;CACvB;AACF;AAGA,SAAgBG,EAAiB,GAAuD;CACtF,OAAO,OAAQC,EAAqB,OAAQ;AAC9C"}
|
|
@@ -109,7 +109,7 @@ function v(e, t, n) {
|
|
|
109
109
|
}
|
|
110
110
|
function y(e, t) {
|
|
111
111
|
let n = /* @__PURE__ */ new Map();
|
|
112
|
-
for (let r of t) {
|
|
112
|
+
for (let r of new Set(t)) {
|
|
113
113
|
let t = u(e, r);
|
|
114
114
|
if (!t) continue;
|
|
115
115
|
let i = n.get(t.sub);
|
|
@@ -126,13 +126,13 @@ function y(e, t) {
|
|
|
126
126
|
let n = e.anchors[t];
|
|
127
127
|
if (n) {
|
|
128
128
|
r.sort((e, t) => t - e);
|
|
129
|
-
for (let
|
|
130
|
-
let t = n[
|
|
131
|
-
if (
|
|
132
|
-
let { c1: e, c2:
|
|
133
|
-
|
|
129
|
+
for (let i of r) {
|
|
130
|
+
let r = e.closed[t] && n.length > 2, a = r ? n[(i - 1 + n.length) % n.length] : n[i - 1], o = r ? n[(i + 1) % n.length] : n[i + 1];
|
|
131
|
+
if (a && o) {
|
|
132
|
+
let { c1: e, c2: t } = c(a, o);
|
|
133
|
+
a.outHandle = e, o.inHandle = t;
|
|
134
134
|
}
|
|
135
|
-
n.splice(
|
|
135
|
+
n.splice(i, 1);
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -143,7 +143,18 @@ function b(e, t) {
|
|
|
143
143
|
let n = u(e, t);
|
|
144
144
|
if (!n || !e.closed[n.sub]) return !1;
|
|
145
145
|
let r = e.anchors[n.sub];
|
|
146
|
-
|
|
146
|
+
if (!r) return !1;
|
|
147
|
+
let { inHandle: i, ...a } = r[n.idx], o = {
|
|
148
|
+
x: a.x,
|
|
149
|
+
y: a.y,
|
|
150
|
+
...i ? { inHandle: i } : {}
|
|
151
|
+
};
|
|
152
|
+
return e.anchors[n.sub] = [
|
|
153
|
+
a,
|
|
154
|
+
...r.slice(n.idx + 1),
|
|
155
|
+
...r.slice(0, n.idx),
|
|
156
|
+
o
|
|
157
|
+
], e.closed[n.sub] = !1, !0;
|
|
147
158
|
}
|
|
148
159
|
function x(e, t) {
|
|
149
160
|
let n = t.x + t.width, r = t.y + t.height, i = [], a = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anchorEdits.js","names":["p3","locateAnchor","flatIndex","remaining","sub","set","len","flatAnchorIndex","idx","base","s","anchorAt","loc","editAnchorSet","pathToAnchors","path","edit","anchorsToPath","translateAnchorBy","a","dx","dy","moveHandleTo","wasSmooth","isAnchorSmooth","target","toX","toY","side","oppositeSide","opposite","breakSmoothness","mirrored","insertAnchorOnSegment","ends","segmentEnds","args","at","isStraightSegment","b","p0","p1","p2","splitCubicAtT","left","right","segmentAt","scale","opts","onScreen","p","screenAnchors","hit","nearestSegmentT","x","y","point","pointOnSegment","tol","withinPxRadius","t","cubicPointAt","deleteAnchorsAt","bySub","flat","flatIndexes","list","survivors","doomed","indices","i","prev","next","fitCubicThroughDeletion","c1","c2","openSubpathAt","anchorsInRect","x2","rect","y2","hits"],"sources":["../../../src/features/paths/anchorEdits.ts"],"sourcesContent":["/**\n * Pure anchor-set edits — the geometry half of path editing, with no\n * dependency on any tool's scratch or on the action layer.\n *\n * The model is the one `pathToAnchors` produces: subpath-major\n * `PenAnchor[][]` plus a parallel `closed: boolean[]`. Every function here\n * mutates that structure in place; callers decode with `pathToAnchors`,\n * apply, and re-encode with `anchorsToPath` (see {@link editAnchorSet}).\n *\n * ## Two ways to name an anchor\n *\n * Callers address anchors by **flat index** — the sequential position in\n * walk order across all subpaths, which is exactly what\n * `enumerateAnchors` reports and therefore what the dispatcher's\n * `anchor:N` / `controlIn:N` / `controlOut:N` affordance kinds carry.\n * Internally, operations that have to reason about subpath structure\n * (deletion, scissors) need the `(sub, idx)` pair instead.\n * {@link locateAnchor} and {@link flatAnchorIndex} convert.\n *\n * The two orders agree because `pathToAnchors` and `enumerateAnchors`\n * walk the same command stream and emit one anchor per M/L/C/Q — see the\n * invariant test in `anchorEdits.test.ts`. If you change either walker,\n * that test is the tripwire.\n */\n\nimport { cubicPointAt, fitCubicThroughDeletion, splitCubicAtT, type PolygonPath } from '@weasel-js/geom';\nimport {\n pathToAnchors,\n anchorsToPath,\n isAnchorSmooth,\n isStraightSegment,\n nearestSegmentT,\n segmentEnds,\n type PenAnchor,\n} from './anchors';\nimport { withinPxRadius, type Scale2 } from 'core/viewport/pxExtent';\n\n/** Subpath-major anchor model — the decoded form of a `PolygonPath`. */\nexport interface AnchorSet {\n anchors: PenAnchor[][];\n closed: boolean[];\n}\n\n/** A world-space rectangle, as produced by a drag-rect gesture. */\nexport interface AnchorRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n// ---------------------------------------------------------------------------\n// Addressing\n// ---------------------------------------------------------------------------\n\n/** Total anchors across every subpath. */\nexport function anchorCount(set: AnchorSet): number {\n let n = 0;\n for (const sub of set.anchors) n += sub.length;\n return n;\n}\n\n/** Flat index → `(sub, idx)`. Returns null when out of range. */\nexport function locateAnchor(\n set: AnchorSet,\n flatIndex: number,\n): { sub: number; idx: number } | null {\n if (flatIndex < 0) return null;\n let remaining = flatIndex;\n for (let sub = 0; sub < set.anchors.length; sub++) {\n const len = set.anchors[sub].length;\n if (remaining < len) return { sub, idx: remaining };\n remaining -= len;\n }\n return null;\n}\n\n/** `(sub, idx)` → flat index. Returns -1 when out of range. */\nexport function flatAnchorIndex(set: AnchorSet, sub: number, idx: number): number {\n if (sub < 0 || sub >= set.anchors.length) return -1;\n if (idx < 0 || idx >= set.anchors[sub].length) return -1;\n let base = 0;\n for (let s = 0; s < sub; s++) base += set.anchors[s].length;\n return base + idx;\n}\n\n/** Resolve a flat index to the anchor itself. Null when out of range. */\nexport function anchorAt(set: AnchorSet, flatIndex: number): PenAnchor | null {\n const loc = locateAnchor(set, flatIndex);\n if (!loc) return null;\n return set.anchors[loc.sub][loc.idx] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Decode / apply / encode\n// ---------------------------------------------------------------------------\n\n/**\n * Decode `path`, hand the mutable anchor set to `edit`, and re-encode.\n * Returns the new path, or null when `edit` reports that it changed\n * nothing (returning `false`) — callers use that to skip a no-op commit\n * and avoid pushing an empty history entry.\n */\nexport function editAnchorSet(\n path: PolygonPath,\n edit: (set: AnchorSet) => boolean | void,\n): PolygonPath | null {\n const set = pathToAnchors(path) as AnchorSet;\n const changed = edit(set);\n if (changed === false) return null;\n return anchorsToPath(set.anchors, set.closed);\n}\n\n// ---------------------------------------------------------------------------\n// Edits\n// ---------------------------------------------------------------------------\n\n/** Translate an anchor and both of its handles by `(dx, dy)`. */\nexport function translateAnchorBy(\n set: AnchorSet,\n flatIndex: number,\n dx: number,\n dy: number,\n): boolean {\n const a = anchorAt(set, flatIndex);\n if (!a) return false;\n a.x += dx;\n a.y += dy;\n if (a.inHandle) {\n a.inHandle.x += dx;\n a.inHandle.y += dy;\n }\n if (a.outHandle) {\n a.outHandle.x += dx;\n a.outHandle.y += dy;\n }\n return true;\n}\n\n/**\n * Move one control handle to an absolute world position.\n *\n * When the anchor was smooth (handles collinear through the anchor) and\n * `breakSmoothness` is false, the opposite handle is mirrored so the curve\n * stays smooth — Illustrator's default. Alt-drag sets `breakSmoothness`\n * and the two handles become independent.\n */\nexport function moveHandleTo(\n set: AnchorSet,\n flatIndex: number,\n side: 'in' | 'out',\n toX: number,\n toY: number,\n breakSmoothness: boolean,\n): boolean {\n const a = anchorAt(set, flatIndex);\n if (!a) return false;\n const wasSmooth = isAnchorSmooth(a);\n const target = { x: toX, y: toY };\n if (side === 'out') a.outHandle = target;\n else a.inHandle = target;\n const oppositeSide = side === 'out' ? 'in' : 'out';\n const opposite = oppositeSide === 'in' ? a.inHandle : a.outHandle;\n if (!breakSmoothness && wasSmooth && opposite) {\n const mirrored = { x: 2 * a.x - target.x, y: 2 * a.y - target.y };\n if (oppositeSide === 'in') a.inHandle = mirrored;\n else a.outHandle = mirrored;\n }\n return true;\n}\n\n/**\n * Split the segment `(sub, segIdx) → (sub, segIdx + 1)` at parameter `t`,\n * inserting a new anchor there. On a closed subpath `segIdx` may also name\n * the closing segment (`sub.length - 1`); its new anchor is appended.\n *\n * A straight segment splits linearly and stays straight — the new anchor\n * has no handles. A curved one splits by de Casteljau, which preserves the\n * curve exactly: the two halves trace the original path.\n *\n * Returns the new anchor's flat index, or -1 when the segment doesn't\n * exist.\n */\nexport function insertAnchorOnSegment(\n set: AnchorSet,\n args: { sub: number; segIdx: number; t: number },\n): number {\n const ends = segmentEnds(set.anchors, set.closed, args.sub, args.segIdx);\n if (!ends) return -1;\n const { a, b } = ends;\n const at = args.segIdx + 1;\n if (isStraightSegment(a, b)) {\n set.anchors[args.sub].splice(at, 0, {\n x: a.x + (b.x - a.x) * args.t,\n y: a.y + (b.y - a.y) * args.t,\n });\n return flatAnchorIndex(set, args.sub, at);\n }\n const p0 = a, p1 = a.outHandle ?? a, p2 = b.inHandle ?? b, p3 = b;\n const { left, right } = splitCubicAtT(p0, p1, p2, p3, args.t);\n a.outHandle = { x: left[1].x, y: left[1].y };\n b.inHandle = { x: right[2].x, y: right[2].y };\n set.anchors[args.sub].splice(at, 0, {\n x: left[3].x,\n y: left[3].y,\n inHandle: { x: left[2].x, y: left[2].y },\n outHandle: { x: right[1].x, y: right[1].y },\n });\n return flatAnchorIndex(set, args.sub, at);\n}\n\n/** A point on a segment, as {@link segmentAt} reports it. */\nexport interface SegmentHit {\n sub: number;\n segIdx: number;\n /** Parameter on the segment — what {@link insertAnchorOnSegment} takes. */\n t: number;\n /** The point on the path at `t`, in the set's own space. */\n x: number;\n y: number;\n}\n\n/**\n * The segment point under `(x, y)`, when it lies within `tolerancePx` of\n * the path on screen and no nearer than that to either of the segment's\n * own anchors — a press on an anchor is about the anchor, not the segment\n * beside it.\n *\n * `scale` is the view's, so the tolerance holds per axis under\n * non-uniform zoom. The search runs in screen-scaled space: scaling is\n * affine, so the curve's parameterization, and therefore `t`, carries\n * straight back to world space.\n */\nexport function segmentAt(\n set: AnchorSet,\n x: number,\n y: number,\n opts: { tolerancePx: number; scale?: Scale2 },\n): SegmentHit | null {\n const scale = opts.scale ?? { x: 1, y: 1 };\n const onScreen = (p: { x: number; y: number }) => ({ x: p.x * scale.x, y: p.y * scale.y });\n const screenAnchors = set.anchors.map((sub) => sub.map((a) => ({\n ...onScreen(a),\n ...(a.inHandle ? { inHandle: onScreen(a.inHandle) } : {}),\n ...(a.outHandle ? { outHandle: onScreen(a.outHandle) } : {}),\n })));\n const hit = nearestSegmentT(screenAnchors, x * scale.x, y * scale.y, set.closed);\n if (!hit) return null;\n const ends = segmentEnds(set.anchors, set.closed, hit.sub, hit.segIdx)!;\n const point = pointOnSegment(ends.a, ends.b, hit.t);\n const tol = opts.tolerancePx;\n if (!withinPxRadius(point.x - x, point.y - y, tol, scale)) return null;\n if (withinPxRadius(point.x - ends.a.x, point.y - ends.a.y, tol, scale)) return null;\n if (withinPxRadius(point.x - ends.b.x, point.y - ends.b.y, tol, scale)) return null;\n return { sub: hit.sub, segIdx: hit.segIdx, t: hit.t, x: point.x, y: point.y };\n}\n\nfunction pointOnSegment(a: PenAnchor, b: PenAnchor, t: number): { x: number; y: number } {\n if (isStraightSegment(a, b)) return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n return cubicPointAt(a, a.outHandle ?? a, b.inHandle ?? b, b, t);\n}\n\n/**\n * Delete the anchors at `flatIndexes`. Where a deleted anchor had\n * neighbors on both sides, the surviving segment is refitted through the\n * gap (`fitCubicThroughDeletion`) so the path keeps its shape instead of\n * snapping to a straight line.\n *\n * Subpaths left with fewer than two anchors are dropped entirely — a\n * one-anchor subpath has no segments and would serialize to a stray `M`.\n *\n * If that would leave the path with **no** subpaths at all, the whole\n * deletion is refused (returns false, `set` untouched). Committing an\n * empty path turns the node into an invisible zombie: still selected,\n * still in the scene, nothing on screen and no anchors left to grab it\n * by. Deleting the object is the node-level Delete action's job, and the\n * user asked to delete anchors.\n */\nexport function deleteAnchorsAt(set: AnchorSet, flatIndexes: Iterable<number>): boolean {\n const bySub = new Map<number, number[]>();\n for (const flat of flatIndexes) {\n const loc = locateAnchor(set, flat);\n if (!loc) continue;\n const list = bySub.get(loc.sub);\n if (list) list.push(loc.idx);\n else bySub.set(loc.sub, [loc.idx]);\n }\n if (bySub.size === 0) return false;\n\n // Refuse up front if this would wipe the path out — cheaper and safer\n // than mutating and rolling back.\n let survivors = 0;\n for (let s = 0; s < set.anchors.length; s++) {\n const doomed = bySub.get(s)?.length ?? 0;\n if (set.anchors[s].length - doomed >= 2) survivors++;\n }\n if (survivors === 0) return false;\n\n for (const [s, indices] of bySub) {\n const sub = set.anchors[s];\n if (!sub) continue;\n // Descending so each splice leaves the not-yet-processed indices valid.\n indices.sort((a, b) => b - a);\n for (const i of indices) {\n const prev = sub[i - 1];\n const next = sub[i + 1];\n if (prev && next) {\n const { c1, c2 } = fitCubicThroughDeletion(prev, next);\n prev.outHandle = c1;\n next.inHandle = c2;\n }\n sub.splice(i, 1);\n }\n }\n\n // Drop degenerate subpaths, back to front so indices stay valid.\n for (let s = set.anchors.length - 1; s >= 0; s--) {\n if (set.anchors[s].length < 2) {\n set.anchors.splice(s, 1);\n set.closed.splice(s, 1);\n }\n }\n return true;\n}\n\n/**\n * Scissors: open a closed subpath at the given anchor, rotating the\n * anchor list so the cut point becomes the new start/end. No-ops on an\n * already-open subpath — there's nothing to cut.\n */\nexport function openSubpathAt(set: AnchorSet, flatIndex: number): boolean {\n const loc = locateAnchor(set, flatIndex);\n if (!loc) return false;\n if (!set.closed[loc.sub]) return false;\n const sub = set.anchors[loc.sub];\n if (!sub) return false;\n set.anchors[loc.sub] = [...sub.slice(loc.idx), ...sub.slice(0, loc.idx)];\n set.closed[loc.sub] = false;\n return true;\n}\n\n/** Flat indices of every anchor whose on-curve point falls inside `rect`. */\nexport function anchorsInRect(set: AnchorSet, rect: AnchorRect): number[] {\n const x2 = rect.x + rect.width;\n const y2 = rect.y + rect.height;\n const hits: number[] = [];\n let flat = 0;\n for (const sub of set.anchors) {\n for (const a of sub) {\n if (a.x >= rect.x && a.x <= x2 && a.y >= rect.y && a.y <= y2) hits.push(flat);\n flat++;\n }\n }\n return hits;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"anchorEdits.js","names":["p3","locateAnchor","flatIndex","remaining","sub","set","len","flatAnchorIndex","idx","base","s","anchorAt","loc","editAnchorSet","pathToAnchors","path","edit","anchorsToPath","translateAnchorBy","a","dx","dy","moveHandleTo","wasSmooth","isAnchorSmooth","target","toX","toY","side","oppositeSide","opposite","breakSmoothness","mirrored","insertAnchorOnSegment","ends","segmentEnds","args","at","isStraightSegment","b","p0","p1","p2","splitCubicAtT","left","right","segmentAt","scale","opts","onScreen","p","screenAnchors","hit","nearestSegmentT","x","y","point","pointOnSegment","tol","withinPxRadius","t","cubicPointAt","deleteAnchorsAt","bySub","flat","flatIndexes","list","survivors","doomed","indices","wraps","prev","next","fitCubicThroughDeletion","c1","c2","openSubpathAt","start","end","inHandle","anchorsInRect","x2","rect","y2","hits"],"sources":["../../../src/features/paths/anchorEdits.ts"],"sourcesContent":["/**\n * Pure anchor-set edits — the geometry half of path editing, with no\n * dependency on any tool's scratch or on the action layer.\n *\n * The model is the one `pathToAnchors` produces: subpath-major\n * `PenAnchor[][]` plus a parallel `closed: boolean[]`. Every function here\n * mutates that structure in place; callers decode with `pathToAnchors`,\n * apply, and re-encode with `anchorsToPath` (see {@link editAnchorSet}).\n *\n * ## Two ways to name an anchor\n *\n * Callers address anchors by **flat index** — the sequential position in\n * walk order across all subpaths, which is exactly what\n * `enumerateAnchors` reports and therefore what the dispatcher's\n * `anchor:N` / `controlIn:N` / `controlOut:N` affordance kinds carry.\n * Internally, operations that have to reason about subpath structure\n * (deletion, scissors) need the `(sub, idx)` pair instead.\n * {@link locateAnchor} and {@link flatAnchorIndex} convert.\n *\n * The two orders agree because `pathToAnchors` and `enumerateAnchors`\n * walk the same command stream and emit one anchor per M/L/C/Q, less the\n * end of a closing curve that lands on its subpath's first point — see the\n * invariant test in `anchorEdits.test.ts`. `countPathAnchors`, which sizes\n * the renderer's per-anchor arrays, counts that end too and so can be one\n * higher per closed subpath. If you change either walker,\n * that test is the tripwire.\n */\n\nimport { cubicPointAt, fitCubicThroughDeletion, splitCubicAtT, type PolygonPath } from '@weasel-js/geom';\nimport {\n pathToAnchors,\n anchorsToPath,\n isAnchorSmooth,\n isStraightSegment,\n nearestSegmentT,\n segmentEnds,\n type PenAnchor,\n} from './anchors';\nimport { withinPxRadius, type Scale2 } from 'core/viewport/pxExtent';\n\n/** Subpath-major anchor model — the decoded form of a `PolygonPath`. */\nexport interface AnchorSet {\n anchors: PenAnchor[][];\n closed: boolean[];\n}\n\n/** A world-space rectangle, as produced by a drag-rect gesture. */\nexport interface AnchorRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n// ---------------------------------------------------------------------------\n// Addressing\n// ---------------------------------------------------------------------------\n\n/** Total anchors across every subpath. */\nexport function anchorCount(set: AnchorSet): number {\n let n = 0;\n for (const sub of set.anchors) n += sub.length;\n return n;\n}\n\n/** Flat index → `(sub, idx)`. Returns null when out of range. */\nexport function locateAnchor(\n set: AnchorSet,\n flatIndex: number,\n): { sub: number; idx: number } | null {\n if (flatIndex < 0) return null;\n let remaining = flatIndex;\n for (let sub = 0; sub < set.anchors.length; sub++) {\n const len = set.anchors[sub].length;\n if (remaining < len) return { sub, idx: remaining };\n remaining -= len;\n }\n return null;\n}\n\n/** `(sub, idx)` → flat index. Returns -1 when out of range. */\nexport function flatAnchorIndex(set: AnchorSet, sub: number, idx: number): number {\n if (sub < 0 || sub >= set.anchors.length) return -1;\n if (idx < 0 || idx >= set.anchors[sub].length) return -1;\n let base = 0;\n for (let s = 0; s < sub; s++) base += set.anchors[s].length;\n return base + idx;\n}\n\n/** Resolve a flat index to the anchor itself. Null when out of range. */\nexport function anchorAt(set: AnchorSet, flatIndex: number): PenAnchor | null {\n const loc = locateAnchor(set, flatIndex);\n if (!loc) return null;\n return set.anchors[loc.sub][loc.idx] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Decode / apply / encode\n// ---------------------------------------------------------------------------\n\n/**\n * Decode `path`, hand the mutable anchor set to `edit`, and re-encode.\n * Returns the new path, or null when `edit` reports that it changed\n * nothing (returning `false`) — callers use that to skip a no-op commit\n * and avoid pushing an empty history entry.\n */\nexport function editAnchorSet(\n path: PolygonPath,\n edit: (set: AnchorSet) => boolean | void,\n): PolygonPath | null {\n const set = pathToAnchors(path) as AnchorSet;\n const changed = edit(set);\n if (changed === false) return null;\n return anchorsToPath(set.anchors, set.closed);\n}\n\n// ---------------------------------------------------------------------------\n// Edits\n// ---------------------------------------------------------------------------\n\n/** Translate an anchor and both of its handles by `(dx, dy)`. */\nexport function translateAnchorBy(\n set: AnchorSet,\n flatIndex: number,\n dx: number,\n dy: number,\n): boolean {\n const a = anchorAt(set, flatIndex);\n if (!a) return false;\n a.x += dx;\n a.y += dy;\n if (a.inHandle) {\n a.inHandle.x += dx;\n a.inHandle.y += dy;\n }\n if (a.outHandle) {\n a.outHandle.x += dx;\n a.outHandle.y += dy;\n }\n return true;\n}\n\n/**\n * Move one control handle to an absolute world position.\n *\n * When the anchor was smooth (handles collinear through the anchor) and\n * `breakSmoothness` is false, the opposite handle is mirrored so the curve\n * stays smooth — Illustrator's default. Alt-drag sets `breakSmoothness`\n * and the two handles become independent.\n */\nexport function moveHandleTo(\n set: AnchorSet,\n flatIndex: number,\n side: 'in' | 'out',\n toX: number,\n toY: number,\n breakSmoothness: boolean,\n): boolean {\n const a = anchorAt(set, flatIndex);\n if (!a) return false;\n const wasSmooth = isAnchorSmooth(a);\n const target = { x: toX, y: toY };\n if (side === 'out') a.outHandle = target;\n else a.inHandle = target;\n const oppositeSide = side === 'out' ? 'in' : 'out';\n const opposite = oppositeSide === 'in' ? a.inHandle : a.outHandle;\n if (!breakSmoothness && wasSmooth && opposite) {\n const mirrored = { x: 2 * a.x - target.x, y: 2 * a.y - target.y };\n if (oppositeSide === 'in') a.inHandle = mirrored;\n else a.outHandle = mirrored;\n }\n return true;\n}\n\n/**\n * Split the segment `(sub, segIdx) → (sub, segIdx + 1)` at parameter `t`,\n * inserting a new anchor there. On a closed subpath `segIdx` may also name\n * the closing segment (`sub.length - 1`); its new anchor is appended.\n *\n * A straight segment splits linearly and stays straight — the new anchor\n * has no handles. A curved one splits by de Casteljau, which preserves the\n * curve exactly: the two halves trace the original path.\n *\n * Returns the new anchor's flat index, or -1 when the segment doesn't\n * exist.\n */\nexport function insertAnchorOnSegment(\n set: AnchorSet,\n args: { sub: number; segIdx: number; t: number },\n): number {\n const ends = segmentEnds(set.anchors, set.closed, args.sub, args.segIdx);\n if (!ends) return -1;\n const { a, b } = ends;\n const at = args.segIdx + 1;\n if (isStraightSegment(a, b)) {\n set.anchors[args.sub].splice(at, 0, {\n x: a.x + (b.x - a.x) * args.t,\n y: a.y + (b.y - a.y) * args.t,\n });\n return flatAnchorIndex(set, args.sub, at);\n }\n const p0 = a, p1 = a.outHandle ?? a, p2 = b.inHandle ?? b, p3 = b;\n const { left, right } = splitCubicAtT(p0, p1, p2, p3, args.t);\n a.outHandle = { x: left[1].x, y: left[1].y };\n b.inHandle = { x: right[2].x, y: right[2].y };\n set.anchors[args.sub].splice(at, 0, {\n x: left[3].x,\n y: left[3].y,\n inHandle: { x: left[2].x, y: left[2].y },\n outHandle: { x: right[1].x, y: right[1].y },\n });\n return flatAnchorIndex(set, args.sub, at);\n}\n\n/** A point on a segment, as {@link segmentAt} reports it. */\nexport interface SegmentHit {\n sub: number;\n segIdx: number;\n /** Parameter on the segment — what {@link insertAnchorOnSegment} takes. */\n t: number;\n /** The point on the path at `t`, in the set's own space. */\n x: number;\n y: number;\n}\n\n/**\n * The segment point under `(x, y)`, when it lies within `tolerancePx` of\n * the path on screen and no nearer than that to either of the segment's\n * own anchors — a press on an anchor is about the anchor, not the segment\n * beside it.\n *\n * `scale` is the view's, so the tolerance holds per axis under\n * non-uniform zoom. The search runs in screen-scaled space: scaling is\n * affine, so the curve's parameterization, and therefore `t`, carries\n * straight back to world space.\n */\nexport function segmentAt(\n set: AnchorSet,\n x: number,\n y: number,\n opts: { tolerancePx: number; scale?: Scale2 },\n): SegmentHit | null {\n const scale = opts.scale ?? { x: 1, y: 1 };\n const onScreen = (p: { x: number; y: number }) => ({ x: p.x * scale.x, y: p.y * scale.y });\n const screenAnchors = set.anchors.map((sub) => sub.map((a) => ({\n ...onScreen(a),\n ...(a.inHandle ? { inHandle: onScreen(a.inHandle) } : {}),\n ...(a.outHandle ? { outHandle: onScreen(a.outHandle) } : {}),\n })));\n const hit = nearestSegmentT(screenAnchors, x * scale.x, y * scale.y, set.closed);\n if (!hit) return null;\n const ends = segmentEnds(set.anchors, set.closed, hit.sub, hit.segIdx)!;\n const point = pointOnSegment(ends.a, ends.b, hit.t);\n const tol = opts.tolerancePx;\n if (!withinPxRadius(point.x - x, point.y - y, tol, scale)) return null;\n if (withinPxRadius(point.x - ends.a.x, point.y - ends.a.y, tol, scale)) return null;\n if (withinPxRadius(point.x - ends.b.x, point.y - ends.b.y, tol, scale)) return null;\n return { sub: hit.sub, segIdx: hit.segIdx, t: hit.t, x: point.x, y: point.y };\n}\n\nfunction pointOnSegment(a: PenAnchor, b: PenAnchor, t: number): { x: number; y: number } {\n if (isStraightSegment(a, b)) return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n return cubicPointAt(a, a.outHandle ?? a, b.inHandle ?? b, b, t);\n}\n\n/**\n * Delete the anchors at `flatIndexes`. Where a deleted anchor had\n * neighbors on both sides — across the seam too, on a closed subpath — the\n * surviving segment is refitted through the gap (`fitCubicThroughDeletion`)\n * so the path keeps its shape instead of snapping to a straight line.\n *\n * Subpaths left with fewer than two anchors are dropped entirely — a\n * one-anchor subpath has no segments and would serialize to a stray `M`.\n *\n * If that would leave the path with **no** subpaths at all, the whole\n * deletion is refused (returns false, `set` untouched). Committing an\n * empty path turns the node into an invisible zombie: still selected,\n * still in the scene, nothing on screen and no anchors left to grab it\n * by. Deleting the object is the node-level Delete action's job, and the\n * user asked to delete anchors.\n */\nexport function deleteAnchorsAt(set: AnchorSet, flatIndexes: Iterable<number>): boolean {\n const bySub = new Map<number, number[]>();\n for (const flat of new Set(flatIndexes)) {\n const loc = locateAnchor(set, flat);\n if (!loc) continue;\n const list = bySub.get(loc.sub);\n if (list) list.push(loc.idx);\n else bySub.set(loc.sub, [loc.idx]);\n }\n if (bySub.size === 0) return false;\n\n // Refuse up front if this would wipe the path out — cheaper and safer\n // than mutating and rolling back.\n let survivors = 0;\n for (let s = 0; s < set.anchors.length; s++) {\n const doomed = bySub.get(s)?.length ?? 0;\n if (set.anchors[s].length - doomed >= 2) survivors++;\n }\n if (survivors === 0) return false;\n\n for (const [s, indices] of bySub) {\n const sub = set.anchors[s];\n if (!sub) continue;\n // Descending so each splice leaves the not-yet-processed indices valid.\n indices.sort((a, b) => b - a);\n for (const i of indices) {\n const wraps = set.closed[s] && sub.length > 2;\n const prev = wraps ? sub[(i - 1 + sub.length) % sub.length] : sub[i - 1];\n const next = wraps ? sub[(i + 1) % sub.length] : sub[i + 1];\n if (prev && next) {\n const { c1, c2 } = fitCubicThroughDeletion(prev, next);\n prev.outHandle = c1;\n next.inHandle = c2;\n }\n sub.splice(i, 1);\n }\n }\n\n // Drop degenerate subpaths, back to front so indices stay valid.\n for (let s = set.anchors.length - 1; s >= 0; s--) {\n if (set.anchors[s].length < 2) {\n set.anchors.splice(s, 1);\n set.closed.splice(s, 1);\n }\n }\n return true;\n}\n\n/**\n * Scissors: open a closed subpath at the given anchor. The anchor list is\n * rotated to start there and a second anchor at the same point ends it, so\n * every segment survives and the two ends can be pulled apart. No-ops on an\n * already-open subpath — there's nothing to cut.\n */\nexport function openSubpathAt(set: AnchorSet, flatIndex: number): boolean {\n const loc = locateAnchor(set, flatIndex);\n if (!loc) return false;\n if (!set.closed[loc.sub]) return false;\n const sub = set.anchors[loc.sub];\n if (!sub) return false;\n const { inHandle, ...start } = sub[loc.idx];\n const end: PenAnchor = { x: start.x, y: start.y, ...(inHandle ? { inHandle } : {}) };\n set.anchors[loc.sub] = [start, ...sub.slice(loc.idx + 1), ...sub.slice(0, loc.idx), end];\n set.closed[loc.sub] = false;\n return true;\n}\n\n/** Flat indices of every anchor whose on-curve point falls inside `rect`. */\nexport function anchorsInRect(set: AnchorSet, rect: AnchorRect): number[] {\n const x2 = rect.x + rect.width;\n const y2 = rect.y + rect.height;\n const hits: number[] = [];\n let flat = 0;\n for (const sub of set.anchors) {\n for (const a of sub) {\n if (a.x >= rect.x && a.x <= x2 && a.y >= rect.y && a.y <= y2) hits.push(flat);\n flat++;\n }\n }\n return hits;\n}\n"],"mappings":"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"}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { PATH_C as e, PATH_L as t, PATH_M as n, PATH_Q as r, PATH_Z as i, PathBuilder as a, forEachSegment as o, nearestOnCubic as s, nearestOnLine as c } from "@weasel-js/geom";
|
|
2
2
|
//#region src/features/paths/anchors.ts
|
|
3
3
|
function l(a) {
|
|
4
|
-
let { commands: s, coords: c } = a, l = [],
|
|
4
|
+
let { commands: s, coords: c } = a, l = [], d = [], f = null;
|
|
5
5
|
return o(s, c, (a, o) => {
|
|
6
6
|
switch (a) {
|
|
7
7
|
case n:
|
|
8
|
-
|
|
8
|
+
f && (l.push(f), d.push(!1)), f = [{
|
|
9
9
|
x: c[o],
|
|
10
10
|
y: c[o + 1]
|
|
11
11
|
}];
|
|
12
12
|
break;
|
|
13
13
|
case t:
|
|
14
|
-
if (!
|
|
15
|
-
|
|
14
|
+
if (!f) throw Error("pathToAnchors: L without prior M");
|
|
15
|
+
f.push({
|
|
16
16
|
x: c[o],
|
|
17
17
|
y: c[o + 1]
|
|
18
18
|
});
|
|
19
19
|
break;
|
|
20
20
|
case e: {
|
|
21
|
-
if (!
|
|
22
|
-
let e =
|
|
21
|
+
if (!f) throw Error("pathToAnchors: C without prior M");
|
|
22
|
+
let e = f[f.length - 1];
|
|
23
23
|
e.outHandle = {
|
|
24
24
|
x: c[o],
|
|
25
25
|
y: c[o + 1]
|
|
26
|
-
},
|
|
26
|
+
}, f.push({
|
|
27
27
|
x: c[o + 4],
|
|
28
28
|
y: c[o + 5],
|
|
29
29
|
inHandle: {
|
|
@@ -34,12 +34,12 @@ function l(a) {
|
|
|
34
34
|
break;
|
|
35
35
|
}
|
|
36
36
|
case r: {
|
|
37
|
-
if (!
|
|
37
|
+
if (!f) throw Error("pathToAnchors: Q without prior M");
|
|
38
38
|
let e = {
|
|
39
39
|
x: c[o],
|
|
40
40
|
y: c[o + 1]
|
|
41
|
-
}, t =
|
|
42
|
-
t.outHandle = e,
|
|
41
|
+
}, t = f[f.length - 1];
|
|
42
|
+
t.outHandle = e, f.push({
|
|
43
43
|
x: c[o + 2],
|
|
44
44
|
y: c[o + 3],
|
|
45
45
|
inHandle: { ...e }
|
|
@@ -47,16 +47,21 @@ function l(a) {
|
|
|
47
47
|
break;
|
|
48
48
|
}
|
|
49
49
|
case i:
|
|
50
|
-
|
|
50
|
+
f &&= (u(f), l.push(f), d.push(!0), null);
|
|
51
51
|
break;
|
|
52
52
|
default: throw Error(`pathToAnchors: unknown command ${a}`);
|
|
53
53
|
}
|
|
54
|
-
}),
|
|
54
|
+
}), f && (l.push(f), d.push(!1)), {
|
|
55
55
|
anchors: l,
|
|
56
|
-
closed:
|
|
56
|
+
closed: d
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
function u(e
|
|
59
|
+
function u(e) {
|
|
60
|
+
if (e.length < 2) return;
|
|
61
|
+
let t = e[0], n = e[e.length - 1];
|
|
62
|
+
n.inHandle && n.x === t.x && n.y === t.y && (e.pop(), t.inHandle = n.inHandle);
|
|
63
|
+
}
|
|
64
|
+
function d(e, t) {
|
|
60
65
|
let n = new a();
|
|
61
66
|
for (let r = 0; r < e.length; r++) {
|
|
62
67
|
let i = e[r];
|
|
@@ -94,15 +99,15 @@ function u(e, t) {
|
|
|
94
99
|
}
|
|
95
100
|
return n.build();
|
|
96
101
|
}
|
|
97
|
-
var
|
|
98
|
-
function
|
|
102
|
+
var f = .001;
|
|
103
|
+
function p(e) {
|
|
99
104
|
if (!e.inHandle || !e.outHandle) return !1;
|
|
100
105
|
let t = e.inHandle.x - e.x, n = e.inHandle.y - e.y, r = e.outHandle.x - e.x, i = e.outHandle.y - e.y, a = Math.hypot(t, n), o = Math.hypot(r, i);
|
|
101
106
|
if (a === 0 || o === 0) return !1;
|
|
102
107
|
let s = (t * i - n * r) / (a * o);
|
|
103
|
-
return Math.abs(s) <
|
|
108
|
+
return Math.abs(s) < f;
|
|
104
109
|
}
|
|
105
|
-
function
|
|
110
|
+
function m(i) {
|
|
106
111
|
if (i.kind === "rect") return 4;
|
|
107
112
|
let a = i.commands, o = 0;
|
|
108
113
|
for (let i = 0; i < a.length; i++) {
|
|
@@ -111,7 +116,7 @@ function p(i) {
|
|
|
111
116
|
}
|
|
112
117
|
return o;
|
|
113
118
|
}
|
|
114
|
-
function
|
|
119
|
+
function h(e, t, n, r) {
|
|
115
120
|
let i = e[n];
|
|
116
121
|
return !i || r < 0 ? null : r + 1 < i.length ? {
|
|
117
122
|
a: i[r],
|
|
@@ -121,24 +126,24 @@ function m(e, t, n, r) {
|
|
|
121
126
|
b: i[0]
|
|
122
127
|
} : null;
|
|
123
128
|
}
|
|
124
|
-
function
|
|
129
|
+
function g(e, t) {
|
|
125
130
|
return e.outHandle == null && t.inHandle == null;
|
|
126
131
|
}
|
|
127
|
-
function
|
|
128
|
-
let i = e.outHandle ?? e, a = t.inHandle ?? t, o =
|
|
132
|
+
function _(e, t, n, r) {
|
|
133
|
+
let i = e.outHandle ?? e, a = t.inHandle ?? t, o = g(e, t) ? c(n, r, e.x, e.y, t.x, t.y) : s(n, r, e.x, e.y, i.x, i.y, a.x, a.y, t.x, t.y);
|
|
129
134
|
return {
|
|
130
135
|
t: o.t,
|
|
131
136
|
d2: o.dist * o.dist
|
|
132
137
|
};
|
|
133
138
|
}
|
|
134
|
-
function
|
|
139
|
+
function v(e, t, n, r) {
|
|
135
140
|
let i = Infinity, a = null;
|
|
136
141
|
for (let o = 0; o < e.length; o++) {
|
|
137
142
|
let s = e[o].length - +!r?.[o];
|
|
138
143
|
for (let c = 0; c < s; c++) {
|
|
139
|
-
let s =
|
|
144
|
+
let s = h(e, r, o, c);
|
|
140
145
|
if (!s) continue;
|
|
141
|
-
let l =
|
|
146
|
+
let l = _(s.a, s.b, t, n);
|
|
142
147
|
l.d2 < i && (i = l.d2, a = {
|
|
143
148
|
sub: o,
|
|
144
149
|
segIdx: c,
|
|
@@ -149,6 +154,6 @@ function _(e, t, n, r) {
|
|
|
149
154
|
return a;
|
|
150
155
|
}
|
|
151
156
|
//#endregion
|
|
152
|
-
export {
|
|
157
|
+
export { d as anchorsToPath, m as countPathAnchors, p as isAnchorSmooth, g as isStraightSegment, v as nearestSegmentT, l as pathToAnchors, h as segmentEnds };
|
|
153
158
|
|
|
154
159
|
//# sourceMappingURL=anchors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anchors.js","names":["pathToAnchors","path","anchors","closed","current","forEachSegment","commands","coords","cmd","PATH_M","ci","PATH_L","PATH_C","prev","PATH_Q","handle","PATH_Z","anchorsToPath","b","PathBuilder","s","sub","i","cur","c1","c2","last","first","SMOOTH_THRESHOLD","isAnchorSmooth","a","inDX","inDY","outDX","outDY","inLen","outLen","cross","countPathAnchors","cmds","n","c","segmentEnds","segIdx","isStraightSegment","nearestOnSegment","p1","p2","hit","nearestOnLine","x","y","nearestOnCubic","nearestSegmentT","bestD2","best","segCount","ends","wx","wy"],"sources":["../../../src/features/paths/anchors.ts"],"sourcesContent":["/**\n * Counts the path anchors used by the per-anchor coloring surface. An\n * \"anchor\" is the destination point of a path command: M, L, C, Q each\n * contribute one (the (x, y) where the pen ends up); Z contributes none\n * (it closes back to the subpath's first M). RectPath has four implicit\n * anchors — the corners — matching its M/L/L/L/Z stroke tessellation.\n *\n * Consumers use this to size their per-anchor color array; the renderer\n * uses it to validate the array length in dev builds.\n */\n\nimport {\n PATH_C,\n PATH_L,\n PATH_M,\n PATH_Q,\n PATH_Z,\n type Path,\n type PolygonPath,\n forEachSegment,\n nearestOnCubic,\n nearestOnLine,\n PathBuilder,\n} from '@weasel-js/geom';\n\n/** One anchor of an editable path: its on-curve point plus the two control\n * handles that shape the segments either side of it. Handles are in the same\n * space as the point, and absent when the adjoining segment is straight. */\nexport interface PenAnchor {\n x: number;\n y: number;\n inHandle?: { x: number; y: number };\n outHandle?: { x: number; y: number };\n}\n\n/**\n * Derive a per-subpath anchor model from a PolygonPath. Subpaths split on\n * every `M` command; a subpath is closed iff it ends with `Z`.\n *\n * Cubic-segment control points become the outHandle of the previous anchor\n * and the inHandle of the next anchor. Quadratic segments are upgraded to\n * cubics (each control reused for both adjacent handles) — this loses no\n * geometry. Linear segments produce anchors with no handles.\n */\nexport function pathToAnchors(\n path: PolygonPath,\n): { anchors: PenAnchor[][]; closed: boolean[] } {\n const { commands, coords } = path;\n const anchors: PenAnchor[][] = [];\n const closed: boolean[] = [];\n let current: PenAnchor[] | null = null;\n\n forEachSegment(commands, coords, (cmd, ci) => {\n switch (cmd) {\n case PATH_M: {\n if (current) { anchors.push(current); closed.push(false); }\n current = [{ x: coords[ci], y: coords[ci + 1] }];\n break;\n }\n case PATH_L: {\n if (!current) throw new Error('pathToAnchors: L without prior M');\n current.push({ x: coords[ci], y: coords[ci + 1] });\n break;\n }\n case PATH_C: {\n if (!current) throw new Error('pathToAnchors: C without prior M');\n const prev = current[current.length - 1];\n prev.outHandle = { x: coords[ci], y: coords[ci + 1] };\n current.push({\n x: coords[ci + 4], y: coords[ci + 5],\n inHandle: { x: coords[ci + 2], y: coords[ci + 3] },\n });\n break;\n }\n case PATH_Q: {\n if (!current) throw new Error('pathToAnchors: Q without prior M');\n // Quadratic → cubic: handle is the same control point on both sides.\n const handle = { x: coords[ci], y: coords[ci + 1] };\n const prev = current[current.length - 1];\n prev.outHandle = handle;\n current.push({ x: coords[ci + 2], y: coords[ci + 3], inHandle: { ...handle } });\n break;\n }\n case PATH_Z: {\n if (current) { anchors.push(current); closed.push(true); current = null; }\n break;\n }\n default:\n throw new Error(`pathToAnchors: unknown command ${cmd}`);\n }\n });\n if (current) { anchors.push(current); closed.push(false); }\n return { anchors, closed };\n}\n\n/**\n * Serialize the per-subpath anchor model back to a PolygonPath. Inverse of\n * `pathToAnchors`. Curve-vs-line decision per segment:\n * - Both adjacent handles absent → straight L segment.\n * - Either handle present → C segment, with missing handles defaulting to\n * the anchor point itself (degenerate but valid; renders as a near-line).\n */\nexport function anchorsToPath(\n anchors: PenAnchor[][],\n closed: boolean[],\n): PolygonPath {\n const b = new PathBuilder();\n for (let s = 0; s < anchors.length; s++) {\n const sub = anchors[s];\n if (sub.length === 0) continue;\n b.moveTo(sub[0].x, sub[0].y);\n for (let i = 1; i < sub.length; i++) {\n const prev = sub[i - 1];\n const cur = sub[i];\n const hasHandle = prev.outHandle != null || cur.inHandle != null;\n if (!hasHandle) {\n b.lineTo(cur.x, cur.y);\n } else {\n const c1 = prev.outHandle ?? { x: prev.x, y: prev.y };\n const c2 = cur.inHandle ?? { x: cur.x, y: cur.y };\n b.curveTo(c1.x, c1.y, c2.x, c2.y, cur.x, cur.y);\n }\n }\n if (closed[s]) {\n // Bridge last → first if they have curve handles; either way emit Z.\n const last = sub[sub.length - 1];\n const first = sub[0];\n const hasHandle = last.outHandle != null || first.inHandle != null;\n if (hasHandle) {\n const c1 = last.outHandle ?? { x: last.x, y: last.y };\n const c2 = first.inHandle ?? { x: first.x, y: first.y };\n b.curveTo(c1.x, c1.y, c2.x, c2.y, first.x, first.y);\n }\n b.close();\n }\n }\n return b.build();\n}\n\nconst SMOOTH_THRESHOLD = 0.001;\n\n/**\n * Returns true if the anchor's in-handle and out-handle are collinear with\n * the anchor point, indicating \"smooth\" (mirror-drag) behavior. Detected via\n * the magnitude of the normalized cross product of the two handle vectors.\n *\n * Edge cases:\n * - Missing in or out handle → false (corner by definition).\n * - Either handle at zero distance from anchor → false.\n */\nexport function isAnchorSmooth(a: PenAnchor): boolean {\n if (!a.inHandle || !a.outHandle) return false;\n const inDX = a.inHandle.x - a.x;\n const inDY = a.inHandle.y - a.y;\n const outDX = a.outHandle.x - a.x;\n const outDY = a.outHandle.y - a.y;\n const inLen = Math.hypot(inDX, inDY);\n const outLen = Math.hypot(outDX, outDY);\n if (inLen === 0 || outLen === 0) return false;\n // For collinear handles on opposite sides of the anchor, the cross product\n // of the two handle vectors should be zero. Normalize by both magnitudes.\n const cross = (inDX * outDY - inDY * outDX) / (inLen * outLen);\n return Math.abs(cross) < SMOOTH_THRESHOLD;\n}\n\n/** How many anchors a path has. Matches the per-anchor arrays the renderer\n * expects for `vertexColors`. */\nexport function countPathAnchors(path: Path): number {\n if (path.kind === 'rect') return 4;\n const cmds = path.commands;\n let n = 0;\n for (let i = 0; i < cmds.length; i++) {\n const c = cmds[i];\n if (c === PATH_M || c === PATH_L || c === PATH_C || c === PATH_Q) n++;\n }\n return n;\n}\n\n/** The segment `(sub, segIdx)` as its two end anchors, or null when it\n * doesn't exist. `segIdx === sub.length - 1` names a closed subpath's\n * closing segment, which runs from the last anchor back to the first. */\nexport function segmentEnds(\n anchors: readonly PenAnchor[][],\n closed: readonly boolean[] | undefined,\n sub: number,\n segIdx: number,\n): { a: PenAnchor; b: PenAnchor } | null {\n const s = anchors[sub];\n if (!s || segIdx < 0) return null;\n if (segIdx + 1 < s.length) return { a: s[segIdx], b: s[segIdx + 1] };\n if (closed?.[sub] && s.length > 1 && segIdx === s.length - 1) return { a: s[segIdx], b: s[0] };\n return null;\n}\n\n/** Whether the segment from `a` to `b` is a straight line (no handles). */\nexport function isStraightSegment(a: PenAnchor, b: PenAnchor): boolean {\n return a.outHandle == null && b.inHandle == null;\n}\n\n/** Nearest parameter on one segment to `(x, y)`, with its squared distance.\n * A straight segment is parameterized by arc length (`t` is the fraction of\n * the way from `a` to `b`), matching how {@link insertAnchorOnSegment}\n * splits one. */\nfunction nearestOnSegment(a: PenAnchor, b: PenAnchor, x: number, y: number): { t: number; d2: number } {\n const p1 = a.outHandle ?? a;\n const p2 = b.inHandle ?? b;\n const hit = isStraightSegment(a, b)\n ? nearestOnLine(x, y, a.x, a.y, b.x, b.y)\n : nearestOnCubic(x, y, a.x, a.y, p1.x, p1.y, p2.x, p2.y, b.x, b.y);\n return { t: hit.t, d2: hit.dist * hit.dist };\n}\n\n/** The segment of a decoded path nearest `(wx, wy)`, and the parameter `t`\n * on it closest to that point. Pass `closed` (from `pathToAnchors`) to\n * include each closed subpath's closing segment, reported as\n * `segIdx === sub.length - 1`. `t` spans `[0, 1]`, endpoints included.\n * Returns `null` only when there are no segments. */\nexport function nearestSegmentT(\n anchors: PenAnchor[][],\n wx: number,\n wy: number,\n closed?: readonly boolean[],\n): { sub: number; segIdx: number; t: number } | null {\n let bestD2 = Infinity;\n let best: { sub: number; segIdx: number; t: number } | null = null;\n for (let s = 0; s < anchors.length; s++) {\n const segCount = anchors[s].length - (closed?.[s] ? 0 : 1);\n for (let i = 0; i < segCount; i++) {\n const ends = segmentEnds(anchors, closed, s, i);\n if (!ends) continue;\n const hit = nearestOnSegment(ends.a, ends.b, wx, wy);\n if (hit.d2 < bestD2) {\n bestD2 = hit.d2;\n best = { sub: s, segIdx: i, t: hit.t };\n }\n }\n }\n return best;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"anchors.js","names":["pathToAnchors","path","anchors","closed","current","forEachSegment","commands","coords","cmd","PATH_M","ci","PATH_L","PATH_C","prev","PATH_Q","handle","PATH_Z","foldClosingCurve","sub","first","last","anchorsToPath","b","PathBuilder","s","i","cur","c1","c2","SMOOTH_THRESHOLD","isAnchorSmooth","a","inDX","inDY","outDX","outDY","inLen","outLen","cross","countPathAnchors","cmds","n","c","segmentEnds","segIdx","isStraightSegment","nearestOnSegment","p1","p2","hit","nearestOnLine","x","y","nearestOnCubic","nearestSegmentT","bestD2","best","segCount","ends","wx","wy"],"sources":["../../../src/features/paths/anchors.ts"],"sourcesContent":["/**\n * Counts the path anchors used by the per-anchor coloring surface. An\n * \"anchor\" is the destination point of a path command: M, L, C, Q each\n * contribute one (the (x, y) where the pen ends up); Z contributes none\n * (it closes back to the subpath's first M). RectPath has four implicit\n * anchors — the corners — matching its M/L/L/L/Z stroke tessellation.\n *\n * Consumers use this to size their per-anchor color array; the renderer\n * uses it to validate the array length in dev builds.\n */\n\nimport {\n PATH_C,\n PATH_L,\n PATH_M,\n PATH_Q,\n PATH_Z,\n type Path,\n type PolygonPath,\n forEachSegment,\n nearestOnCubic,\n nearestOnLine,\n PathBuilder,\n} from '@weasel-js/geom';\n\n/** One anchor of an editable path: its on-curve point plus the two control\n * handles that shape the segments either side of it. Handles are in the same\n * space as the point, and absent when the adjoining segment is straight. */\nexport interface PenAnchor {\n x: number;\n y: number;\n inHandle?: { x: number; y: number };\n outHandle?: { x: number; y: number };\n}\n\n/**\n * Derive a per-subpath anchor model from a PolygonPath. Subpaths split on\n * every `M` command; a subpath is closed iff it ends with `Z`.\n *\n * Cubic-segment control points become the outHandle of the previous anchor\n * and the inHandle of the next anchor. A closed subpath whose last curve ends\n * exactly on its first point holds that point twice; the two are one anchor,\n * and the curve's second control is that anchor's inHandle. Quadratic segments are upgraded to\n * cubics (each control reused for both adjacent handles) — this loses no\n * geometry. Linear segments produce anchors with no handles.\n */\nexport function pathToAnchors(\n path: PolygonPath,\n): { anchors: PenAnchor[][]; closed: boolean[] } {\n const { commands, coords } = path;\n const anchors: PenAnchor[][] = [];\n const closed: boolean[] = [];\n let current: PenAnchor[] | null = null;\n\n forEachSegment(commands, coords, (cmd, ci) => {\n switch (cmd) {\n case PATH_M: {\n if (current) { anchors.push(current); closed.push(false); }\n current = [{ x: coords[ci], y: coords[ci + 1] }];\n break;\n }\n case PATH_L: {\n if (!current) throw new Error('pathToAnchors: L without prior M');\n current.push({ x: coords[ci], y: coords[ci + 1] });\n break;\n }\n case PATH_C: {\n if (!current) throw new Error('pathToAnchors: C without prior M');\n const prev = current[current.length - 1];\n prev.outHandle = { x: coords[ci], y: coords[ci + 1] };\n current.push({\n x: coords[ci + 4], y: coords[ci + 5],\n inHandle: { x: coords[ci + 2], y: coords[ci + 3] },\n });\n break;\n }\n case PATH_Q: {\n if (!current) throw new Error('pathToAnchors: Q without prior M');\n // Quadratic → cubic: handle is the same control point on both sides.\n const handle = { x: coords[ci], y: coords[ci + 1] };\n const prev = current[current.length - 1];\n prev.outHandle = handle;\n current.push({ x: coords[ci + 2], y: coords[ci + 3], inHandle: { ...handle } });\n break;\n }\n case PATH_Z: {\n if (current) {\n foldClosingCurve(current);\n anchors.push(current);\n closed.push(true);\n current = null;\n }\n break;\n }\n default:\n throw new Error(`pathToAnchors: unknown command ${cmd}`);\n }\n });\n if (current) { anchors.push(current); closed.push(false); }\n return { anchors, closed };\n}\n\n/** Merge a closing curve's end anchor into the subpath's first anchor when\n * the two coincide. */\nfunction foldClosingCurve(sub: PenAnchor[]): void {\n if (sub.length < 2) return;\n const first = sub[0];\n const last = sub[sub.length - 1];\n if (!last.inHandle || last.x !== first.x || last.y !== first.y) return;\n sub.pop();\n first.inHandle = last.inHandle;\n}\n\n/**\n * Serialize the per-subpath anchor model back to a PolygonPath. Inverse of\n * `pathToAnchors`. Curve-vs-line decision per segment:\n * - Both adjacent handles absent → straight L segment.\n * - Either handle present → C segment, with missing handles defaulting to\n * the anchor point itself (degenerate but valid; renders as a near-line).\n */\nexport function anchorsToPath(\n anchors: PenAnchor[][],\n closed: boolean[],\n): PolygonPath {\n const b = new PathBuilder();\n for (let s = 0; s < anchors.length; s++) {\n const sub = anchors[s];\n if (sub.length === 0) continue;\n b.moveTo(sub[0].x, sub[0].y);\n for (let i = 1; i < sub.length; i++) {\n const prev = sub[i - 1];\n const cur = sub[i];\n const hasHandle = prev.outHandle != null || cur.inHandle != null;\n if (!hasHandle) {\n b.lineTo(cur.x, cur.y);\n } else {\n const c1 = prev.outHandle ?? { x: prev.x, y: prev.y };\n const c2 = cur.inHandle ?? { x: cur.x, y: cur.y };\n b.curveTo(c1.x, c1.y, c2.x, c2.y, cur.x, cur.y);\n }\n }\n if (closed[s]) {\n // Bridge last → first if they have curve handles; either way emit Z.\n const last = sub[sub.length - 1];\n const first = sub[0];\n const hasHandle = last.outHandle != null || first.inHandle != null;\n if (hasHandle) {\n const c1 = last.outHandle ?? { x: last.x, y: last.y };\n const c2 = first.inHandle ?? { x: first.x, y: first.y };\n b.curveTo(c1.x, c1.y, c2.x, c2.y, first.x, first.y);\n }\n b.close();\n }\n }\n return b.build();\n}\n\nconst SMOOTH_THRESHOLD = 0.001;\n\n/**\n * Returns true if the anchor's in-handle and out-handle are collinear with\n * the anchor point, indicating \"smooth\" (mirror-drag) behavior. Detected via\n * the magnitude of the normalized cross product of the two handle vectors.\n *\n * Edge cases:\n * - Missing in or out handle → false (corner by definition).\n * - Either handle at zero distance from anchor → false.\n */\nexport function isAnchorSmooth(a: PenAnchor): boolean {\n if (!a.inHandle || !a.outHandle) return false;\n const inDX = a.inHandle.x - a.x;\n const inDY = a.inHandle.y - a.y;\n const outDX = a.outHandle.x - a.x;\n const outDY = a.outHandle.y - a.y;\n const inLen = Math.hypot(inDX, inDY);\n const outLen = Math.hypot(outDX, outDY);\n if (inLen === 0 || outLen === 0) return false;\n // For collinear handles on opposite sides of the anchor, the cross product\n // of the two handle vectors should be zero. Normalize by both magnitudes.\n const cross = (inDX * outDY - inDY * outDX) / (inLen * outLen);\n return Math.abs(cross) < SMOOTH_THRESHOLD;\n}\n\n/** How many anchors a path has. Matches the per-anchor arrays the renderer\n * expects for `vertexColors`. */\nexport function countPathAnchors(path: Path): number {\n if (path.kind === 'rect') return 4;\n const cmds = path.commands;\n let n = 0;\n for (let i = 0; i < cmds.length; i++) {\n const c = cmds[i];\n if (c === PATH_M || c === PATH_L || c === PATH_C || c === PATH_Q) n++;\n }\n return n;\n}\n\n/** The segment `(sub, segIdx)` as its two end anchors, or null when it\n * doesn't exist. `segIdx === sub.length - 1` names a closed subpath's\n * closing segment, which runs from the last anchor back to the first. */\nexport function segmentEnds(\n anchors: readonly PenAnchor[][],\n closed: readonly boolean[] | undefined,\n sub: number,\n segIdx: number,\n): { a: PenAnchor; b: PenAnchor } | null {\n const s = anchors[sub];\n if (!s || segIdx < 0) return null;\n if (segIdx + 1 < s.length) return { a: s[segIdx], b: s[segIdx + 1] };\n if (closed?.[sub] && s.length > 1 && segIdx === s.length - 1) return { a: s[segIdx], b: s[0] };\n return null;\n}\n\n/** Whether the segment from `a` to `b` is a straight line (no handles). */\nexport function isStraightSegment(a: PenAnchor, b: PenAnchor): boolean {\n return a.outHandle == null && b.inHandle == null;\n}\n\n/** Nearest parameter on one segment to `(x, y)`, with its squared distance.\n * A straight segment is parameterized by arc length (`t` is the fraction of\n * the way from `a` to `b`), matching how {@link insertAnchorOnSegment}\n * splits one. */\nfunction nearestOnSegment(a: PenAnchor, b: PenAnchor, x: number, y: number): { t: number; d2: number } {\n const p1 = a.outHandle ?? a;\n const p2 = b.inHandle ?? b;\n const hit = isStraightSegment(a, b)\n ? nearestOnLine(x, y, a.x, a.y, b.x, b.y)\n : nearestOnCubic(x, y, a.x, a.y, p1.x, p1.y, p2.x, p2.y, b.x, b.y);\n return { t: hit.t, d2: hit.dist * hit.dist };\n}\n\n/** The segment of a decoded path nearest `(wx, wy)`, and the parameter `t`\n * on it closest to that point. Pass `closed` (from `pathToAnchors`) to\n * include each closed subpath's closing segment, reported as\n * `segIdx === sub.length - 1`. `t` spans `[0, 1]`, endpoints included.\n * Returns `null` only when there are no segments. */\nexport function nearestSegmentT(\n anchors: PenAnchor[][],\n wx: number,\n wy: number,\n closed?: readonly boolean[],\n): { sub: number; segIdx: number; t: number } | null {\n let bestD2 = Infinity;\n let best: { sub: number; segIdx: number; t: number } | null = null;\n for (let s = 0; s < anchors.length; s++) {\n const segCount = anchors[s].length - (closed?.[s] ? 0 : 1);\n for (let i = 0; i < segCount; i++) {\n const ends = segmentEnds(anchors, closed, s, i);\n if (!ends) continue;\n const hit = nearestOnSegment(ends.a, ends.b, wx, wy);\n if (hit.d2 < bestD2) {\n bestD2 = hit.d2;\n best = { sub: s, segIdx: i, t: hit.t };\n }\n }\n }\n return best;\n}\n"],"mappings":";;AA8CA,SAAgBA,EACd,GAC+C;CAC/C,IAAM,EAAE,aAAU,cAAWC,GACvBC,IAAyB,CAAC,GAC1BC,IAAoB,CAAC,GACvBC,IAA8B;CA+ClC,OA7CAC,EAAeC,GAAUC,IAAS,GAAK,MAAO;EAC5C,QAAQC,GAAR;GACE,KAAKC;IAEH,AADIL,MAAWF,EAAQ,KAAKE,CAAO,GAAGD,EAAO,KAAK,EAAK,IACvD,IAAU,CAAC;KAAE,GAAGI,EAAOG;KAAK,GAAGH,EAAOG,IAAK;IAAG,CAAC;IAC/C;GAEF,KAAKC;IACH,IAAI,CAACP,GAAS,MAAU,MAAM,kCAAkC;IAChE,EAAQ,KAAK;KAAE,GAAGG,EAAOG;KAAK,GAAGH,EAAOG,IAAK;IAAG,CAAC;IACjD;GAEF,KAAKE,GAAQ;IACX,IAAI,CAACR,GAAS,MAAU,MAAM,kCAAkC;IAChE,IAAMS,IAAOT,EAAQA,EAAQ,SAAS;IAEtC,AADA,EAAK,YAAY;KAAE,GAAGG,EAAOG;KAAK,GAAGH,EAAOG,IAAK;IAAG,GACpDN,EAAQ,KAAK;KACX,GAAGG,EAAOG,IAAK;KAAI,GAAGH,EAAOG,IAAK;KAClC,UAAU;MAAE,GAAGH,EAAOG,IAAK;MAAI,GAAGH,EAAOG,IAAK;KAAG;IACnD,CAAC;IACD;GACF;GACA,KAAKI,GAAQ;IACX,IAAI,CAACV,GAAS,MAAU,MAAM,kCAAkC;IAEhE,IAAMW,IAAS;KAAE,GAAGR,EAAOG;KAAK,GAAGH,EAAOG,IAAK;IAAG,GAC5CG,IAAOT,EAAQA,EAAQ,SAAS;IAEtC,AADA,EAAK,YAAYW,GACjBX,EAAQ,KAAK;KAAE,GAAGG,EAAOG,IAAK;KAAI,GAAGH,EAAOG,IAAK;KAAI,UAAU,EAAE,GAAGK,EAAO;IAAE,CAAC;IAC9E;GACF;GACA,KAAKC;IACH,AAIEZ,OAHAa,EAAiBb,CAAO,GACxBF,EAAQ,KAAKE,CAAO,GACpBD,EAAO,KAAK,EAAI,GACN;IAEZ;GAEF,SACE,MAAU,MAAM,kCAAkCK,GAAK;EAC3D;CACF,CAAC,GACGJ,MAAWF,EAAQ,KAAKE,CAAO,GAAGD,EAAO,KAAK,EAAK,IAChD;EAAE;EAAS;CAAO;AAC3B;AAIA,SAASc,EAAiB,GAAwB;CAChD,IAAIC,EAAI,SAAS,GAAG;CACpB,IAAMC,IAAQD,EAAI,IACZE,IAAOF,EAAIA,EAAI,SAAS;CAC9B,AAAKE,EAAK,YAAYA,EAAK,MAAMD,EAAM,KAAKC,EAAK,MAAMD,EAAM,MAC7DD,EAAI,IAAI,GACR,EAAM,WAAWE,EAAK;AACxB;AASA,SAAgBC,EACd,GACA,GACa;CACb,IAAMC,IAAI,IAAIC,EAAY;CAC1B,KAAK,IAAIC,IAAI,GAAGA,IAAItB,EAAQ,QAAQ,KAAK;EACvC,IAAMgB,IAAMhB,EAAQsB;EACpB,IAAIN,EAAI,WAAW,GACnB;KAAE,OAAOA,EAAI,EAAE,CAAC,GAAGA,EAAI,EAAE,CAAC,CAAC;GAC3B,KAAK,IAAIO,IAAI,GAAGA,IAAIP,EAAI,QAAQ,KAAK;IACnC,IAAML,IAAOK,EAAIO,IAAI,IACfC,IAAMR,EAAIO;IAEhB,IADkBZ,EAAK,aAAa,QAAQa,EAAI,YAAY,MAE1D,EAAE,OAAOA,EAAI,GAAGA,EAAI,CAAC;SAChB;KACL,IAAMC,IAAKd,EAAK,aAAa;MAAE,GAAGA,EAAK;MAAG,GAAGA,EAAK;KAAE,GAC9Ce,IAAKF,EAAI,YAAY;MAAE,GAAGA,EAAI;MAAG,GAAGA,EAAI;KAAE;KAChD,EAAE,QAAQC,EAAG,GAAGA,EAAG,GAAGC,EAAG,GAAGA,EAAG,GAAGF,EAAI,GAAGA,EAAI,CAAC;IAChD;GACF;GACA,IAAIvB,EAAOqB,IAAI;IAEb,IAAMJ,IAAOF,EAAIA,EAAI,SAAS,IACxBC,IAAQD,EAAI;IAElB,IADkBE,EAAK,aAAa,QAAQD,EAAM,YAAY,MAC/C;KACb,IAAMQ,IAAKP,EAAK,aAAa;MAAE,GAAGA,EAAK;MAAG,GAAGA,EAAK;KAAE,GAC9CQ,IAAKT,EAAM,YAAY;MAAE,GAAGA,EAAM;MAAG,GAAGA,EAAM;KAAE;KACtD,EAAE,QAAQQ,EAAG,GAAGA,EAAG,GAAGC,EAAG,GAAGA,EAAG,GAAGT,EAAM,GAAGA,EAAM,CAAC;IACpD;IACA,EAAE,MAAM;GACV;EAxB2B;CAyB7B;CACA,OAAOG,EAAE,MAAM;AACjB;AAEA,IAAMO,IAAmB;AAWzB,SAAgBC,EAAe,GAAuB;CACpD,IAAI,CAACC,EAAE,YAAY,CAACA,EAAE,WAAW,OAAO;CACxC,IAAMC,IAAOD,EAAE,SAAS,IAAIA,EAAE,GACxBE,IAAOF,EAAE,SAAS,IAAIA,EAAE,GACxBG,IAAQH,EAAE,UAAU,IAAIA,EAAE,GAC1BI,IAAQJ,EAAE,UAAU,IAAIA,EAAE,GAC1BK,IAAQ,KAAK,MAAMJ,GAAMC,CAAI,GAC7BI,IAAS,KAAK,MAAMH,GAAOC,CAAK;CACtC,IAAIC,MAAU,KAAKC,MAAW,GAAG,OAAO;CAGxC,IAAMC,KAASN,IAAOG,IAAQF,IAAOC,MAAUE,IAAQC;CACvD,OAAO,KAAK,IAAIC,CAAK,IAAIT;AAC3B;AAIA,SAAgBU,EAAiB,GAAoB;CACnD,IAAItC,EAAK,SAAS,QAAQ,OAAO;CACjC,IAAMuC,IAAOvC,EAAK,UACdwC,IAAI;CACR,KAAK,IAAI,IAAI,GAAG,IAAID,EAAK,QAAQ,KAAK;EACpC,IAAME,IAAIF,EAAK;EACf,CAAIE,MAAMjC,KAAUiC,MAAM/B,KAAU+B,MAAM9B,KAAU8B,MAAM5B,MAAQ;CACpE;CACA,OAAO2B;AACT;AAKA,SAAgBE,EACd,GACA,GACA,GACA,GACuC;CACvC,IAAMnB,IAAItB,EAAQgB;CAIlB,OAHI,CAACM,KAAKoB,IAAS,IAAU,OACzBA,IAAS,IAAIpB,EAAE,SAAe;EAAE,GAAGA,EAAEoB;EAAS,GAAGpB,EAAEoB,IAAS;CAAG,IAC/DzC,IAASe,MAAQM,EAAE,SAAS,KAAKoB,MAAWpB,EAAE,SAAS,IAAU;EAAE,GAAGA,EAAEoB;EAAS,GAAGpB,EAAE;CAAG,IACtF;AACT;AAGA,SAAgBqB,EAAkB,GAAc,GAAuB;CACrE,OAAOd,EAAE,aAAa,QAAQT,EAAE,YAAY;AAC9C;AAMA,SAASwB,EAAiB,GAAc,GAAc,GAAW,GAAsC;CACrG,IAAMC,IAAKhB,EAAE,aAAaA,GACpBiB,IAAK1B,EAAE,YAAYA,GACnB2B,IAAMJ,EAAkBd,GAAGT,CAAC,IAC9B4B,EAAcC,GAAGC,GAAGrB,EAAE,GAAGA,EAAE,GAAGT,EAAE,GAAGA,EAAE,CAAC,IACtC+B,EAAeF,GAAGC,GAAGrB,EAAE,GAAGA,EAAE,GAAGgB,EAAG,GAAGA,EAAG,GAAGC,EAAG,GAAGA,EAAG,GAAG1B,EAAE,GAAGA,EAAE,CAAC;CACnE,OAAO;EAAE,GAAG2B,EAAI;EAAG,IAAIA,EAAI,OAAOA,EAAI;CAAK;AAC7C;AAOA,SAAgBK,EACd,GACA,GACA,GACA,GACmD;CACnD,IAAIC,IAAS,UACTC,IAA0D;CAC9D,KAAK,IAAIhC,IAAI,GAAGA,IAAItB,EAAQ,QAAQ,KAAK;EACvC,IAAMuD,IAAWvD,EAAQsB,EAAE,CAAC,SAAU,MAASA;EAC/C,KAAK,IAAIC,IAAI,GAAGA,IAAIgC,GAAU,KAAK;GACjC,IAAMC,IAAOf,EAAYzC,GAASC,GAAQqB,GAAGC,CAAC;GAC9C,IAAI,CAACiC,GAAM;GACX,IAAMT,IAAMH,EAAiBY,EAAK,GAAGA,EAAK,GAAGC,GAAIC,CAAE;GACnD,AAAIX,EAAI,KAAKM,MACX,IAASN,EAAI,IACb,IAAO;IAAE,KAAKzB;IAAG,QAAQC;IAAG,GAAGwB,EAAI;GAAE;EAEzC;CACF;CACA,OAAOO;AACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { pathCrop as e, pathDivide as t, pathExclude as n, pathIntersect as r, pathSubtract as i, pathUnion as a } from "@weasel-js/geom/booleans";
|
|
2
|
-
export { e as pathCrop, t as pathDivide, n as pathExclude, r as pathIntersect, i as pathSubtract, a as pathUnion };
|
|
1
|
+
import { pathCrop as e, pathDivide as t, pathExclude as n, pathIntersect as r, pathSubtract as i, pathUnion as a, trianglesToPath as o } from "@weasel-js/geom/booleans";
|
|
2
|
+
export { e as pathCrop, t as pathDivide, n as pathExclude, r as pathIntersect, i as pathSubtract, a as pathUnion, o as trianglesToPath };
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { poseRotationOf as e, rotatePathAround as t } from "../../core/geometry/poseRotation.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { pathThroughPoseFrame as n, poseFrameInverseOf as r, poseFrameOf as i, poseLinearOf as a } from "../../core/geometry/poseFrame.js";
|
|
3
|
+
import { pathPoseDescriptor as o } from "./poseDescriptor.js";
|
|
4
|
+
import { countPathAnchors as s, pathToAnchors as c } from "./anchors.js";
|
|
5
|
+
import { pathInPoseFrame as l, pathInWorld as u, worldEditToStorage as d } from "./pathInWorld.js";
|
|
4
6
|
import "./anchorEdits.js";
|
|
5
|
-
import { pathInPoseFrame as a, pathInWorld as o, worldEditToStorage as s } from "./pathInWorld.js";
|
|
6
7
|
import "./booleans.js";
|
|
7
|
-
import { circlePath as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { createPathLayer as
|
|
11
|
-
import { pathOriginProjection as
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
8
|
+
import { circlePath as f, rectMarkerPath as p, roundRectPath as m, squarePath as h } from "./markers.js";
|
|
9
|
+
import { createPathEditingOverlayLayer as g } from "./pathEditingOverlayLayer.js";
|
|
10
|
+
import { createPenPreviewLayer as _ } from "./penPreviewLayer.js";
|
|
11
|
+
import { createPathLayer as v } from "./pathLayer.js";
|
|
12
|
+
import { pathOriginProjection as y } from "./originProjection.js";
|
|
13
|
+
import { strokeToPath as b } from "./strokeToPath.js";
|
|
14
|
+
import { DEFAULT_FLATTEN_TOLERANCE as x, PATH_C as S, PATH_CMD_LENGTHS as C, PATH_L as w, PATH_M as T, PATH_Q as E, PATH_Z as D, PathBuilder as O, boundsOfPath as k, composePath as A, decomposePath as j, ellipsePath as M, flattenCubic as N, flattenCubicWithArcLen as P, flattenQuadratic as F, flattenQuadraticWithArcLen as I, linePath as L, pathCommandCoordCount as R, pathContainsPoint as z, pathContainsPolygon as B, pathContainsRect as V, pathDistanceToPoint as H, pathFromD as U, pathIntersectsPolygon as W, pathIntersectsRect as G, pathSignedArea as K, pointAlongPath as q, pointInPath as J, polygonFromPoints as Y, polylineFromPoints as X, rectPath as Z, regularPolygonPath as Q, reversePath as $, scalePathToBounds as ee, splitSubpaths as te, starPath as ne, transformPath as re, translatePath as ie, unionBoundsPath as ae } from "@weasel-js/geom";
|
|
15
|
+
import { snipPathByPolyline as oe, splitPathByPolyline as se, splitPathBySegment as ce } from "@weasel-js/geom/booleans";
|
|
14
16
|
import "@weasel-js/geom/curves";
|
|
15
|
-
export {
|
|
17
|
+
export { x as DEFAULT_FLATTEN_TOLERANCE, S as PATH_C, C as PATH_CMD_LENGTHS, w as PATH_L, T as PATH_M, E as PATH_Q, D as PATH_Z, O as PathBuilder, k as boundsOfPath, f as circlePath, A as composePath, s as countPathAnchors, g as createPathEditingOverlayLayer, v as createPathLayer, _ as createPenPreviewLayer, j as decomposePath, M as ellipsePath, N as flattenCubic, P as flattenCubicWithArcLen, F as flattenQuadratic, I as flattenQuadraticWithArcLen, L as linePath, R as pathCommandCoordCount, z as pathContainsPoint, B as pathContainsPolygon, V as pathContainsRect, H as pathDistanceToPoint, U as pathFromD, l as pathInPoseFrame, u as pathInWorld, W as pathIntersectsPolygon, G as pathIntersectsRect, y as pathOriginProjection, o as pathPoseDescriptor, K as pathSignedArea, n as pathThroughPoseFrame, c as pathToAnchors, q as pointAlongPath, J as pointInPath, Y as polygonFromPoints, X as polylineFromPoints, r as poseFrameInverseOf, i as poseFrameOf, a as poseLinearOf, e as poseRotationOf, p as rectMarkerPath, Z as rectPath, Q as regularPolygonPath, $ as reversePath, t as rotatePathAround, m as roundRectPath, ee as scalePathToBounds, oe as snipPathByPolyline, se as splitPathByPolyline, ce as splitPathBySegment, te as splitSubpaths, h as squarePath, ne as starPath, b as strokeToPath, re as transformPath, ie as translatePath, ae as unionBoundsPath, d as worldEditToStorage };
|