@weasel-js/core 1.11.0 → 1.12.0
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 +611 -0
- package/README.md +1 -1
- package/dist/WeaselProvider.js +1 -1
- package/dist/affordances/pathAnchors.js +10 -6
- package/dist/affordances/pathAnchors.js.map +1 -1
- package/dist/animation/index.js +3 -3
- package/dist/animation/rig/bindRig.js +1 -1
- package/dist/animation/rig/rigNode.js.map +1 -1
- package/dist/barrel/canvas.js +9 -3
- package/dist/barrel/interactions.js +5 -4
- package/dist/barrel/paint.js +4 -4
- package/dist/barrel/paths.js +10 -6
- package/dist/barrel/renderer.js +5 -3
- package/dist/barrel/routing.js +7 -12
- package/dist/barrel/scene.js +9 -5
- package/dist/barrel/text.js +2 -2
- package/dist/barrel/viewport.js +9 -4
- package/dist/canvas/Canvas.js +5 -2
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +23 -34
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/ModalityHud.js +1 -1
- package/dist/canvas/NodeShape.js +4 -1
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
- package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js +17 -3
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
- package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
- package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
- package/dist/canvas/SceneCanvas/builtinToolSets.js +30 -0
- package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -0
- package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -0
- package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
- package/dist/canvas/SceneCanvas/coordTrace.js +5 -16
- package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +5 -0
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/features.js +45 -5
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/props.js +1 -11
- package/dist/canvas/SceneCanvas/props.js.map +1 -1
- package/dist/canvas/SceneCanvas/scenePointQuery.js +7 -3
- package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
- package/dist/canvas/SceneCanvas/toolRegistry.js +8 -4
- package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
- package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
- package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
- package/dist/canvas/SceneCanvas/useActionsPropResolver.js +1 -11
- package/dist/canvas/SceneCanvas/useActionsPropResolver.js.map +1 -1
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useBuiltinTools.js +2 -9
- package/dist/canvas/SceneCanvas/useBuiltinTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +94 -0
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -0
- package/dist/canvas/SceneCanvas/useCanvasSelection.js +7 -2
- package/dist/canvas/SceneCanvas/useCanvasSelection.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +15 -10
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useNodeKinds.js +1 -1
- package/dist/canvas/SceneCanvas/useNodeKinds.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
- package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +23 -4
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -3
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneChrome.js +6 -7
- package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +24 -7
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
- package/dist/canvas/SceneCanvas/useScenePicking.js +2 -3
- package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +2 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +5 -5
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +27 -24
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SurfaceCanvas.js +5 -22
- package/dist/canvas/SurfaceCanvas.js.map +1 -1
- package/dist/canvas/ViewMarker.js +35 -0
- package/dist/canvas/ViewMarker.js.map +1 -0
- package/dist/canvas/canvasRenderer.js +9 -5
- package/dist/canvas/canvasRenderer.js.map +1 -1
- package/dist/canvas/contextAttributes.js +14 -0
- package/dist/canvas/contextAttributes.js.map +1 -0
- package/dist/canvas/defaultDrawOne.js +2 -0
- package/dist/canvas/defaultDrawOne.js.map +1 -1
- package/dist/canvas/deps/booleans.js +2 -1
- package/dist/canvas/deps/booleans.js.map +1 -1
- package/dist/canvas/deps/editAnchors.js +9 -113
- package/dist/canvas/deps/editAnchors.js.map +1 -1
- package/dist/canvas/deps/hitTestArea.js +2 -2
- package/dist/canvas/deps/hitTestArea.js.map +1 -1
- package/dist/canvas/deps/insert.js +10 -4
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/pathEditCommits.js +189 -0
- package/dist/canvas/deps/pathEditCommits.js.map +1 -0
- package/dist/canvas/deps/snap.js +12 -9
- package/dist/canvas/deps/snap.js.map +1 -1
- package/dist/canvas/deps/transformFollowers.js +20 -0
- package/dist/canvas/deps/transformFollowers.js.map +1 -0
- package/dist/canvas/derivedPath.js +20 -1
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/drawnBounds.js +38 -4
- package/dist/canvas/drawnBounds.js.map +1 -1
- package/dist/canvas/inheritedPaints.js +44 -0
- package/dist/canvas/inheritedPaints.js.map +1 -0
- package/dist/canvas/nodeShapes/derivedPainter.js +23 -16
- package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/nodePaint.js +13 -34
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
- package/dist/canvas/nodeShapes/pathPainter.js +12 -10
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
- package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/shapePainter.js +12 -11
- package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
- package/dist/canvas/nodeShapes/textPainter.js +13 -12
- package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
- package/dist/canvas/overlayRoleStrokes.js +12 -0
- package/dist/canvas/overlayRoleStrokes.js.map +1 -1
- package/dist/canvas/pathOverlay.js +27 -7
- package/dist/canvas/pathOverlay.js.map +1 -1
- package/dist/canvas/pickReach.js +3 -3
- package/dist/canvas/pickReach.js.map +1 -1
- package/dist/canvas/pickWalk.js +3 -1
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/renderDebugSnapshot.js +1 -0
- package/dist/canvas/renderDebugSnapshot.js.map +1 -1
- package/dist/canvas/renderSceneToPixels.js +7 -6
- package/dist/canvas/renderSceneToPixels.js.map +1 -1
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneSlotWiring.js +2 -1
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +2 -1
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/surfaceContribution.js +12 -8
- package/dist/canvas/surfaceContribution.js.map +1 -1
- package/dist/canvas/useCanvasPaint.js +2 -1
- package/dist/canvas/useCanvasPaint.js.map +1 -1
- package/dist/canvas/useCanvasView.js +4 -2
- package/dist/canvas/useCanvasView.js.map +1 -1
- package/dist/canvas/useInFlightBounds.js +52 -0
- package/dist/canvas/useInFlightBounds.js.map +1 -0
- package/dist/canvas/useLayerStack.js +6 -2
- package/dist/canvas/useLayerStack.js.map +1 -1
- package/dist/canvas/useViewAim.js +121 -0
- package/dist/canvas/useViewAim.js.map +1 -0
- package/dist/canvas/useViewHelpers.js +7 -9
- package/dist/canvas/useViewHelpers.js.map +1 -1
- package/dist/canvas/useViewNesting.js +3 -2
- package/dist/canvas/useViewNesting.js.map +1 -1
- package/dist/canvas/useViewRegistrySurface.js +4 -2
- package/dist/canvas/useViewRegistrySurface.js.map +1 -1
- package/dist/canvas/viewMarkers.js +63 -0
- package/dist/canvas/viewMarkers.js.map +1 -0
- package/dist/canvas/viewRegistry.js +27 -1
- package/dist/canvas/viewRegistry.js.map +1 -1
- package/dist/clipboard.d.ts +3 -2
- package/dist/{clone-DZDrfIlU.d.ts → clone-C3e0OTuF.d.ts} +8 -21
- package/dist/clone.d.ts +4 -3
- package/dist/core/geometry/autoPoseDescriptor.js +1 -1
- package/dist/core/ops/setDependsOn.js +37 -0
- package/dist/core/ops/setDependsOn.js.map +1 -0
- package/dist/core/ops/writtenBy.js +53 -0
- package/dist/core/ops/writtenBy.js.map +1 -0
- package/dist/core/pages/fitViewToPage.js +12 -1
- package/dist/core/pages/fitViewToPage.js.map +1 -1
- package/dist/core/pages/pageEdits.js +163 -0
- package/dist/core/pages/pageEdits.js.map +1 -0
- package/dist/core/pages/pages.js +30 -1
- package/dist/core/pages/pages.js.map +1 -1
- package/dist/core/paint/meshKindLazy.js +59 -2
- package/dist/core/paint/meshKindLazy.js.map +1 -1
- package/dist/core/scene/NodeRouting.js +8 -3
- package/dist/core/scene/NodeRouting.js.map +1 -1
- package/dist/core/scene/arrivalWindow.js.map +1 -1
- package/dist/core/scene/carryDescendants.js +51 -0
- package/dist/core/scene/carryDescendants.js.map +1 -0
- package/dist/core/scene/clipNothing.js +13 -0
- package/dist/core/scene/clipNothing.js.map +1 -0
- package/dist/core/scene/depMemo.js +11 -3
- package/dist/core/scene/depMemo.js.map +1 -1
- package/dist/core/scene/dependents.js +7 -1
- package/dist/core/scene/dependents.js.map +1 -1
- package/dist/core/scene/derivedParts.js +10 -11
- package/dist/core/scene/derivedParts.js.map +1 -1
- package/dist/core/scene/docValues.js +57 -0
- package/dist/core/scene/docValues.js.map +1 -0
- package/dist/core/scene/editPass.js +111 -0
- package/dist/core/scene/editPass.js.map +1 -0
- package/dist/core/scene/effectivePose.js +13 -2
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/fieldEdits.js +4 -1
- package/dist/core/scene/fieldEdits.js.map +1 -1
- package/dist/core/scene/fontReads.js +37 -0
- package/dist/core/scene/fontReads.js.map +1 -0
- package/dist/core/scene/historyFacade.js +1 -0
- package/dist/core/scene/historyFacade.js.map +1 -1
- package/dist/core/scene/inheritedPaint.js +75 -0
- package/dist/core/scene/inheritedPaint.js.map +1 -0
- package/dist/core/scene/kitRegistry.js +5 -0
- package/dist/core/scene/kitRegistry.js.map +1 -1
- package/dist/core/scene/layoutPass.js +28 -3
- package/dist/core/scene/layoutPass.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +15 -7
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/pathEffects/blend.js.map +1 -1
- package/dist/core/scene/pathEffects/derive.js +7 -2
- package/dist/core/scene/pathEffects/derive.js.map +1 -1
- package/dist/core/scene/pathEffects/effects.js +3 -1
- package/dist/core/scene/pathEffects/effects.js.map +1 -1
- package/dist/core/scene/pathEffects/textAlong.js +95 -0
- package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
- package/dist/core/scene/recorder.js +28 -19
- package/dist/core/scene/recorder.js.map +1 -1
- package/dist/core/scene/scene.js +15 -9
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneChanges.js +13 -0
- package/dist/core/scene/sceneChanges.js.map +1 -0
- package/dist/core/scene/sceneEdits.js +8 -1
- package/dist/core/scene/sceneEdits.js.map +1 -1
- package/dist/core/scene/sceneMutators.js +1 -0
- package/dist/core/scene/sceneMutators.js.map +1 -1
- package/dist/core/scene/sceneRecording.js +20 -3
- package/dist/core/scene/sceneRecording.js.map +1 -1
- package/dist/core/scene/sceneSelection.js +4 -1
- package/dist/core/scene/sceneSelection.js.map +1 -1
- package/dist/core/scene/sceneState.js +11 -5
- package/dist/core/scene/sceneState.js.map +1 -1
- package/dist/core/scene/serialization.js +5 -3
- package/dist/core/scene/serialization.js.map +1 -1
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/unitScopes.js +19 -1
- package/dist/core/scene/unitScopes.js.map +1 -1
- package/dist/core/selection/usePublishSceneSelection.js +6 -12
- package/dist/core/selection/usePublishSceneSelection.js.map +1 -1
- package/dist/core/shapeKinds.js +25 -11
- package/dist/core/shapeKinds.js.map +1 -1
- package/dist/{cornerHandles-Camw_yeu.d.ts → cornerHandles-p8fDIAKt.d.ts} +3 -2
- package/dist/debug/createDebugOverlayLayer.js +16 -111
- package/dist/debug/createDebugOverlayLayer.js.map +1 -1
- package/dist/debug/createDebugSink.js +2 -1
- package/dist/debug/createDebugSink.js.map +1 -1
- package/dist/debug/defaultTheme.js +4 -2
- package/dist/debug/defaultTheme.js.map +1 -1
- package/dist/debug/features.js +17 -0
- package/dist/debug/features.js.map +1 -0
- package/dist/debug/overlayPanels.js +117 -0
- package/dist/debug/overlayPanels.js.map +1 -0
- package/dist/debug/parseDebugFlags.js +4 -13
- package/dist/debug/parseDebugFlags.js.map +1 -1
- package/dist/{depSchema-Cgfu_G9s.d.ts → depSchema-mG19aEdD.d.ts} +174 -904
- package/dist/devtools/handlers.js +112 -0
- package/dist/devtools/handlers.js.map +1 -0
- package/dist/devtools/history.js +71 -0
- package/dist/devtools/history.js.map +1 -0
- package/dist/devtools/hook.js +74 -0
- package/dist/devtools/hook.js.map +1 -0
- package/dist/devtools/label.js +25 -0
- package/dist/devtools/label.js.map +1 -0
- package/dist/devtools/notices.js +58 -0
- package/dist/devtools/notices.js.map +1 -0
- package/dist/devtools/protocol.js +7 -0
- package/dist/devtools/protocol.js.map +1 -0
- package/dist/devtools/registry.js +90 -0
- package/dist/devtools/registry.js.map +1 -0
- package/dist/devtools/root.js +80 -0
- package/dist/devtools/root.js.map +1 -0
- package/dist/devtools/serialize.js +86 -0
- package/dist/devtools/serialize.js.map +1 -0
- package/dist/devtools/session.js +111 -0
- package/dist/devtools/session.js.map +1 -0
- package/dist/devtools/transport.js +67 -0
- package/dist/devtools/transport.js.map +1 -0
- package/dist/devtools/useDevtoolsRoot.js +81 -0
- package/dist/devtools/useDevtoolsRoot.js.map +1 -0
- package/dist/devtools-protocol.d.ts +435 -0
- package/dist/devtools-protocol.js +7 -0
- package/dist/devtools.d.ts +8 -0
- package/dist/devtools.js +12 -0
- package/dist/devtools.js.map +1 -0
- package/dist/features/chrome-caps/useHoverTracking.js +16 -10
- package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
- package/dist/features/ingestion/contentHandlers.js +25 -2
- package/dist/features/ingestion/contentHandlers.js.map +1 -1
- package/dist/features/measure/measureCommands.js +7 -5
- package/dist/features/measure/measureCommands.js.map +1 -1
- package/dist/features/measure/measureContribution.js +4 -2
- package/dist/features/measure/measureContribution.js.map +1 -1
- package/dist/features/modes/modeTriggers.js +7 -3
- package/dist/features/modes/modeTriggers.js.map +1 -1
- package/dist/features/pathEffects/effectSources.js +60 -0
- package/dist/features/pathEffects/effectSources.js.map +1 -0
- package/dist/features/pathEffects/readSource.js +17 -1
- package/dist/features/pathEffects/readSource.js.map +1 -1
- package/dist/features/pathEffects/textItems.js +53 -0
- package/dist/features/pathEffects/textItems.js.map +1 -0
- package/dist/features/pathEffects/useEffectSourcesLayer.js +57 -0
- package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
- package/dist/features/paths/index.js +2 -2
- package/dist/features/paths/pathEditExtension.js.map +1 -1
- package/dist/features/paths/pathEditState.js +5 -2
- package/dist/features/paths/pathEditState.js.map +1 -1
- package/dist/features/paths/pathSnap/pathSnapSource.js +5 -5
- package/dist/features/paths/pathSnap/pathSnapSource.js.map +1 -1
- package/dist/features/paths/pathSnap/sceneIndex.js +10 -3
- package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
- package/dist/features/selection/selectionPose.js +4 -1
- package/dist/features/selection/selectionPose.js.map +1 -1
- package/dist/features/snap/gather.js +9 -1
- package/dist/features/snap/gather.js.map +1 -1
- package/dist/features/snap/lazySession.js +17 -0
- package/dist/features/snap/lazySession.js.map +1 -0
- package/dist/features/snap/marks.js.map +1 -1
- package/dist/features/snap/probes2d.js +27 -19
- package/dist/features/snap/probes2d.js.map +1 -1
- package/dist/features/snap/readoutLayer.js.map +1 -1
- package/dist/features/snap/roles.js +2 -1
- package/dist/features/snap/roles.js.map +1 -1
- package/dist/features/snap/rotationSource.js +25 -8
- package/dist/features/snap/rotationSource.js.map +1 -1
- package/dist/features/snap/sceneSession2d.js +6 -4
- package/dist/features/snap/sceneSession2d.js.map +1 -1
- package/dist/features/snap/session2d.js +37 -23
- package/dist/features/snap/session2d.js.map +1 -1
- package/dist/features/snap/spanSnap.js +5 -2
- package/dist/features/snap/spanSnap.js.map +1 -1
- package/dist/features/text/sceneTextDrafts.js +65 -0
- package/dist/features/text/sceneTextDrafts.js.map +1 -0
- package/dist/features/text/textHitAt.js +31 -2
- package/dist/features/text/textHitAt.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +22 -18
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/features/viewports/aim.js +105 -0
- package/dist/features/viewports/aim.js.map +1 -0
- package/dist/features/viewports/viewMarker.js +82 -0
- package/dist/features/viewports/viewMarker.js.map +1 -0
- package/dist/features/viewports/viewMarkerDrag.js +55 -0
- package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
- package/dist/icons.table.js +1 -1
- package/dist/icons.table.js.map +1 -1
- package/dist/icons.table.json +0 -10
- package/dist/index.d.ts +1198 -894
- package/dist/index.js +55 -38
- package/dist/interactions/actions/align/action.js +6 -4
- package/dist/interactions/actions/align/action.js.map +1 -1
- package/dist/interactions/actions/align/align.js.map +1 -1
- package/dist/interactions/actions/booleans/action.js +7 -1
- package/dist/interactions/actions/booleans/action.js.map +1 -1
- package/dist/interactions/actions/booleans/booleans.js +2 -1
- package/dist/interactions/actions/booleans/booleans.js.map +1 -1
- package/dist/interactions/actions/booleans/operands.js +6 -5
- package/dist/interactions/actions/booleans/operands.js.map +1 -1
- package/dist/interactions/actions/clipboard/action.js +27 -17
- package/dist/interactions/actions/clipboard/action.js.map +1 -1
- package/dist/interactions/actions/clone/action.js +1 -1
- package/dist/interactions/actions/clone/behaviors.js +4 -4
- package/dist/interactions/actions/clone/behaviors.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
- package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js +8 -5
- package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
- package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
- package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
- package/dist/interactions/actions/defaults/contextMenu.js +17 -8
- package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
- package/dist/interactions/actions/defaults/createPaintAction.js +2 -2
- package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
- package/dist/interactions/actions/defaults/delete.js +64 -41
- package/dist/interactions/actions/defaults/delete.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +12 -9
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/enterPathEdit.js +19 -9
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/escape.js +11 -8
- package/dist/interactions/actions/defaults/escape.js.map +1 -1
- package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
- package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +21 -15
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +17 -9
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
- package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
- package/dist/interactions/actions/defaults/nudge.js +21 -40
- package/dist/interactions/actions/defaults/nudge.js.map +1 -1
- package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
- package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
- package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
- package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
- package/dist/interactions/actions/defaults/reorder.js +24 -6
- package/dist/interactions/actions/defaults/reorder.js.map +1 -1
- package/dist/interactions/actions/defaults/selectAll.js +8 -5
- package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
- package/dist/interactions/actions/defaults/setFill.js +7 -3
- package/dist/interactions/actions/defaults/setFill.js.map +1 -1
- package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
- package/dist/interactions/actions/defaults/setStroke.js +12 -11
- package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
- package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
- package/dist/interactions/actions/defaults/toolActivate.js +7 -1
- package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
- package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
- package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
- package/dist/interactions/actions/defaults/undoRedo.js +19 -13
- package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
- package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
- package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
- package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
- package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
- package/dist/interactions/actions/distribute/action.js +6 -4
- package/dist/interactions/actions/distribute/action.js.map +1 -1
- package/dist/interactions/actions/drawnBoxes.js +2 -1
- package/dist/interactions/actions/drawnBoxes.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/action.js +10 -10
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/preview.js +41 -8
- package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
- package/dist/interactions/actions/flip/action.js +14 -6
- package/dist/interactions/actions/flip/action.js.map +1 -1
- package/dist/interactions/actions/gestureLifecycle.js +5 -2
- package/dist/interactions/actions/gestureLifecycle.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +1 -2
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/commit.js +1 -1
- package/dist/interactions/actions/insert/parent.js +11 -0
- package/dist/interactions/actions/insert/parent.js.map +1 -0
- package/dist/interactions/actions/insert/placeAction.js +7 -2
- package/dist/interactions/actions/insert/placeAction.js.map +1 -1
- package/dist/interactions/actions/insert/repositionAction.js +2 -1
- package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
- package/dist/interactions/actions/insert/session.js +5 -1
- package/dist/interactions/actions/insert/session.js.map +1 -1
- package/dist/interactions/actions/move/action.js +15 -8
- package/dist/interactions/actions/move/action.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/layoutDrop.js +0 -1
- package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +1 -1
- package/dist/interactions/actions/move/capture.js +1 -1
- package/dist/interactions/actions/move/commit/index.js +0 -2
- package/dist/interactions/actions/move/commit/index.js.map +1 -1
- package/dist/interactions/actions/move/commit/layoutDrop.js +2 -2
- package/dist/interactions/actions/move/commit/layoutRelease.js +2 -2
- package/dist/interactions/actions/move/commit/reparent.js +11 -5
- package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
- package/dist/interactions/actions/move/frame.js +4 -10
- package/dist/interactions/actions/move/frame.js.map +1 -1
- package/dist/interactions/actions/move/landedDescendants.js +20 -17
- package/dist/interactions/actions/move/landedDescendants.js.map +1 -1
- package/dist/interactions/actions/move/layout/container.js +2 -1
- package/dist/interactions/actions/move/layout/container.js.map +1 -1
- package/dist/interactions/actions/move/layout/dragged.js +1 -1
- package/dist/interactions/actions/move/layout/dropPass.js +18 -1
- package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
- package/dist/interactions/actions/move/layout/exits.js +5 -5
- package/dist/interactions/actions/move/layout/exits.js.map +1 -1
- package/dist/interactions/actions/move/session.js +14 -3
- package/dist/interactions/actions/move/session.js.map +1 -1
- package/dist/interactions/actions/move/translate.js +8 -37
- package/dist/interactions/actions/move/translate.js.map +1 -1
- package/dist/interactions/actions/moveNodes.js +87 -0
- package/dist/interactions/actions/moveNodes.js.map +1 -0
- package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
- package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
- package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
- package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
- package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
- package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
- package/dist/interactions/actions/planeInput.js.map +1 -1
- package/dist/interactions/actions/resize/action.js +83 -72
- package/dist/interactions/actions/resize/action.js.map +1 -1
- package/dist/interactions/actions/resize/commit.js +2 -1
- package/dist/interactions/actions/resize/commit.js.map +1 -1
- package/dist/interactions/actions/resize/cornerHandles.js +2 -1
- package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
- package/dist/interactions/actions/resize/session.js +2 -0
- package/dist/interactions/actions/resize/session.js.map +1 -1
- package/dist/interactions/actions/resolvePreviews.js +3 -3
- package/dist/interactions/actions/resolvePreviews.js.map +1 -1
- package/dist/interactions/actions/rotate/action.js +7 -4
- package/dist/interactions/actions/rotate/action.js.map +1 -1
- package/dist/interactions/actions/rotate/commit.js +2 -1
- package/dist/interactions/actions/rotate/commit.js.map +1 -1
- package/dist/interactions/actions/rotate/session.js +2 -0
- package/dist/interactions/actions/rotate/session.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +25 -9
- package/dist/interactions/actions/selectionSubtree.js.map +1 -1
- package/dist/interactions/actions/testUtils.js.map +1 -1
- package/dist/interactions/actions/transform-again/action.js +13 -11
- package/dist/interactions/actions/transform-again/action.js.map +1 -1
- package/dist/interactions/actions/transformFinish.js +106 -0
- package/dist/interactions/actions/transformFinish.js.map +1 -0
- package/dist/json.d.ts +4 -0
- package/dist/json.js +3 -0
- package/dist/kitSettingsSchema.js +8 -5
- package/dist/kitSettingsSchema.js.map +1 -1
- package/dist/{math-CrJULaZI.d.ts → math-B56zxVs5.d.ts} +10 -1
- package/dist/math.d.ts +1 -1
- package/dist/math.js +2 -1
- package/dist/move.d.ts +5 -4
- package/dist/{options-p9GZMXbY.d.ts → options-DBxEryYq.d.ts} +1 -1
- package/dist/resize.d.ts +4 -3
- package/dist/routing.d.ts +1 -1
- package/dist/routing.js +2 -2
- package/dist/test-hook/createTestHook.js +1 -1
- package/dist/test-hook/createTestHook.js.map +1 -1
- package/dist/test-hook/useTestHook.js +2 -1
- package/dist/test-hook/useTestHook.js.map +1 -1
- package/dist/test-seams.d.ts +9 -5
- package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
- package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
- package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
- package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
- package/dist/tools/builtin/hand/useHandTool.js +7 -4
- package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
- package/dist/tools/builtin/image/useImageTool.js +1 -0
- package/dist/tools/builtin/image/useImageTool.js.map +1 -1
- package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
- 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/measure/useMeasureTool.js +12 -5
- package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
- package/dist/tools/builtin/pen/penHandle.js +3 -5
- package/dist/tools/builtin/pen/penHandle.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +14 -6
- 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 -4
- 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/adapterPointQuery.js +3 -2
- package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
- package/dist/tools/builtin/select/pressDecision.js +32 -0
- package/dist/tools/builtin/select/pressDecision.js.map +1 -0
- package/dist/tools/builtin/select/selectionContributions.js +22 -13
- package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +13 -10
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/slice/useSliceTool.js +22 -8
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +13 -4
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/builtin/sticky/stickyNode.js +4 -1
- package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
- package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
- package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
- package/dist/tools/builtin/text/useTextTool.js +3 -1
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/tools/index.js +26 -0
- package/dist/{types-CHVXGfYm.d.ts → types-7puAuVD2.d.ts} +3 -262
- package/dist/types-BN9hbdMI.d.ts +184 -0
- package/dist/types-CniVctmk.d.ts +949 -0
- package/dist/types-oFeQ5VjV.d.ts +264 -0
- package/dist/util/paint.js +1 -3
- package/dist/util/paint.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +39 -25
- package/dist/canvas/SceneCanvas/useBuiltinRigTools.js +0 -65
- package/dist/canvas/SceneCanvas/useBuiltinRigTools.js.map +0 -1
- package/dist/tools/builtin/bone/boneActions.js +0 -156
- package/dist/tools/builtin/bone/boneActions.js.map +0 -1
- package/dist/tools/builtin/bone/boneOverlay.js +0 -71
- package/dist/tools/builtin/bone/boneOverlay.js.map +0 -1
- package/dist/tools/builtin/bone/placedRig.js +0 -28
- package/dist/tools/builtin/bone/placedRig.js.map +0 -1
- package/dist/tools/builtin/bone/rigEditState.js +0 -30
- package/dist/tools/builtin/bone/rigEditState.js.map +0 -1
- package/dist/tools/builtin/bone/useBoneTool.js +0 -125
- package/dist/tools/builtin/bone/useBoneTool.js.map +0 -1
- package/dist/tools/builtin/weight/useWeightTool.js +0 -101
- package/dist/tools/builtin/weight/useWeightTool.js.map +0 -1
- package/dist/tools/builtin/weight/weightActions.js +0 -140
- package/dist/tools/builtin/weight/weightActions.js.map +0 -1
- package/dist/tools/builtin/weight/weightOverlay.js +0 -78
- package/dist/tools/builtin/weight/weightOverlay.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hitTestArea.js","names":[],"sources":["../../../src/canvas/deps/hitTestArea.ts"],"sourcesContent":["/**\n * Silhouette-aware area hit-test for marquee and lasso selection.\n *\n * The per-node decision is `regionTakes` (`core/geometry/regionHit`). This\n * module supplies the scene walk and the node's drawn outline: the painter's\n * world-frame silhouette (`findShapeSilhouette`), or with no painter the pose\n * rect, rotated if the pose is.\n */\nimport type { Scene, NodeId } from 'core/scene/types';\nimport { applyToPoint, pathIntersectsRect, type Affine } from '@weasel-js/geom';\nimport type { LassoHitMode } from 'core/adapters/types';\nimport {\n poseDescriptorForNode,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport {\n pickWalk,\n scenePickSource,\n type PickQuery,\n type ScenePickSourceOptions,\n} from 'canvas/pickWalk';\nimport { rectToPlane, toPlane, type PlaneMap } from 'core/viewport/parallax';\n\nimport type { HitTestView } from 'interactions/actions/depSchema';\nimport { findShapeInk, findShapeSilhouette } from 'canvas/NodeShape';\nimport {\n polygonRegion,\n poseOutline,\n rectRegion,\n regionBoundsOf,\n regionOf,\n regionTakes,\n type SelectRegion,\n} from 'core/geometry/regionHit';\n\nexport interface AABBBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** What a region query takes back beyond leaves. */\nexport interface RegionQueryOptions {\n /** Default `false`: a region that takes a container and its children\n * selects the same ink twice. A bare-adapter consumer, which has nothing\n * to fold children back into their container, asks for them. */\n includeContainers?: boolean;\n}\n\n\n/** The pick options a region dep hands the shared walk: the asking view's\n * layer gate and camera, and the surface's alpha. */\nexport function regionPickOptions(\n view: HitTestView | undefined,\n alphaOf: ((id: string) => number) | undefined,\n): ScenePickSourceOptions<unknown> {\n const camera = view?.get?.();\n return {\n ...(alphaOf ? { alphaOf } : {}),\n ...(view?.layerIsPainted ? { layerIsPainted: (layer: string) => view.layerIsPainted!(layer) } : {}),\n ...(camera ? { camera } : {}),\n };\n}\n\n/** Marquee entry point: every node the rect `bounds` meets. */\nexport function hitTestArea(\n scene: Scene<unknown, string, unknown>,\n bounds: AABBBounds,\n opts?: ScenePickSourceOptions<unknown>,\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n query: RegionQueryOptions = {},\n): NodeId[] {\n return walkArea(scene, rectRegion(bounds), 'intersect', opts, descriptor, query);\n}\n\n/**\n * Every node an arbitrary closed area polygon meets. `area` is interleaved\n * `[x0,y0,x1,y1,…]`; `areaBounds` is its AABB, derived when omitted.\n */\nexport function hitTestAreaPolygon(\n scene: Scene<unknown, string, unknown>,\n area: ArrayLike<number>,\n areaBounds?: AABBBounds,\n /** True when `area` IS its own bounding rect, so `areaBounds` containment\n * implies containment in the area proper. Never pass this for a lasso\n * polygon — a node inside the hull's box can miss the hull. */\n areaIsRect = false,\n /** The asking view's alpha and layer accessors. A bare scene answers for\n * itself; a view that dims or reorders layers has to supply its own. */\n opts?: ScenePickSourceOptions<unknown>,\n /** How to read this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n query: RegionQueryOptions = {},\n): NodeId[] {\n const region = regionOf(area, areaBounds, areaIsRect);\n return region ? walkArea(scene, region, 'intersect', opts, descriptor, query) : [];\n}\n\n/**\n * Lasso entry point: which nodes a closed lasso polygon picks under `mode`\n * (see `regionTakes`).\n */\nexport function hitTestLassoPolygon(\n scene: Scene<unknown, string, unknown>,\n polygon: ReadonlyArray<{ x: number; y: number }>,\n mode: LassoHitMode,\n opts?: ScenePickSourceOptions<unknown>,\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n query: RegionQueryOptions = {},\n): NodeId[] {\n const region = polygonRegion(polygon);\n return region ? walkArea(scene, region, mode, opts, descriptor, query) : [];\n}\n\n/** `shape` as a region reads it: a polygon path's open subpaths enclose\n * nothing when the node's painter lays down no fill. */\nfunction asInked<T>(filled: () => boolean, shape: T): T {\n if ((shape as { kind?: unknown } | null)?.kind !== 'polygon') return shape;\n return filled() ? shape : { ...shape, openFill: 'none' };\n}\n\n/** The walk every region query shares: skip containers unless asked, gate on\n * the clip, and let `regionTakes` decide each node. */\nfunction walkArea(\n scene: Scene<unknown, string, unknown>,\n region: SelectRegion,\n mode: LassoHitMode,\n opts: ScenePickSourceOptions<unknown> | undefined,\n descriptor: PoseDescriptor<unknown>,\n query: RegionQueryOptions,\n): NodeId[] {\n const regionQuery = (r: SelectRegion): PickQuery<unknown> => ({\n includeContainers: query.includeContainers === true,\n clipAdmits: (clip, node, pose) => {\n const g = poseDescriptorForNode(descriptor, node);\n return pathIntersectsRect(clip, visualBoundsViaDescriptor(pose, g))\n && pathIntersectsRect(clip, r.bounds);\n },\n hits: (node, pose) => {\n // The descriptor sees the node here, so a pose shape whose extent\n // depends on `node.data` bounds itself rather than its pose's default.\n const g = poseDescriptorForNode(descriptor, node);\n const b = regionBoundsOf(node as object, pose, g);\n let ink: boolean | undefined;\n const filled = (): boolean => ink ??= findShapeInk(node as never, pose)?.filled !== false;\n return regionTakes(r, mode, asInked(filled, pose), b, () =>\n asInked(filled, findShapeSilhouette(node as never, pose) ?? poseOutline(pose, b)), filled);\n },\n inPlane: (m) => regionQuery(regionInPlane(r, m)),\n inCopy: (m) => regionQuery(regionInCopy(r, m)),\n });\n return pickWalk<unknown>(scenePickSource(scene, opts), regionQuery(region)) as NodeId[];\n}\n\n/** `r` carried into a plane's world. The map is axis-aligned, so a rect stays\n * one; a flipped axis reverses the winding, which no region test reads. */\nfunction regionInPlane(r: SelectRegion, m: PlaneMap): SelectRegion {\n const coords: number[] = [];\n for (const v of r.verts) {\n const p = toPlane(m, v);\n coords.push(p.x, p.y);\n }\n return regionOf(coords, rectToPlane(m, r.bounds), r.isRect)!;\n}\n\n/** `r` carried by an affine into a copy's source frame. A rect stays one only\n * while the map keeps the axes; a turn makes it a polygon. */\nfunction regionInCopy(r: SelectRegion, m: Affine): SelectRegion {\n const coords: number[] = [];\n let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n for (const v of r.verts) {\n const [x, y] = applyToPoint(m, v.x, v.y);\n coords.push(x, y);\n x0 = Math.min(x0, x); y0 = Math.min(y0, y); x1 = Math.max(x1, x); y1 = Math.max(y1, y);\n }\n const keepsAxes = (m[1] === 0 && m[2] === 0) || (m[0] === 0 && m[3] === 0);\n return regionOf(coords, { x: x0, y: y0, width: x1 - x0, height: y1 - y0 }, r.isRect && keepsAxes)!;\n}\n"],"mappings":";;;;;;;;;;;AAuDA,SAAgB,kBACd,MACA,SACiC;CACjC,MAAM,SAAS,MAAM,MAAM;CAC3B,OAAO;EACL,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC7B,GAAI,MAAM,iBAAiB,EAAE,iBAAiB,UAAkB,KAAK,eAAgB,KAAK,EAAE,IAAI,CAAC;EACjG,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAC7B;AACF;;AAGA,SAAgB,YACd,OACA,QACA,MACA,aAAsC,sBACtC,QAA4B,CAAC,GACnB;CACV,OAAO,SAAS,OAAO,WAAW,MAAM,GAAG,aAAa,MAAM,YAAY,KAAK;AACjF;;;;;AA6BA,SAAgB,oBACd,OACA,SACA,MACA,MACA,aAAsC,sBACtC,QAA4B,CAAC,GACnB;CACV,MAAM,SAAS,cAAc,OAAO;CACpC,OAAO,SAAS,SAAS,OAAO,QAAQ,MAAM,MAAM,YAAY,KAAK,IAAI,CAAC;AAC5E;;;AAIA,SAAS,QAAW,QAAuB,OAAa;CACtD,IAAK,OAAqC,SAAS,WAAW,OAAO;CACrE,OAAO,OAAO,IAAI,QAAQ;EAAE,GAAG;EAAO,UAAU;CAAO;AACzD;;;AAIA,SAAS,SACP,OACA,QACA,MACA,MACA,YACA,OACU;CACV,MAAM,eAAe,OAAyC;EAC5D,mBAAmB,MAAM,sBAAsB;EAC/C,aAAa,MAAM,MAAM,SAAS;GAChC,MAAM,IAAI,sBAAsB,YAAY,IAAI;GAChD,OAAO,mBAAmB,MAAM,0BAA0B,MAAM,CAAC,CAAC,KAC7D,mBAAmB,MAAM,EAAE,MAAM;EACxC;EACA,OAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"hitTestArea.js","names":[],"sources":["../../../src/canvas/deps/hitTestArea.ts"],"sourcesContent":["/**\n * Silhouette-aware area hit-test for marquee and lasso selection.\n *\n * The per-node decision is `regionTakes` (`core/geometry/regionHit`). This\n * module supplies the scene walk and the node's drawn outline: the painter's\n * world-frame silhouette (`findShapeSilhouette`), or with no painter the pose\n * rect, rotated if the pose is.\n */\nimport type { Scene, NodeId } from 'core/scene/types';\nimport { applyToPoint, pathIntersectsRect, type Affine } from '@weasel-js/geom';\nimport type { LassoHitMode } from 'core/adapters/types';\nimport {\n poseDescriptorForNode,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport {\n pickWalk,\n scenePickSource,\n type PickQuery,\n type ScenePickSourceOptions,\n} from 'canvas/pickWalk';\nimport { rectToPlane, toPlane, type PlaneMap } from 'core/viewport/parallax';\n\nimport type { HitTestView } from 'interactions/actions/depSchema';\nimport { findShapeInk, findShapeSilhouette } from 'canvas/NodeShape';\nimport {\n polygonRegion,\n poseOutline,\n rectRegion,\n regionBoundsOf,\n regionOf,\n regionTakes,\n type SelectRegion,\n} from 'core/geometry/regionHit';\n\nexport interface AABBBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** What a region query takes back beyond leaves. */\nexport interface RegionQueryOptions {\n /** Default `false`: a region that takes a container and its children\n * selects the same ink twice. A bare-adapter consumer, which has nothing\n * to fold children back into their container, asks for them. */\n includeContainers?: boolean;\n}\n\n\n/** The pick options a region dep hands the shared walk: the asking view's\n * layer gate and camera, and the surface's alpha. */\nexport function regionPickOptions(\n view: HitTestView | undefined,\n alphaOf: ((id: string) => number) | undefined,\n): ScenePickSourceOptions<unknown> {\n const camera = view?.get?.();\n return {\n ...(alphaOf ? { alphaOf } : {}),\n ...(view?.layerIsPainted ? { layerIsPainted: (layer: string) => view.layerIsPainted!(layer) } : {}),\n ...(camera ? { camera } : {}),\n };\n}\n\n/** Marquee entry point: every node the rect `bounds` meets. */\nexport function hitTestArea(\n scene: Scene<unknown, string, unknown>,\n bounds: AABBBounds,\n opts?: ScenePickSourceOptions<unknown>,\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n query: RegionQueryOptions = {},\n): NodeId[] {\n return walkArea(scene, rectRegion(bounds), 'intersect', opts, descriptor, query);\n}\n\n/**\n * Every node an arbitrary closed area polygon meets. `area` is interleaved\n * `[x0,y0,x1,y1,…]`; `areaBounds` is its AABB, derived when omitted.\n */\nexport function hitTestAreaPolygon(\n scene: Scene<unknown, string, unknown>,\n area: ArrayLike<number>,\n areaBounds?: AABBBounds,\n /** True when `area` IS its own bounding rect, so `areaBounds` containment\n * implies containment in the area proper. Never pass this for a lasso\n * polygon — a node inside the hull's box can miss the hull. */\n areaIsRect = false,\n /** The asking view's alpha and layer accessors. A bare scene answers for\n * itself; a view that dims or reorders layers has to supply its own. */\n opts?: ScenePickSourceOptions<unknown>,\n /** How to read this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n query: RegionQueryOptions = {},\n): NodeId[] {\n const region = regionOf(area, areaBounds, areaIsRect);\n return region ? walkArea(scene, region, 'intersect', opts, descriptor, query) : [];\n}\n\n/**\n * Lasso entry point: which nodes a closed lasso polygon picks under `mode`\n * (see `regionTakes`).\n */\nexport function hitTestLassoPolygon(\n scene: Scene<unknown, string, unknown>,\n polygon: ReadonlyArray<{ x: number; y: number }>,\n mode: LassoHitMode,\n opts?: ScenePickSourceOptions<unknown>,\n descriptor: PoseDescriptor<unknown> = AUTO_POSE_DESCRIPTOR,\n query: RegionQueryOptions = {},\n): NodeId[] {\n const region = polygonRegion(polygon);\n return region ? walkArea(scene, region, mode, opts, descriptor, query) : [];\n}\n\n/** `shape` as a region reads it: a polygon path's open subpaths enclose\n * nothing when the node's painter lays down no fill. */\nfunction asInked<T>(filled: () => boolean, shape: T): T {\n if ((shape as { kind?: unknown } | null)?.kind !== 'polygon') return shape;\n return filled() ? shape : { ...shape, openFill: 'none' };\n}\n\n/** The walk every region query shares: skip containers unless asked, gate on\n * the clip, and let `regionTakes` decide each node. */\nfunction walkArea(\n scene: Scene<unknown, string, unknown>,\n region: SelectRegion,\n mode: LassoHitMode,\n opts: ScenePickSourceOptions<unknown> | undefined,\n descriptor: PoseDescriptor<unknown>,\n query: RegionQueryOptions,\n): NodeId[] {\n const regionQuery = (r: SelectRegion): PickQuery<unknown> => ({\n includeContainers: query.includeContainers === true,\n clipAdmits: (clip, node, pose) => {\n const g = poseDescriptorForNode(descriptor, node);\n return pathIntersectsRect(clip, visualBoundsViaDescriptor(pose, g))\n && pathIntersectsRect(clip, r.bounds);\n },\n hits: (node, pose, _derived, inherited) => {\n // The descriptor sees the node here, so a pose shape whose extent\n // depends on `node.data` bounds itself rather than its pose's default.\n const g = poseDescriptorForNode(descriptor, node);\n const b = regionBoundsOf(node as object, pose, g);\n let ink: boolean | undefined;\n const filled = (): boolean => ink ??= findShapeInk(node as never, pose, { inherited })?.filled !== false;\n return regionTakes(r, mode, asInked(filled, pose), b, () =>\n asInked(filled, findShapeSilhouette(node as never, pose) ?? poseOutline(pose, b)), filled);\n },\n inPlane: (m) => regionQuery(regionInPlane(r, m)),\n inCopy: (m) => regionQuery(regionInCopy(r, m)),\n });\n return pickWalk<unknown>(scenePickSource(scene, opts), regionQuery(region)) as NodeId[];\n}\n\n/** `r` carried into a plane's world. The map is axis-aligned, so a rect stays\n * one; a flipped axis reverses the winding, which no region test reads. */\nfunction regionInPlane(r: SelectRegion, m: PlaneMap): SelectRegion {\n const coords: number[] = [];\n for (const v of r.verts) {\n const p = toPlane(m, v);\n coords.push(p.x, p.y);\n }\n return regionOf(coords, rectToPlane(m, r.bounds), r.isRect)!;\n}\n\n/** `r` carried by an affine into a copy's source frame. A rect stays one only\n * while the map keeps the axes; a turn makes it a polygon. */\nfunction regionInCopy(r: SelectRegion, m: Affine): SelectRegion {\n const coords: number[] = [];\n let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n for (const v of r.verts) {\n const [x, y] = applyToPoint(m, v.x, v.y);\n coords.push(x, y);\n x0 = Math.min(x0, x); y0 = Math.min(y0, y); x1 = Math.max(x1, x); y1 = Math.max(y1, y);\n }\n const keepsAxes = (m[1] === 0 && m[2] === 0) || (m[0] === 0 && m[3] === 0);\n return regionOf(coords, { x: x0, y: y0, width: x1 - x0, height: y1 - y0 }, r.isRect && keepsAxes)!;\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { composeWorldPose, composesFrames } from "../../core/scene/composeFrame.js";
|
|
2
2
|
import { definesFrame } from "../../core/scene/effectivePose.js";
|
|
3
|
-
import { cycleFill, solid, strokeOf } from "../../util/paint.js";
|
|
4
3
|
import { insertPreviewExtent } from "../insertPreviewExtent.js";
|
|
5
4
|
import { minSizeOf } from "../../core/scene/minSize.js";
|
|
5
|
+
import { insertParent } from "../../interactions/actions/insert/parent.js";
|
|
6
6
|
import { createInsertOp } from "../../core/ops/create.js";
|
|
7
|
+
import { cycleFill, solid, strokeOf } from "../../util/paint.js";
|
|
7
8
|
import { stickyData } from "../../tools/builtin/sticky/stickyNode.js";
|
|
8
9
|
import { ellipsePath, linePath, polygonFromPoints, rectPath, regularPolygonPath, schneiderFit, starPath } from "@weasel-js/geom";
|
|
9
10
|
import { useRef } from "react";
|
|
10
11
|
import { useLatest } from "@weasel-js/react";
|
|
12
|
+
import { DEFAULT_TEXT_FILL } from "@weasel-js/text";
|
|
11
13
|
import { useDepRegistry, useDepSource } from "@weasel-js/routing/react";
|
|
12
14
|
|
|
13
15
|
//#region src/canvas/deps/insert.ts
|
|
@@ -68,7 +70,10 @@ function boxKindData(kind, bounds, extras, painted) {
|
|
|
68
70
|
...e.closed ? {} : { fill: null }
|
|
69
71
|
};
|
|
70
72
|
}
|
|
71
|
-
case "text": return {
|
|
73
|
+
case "text": return {
|
|
74
|
+
text: extras.text ?? "",
|
|
75
|
+
fill: DEFAULT_TEXT_FILL
|
|
76
|
+
};
|
|
72
77
|
case "sticky": return stickyData(extras);
|
|
73
78
|
case "image": {
|
|
74
79
|
const e = extras;
|
|
@@ -86,12 +91,13 @@ function boxKindData(kind, bounds, extras, painted) {
|
|
|
86
91
|
* or the top of the tree, on the first layer, when it names no container.
|
|
87
92
|
*/
|
|
88
93
|
function insertSite(sc, into) {
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
94
|
+
const parentId = insertParent(sc, into);
|
|
95
|
+
if (parentId === null) return {
|
|
91
96
|
parent: null,
|
|
92
97
|
layer: sc.layers[0]?.id ?? "default",
|
|
93
98
|
local: (p) => p
|
|
94
99
|
};
|
|
100
|
+
const parent = sc.get(parentId);
|
|
95
101
|
const composition = sc.composition;
|
|
96
102
|
if (composition === void 0 || !composesFrames(composition)) return {
|
|
97
103
|
parent: parent.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insert.js","names":["e"],"sources":["../../../src/canvas/deps/insert.ts"],"sourcesContent":["/**\n * `useInsertDepSource` — wires the `insert` dep consumed by `insertAction`.\n *\n * `line`, `polygon`, `star`, and `pencil` receive the drag AABB from the insertAction invoker,\n * but `extras` may carry richer geometry (endpoints, center+radius+rotation, point trail).\n * `insertPreviewExtent` resolves the two into the node's real extent — the same resolution the\n * live preview and the reported gesture bounds use.\n */\nimport { useRef } from 'react';\nimport { useDepRegistry, useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { InsertDep, InsertExtras, InsertLive } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { Op } from 'core/ops/types';\nimport { createInsertOp } from 'core/ops/create';\nimport {\n rectPath,\n ellipsePath,\n regularPolygonPath,\n starPath,\n linePath,\n polygonFromPoints,\n schneiderFit,\n type PathFillRule,\n type PolygonPath,\n} from '@weasel-js/geom';\nimport { insertPreviewExtent } from '../insertPreviewExtent';\nimport type { ShapeGenerator } from 'features/paths/shapeGenerator';\nimport type { Stroke, Fill } from '@weasel-js/paint';\nimport { cycleFill, solid, strokeOf } from '../../util/paint';\nimport { composesFrames, composeWorldPose } from 'core/scene/composeFrame';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport { stickyData, type StickyStyle } from '../../tools/builtin/sticky/stickyNode';\nimport { minSizeOf } from 'core/scene/minSize';\n\ninterface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/**\n * Consumer-supplied node factory for one insert `kind`. Given the drag AABB\n * and the tool's `extras`, returns the node's `data` (in the consumer's own\n * data shape) plus an optional `pose` override — the dep supplies id, layer,\n * and the undoable insert op. Return `null` to reject the insert.\n *\n * A factory registered for a kit kind (`rect`, `line`, …) fully replaces the\n * kit's default `{ path, fill }` factory for that kind; a factory for a novel\n * kind adds support the kit doesn't ship (e.g. `text`). To change only the\n * paint of a kit shape, pass an {@link InsertPaint}. See\n * `SceneCanvas`'s `insertNodeFactories` prop.\n */\nexport type InsertNodeFactory = (\n bounds: { x: number; y: number; width: number; height: number },\n extras: InsertExtras,\n) => { data: unknown; pose?: unknown } | null;\n\n/**\n * The paint a kit-built shape is inserted with. `kind` is the shape being\n * built and `seq` counts this canvas's inserts from 1. A key left out stays\n * off the node, so its painter's default applies; `null` is \"none\". A pencil\n * trail too short to fit is built as a rect and asks as one. A line and an\n * open pen path are inserted unfilled: a fill given to either is dropped.\n * `fillRule` is the rule a path that carries one is built with, where the\n * kit's own is nonzero; a rect or an ellipse has none to set.\n *\n * It paints only the shapes the kit builds: an `InsertNodeFactory` owns its\n * node's whole `data`. See `SceneCanvas`'s `insertPaint` prop.\n */\nexport type InsertPaint = (kind: string, seq: number) => {\n fill?: Fill | null;\n stroke?: Stroke | null;\n fillRule?: PathFillRule;\n};\n\nfunction paintKeys({ fill, stroke }: ReturnType<InsertPaint>): { fill?: Fill | null; stroke?: Stroke | null } {\n return { ...(fill !== undefined ? { fill } : {}), ...(stroke !== undefined ? { stroke } : {}) };\n}\n\nfunction withFillRule(data: unknown, rule: PathFillRule | undefined): unknown {\n const path = (data as { path?: object } | undefined)?.path;\n if (rule === undefined || path === undefined || !('fillRule' in path)) return data;\n return { ...(data as object), path: { ...path, fillRule: rule } };\n}\n\n/** The default {@link InsertPaint}: the next color of the kit palette, as a\n * fill on a closed shape, a fill and a stroke on a pen path, and a stroke\n * alone on a line or a pencil trail. */\nexport const paletteInsertPaint: InsertPaint = (kind, seq) => {\n const color = cycleFill(seq);\n if (kind === 'line' || kind === 'pencil') return { stroke: strokeOf(color, 2) };\n if (kind === 'path') return { fill: { paint: solid(color) }, stroke: strokeOf(color, 2) };\n return { fill: { paint: solid(color) } };\n};\n\n/** The kit's default data for a kind drawn as a box, or `null` for a kind it has none for. */\nfunction boxKindData(\n kind: string,\n bounds: { x: number; y: number; width: number; height: number },\n extras: InsertExtras,\n painted: (as?: string) => ReturnType<typeof paintKeys>,\n): unknown {\n switch (kind) {\n case 'rect':\n // The rect path lives at the origin and the pose carries position:\n // `pathInPoseFrame` rebases a rect path onto the pose box, so don't\n // double-count the position.\n return { path: rectPath(0, 0, bounds.width, bounds.height), ...painted() };\n case 'ellipse':\n return { path: ellipsePath(bounds), ...painted() };\n case 'path': {\n const e = extras as { path: PolygonPath; closed: boolean; data?: Readonly<Record<string, unknown>> };\n return { ...e.data, path: e.path, ...painted(), ...(e.closed ? {} : { fill: null }) };\n }\n case 'text': {\n // The built-in text tool drags an empty text box you then type into, so\n // `extras` carries no content. The kit:text painter matches on\n // `data.text != null` and applies style defaults at render time, so a bare\n // `{ text }` is a complete, editable node.\n const e = extras as Partial<{ text: string }>;\n return { text: e.text ?? '' };\n }\n case 'sticky':\n return stickyData(extras as StickyStyle & { text?: string });\n case 'image': {\n // The bitmap is loaded + cached by `imageCache` keyed on `src`;\n // only the (serializable) `src` lives on the node.\n const e = extras as Partial<{ src: string; opacity: number }>;\n return { image: { src: e.src ?? '', ...(e.opacity !== undefined ? { opacity: e.opacity } : {}) } };\n }\n default:\n return null;\n }\n}\n\n/**\n * Where an insert lands: the container the `insertInto` dep names, on that container's\n * layer, with the pose made local to it when the scene composes frames —\n * or the top of the tree, on the first layer, when it names no container.\n */\nfunction insertSite(\n sc: Scene<unknown, string, unknown>,\n into: string | null | undefined,\n): { parent: NodeId | null; layer: string; local: (world: unknown) => unknown } {\n const parent = into == null ? undefined : sc.get(into as NodeId);\n if (parent === undefined || parent.kind !== 'container') {\n return { parent: null, layer: (sc.layers[0]?.id ?? 'default') as string, local: (p) => p };\n }\n const composition = sc.composition;\n if (composition === undefined || !composesFrames(composition)) {\n return { parent: parent.id, layer: parent.layer, local: (p) => p };\n }\n const frame = composeWorldPose({\n getPose: (id) => sc.get(id as NodeId)!.pose,\n getParent: (id) => sc.get(id as NodeId)?.parent ?? null,\n definesFrame: (id) => definesFrame(sc.get(id as NodeId) ?? {}),\n }, parent.id, composition.compose);\n return { parent: parent.id, layer: parent.layer, local: (p) => composition.decompose(frame, p) };\n}\n\nexport function useInsertDepSource(\n scene: Scene<unknown, string, unknown>,\n adapter: OpsApplier,\n factories?: Record<string, InsertNodeFactory>,\n paint: InsertPaint = paletteInsertPaint,\n): void {\n const sceneRef = useLatest(scene);\n const adapterRef = useLatest(adapter);\n const factoriesRef = useLatest(factories);\n const paintRef = useLatest(paint);\n const registry = useDepRegistry();\n const insertSeqRef = useRef(0);\n const liveRef = useRef<InsertLive>({ gesture: null });\n useDepSource('insertLive', () => liveRef.current);\n\n useDepSource('insert', (): InsertDep => {\n const sc = sceneRef.current;\n const ad = adapterRef.current;\n\n const site = () => insertSite(sc, registry.get('insertInto')?.get());\n const build: NonNullable<InsertDep['build']> = (bounds, extras) => {\n const kind = extras.kind;\n const { parent, layer, local } = site();\n const seq = ++insertSeqRef.current;\n const id = sc.mintId();\n let asked: ReturnType<InsertPaint> | undefined;\n const painted = (as: string = kind) => paintKeys(asked = paintRef.current(as, seq));\n\n // The nascent node's extent — drag AABB reconciled with whatever\n // richer geometry `extras` carries.\n const extent = insertPreviewExtent({ shape: kind, bounds, extras });\n const pose: unknown = extent.bounds;\n\n // Consumer factory wins over the kit default for this kind. It owns the node's\n // data + optional pose; the dep still supplies id/layer/op.\n const factory = factoriesRef.current?.[kind];\n if (factory) {\n const built = factory(bounds, extras);\n if (built === null) return null;\n return {\n id,\n ops: [createInsertOp({ node: { id, kind: 'leaf', layer, parent, pose: local(built.pose ?? pose), data: built.data } as unknown as { id: string }, label: `Insert ${kind}` })],\n };\n }\n\n const geom = extent.geometry;\n let data: unknown;\n\n switch (geom.kind) {\n case 'line':\n data = { path: linePath(geom.a, geom.b), ...painted(), fill: null };\n break;\n case 'polygon':\n data = {\n path: regularPolygonPath(geom.center, geom.radius, geom.sides, geom.rotation),\n generator: { kind: 'polygon', sides: geom.sides, rotation: geom.rotation } satisfies ShapeGenerator,\n ...painted(),\n };\n break;\n case 'star':\n data = {\n path: starPath(geom.center, geom.outerRadius, geom.points, geom.innerRadius, geom.rotation),\n generator: {\n kind: 'star',\n points: geom.points,\n innerRadiusRatio: geom.outerRadius === 0 ? 0.5 : geom.innerRadius / geom.outerRadius,\n rotation: geom.rotation,\n } satisfies ShapeGenerator,\n ...painted(),\n };\n break;\n case 'pencil': {\n const samples = geom.samples as { x: number; y: number }[];\n if (samples.length >= 4) {\n data = { path: schneiderFit(samples, 2.0), ...painted() };\n } else if (samples.length >= 2) {\n data = { path: polygonFromPoints(samples), ...painted() };\n } else {\n data = { path: rectPath(0, 0, extent.bounds.width, extent.bounds.height), ...painted('rect') };\n }\n break;\n }\n case 'box':\n data = boxKindData(kind, extent.bounds, extras, painted);\n if (data === null) {\n console.warn(`weasel insertDep: no factory for kind=\"${kind}\". Skipping insert.`);\n return null;\n }\n break;\n }\n\n data = withFillRule(data, asked?.fillRule);\n const min = minSizeOf(extras);\n if (min) data = { ...(data as object), minSize: min };\n return {\n id,\n ops: [createInsertOp({ node: { id, kind: 'leaf', layer, parent, pose: local(pose), data } as unknown as { id: string }, label: `Insert ${kind}` })],\n };\n };\n return {\n layer: () => site().layer,\n build,\n commit(bounds, extras, ends): NodeId | null {\n const built = build(bounds, extras, ends);\n if (built === null) return null;\n ad.applyOps(built.ops, `Insert ${extras.kind}`);\n return built.id;\n },\n };\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"insert.js","names":["e"],"sources":["../../../src/canvas/deps/insert.ts"],"sourcesContent":["/**\n * `useInsertDepSource` — wires the `insert` dep consumed by `insertAction`.\n *\n * `line`, `polygon`, `star`, and `pencil` receive the drag AABB from the insertAction invoker,\n * but `extras` may carry richer geometry (endpoints, center+radius+rotation, point trail).\n * `insertPreviewExtent` resolves the two into the node's real extent — the same resolution the\n * live preview and the reported gesture bounds use.\n */\nimport { useRef } from 'react';\nimport { useDepRegistry, useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { InsertDep, InsertExtras, InsertLive } from 'interactions/actions/depSchema';\nimport { insertParent } from 'interactions/actions/insert/parent';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { Op } from 'core/ops/types';\nimport { createInsertOp } from 'core/ops/create';\nimport {\n rectPath,\n ellipsePath,\n regularPolygonPath,\n starPath,\n linePath,\n polygonFromPoints,\n schneiderFit,\n type PathFillRule,\n type PolygonPath,\n} from '@weasel-js/geom';\nimport { insertPreviewExtent } from '../insertPreviewExtent';\nimport type { ShapeGenerator } from 'features/paths/shapeGenerator';\nimport type { Stroke, Fill } from '@weasel-js/paint';\nimport { DEFAULT_TEXT_FILL } from '@weasel-js/text';\nimport { cycleFill, solid, strokeOf } from '../../util/paint';\nimport { composesFrames, composeWorldPose } from 'core/scene/composeFrame';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport { stickyData, type StickyStyle } from '../../tools/builtin/sticky/stickyNode';\nimport { minSizeOf } from 'core/scene/minSize';\n\ninterface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/**\n * Consumer-supplied node factory for one insert `kind`. Given the drag AABB\n * and the tool's `extras`, returns the node's `data` (in the consumer's own\n * data shape) plus an optional `pose` override — the dep supplies id, layer,\n * and the undoable insert op. Return `null` to reject the insert.\n *\n * A factory registered for a kit kind (`rect`, `line`, …) fully replaces the\n * kit's default `{ path, fill }` factory for that kind; a factory for a novel\n * kind adds support the kit doesn't ship (e.g. `text`). To change only the\n * paint of a kit shape, pass an {@link InsertPaint}. See\n * `SceneCanvas`'s `insertNodeFactories` prop.\n */\nexport type InsertNodeFactory = (\n bounds: { x: number; y: number; width: number; height: number },\n extras: InsertExtras,\n) => { data: unknown; pose?: unknown } | null;\n\n/**\n * The paint a kit-built shape is inserted with. `kind` is the shape being\n * built and `seq` counts this canvas's inserts from 1. A key left out stays\n * off the node, which paints none of it, the same as `null`. A pencil\n * trail too short to fit is built as a rect and asks as one. A line and an\n * open pen path are inserted unfilled: a fill given to either is dropped.\n * `fillRule` is the rule a path that carries one is built with, where the\n * kit's own is nonzero; a rect or an ellipse has none to set.\n *\n * It paints only the shapes the kit builds: an `InsertNodeFactory` owns its\n * node's whole `data`. See `SceneCanvas`'s `insertPaint` prop.\n */\nexport type InsertPaint = (kind: string, seq: number) => {\n fill?: Fill | null;\n stroke?: Stroke | null;\n fillRule?: PathFillRule;\n};\n\nfunction paintKeys({ fill, stroke }: ReturnType<InsertPaint>): { fill?: Fill | null; stroke?: Stroke | null } {\n return { ...(fill !== undefined ? { fill } : {}), ...(stroke !== undefined ? { stroke } : {}) };\n}\n\nfunction withFillRule(data: unknown, rule: PathFillRule | undefined): unknown {\n const path = (data as { path?: object } | undefined)?.path;\n if (rule === undefined || path === undefined || !('fillRule' in path)) return data;\n return { ...(data as object), path: { ...path, fillRule: rule } };\n}\n\n/** The default {@link InsertPaint}: the next color of the kit palette, as a\n * fill on a closed shape, a fill and a stroke on a pen path, and a stroke\n * alone on a line or a pencil trail. */\nexport const paletteInsertPaint: InsertPaint = (kind, seq) => {\n const color = cycleFill(seq);\n if (kind === 'line' || kind === 'pencil') return { stroke: strokeOf(color, 2) };\n if (kind === 'path') return { fill: { paint: solid(color) }, stroke: strokeOf(color, 2) };\n return { fill: { paint: solid(color) } };\n};\n\n/** The kit's default data for a kind drawn as a box, or `null` for a kind it has none for. */\nfunction boxKindData(\n kind: string,\n bounds: { x: number; y: number; width: number; height: number },\n extras: InsertExtras,\n painted: (as?: string) => ReturnType<typeof paintKeys>,\n): unknown {\n switch (kind) {\n case 'rect':\n // The rect path lives at the origin and the pose carries position:\n // `pathInPoseFrame` rebases a rect path onto the pose box, so don't\n // double-count the position.\n return { path: rectPath(0, 0, bounds.width, bounds.height), ...painted() };\n case 'ellipse':\n return { path: ellipsePath(bounds), ...painted() };\n case 'path': {\n const e = extras as { path: PolygonPath; closed: boolean; data?: Readonly<Record<string, unknown>> };\n return { ...e.data, path: e.path, ...painted(), ...(e.closed ? {} : { fill: null }) };\n }\n case 'text': {\n // The built-in text tool drags an empty text box you then type into, so\n // `extras` carries no content. The kit:text painter matches on\n // `data.text != null` and applies style defaults at render time.\n const e = extras as Partial<{ text: string }>;\n return { text: e.text ?? '', fill: DEFAULT_TEXT_FILL };\n }\n case 'sticky':\n return stickyData(extras as StickyStyle & { text?: string });\n case 'image': {\n // The bitmap is loaded + cached by `imageCache` keyed on `src`;\n // only the (serializable) `src` lives on the node.\n const e = extras as Partial<{ src: string; opacity: number }>;\n return { image: { src: e.src ?? '', ...(e.opacity !== undefined ? { opacity: e.opacity } : {}) } };\n }\n default:\n return null;\n }\n}\n\n/**\n * Where an insert lands: the container the `insertInto` dep names, on that container's\n * layer, with the pose made local to it when the scene composes frames —\n * or the top of the tree, on the first layer, when it names no container.\n */\nfunction insertSite(\n sc: Scene<unknown, string, unknown>,\n into: string | null | undefined,\n): { parent: NodeId | null; layer: string; local: (world: unknown) => unknown } {\n const parentId = insertParent(sc, into);\n if (parentId === null) {\n return { parent: null, layer: (sc.layers[0]?.id ?? 'default') as string, local: (p) => p };\n }\n const parent = sc.get(parentId)!;\n const composition = sc.composition;\n if (composition === undefined || !composesFrames(composition)) {\n return { parent: parent.id, layer: parent.layer, local: (p) => p };\n }\n const frame = composeWorldPose({\n getPose: (id) => sc.get(id as NodeId)!.pose,\n getParent: (id) => sc.get(id as NodeId)?.parent ?? null,\n definesFrame: (id) => definesFrame(sc.get(id as NodeId) ?? {}),\n }, parent.id, composition.compose);\n return { parent: parent.id, layer: parent.layer, local: (p) => composition.decompose(frame, p) };\n}\n\nexport function useInsertDepSource(\n scene: Scene<unknown, string, unknown>,\n adapter: OpsApplier,\n factories?: Record<string, InsertNodeFactory>,\n paint: InsertPaint = paletteInsertPaint,\n): void {\n const sceneRef = useLatest(scene);\n const adapterRef = useLatest(adapter);\n const factoriesRef = useLatest(factories);\n const paintRef = useLatest(paint);\n const registry = useDepRegistry();\n const insertSeqRef = useRef(0);\n const liveRef = useRef<InsertLive>({ gesture: null });\n useDepSource('insertLive', () => liveRef.current);\n\n useDepSource('insert', (): InsertDep => {\n const sc = sceneRef.current;\n const ad = adapterRef.current;\n\n const site = () => insertSite(sc, registry.get('insertInto')?.get());\n const build: NonNullable<InsertDep['build']> = (bounds, extras) => {\n const kind = extras.kind;\n const { parent, layer, local } = site();\n const seq = ++insertSeqRef.current;\n const id = sc.mintId();\n let asked: ReturnType<InsertPaint> | undefined;\n const painted = (as: string = kind) => paintKeys(asked = paintRef.current(as, seq));\n\n // The nascent node's extent — drag AABB reconciled with whatever\n // richer geometry `extras` carries.\n const extent = insertPreviewExtent({ shape: kind, bounds, extras });\n const pose: unknown = extent.bounds;\n\n // Consumer factory wins over the kit default for this kind. It owns the node's\n // data + optional pose; the dep still supplies id/layer/op.\n const factory = factoriesRef.current?.[kind];\n if (factory) {\n const built = factory(bounds, extras);\n if (built === null) return null;\n return {\n id,\n ops: [createInsertOp({ node: { id, kind: 'leaf', layer, parent, pose: local(built.pose ?? pose), data: built.data } as unknown as { id: string }, label: `Insert ${kind}` })],\n };\n }\n\n const geom = extent.geometry;\n let data: unknown;\n\n switch (geom.kind) {\n case 'line':\n data = { path: linePath(geom.a, geom.b), ...painted(), fill: null };\n break;\n case 'polygon':\n data = {\n path: regularPolygonPath(geom.center, geom.radius, geom.sides, geom.rotation),\n generator: { kind: 'polygon', sides: geom.sides, rotation: geom.rotation } satisfies ShapeGenerator,\n ...painted(),\n };\n break;\n case 'star':\n data = {\n path: starPath(geom.center, geom.outerRadius, geom.points, geom.innerRadius, geom.rotation),\n generator: {\n kind: 'star',\n points: geom.points,\n innerRadiusRatio: geom.outerRadius === 0 ? 0.5 : geom.innerRadius / geom.outerRadius,\n rotation: geom.rotation,\n } satisfies ShapeGenerator,\n ...painted(),\n };\n break;\n case 'pencil': {\n const samples = geom.samples as { x: number; y: number }[];\n if (samples.length >= 4) {\n data = { path: schneiderFit(samples, 2.0), ...painted() };\n } else if (samples.length >= 2) {\n data = { path: polygonFromPoints(samples), ...painted() };\n } else {\n data = { path: rectPath(0, 0, extent.bounds.width, extent.bounds.height), ...painted('rect') };\n }\n break;\n }\n case 'box':\n data = boxKindData(kind, extent.bounds, extras, painted);\n if (data === null) {\n console.warn(`weasel insertDep: no factory for kind=\"${kind}\". Skipping insert.`);\n return null;\n }\n break;\n }\n\n data = withFillRule(data, asked?.fillRule);\n const min = minSizeOf(extras);\n if (min) data = { ...(data as object), minSize: min };\n return {\n id,\n ops: [createInsertOp({ node: { id, kind: 'leaf', layer, parent, pose: local(pose), data } as unknown as { id: string }, label: `Insert ${kind}` })],\n };\n };\n return {\n layer: () => site().layer,\n build,\n commit(bounds, extras, ends): NodeId | null {\n const built = build(bounds, extras, ends);\n if (built === null) return null;\n ad.applyOps(built.ops, `Insert ${extras.kind}`);\n return built.id;\n },\n };\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { sceneDependentsOf } from "../../core/scene/dependents.js";
|
|
2
|
+
import { createTransformOp } from "../../core/ops/transform.js";
|
|
3
|
+
import { createSetDataOp } from "../../core/ops/setData.js";
|
|
4
|
+
import { createDeleteOp } from "../../core/ops/delete.js";
|
|
5
|
+
import { pathEditPathOf, storeEditablePath } from "../../features/paths/editablePath.js";
|
|
6
|
+
import { withoutShapeGenerator } from "../../features/paths/shapeGenerator.js";
|
|
7
|
+
import { withoutGeometryBoundData } from "../../features/paths/pathEditExtension.js";
|
|
8
|
+
import { writtenBy } from "../../core/ops/writtenBy.js";
|
|
9
|
+
|
|
10
|
+
//#region src/canvas/deps/pathEditCommits.ts
|
|
11
|
+
/** The scene as an extension's `finishEdit` reads it. */
|
|
12
|
+
function finishCtx(sc, id, extensions) {
|
|
13
|
+
return {
|
|
14
|
+
id,
|
|
15
|
+
node(nid) {
|
|
16
|
+
const node = sc.get(nid);
|
|
17
|
+
return node ? {
|
|
18
|
+
pose: node.pose,
|
|
19
|
+
data: node.data,
|
|
20
|
+
dependsOn: node.dependsOn
|
|
21
|
+
} : null;
|
|
22
|
+
},
|
|
23
|
+
path: (nid) => pathEditPathOf(sc.get(nid), extensions),
|
|
24
|
+
dependents: (nid) => sceneDependentsOf(sc, nid)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** `o` laid over what `others` already holds for its node. */
|
|
28
|
+
function addOther(others, o) {
|
|
29
|
+
const had = others.get(o.id);
|
|
30
|
+
others.set(o.id, {
|
|
31
|
+
id: o.id,
|
|
32
|
+
path: o.path ?? had?.path,
|
|
33
|
+
data: o.data || had?.data ? {
|
|
34
|
+
...had?.data,
|
|
35
|
+
...o.data
|
|
36
|
+
} : void 0,
|
|
37
|
+
remove: o.remove || had?.remove
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** `worldPath` finished by each of `extensions` in turn, the data they set
|
|
41
|
+
* beside it, and the other nodes they change; null when one cannot finish it. */
|
|
42
|
+
function finishEdit(extensions, node, worldPath, origins, ctx) {
|
|
43
|
+
let path = worldPath;
|
|
44
|
+
let data = null;
|
|
45
|
+
const others = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const ext of extensions) {
|
|
47
|
+
if (!ext.finishEdit) continue;
|
|
48
|
+
const done = ext.finishEdit(node, path, origins, ctx);
|
|
49
|
+
if (!done) return null;
|
|
50
|
+
path = done.path;
|
|
51
|
+
if (done.data) data = {
|
|
52
|
+
...data ?? {},
|
|
53
|
+
...done.data
|
|
54
|
+
};
|
|
55
|
+
for (const o of done.others ?? []) addOther(others, o);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
path,
|
|
59
|
+
data,
|
|
60
|
+
others
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** The ops that commit `others`, each from its node as committed. */
|
|
64
|
+
function otherOps(sc, others, label, extensions) {
|
|
65
|
+
return [...others.values()].flatMap((o) => {
|
|
66
|
+
const node = sc.get(o.id);
|
|
67
|
+
if (!node) return [];
|
|
68
|
+
if (o.remove) return [createDeleteOp({
|
|
69
|
+
node: sc.get(o.id),
|
|
70
|
+
label
|
|
71
|
+
})];
|
|
72
|
+
if (o.path) return reshapeOps(o.id, node, o.path, label, o.data ?? null, extensions);
|
|
73
|
+
return o.data ? [createSetDataOp({
|
|
74
|
+
id: o.id,
|
|
75
|
+
from: node.data,
|
|
76
|
+
to: {
|
|
77
|
+
...node.data,
|
|
78
|
+
...o.data
|
|
79
|
+
},
|
|
80
|
+
label
|
|
81
|
+
})] : [];
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** The ops that store `worldPath` where `node` keeps its polygon, with `patch` laid over its data. */
|
|
85
|
+
function reshapeOps(id, node, worldPath, label, patch, extensions) {
|
|
86
|
+
const next = storeEditablePath(node, worldPath, extensions);
|
|
87
|
+
if (!next) return [];
|
|
88
|
+
const reshape = createTransformOp({
|
|
89
|
+
id,
|
|
90
|
+
from: node.pose,
|
|
91
|
+
to: next.pose,
|
|
92
|
+
label
|
|
93
|
+
});
|
|
94
|
+
const data = withoutGeometryBoundData(next.data === node.data ? node.data : withoutShapeGenerator(next.data), [
|
|
95
|
+
"skin",
|
|
96
|
+
"constraints",
|
|
97
|
+
"representation"
|
|
98
|
+
]);
|
|
99
|
+
if (data === node.data && !patch) return [reshape];
|
|
100
|
+
return [reshape, createSetDataOp({
|
|
101
|
+
id,
|
|
102
|
+
from: node.data,
|
|
103
|
+
to: {
|
|
104
|
+
...data,
|
|
105
|
+
...patch
|
|
106
|
+
},
|
|
107
|
+
label
|
|
108
|
+
})];
|
|
109
|
+
}
|
|
110
|
+
/** The other nodes `extensions` change along with a transform that leaves the nodes in `after` as given; null when one refuses it. */
|
|
111
|
+
function transformOthers(sc, extensions, after) {
|
|
112
|
+
const others = /* @__PURE__ */ new Map();
|
|
113
|
+
const finishing = extensions.filter((e) => e.finishTransform);
|
|
114
|
+
if (finishing.length === 0 || after.size === 0) return others;
|
|
115
|
+
const nodeAfter = (id) => {
|
|
116
|
+
const node = sc.get(id);
|
|
117
|
+
if (!node) return null;
|
|
118
|
+
const written = after.get(id);
|
|
119
|
+
return {
|
|
120
|
+
pose: written?.pose ?? node.pose,
|
|
121
|
+
data: written?.data ?? node.data,
|
|
122
|
+
dependsOn: node.dependsOn
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
const ctx = {
|
|
126
|
+
ids: [...after.keys()],
|
|
127
|
+
node: nodeAfter,
|
|
128
|
+
path: (id) => pathEditPathOf(nodeAfter(id) ?? void 0, extensions),
|
|
129
|
+
before: (id) => pathEditPathOf(sc.get(id), extensions),
|
|
130
|
+
dependents: (id) => sceneDependentsOf(sc, id)
|
|
131
|
+
};
|
|
132
|
+
for (const ext of finishing) {
|
|
133
|
+
const done = ext.finishTransform(ctx);
|
|
134
|
+
if (!done) return null;
|
|
135
|
+
for (const o of done) if (!after.has(o.id)) addOther(others, o);
|
|
136
|
+
}
|
|
137
|
+
return others;
|
|
138
|
+
}
|
|
139
|
+
/** `ops`, a whole-node transform's batch, with the ops `extensions` add for the other nodes it changes; null when one refuses it. */
|
|
140
|
+
function transformOps(sc, extensions, ops, label) {
|
|
141
|
+
const after = extensions.some((e) => e.finishTransform) ? writtenBy(sc, ops) : null;
|
|
142
|
+
if (!after || after.size === 0) return [...ops];
|
|
143
|
+
const others = transformOthers(sc, extensions, after);
|
|
144
|
+
return others && [...ops, ...otherOps(sc, others, label, extensions)];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The other nodes `extensions` change along with a transform frame that
|
|
148
|
+
* poses the nodes in `poses` as given, each as it would be stored — what
|
|
149
|
+
* {@link transformOps} would commit for that frame, read the same way. Empty
|
|
150
|
+
* when nothing changes with it, or an extension refuses it.
|
|
151
|
+
*/
|
|
152
|
+
function transformPreview(sc, extensions, poses) {
|
|
153
|
+
if (poses.size === 0 || !extensions.some((e) => e.finishTransform)) return [];
|
|
154
|
+
const after = /* @__PURE__ */ new Map();
|
|
155
|
+
for (const [id, pose] of poses) {
|
|
156
|
+
const node = sc.get(id);
|
|
157
|
+
if (node) after.set(id, {
|
|
158
|
+
pose,
|
|
159
|
+
data: node.data
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return [...transformOthers(sc, extensions, after)?.values() ?? []].flatMap((o) => {
|
|
163
|
+
const node = sc.get(o.id);
|
|
164
|
+
const stored = node && o.path && !o.remove ? storeEditablePath(node, o.path, extensions) : null;
|
|
165
|
+
if (!node || !stored) return [];
|
|
166
|
+
return [stored.data === node.data ? {
|
|
167
|
+
id: o.id,
|
|
168
|
+
pose: stored.pose
|
|
169
|
+
} : {
|
|
170
|
+
id: o.id,
|
|
171
|
+
pose: stored.pose,
|
|
172
|
+
data: stored.data
|
|
173
|
+
}];
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/** The transform follower `extensions` make: each one's `finishTransform`, as
|
|
177
|
+
* {@link transformOps} and {@link transformPreview} read it. Null when none
|
|
178
|
+
* has one, so a transform runs no follower for them. */
|
|
179
|
+
function pathEditFollower(sc, extensions) {
|
|
180
|
+
if (!extensions.some((e) => e.finishTransform)) return null;
|
|
181
|
+
return {
|
|
182
|
+
finish: (ops, label) => transformOps(sc, extensions, ops, label),
|
|
183
|
+
preview: (poses) => transformPreview(sc, extensions, poses)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
//#endregion
|
|
188
|
+
export { finishCtx, finishEdit, otherOps, pathEditFollower, reshapeOps, transformOps, transformPreview };
|
|
189
|
+
//# sourceMappingURL=pathEditCommits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathEditCommits.js","names":[],"sources":["../../../src/canvas/deps/pathEditCommits.ts"],"sourcesContent":["/**\n * What the `editAnchors` dep commits: an edit finished by every path-edit\n * extension, the other nodes the extensions change with it, and the same for\n * a whole-node transform's batch.\n */\nimport type { PointOrigin, PolygonPath } from '@weasel-js/geom';\nimport type { Op } from 'core/ops/types';\nimport { createTransformOp } from 'core/ops/transform';\nimport { createSetDataOp } from 'core/ops/setData';\nimport { createDeleteOp } from 'core/ops/delete';\nimport { writtenBy } from 'core/ops/writtenBy';\nimport type { TransformFollower, TransformPreviewNode } from 'interactions/actions/transformFinish';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { sceneDependentsOf } from 'core/scene/dependents';\nimport { pathEditPathOf, storeEditablePath } from 'features/paths/editablePath';\nimport { withoutShapeGenerator } from 'features/paths/shapeGenerator';\nimport {\n withoutGeometryBoundData, type PathEditExtension, type PathEditFinishCtx, type PathEditOther, type PathTransformFinishCtx,\n} from 'features/paths/pathEditExtension';\n\ntype AnyScene = Scene<unknown, string, unknown>;\nexport type EditableNode = { pose: unknown; data: unknown };\n\n/** What {@link finishEdit} answers: the edited path, the data set beside it, and the other nodes it changes, by id. */\nexport interface Finished {\n path: PolygonPath;\n data: Record<string, unknown> | null;\n others: Map<string, PathEditOther>;\n}\n\n/** The scene as an extension's `finishEdit` reads it. */\nexport function finishCtx(sc: AnyScene, id: string, extensions: readonly PathEditExtension[]): PathEditFinishCtx {\n return {\n id,\n node(nid) {\n const node = sc.get(nid as NodeId);\n return node ? { pose: node.pose, data: node.data, dependsOn: node.dependsOn } : null;\n },\n path: (nid) => pathEditPathOf(sc.get(nid as NodeId) as EditableNode | undefined, extensions),\n dependents: (nid) => sceneDependentsOf(sc, nid as NodeId),\n };\n}\n\n/** `o` laid over what `others` already holds for its node. */\nfunction addOther(others: Map<string, PathEditOther>, o: PathEditOther): void {\n const had = others.get(o.id);\n others.set(o.id, {\n id: o.id,\n path: o.path ?? had?.path,\n data: o.data || had?.data ? { ...had?.data, ...o.data } : undefined,\n remove: o.remove || had?.remove,\n });\n}\n\n/** `worldPath` finished by each of `extensions` in turn, the data they set\n * beside it, and the other nodes they change; null when one cannot finish it. */\nexport function finishEdit(\n extensions: readonly PathEditExtension[],\n node: EditableNode,\n worldPath: PolygonPath,\n origins: readonly (PointOrigin | null)[] | undefined,\n ctx: PathEditFinishCtx,\n): Finished | null {\n let path = worldPath;\n let data: Record<string, unknown> | null = null;\n const others = new Map<string, PathEditOther>();\n for (const ext of extensions) {\n if (!ext.finishEdit) continue;\n const done = ext.finishEdit(node, path, origins, ctx);\n if (!done) return null;\n path = done.path;\n if (done.data) data = { ...(data ?? {}), ...done.data };\n for (const o of done.others ?? []) addOther(others, o);\n }\n return { path, data, others };\n}\n\n/** The ops that commit `others`, each from its node as committed. */\nexport function otherOps(\n sc: AnyScene,\n others: ReadonlyMap<string, PathEditOther>,\n label: string,\n extensions: readonly PathEditExtension[],\n): Op[] {\n return [...others.values()].flatMap((o) => {\n const node = sc.get(o.id as NodeId) as EditableNode | undefined;\n if (!node) return [];\n if (o.remove) return [createDeleteOp({ node: sc.get(o.id as NodeId)!, label })];\n if (o.path) return reshapeOps(o.id, node, o.path, label, o.data ?? null, extensions);\n return o.data ? [createSetDataOp({ id: o.id, from: node.data, to: { ...(node.data as object), ...o.data }, label })] : [];\n });\n}\n\n/** The ops that store `worldPath` where `node` keeps its polygon, with `patch` laid over its data. */\nexport function reshapeOps(\n id: string,\n node: EditableNode,\n worldPath: PolygonPath,\n label: string,\n patch: Record<string, unknown> | null,\n extensions: readonly PathEditExtension[],\n): Op[] {\n const next = storeEditablePath(node, worldPath, extensions);\n if (!next) return [];\n const reshape = createTransformOp({ id, from: node.pose, to: next.pose, label });\n // A path edited by hand is no longer what its generator or warp makes: it becomes plain geometry.\n // Its skin and constraints stay, renumbered by the caller's patch where it has one, and the\n // form it was drawn from as storing it left it.\n const data = withoutGeometryBoundData(\n next.data === node.data ? node.data : withoutShapeGenerator(next.data), ['skin', 'constraints', 'representation'],\n );\n if (data === node.data && !patch) return [reshape];\n return [reshape, createSetDataOp({ id, from: node.data, to: { ...(data as object), ...patch }, label })];\n}\n\n/** The other nodes `extensions` change along with a transform that leaves the nodes in `after` as given; null when one refuses it. */\nfunction transformOthers(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n after: ReadonlyMap<string, EditableNode>,\n): Map<string, PathEditOther> | null {\n const others = new Map<string, PathEditOther>();\n const finishing = extensions.filter((e) => e.finishTransform);\n if (finishing.length === 0 || after.size === 0) return others;\n const nodeAfter = (id: string) => {\n const node = sc.get(id as NodeId);\n if (!node) return null;\n const written = after.get(id);\n return { pose: written?.pose ?? node.pose, data: written?.data ?? node.data, dependsOn: node.dependsOn };\n };\n const ctx: PathTransformFinishCtx = {\n ids: [...after.keys()],\n node: nodeAfter,\n path: (id) => pathEditPathOf(nodeAfter(id) ?? undefined, extensions),\n before: (id) => pathEditPathOf(sc.get(id as NodeId) as EditableNode | undefined, extensions),\n dependents: (id) => sceneDependentsOf(sc, id as NodeId),\n };\n for (const ext of finishing) {\n const done = ext.finishTransform!(ctx);\n if (!done) return null;\n for (const o of done) if (!after.has(o.id)) addOther(others, o);\n }\n return others;\n}\n\n/** `ops`, a whole-node transform's batch, with the ops `extensions` add for the other nodes it changes; null when one refuses it. */\nexport function transformOps(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n ops: readonly Op[],\n label: string,\n): Op[] | null {\n const after = extensions.some((e) => e.finishTransform) ? writtenBy(sc, ops) : null;\n if (!after || after.size === 0) return [...ops];\n const others = transformOthers(sc, extensions, after);\n return others && [...ops, ...otherOps(sc, others, label, extensions)];\n}\n\n/**\n * The other nodes `extensions` change along with a transform frame that\n * poses the nodes in `poses` as given, each as it would be stored — what\n * {@link transformOps} would commit for that frame, read the same way. Empty\n * when nothing changes with it, or an extension refuses it.\n */\nexport function transformPreview(\n sc: AnyScene,\n extensions: readonly PathEditExtension[],\n poses: ReadonlyMap<string, unknown>,\n): TransformPreviewNode[] {\n if (poses.size === 0 || !extensions.some((e) => e.finishTransform)) return [];\n const after = new Map<string, EditableNode>();\n for (const [id, pose] of poses) {\n const node = sc.get(id as NodeId);\n if (node) after.set(id, { pose, data: node.data });\n }\n const others = transformOthers(sc, extensions, after);\n return [...(others?.values() ?? [])].flatMap((o) => {\n const node = sc.get(o.id as NodeId) as EditableNode | undefined;\n const stored = node && o.path && !o.remove ? storeEditablePath(node, o.path, extensions) : null;\n if (!node || !stored) return [];\n return [stored.data === node.data ? { id: o.id, pose: stored.pose } : { id: o.id, pose: stored.pose, data: stored.data }];\n });\n}\n\n/** The transform follower `extensions` make: each one's `finishTransform`, as\n * {@link transformOps} and {@link transformPreview} read it. Null when none\n * has one, so a transform runs no follower for them. */\nexport function pathEditFollower(sc: AnyScene, extensions: readonly PathEditExtension[]): TransformFollower | null {\n if (!extensions.some((e) => e.finishTransform)) return null;\n return {\n finish: (ops, label) => transformOps(sc, extensions, ops, label),\n preview: (poses) => transformPreview(sc, extensions, poses),\n };\n}\n"],"mappings":"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"}
|
package/dist/canvas/deps/snap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { snapSourcesFor } from "../../features/snap/gather.js";
|
|
1
|
+
import { assertSnapSourceIds, snapSourcesFor } from "../../features/snap/gather.js";
|
|
2
2
|
import { DEFAULT_SNAP_PX } from "../../features/snap/session2d.js";
|
|
3
3
|
import { ownGeometryAt, pathSnapSource } from "../../features/paths/pathSnap/pathSnapSource.js";
|
|
4
4
|
import { sceneSession2d } from "../../features/snap/sceneSession2d.js";
|
|
@@ -20,12 +20,12 @@ var DEFAULT_SNAP_SOURCES = [rotationSnapSource(), {
|
|
|
20
20
|
...pathSnapSource({ only: ["anchor"] }),
|
|
21
21
|
gestures: ["point"]
|
|
22
22
|
}];
|
|
23
|
-
/** `listed`,
|
|
24
|
-
*
|
|
23
|
+
/** `listed`, which must not repeat an id, then each default whose id is
|
|
24
|
+
* neither listed nor in `omit`. */
|
|
25
25
|
function withDefaults(listed, omit = []) {
|
|
26
|
-
|
|
27
|
-
const taken = /* @__PURE__ */ new Set([...
|
|
28
|
-
return [...listed
|
|
26
|
+
assertSnapSourceIds(listed);
|
|
27
|
+
const taken = /* @__PURE__ */ new Set([...listed.map((s) => s.id), ...omit]);
|
|
28
|
+
return [...listed, ...DEFAULT_SNAP_SOURCES.filter((s) => !taken.has(s.id))];
|
|
29
29
|
}
|
|
30
30
|
function useSnapDepSource(viewRef, session, store) {
|
|
31
31
|
const sessionRef = useLatest(session);
|
|
@@ -34,9 +34,12 @@ function useSnapDepSource(viewRef, session, store) {
|
|
|
34
34
|
useDepSource("snap", () => ({ begin: (req) => {
|
|
35
35
|
const s = sessionRef.current;
|
|
36
36
|
const view = viewRef.current;
|
|
37
|
-
const
|
|
37
|
+
const omit = new Set(req.omit);
|
|
38
|
+
const sources = [...withDefaults([...s.sources, ...s.registry?.list() ?? []], s.omit).filter((src) => !omit.has(src.id)), ...req.sources ?? []];
|
|
39
|
+
assertSnapSourceIds(sources);
|
|
38
40
|
const wantsGeometry = snapSourcesFor(sources, req.gesture).some((src) => src.roles.includes("geometry"));
|
|
39
41
|
const debug = s.debug?.() ?? void 0;
|
|
42
|
+
const reachPx = s.reachPx ?? kitSettings.get("targets.snapPx") ?? DEFAULT_SNAP_PX;
|
|
40
43
|
return sceneSession2d({
|
|
41
44
|
req,
|
|
42
45
|
scene: s.scene,
|
|
@@ -45,9 +48,9 @@ function useSnapDepSource(viewRef, session, store) {
|
|
|
45
48
|
...wantsGeometry ? { geometryAt: (press, ids) => ownGeometryAt(s.scene, press, ids, view?.scale ?? {
|
|
46
49
|
x: 1,
|
|
47
50
|
y: 1
|
|
48
|
-
}) } : {},
|
|
51
|
+
}, reachPx) } : {},
|
|
49
52
|
view: () => viewRef.current,
|
|
50
|
-
reachPx
|
|
53
|
+
reachPx,
|
|
51
54
|
...s.poseDescriptor ? { poseDescriptor: s.poseDescriptor } : {},
|
|
52
55
|
...s.originProjection ? { originProjection: s.originProjection } : {},
|
|
53
56
|
...debug ? { debug } : {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snap.js","names":[],"sources":["../../../src/canvas/deps/snap.ts"],"sourcesContent":["/**\n * `useSnapDepSource` — wires the `snap` dep whose `begin` every snapping\n * gesture runs: a session over the snap sources `session` names, writing its\n * marks to the outcome store this hook returns.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useKitSettings, useLatest } from '@weasel-js/react';\nimport type { DebugSink } from '@weasel-js/routing';\nimport type { SnapDep } from 'interactions/actions/snapDep';\nimport { ownGeometryAt } from 'features/paths/pathSnap/pathSnapSource';\nimport type { View } from 'core/viewport/view';\nimport type { Scene } from 'core/scene/types';\nimport type { DrawnBoxes } from 'interactions/actions/drawnBoxes';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport type { OriginProjection } from 'interactions/gestures/shared/originProjection';\nimport type { SnapOutcomeStore } from 'features/snap/outcomeStore';\nimport type { SnapSourceRegistry } from 'features/snap/registry';\nimport { snapSourcesFor } from 'features/snap/gather';\nimport { sceneSession2d } from 'features/snap/sceneSession2d';\nimport { DEFAULT_SNAP_PX } from 'features/snap/session2d';\nimport type { SnapReadoutDep } from 'features/snap/readoutDep';\nimport { rotationSnapSource } from 'features/snap/rotationSource';\nimport { pathSnapSource } from 'features/paths/pathSnap/pathSnapSource';\nimport type { SnapSource } from 'features/snap/types';\n\ntype Point = { x: number; y: number };\n\n/** What `begin` reads: the scene its probes come from and the sources it asks. */\nexport interface SnapSessionSources {\n scene: Scene<unknown, string, unknown>;\n /** The boxes the subject is drawn with, read when a session begins. */\n boxes: () => DrawnBoxes;\n /** `<SceneCanvas snap.sources>`, then the presets' `snapSources`. */\n sources: readonly SnapSource[];\n /** Sources `useSnapSource` adds, after `sources`. */\n registry?: SnapSourceRegistry;\n /** Ids of the canvas's default sources to leave out. */\n omit?: readonly string[];\n /** Overrides the kit setting `targets.snapPx`. */\n reachPx?: number;\n /** How a node's rotation is read, for angle targets. */\n poseDescriptor?: PoseDescriptor<unknown>;\n /** How a pose's origin is read, for the `origin` probe. */\n originProjection?: OriginProjection<unknown>;\n /** The canvas's debug sink, read when a session begins: each frame's\n * candidates are recorded into it. */\n debug?: () => DebugSink | null | undefined;\n}\n\n/** The sources every canvas asks after its own, unless `omit` names one:\n * quarter turns and other nodes' angles for a rotate, and other nodes'\n * anchors for a point gesture, under `pathSnapSource`'s own id, so a\n * consumer's `pathSnapSource()` takes its place. */\nconst DEFAULT_SNAP_SOURCES: readonly SnapSource[] = [\n rotationSnapSource(),\n { ...pathSnapSource({ only: ['anchor'] }), gestures: ['point'] },\n];\n\n/** `listed`,
|
|
1
|
+
{"version":3,"file":"snap.js","names":[],"sources":["../../../src/canvas/deps/snap.ts"],"sourcesContent":["/**\n * `useSnapDepSource` — wires the `snap` dep whose `begin` every snapping\n * gesture runs: a session over the snap sources `session` names, writing its\n * marks to the outcome store this hook returns.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useKitSettings, useLatest } from '@weasel-js/react';\nimport type { DebugSink } from '@weasel-js/routing';\nimport type { SnapDep } from 'interactions/actions/snapDep';\nimport { ownGeometryAt } from 'features/paths/pathSnap/pathSnapSource';\nimport type { View } from 'core/viewport/view';\nimport type { Scene } from 'core/scene/types';\nimport type { DrawnBoxes } from 'interactions/actions/drawnBoxes';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport type { OriginProjection } from 'interactions/gestures/shared/originProjection';\nimport type { SnapOutcomeStore } from 'features/snap/outcomeStore';\nimport type { SnapSourceRegistry } from 'features/snap/registry';\nimport { assertSnapSourceIds, snapSourcesFor } from 'features/snap/gather';\nimport { sceneSession2d } from 'features/snap/sceneSession2d';\nimport { DEFAULT_SNAP_PX } from 'features/snap/session2d';\nimport type { SnapReadoutDep } from 'features/snap/readoutDep';\nimport { rotationSnapSource } from 'features/snap/rotationSource';\nimport { pathSnapSource } from 'features/paths/pathSnap/pathSnapSource';\nimport type { SnapSource } from 'features/snap/types';\n\ntype Point = { x: number; y: number };\n\n/** What `begin` reads: the scene its probes come from and the sources it asks. */\nexport interface SnapSessionSources {\n scene: Scene<unknown, string, unknown>;\n /** The boxes the subject is drawn with, read when a session begins. */\n boxes: () => DrawnBoxes;\n /** `<SceneCanvas snap.sources>`, then the presets' `snapSources`. */\n sources: readonly SnapSource[];\n /** Sources `useSnapSource` adds, after `sources`. */\n registry?: SnapSourceRegistry;\n /** Ids of the canvas's default sources to leave out. */\n omit?: readonly string[];\n /** Overrides the kit setting `targets.snapPx`. */\n reachPx?: number;\n /** How a node's rotation is read, for angle targets. */\n poseDescriptor?: PoseDescriptor<unknown>;\n /** How a pose's origin is read, for the `origin` probe. */\n originProjection?: OriginProjection<unknown>;\n /** The canvas's debug sink, read when a session begins: each frame's\n * candidates are recorded into it. */\n debug?: () => DebugSink | null | undefined;\n}\n\n/** The sources every canvas asks after its own, unless `omit` names one:\n * quarter turns and other nodes' angles for a rotate, and other nodes'\n * anchors for a point gesture, under `pathSnapSource`'s own id, so a\n * consumer's `pathSnapSource()` takes its place. */\nconst DEFAULT_SNAP_SOURCES: readonly SnapSource[] = [\n rotationSnapSource(),\n { ...pathSnapSource({ only: ['anchor'] }), gestures: ['point'] },\n];\n\n/** `listed`, which must not repeat an id, then each default whose id is\n * neither listed nor in `omit`. */\nfunction withDefaults(listed: readonly SnapSource[], omit: readonly string[] = []): SnapSource[] {\n assertSnapSourceIds(listed);\n const taken = new Set([...listed.map((s) => s.id), ...omit]);\n return [...listed, ...DEFAULT_SNAP_SOURCES.filter((s) => !taken.has(s.id))];\n}\n\nexport function useSnapDepSource(\n viewRef: React.RefObject<View | null>,\n session: SnapSessionSources,\n /** The canvas's outcome store, which its readout layer draws. */\n store: SnapOutcomeStore,\n): SnapOutcomeStore {\n const sessionRef = useLatest(session);\n const kitSettings = useKitSettings();\n useDepSource('snapReadout', (): SnapReadoutDep => ({ store }));\n\n useDepSource('snap', (): SnapDep => ({\n begin: (req) => {\n const s = sessionRef.current;\n const view = viewRef.current;\n const omit = new Set(req.omit);\n const sources = [\n ...withDefaults([...s.sources, ...(s.registry?.list() ?? [])], s.omit).filter((src) => !omit.has(src.id)),\n ...(req.sources ?? []),\n ];\n assertSnapSourceIds(sources);\n const wantsGeometry = snapSourcesFor(sources, req.gesture).some((src) => src.roles.includes('geometry'));\n const debug = s.debug?.() ?? undefined;\n const reachPx = s.reachPx ?? kitSettings.get('targets.snapPx') ?? DEFAULT_SNAP_PX;\n return sceneSession2d({\n req,\n scene: s.scene,\n boxes: s.boxes(),\n sources,\n ...(wantsGeometry ? { geometryAt: (press: Point, ids: readonly string[]) => ownGeometryAt(s.scene, press, ids, view?.scale ?? { x: 1, y: 1 }, reachPx) } : {}),\n view: () => viewRef.current,\n reachPx,\n ...(s.poseDescriptor ? { poseDescriptor: s.poseDescriptor } : {}),\n ...(s.originProjection ? { originProjection: s.originProjection } : {}),\n ...(debug ? { debug } : {}),\n store,\n });\n },\n }));\n return store;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqDA,IAAM,uBAA8C,CAClD,mBAAmB,GACnB;CAAE,GAAG,eAAe,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;CAAG,UAAU,CAAC,OAAO;AAAE,CACjE;;;AAIA,SAAS,aAAa,QAA+B,OAA0B,CAAC,GAAiB;CAC/F,oBAAoB,MAAM;CAC1B,MAAM,wBAAQ,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,MAAM,EAAE,EAAE,GAAG,GAAG,IAAI,CAAC;CAC3D,OAAO,CAAC,GAAG,QAAQ,GAAG,qBAAqB,QAAQ,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;AAC5E;AAEA,SAAgB,iBACd,SACA,SAEA,OACkB;CAClB,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,cAAc,eAAe;CACnC,aAAa,sBAAsC,EAAE,MAAM,EAAE;CAE7D,aAAa,eAAwB,EACnC,QAAQ,QAAQ;EACd,MAAM,IAAI,WAAW;EACrB,MAAM,OAAO,QAAQ;EACrB,MAAM,OAAO,IAAI,IAAI,IAAI,IAAI;EAC7B,MAAM,UAAU,CACd,GAAG,aAAa,CAAC,GAAG,EAAE,SAAS,GAAI,EAAE,UAAU,KAAK,KAAK,CAAC,CAAE,GAAG,EAAE,IAAI,CAAC,CAAC,QAAQ,QAAQ,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC,GACxG,GAAI,IAAI,WAAW,CAAC,CACtB;EACA,oBAAoB,OAAO;EAC3B,MAAM,gBAAgB,eAAe,SAAS,IAAI,OAAO,CAAC,CAAC,MAAM,QAAQ,IAAI,MAAM,SAAS,UAAU,CAAC;EACvG,MAAM,QAAQ,EAAE,QAAQ,KAAK;EAC7B,MAAM,UAAU,EAAE,WAAW,YAAY,IAAI,gBAAgB,KAAK;EAClE,OAAO,eAAe;GACpB;GACA,OAAO,EAAE;GACT,OAAO,EAAE,MAAM;GACf;GACA,GAAI,gBAAgB,EAAE,aAAa,OAAc,QAA2B,cAAc,EAAE,OAAO,OAAO,KAAK,MAAM,SAAS;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG,OAAO,EAAE,IAAI,CAAC;GAC5J,YAAY,QAAQ;GACpB;GACA,GAAI,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,IAAI,CAAC;GAC/D,GAAI,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,IAAI,CAAC;GACrE,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;GACzB;EACF,CAAC;CACH,EACF,EAAE;CACF,OAAO;AACT"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useDepSource } from "@weasel-js/routing/react";
|
|
2
|
+
|
|
3
|
+
//#region src/canvas/deps/transformFollowers.ts
|
|
4
|
+
/**
|
|
5
|
+
* Add a follower to the `transformFollowers` dep for the lifetime of the
|
|
6
|
+
* calling component, after every one already there in its scope and the
|
|
7
|
+
* scopes above. `follower` is read each time the dep is; answering null adds
|
|
8
|
+
* nothing.
|
|
9
|
+
*/
|
|
10
|
+
function useTransformFollower(follower) {
|
|
11
|
+
useDepSource("transformFollowers", (below) => {
|
|
12
|
+
const under = below() ?? [];
|
|
13
|
+
const mine = follower();
|
|
14
|
+
return mine === null ? under : [...under, mine];
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
export { useTransformFollower };
|
|
20
|
+
//# sourceMappingURL=transformFollowers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformFollowers.js","names":[],"sources":["../../../src/canvas/deps/transformFollowers.ts"],"sourcesContent":["import { useDepSource } from '@weasel-js/routing/react';\nimport type { TransformFollower } from 'interactions/actions/transformFinish';\n\n/**\n * Add a follower to the `transformFollowers` dep for the lifetime of the\n * calling component, after every one already there in its scope and the\n * scopes above. `follower` is read each time the dep is; answering null adds\n * nothing.\n */\nexport function useTransformFollower(follower: () => TransformFollower | null): void {\n useDepSource('transformFollowers', (below) => {\n const under = below() ?? [];\n const mine = follower();\n return mine === null ? under : [...under, mine];\n });\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,qBAAqB,UAAgD;CACnF,aAAa,uBAAuB,UAAU;EAC5C,MAAM,QAAQ,MAAM,KAAK,CAAC;EAC1B,MAAM,OAAO,SAAS;EACtB,OAAO,SAAS,OAAO,QAAQ,CAAC,GAAG,OAAO,IAAI;CAChD,CAAC;AACH"}
|
|
@@ -174,7 +174,26 @@ function withoutInheritedFrame(path, painted, own) {
|
|
|
174
174
|
});
|
|
175
175
|
return carryPathBy(path, redo === null ? undo : multiply(undo, redo));
|
|
176
176
|
}
|
|
177
|
+
var drawnScenes = /* @__PURE__ */ new WeakMap();
|
|
178
|
+
/** `scene` read as `drawnBoundsOf` reads one, each node that shows a computed
|
|
179
|
+
* path answering it. One object per scene, so it can key a memo. */
|
|
180
|
+
function drawnBoundsScene(scene) {
|
|
181
|
+
let held = drawnScenes.get(scene);
|
|
182
|
+
if (held === void 0) {
|
|
183
|
+
const pathOf = sceneDerivedPathOf(scene);
|
|
184
|
+
held = {
|
|
185
|
+
get: (id) => scene.get(id),
|
|
186
|
+
childrenOf: (id) => scene.childrenOf(id),
|
|
187
|
+
derivedPathOf: (id) => {
|
|
188
|
+
const node = scene.get(id);
|
|
189
|
+
return node !== void 0 && showsComputedPath(scene, node) ? pathOf(node) : null;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
drawnScenes.set(scene, held);
|
|
193
|
+
}
|
|
194
|
+
return held;
|
|
195
|
+
}
|
|
177
196
|
|
|
178
197
|
//#endregion
|
|
179
|
-
export { resolveDerivedPath, sceneDepLookup, sceneDerivedPathOf, scenePaintedPoseOf, sceneShownToPose, showsComputedPath, withDerivedPaths };
|
|
198
|
+
export { drawnBoundsScene, resolveDerivedPath, sceneDepLookup, sceneDerivedPathOf, scenePaintedPoseOf, sceneShownToPose, showsComputedPath, withDerivedPaths };
|
|
180
199
|
//# sourceMappingURL=derivedPath.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derivedPath.js","names":[],"sources":["../../src/canvas/derivedPath.ts"],"sourcesContent":["/**\n * The render walk's half of derived paths: resolving one against a scene, and\n * handing it to the painter.\n *\n * The resolver itself lives in `core/scene/derivedPath.ts`, beside\n * `derivedPose` — a *pose* can derive from a dependency's path, so\n * `effectivePose` has to be able to reach it.\n */\nimport type { DerivedDep, Node, NodeId, Scene } from 'core/scene/types';\nimport { multiply, poseFrameInverseOf, poseFrameOf, type Path } from '@weasel-js/geom';\nimport { asNodeId } from 'core/scene/types';\nimport { definesFrame, derivedDepOf, effectivePose } from 'core/scene/effectivePose';\nimport { composesFrames, composeWorldPose, type PoseAdapter } from 'core/scene/composeFrame';\nimport { resolveDerivedPath } from 'core/scene/derivedPath';\nimport { createNodeMemoScope, type NodeMemoScope } from 'core/scene/nodeMemo';\nimport { carryBoxPath, carryPathBy, pathAsPainted } from 'core/scene/depFrame';\nimport type { SceneViewDrawOne } from './NodeShape';\n\nexport { resolveDerivedPath } from 'core/scene/derivedPath';\n\ntype ToPose<TData, TLayer extends string, TPose> = (node: Node<TData, TLayer, TPose>) => TPose;\n\n/** `node` as the scene shows it, its `pose` an override's or a derived one: the stored node\n * itself where the two agree. */\nfunction shownNode<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n node: Node<TData, TLayer, TPose>,\n): Node<TData, TLayer, TPose> {\n const pose = effectivePose(scene, node);\n return pose === node.pose ? node : { ...node, pose };\n}\n\n/** A slot's `toPose`, handed each node as the scene shows it, so a view drawn through one\n * shows a drag in flight and a rig's motion as every other view does. */\nexport function sceneShownToPose<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose: ToPose<TData, TLayer, TPose>,\n): ToPose<TData, TLayer, TPose> {\n return (node) => toPose(shownNode(scene, node));\n}\n\n/**\n * `(node) => the path its pose override shows it by`, in the frame the reader paints in: as\n * the override has it, or under a `toPose` carried from the box the scene shows the node at\n * to the box `toPose` answers.\n */\nfunction sceneOverridePathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: ToPose<TData, TLayer, TPose>,\n): (node: Node<TData, TLayer, TPose>) => Path | undefined {\n if (toPose === undefined) return (node) => scene.overrides.read(node.id)?.path;\n return (node) => {\n const path = scene.overrides.read(node.id)?.path;\n if (path === undefined) return undefined;\n const shown = shownNode(scene, node);\n return carryBoxPath(path, shown.pose, toPose(shown));\n };\n}\n\n/**\n * `(node) => the pose it is painted at`: the slot's `toPose` of the node as the scene shows\n * it if it has one, else the scene's effective pose, folded through the parent chain when\n * the scene's composition makes a stored pose local to its container.\n */\nexport function scenePaintedPoseOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => TPose {\n const local = toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node);\n if (!composesFrames(scene.composition)) return local;\n const chain: PoseAdapter<TPose> = {\n getPose: (id) => {\n const n = scene.get(asNodeId(id));\n return n ? local(n) : (undefined as unknown as TPose);\n },\n getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,\n definesFrame: (id) => {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n };\n return (node) => composeWorldPose(chain, node.id, scene.composition.compose);\n}\n\n/**\n * `(id) => that node, the pose it is painted at, and the path it derives` —\n * resolved against the scene, so a dependency that is itself derived resolves\n * before it is read.\n *\n * A derived edge is drawn from these coordinates and must meet the nodes it\n * connects, so this reads the pose each one is painted at: see\n * {@link scenePaintedPoseOf}.\n */\nexport function sceneDepLookup<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (id: NodeId) => DerivedDep<TPose> | undefined {\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n if (slot === undefined) return (id) => derivedDepOf(scene, id);\n const ctx: SlotLookup<TPose> = {\n at: scenePaintedPoseOf(scene, toPose) as (node: unknown) => TPose,\n local: (toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node)) as (node: unknown) => TPose,\n slot,\n shown: sceneOverridePathOf(scene, toPose) as (node: unknown) => Path | undefined,\n childrenOf: (id: NodeId) => scene.childrenOf(id),\n lookup: (id: NodeId) => {\n const node = scene.get(id);\n return node === undefined ? undefined : new SlotDep(node, ctx);\n },\n };\n return ctx.lookup;\n}\n\ninterface SlotLookup<TPose> {\n at: (node: unknown) => TPose;\n /** The pose the node is shown at in its own frame: the box its override path is drawn in. */\n local: (node: unknown) => TPose;\n slot: string | NodeMemoScope;\n /** The path a pose override shows a node by, where it has one. */\n shown: (node: unknown) => Path | undefined;\n childrenOf: (id: NodeId) => readonly NodeId[];\n lookup: (id: NodeId) => DerivedDep<TPose> | undefined;\n}\n\n/** A class, not an object literal with a `get path()` — see `SceneDep` in\n * `core/scene/effectivePose.ts` for why. */\nclass SlotDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(posed: unknown, private readonly ctx: SlotLookup<TPose>) {\n this.node = posed as Node<unknown, string, TPose>;\n this.pose = ctx.at(posed);\n }\n\n get path(): Path | null {\n const shown = this.ctx.shown(this.node);\n if (shown !== undefined) return pathAsPainted(shown, this.ctx.local(this.node), this.pose);\n return resolveDerivedPath(\n this.node as never, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot,\n );\n }\n}\n\n/** Whether something outside `node`'s data says what path it shows: it derives one, or a\n * pose override carries one. The gate both scene walks check before resolving a path. */\nexport function showsComputedPath<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n node: { id: NodeId; dependsOn?: readonly NodeId[] | 'children' },\n): boolean {\n return node.dependsOn !== undefined || scene.overrides.read(node.id)?.path !== undefined;\n}\n\n/**\n * `(node) => the path it shows in place of its data's`: a pose override's `path`, else the\n * one it derives, read through {@link sceneDepLookup}. A reader painting through a `toPose`\n * is answered the override's path carried into the box its `toPose` answers.\n */\nexport function sceneDerivedPathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => Path | null {\n const depOf = sceneDepLookup(scene, toPose);\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n const childrenOf = (id: NodeId): readonly NodeId[] => scene.childrenOf(id);\n const derived = (node: Node<TData, TLayer, TPose>): Path | null =>\n resolveDerivedPath(node, depOf, childrenOf, slot);\n const overridePathOf = sceneOverridePathOf(scene, toPose);\n return (node) => overridePathOf(node) ?? derived(node);\n}\n\nconst toPoseScopes = new WeakMap<object, { local?: NodeMemoScope; world?: NodeMemoScope }>();\n\n/** Where one way of reading poses memoizes its paths, or `undefined` for the\n * scene's own slot: two readers painting one scene at different poses must\n * not serve each other's paths. Every reader composing stored poses shares a\n * slot; a `toPose` gets a scope that is collected with the function. */\nfunction memoSlotFor(\n toPose: object | undefined,\n composes: boolean,\n): string | NodeMemoScope | undefined {\n const frame = composes ? 'world' : 'local';\n if (toPose === undefined) return frame === 'world' ? 'kit:derivedPath:world' : undefined;\n let scopes = toPoseScopes.get(toPose);\n if (scopes === undefined) {\n scopes = {};\n toPoseScopes.set(toPose, scopes);\n }\n return (scopes[frame] ??= createNodeMemoScope());\n}\n\n/**\n * Wrap a `drawOne` so a node that derives its geometry, or whose pose override\n * carries a path, arrives at the painter with the resolved path. The painter has no scene handle and so cannot read\n * the dependencies' poses itself; both scene walks wrap here instead.\n *\n * A node that derives nothing passes the caller's `ctx` through untouched —\n * this runs per node per frame, and an added `{ derivedPath: null }` would be\n * an allocation per node for a field no painter reads.\n */\nexport function withDerivedPaths<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const derivedPathOf = sceneDerivedPathOf(scene, toPose);\n const ownPoseOf = composesFrames(scene.composition)\n ? (toPose ? sceneShownToPose(scene, toPose) : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node))\n : null;\n return (node, pose, view, ctx) => {\n if (!showsComputedPath(scene, node)) return drawOne(node, pose, view, ctx);\n let path = derivedPathOf(node);\n if (path !== null && ownPoseOf !== null && scene.overrides.read(node.id)?.path === undefined) {\n path = withoutInheritedFrame(path, pose, ownPoseOf(node));\n }\n return drawOne(node, pose, view, { ...ctx, derivedPath: path });\n };\n}\n\n/**\n * A derived path is drawn from its dependencies' painted poses, so it is already where it\n * shows; but the pose rotation the walk wraps every node in is `painted`'s, which carries its\n * containers' turns as well as its own. Carried back through what the containers add, the\n * wrap turns it by its own frame alone, as it does a root's.\n */\nfunction withoutInheritedFrame(path: Path, painted: unknown, own: unknown): Path {\n const undo = poseFrameInverseOf(painted);\n if (undo === null) return path;\n const box = painted as { x: number; y: number; width: number; height: number };\n const o = own as { rotation?: number; transform?: unknown } | null;\n const redo = poseFrameOf({ x: box.x, y: box.y, width: box.width, height: box.height, rotation: o?.rotation, transform: o?.transform });\n return carryPathBy(path, redo === null ? undo : multiply(undo, redo));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"derivedPath.js","names":[],"sources":["../../src/canvas/derivedPath.ts"],"sourcesContent":["/**\n * The render walk's half of derived paths: resolving one against a scene, and\n * handing it to the painter.\n *\n * The resolver itself lives in `core/scene/derivedPath.ts`, beside\n * `derivedPose` — a *pose* can derive from a dependency's path, so\n * `effectivePose` has to be able to reach it.\n */\nimport type { DerivedDep, Node, NodeId, Scene } from 'core/scene/types';\nimport { multiply, poseFrameInverseOf, poseFrameOf, type Path } from '@weasel-js/geom';\nimport { asNodeId } from 'core/scene/types';\nimport { definesFrame, derivedDepOf, effectivePose } from 'core/scene/effectivePose';\nimport { composesFrames, composeWorldPose, type PoseAdapter } from 'core/scene/composeFrame';\nimport { resolveDerivedPath } from 'core/scene/derivedPath';\nimport { createNodeMemoScope, type NodeMemoScope } from 'core/scene/nodeMemo';\nimport { carryBoxPath, carryPathBy, pathAsPainted } from 'core/scene/depFrame';\nimport type { SceneViewDrawOne } from './NodeShape';\nimport type { DrawnBoundsScene } from './drawnBounds';\n\nexport { resolveDerivedPath } from 'core/scene/derivedPath';\n\ntype ToPose<TData, TLayer extends string, TPose> = (node: Node<TData, TLayer, TPose>) => TPose;\n\n/** `node` as the scene shows it, its `pose` an override's or a derived one: the stored node\n * itself where the two agree. */\nfunction shownNode<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n node: Node<TData, TLayer, TPose>,\n): Node<TData, TLayer, TPose> {\n const pose = effectivePose(scene, node);\n return pose === node.pose ? node : { ...node, pose };\n}\n\n/** A slot's `toPose`, handed each node as the scene shows it, so a view drawn through one\n * shows a drag in flight and a rig's motion as every other view does. */\nexport function sceneShownToPose<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose: ToPose<TData, TLayer, TPose>,\n): ToPose<TData, TLayer, TPose> {\n return (node) => toPose(shownNode(scene, node));\n}\n\n/**\n * `(node) => the path its pose override shows it by`, in the frame the reader paints in: as\n * the override has it, or under a `toPose` carried from the box the scene shows the node at\n * to the box `toPose` answers.\n */\nfunction sceneOverridePathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: ToPose<TData, TLayer, TPose>,\n): (node: Node<TData, TLayer, TPose>) => Path | undefined {\n if (toPose === undefined) return (node) => scene.overrides.read(node.id)?.path;\n return (node) => {\n const path = scene.overrides.read(node.id)?.path;\n if (path === undefined) return undefined;\n const shown = shownNode(scene, node);\n return carryBoxPath(path, shown.pose, toPose(shown));\n };\n}\n\n/**\n * `(node) => the pose it is painted at`: the slot's `toPose` of the node as the scene shows\n * it if it has one, else the scene's effective pose, folded through the parent chain when\n * the scene's composition makes a stored pose local to its container.\n */\nexport function scenePaintedPoseOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => TPose {\n const local = toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node);\n if (!composesFrames(scene.composition)) return local;\n const chain: PoseAdapter<TPose> = {\n getPose: (id) => {\n const n = scene.get(asNodeId(id));\n return n ? local(n) : (undefined as unknown as TPose);\n },\n getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,\n definesFrame: (id) => {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n };\n return (node) => composeWorldPose(chain, node.id, scene.composition.compose);\n}\n\n/**\n * `(id) => that node, the pose it is painted at, and the path it derives` —\n * resolved against the scene, so a dependency that is itself derived resolves\n * before it is read.\n *\n * A derived edge is drawn from these coordinates and must meet the nodes it\n * connects, so this reads the pose each one is painted at: see\n * {@link scenePaintedPoseOf}.\n */\nexport function sceneDepLookup<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (id: NodeId) => DerivedDep<TPose> | undefined {\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n if (slot === undefined) return (id) => derivedDepOf(scene, id);\n const ctx: SlotLookup<TPose> = {\n at: scenePaintedPoseOf(scene, toPose) as (node: unknown) => TPose,\n local: (toPose\n ? sceneShownToPose(scene, toPose)\n : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node)) as (node: unknown) => TPose,\n slot,\n shown: sceneOverridePathOf(scene, toPose) as (node: unknown) => Path | undefined,\n childrenOf: (id: NodeId) => scene.childrenOf(id),\n lookup: (id: NodeId) => {\n const node = scene.get(id);\n return node === undefined ? undefined : new SlotDep(node, ctx);\n },\n };\n return ctx.lookup;\n}\n\ninterface SlotLookup<TPose> {\n at: (node: unknown) => TPose;\n /** The pose the node is shown at in its own frame: the box its override path is drawn in. */\n local: (node: unknown) => TPose;\n slot: string | NodeMemoScope;\n /** The path a pose override shows a node by, where it has one. */\n shown: (node: unknown) => Path | undefined;\n childrenOf: (id: NodeId) => readonly NodeId[];\n lookup: (id: NodeId) => DerivedDep<TPose> | undefined;\n}\n\n/** A class, not an object literal with a `get path()` — see `SceneDep` in\n * `core/scene/effectivePose.ts` for why. */\nclass SlotDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(posed: unknown, private readonly ctx: SlotLookup<TPose>) {\n this.node = posed as Node<unknown, string, TPose>;\n this.pose = ctx.at(posed);\n }\n\n get path(): Path | null {\n const shown = this.ctx.shown(this.node);\n if (shown !== undefined) return pathAsPainted(shown, this.ctx.local(this.node), this.pose);\n return resolveDerivedPath(\n this.node as never, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot,\n );\n }\n}\n\n/** Whether something outside `node`'s data says what path it shows: it derives one, or a\n * pose override carries one. The gate both scene walks check before resolving a path. */\nexport function showsComputedPath<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n node: { id: NodeId; dependsOn?: readonly NodeId[] | 'children' },\n): boolean {\n return node.dependsOn !== undefined || scene.overrides.read(node.id)?.path !== undefined;\n}\n\n/**\n * `(node) => the path it shows in place of its data's`: a pose override's `path`, else the\n * one it derives, read through {@link sceneDepLookup}. A reader painting through a `toPose`\n * is answered the override's path carried into the box its `toPose` answers.\n */\nexport function sceneDerivedPathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => Path | null {\n const depOf = sceneDepLookup(scene, toPose);\n const slot = memoSlotFor(toPose, composesFrames(scene.composition));\n const childrenOf = (id: NodeId): readonly NodeId[] => scene.childrenOf(id);\n const derived = (node: Node<TData, TLayer, TPose>): Path | null =>\n resolveDerivedPath(node, depOf, childrenOf, slot);\n const overridePathOf = sceneOverridePathOf(scene, toPose);\n return (node) => overridePathOf(node) ?? derived(node);\n}\n\nconst toPoseScopes = new WeakMap<object, { local?: NodeMemoScope; world?: NodeMemoScope }>();\n\n/** Where one way of reading poses memoizes its paths, or `undefined` for the\n * scene's own slot: two readers painting one scene at different poses must\n * not serve each other's paths. Every reader composing stored poses shares a\n * slot; a `toPose` gets a scope that is collected with the function. */\nfunction memoSlotFor(\n toPose: object | undefined,\n composes: boolean,\n): string | NodeMemoScope | undefined {\n const frame = composes ? 'world' : 'local';\n if (toPose === undefined) return frame === 'world' ? 'kit:derivedPath:world' : undefined;\n let scopes = toPoseScopes.get(toPose);\n if (scopes === undefined) {\n scopes = {};\n toPoseScopes.set(toPose, scopes);\n }\n return (scopes[frame] ??= createNodeMemoScope());\n}\n\n/**\n * Wrap a `drawOne` so a node that derives its geometry, or whose pose override\n * carries a path, arrives at the painter with the resolved path. The painter has no scene handle and so cannot read\n * the dependencies' poses itself; both scene walks wrap here instead.\n *\n * A node that derives nothing passes the caller's `ctx` through untouched —\n * this runs per node per frame, and an added `{ derivedPath: null }` would be\n * an allocation per node for a field no painter reads.\n */\nexport function withDerivedPaths<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const derivedPathOf = sceneDerivedPathOf(scene, toPose);\n const ownPoseOf = composesFrames(scene.composition)\n ? (toPose ? sceneShownToPose(scene, toPose) : (node: Node<TData, TLayer, TPose>) => effectivePose(scene, node))\n : null;\n return (node, pose, view, ctx) => {\n if (!showsComputedPath(scene, node)) return drawOne(node, pose, view, ctx);\n let path = derivedPathOf(node);\n if (path !== null && ownPoseOf !== null && scene.overrides.read(node.id)?.path === undefined) {\n path = withoutInheritedFrame(path, pose, ownPoseOf(node));\n }\n return drawOne(node, pose, view, { ...ctx, derivedPath: path });\n };\n}\n\n/**\n * A derived path is drawn from its dependencies' painted poses, so it is already where it\n * shows; but the pose rotation the walk wraps every node in is `painted`'s, which carries its\n * containers' turns as well as its own. Carried back through what the containers add, the\n * wrap turns it by its own frame alone, as it does a root's.\n */\nfunction withoutInheritedFrame(path: Path, painted: unknown, own: unknown): Path {\n const undo = poseFrameInverseOf(painted);\n if (undo === null) return path;\n const box = painted as { x: number; y: number; width: number; height: number };\n const o = own as { rotation?: number; transform?: unknown } | null;\n const redo = poseFrameOf({ x: box.x, y: box.y, width: box.width, height: box.height, rotation: o?.rotation, transform: o?.transform });\n return carryPathBy(path, redo === null ? undo : multiply(undo, redo));\n}\n\nconst drawnScenes = new WeakMap<object, DrawnBoundsScene>();\n\n/** `scene` read as `drawnBoundsOf` reads one, each node that shows a computed\n * path answering it. One object per scene, so it can key a memo. */\nexport function drawnBoundsScene<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): DrawnBoundsScene {\n let held = drawnScenes.get(scene);\n if (held === undefined) {\n const pathOf = sceneDerivedPathOf(scene);\n held = {\n get: (id) => scene.get(id) as never,\n childrenOf: (id) => scene.childrenOf(id),\n derivedPathOf: (id) => {\n const node = scene.get(id);\n return node !== undefined && showsComputedPath(scene, node) ? pathOf(node) : null;\n },\n };\n drawnScenes.set(scene, held);\n }\n return held;\n}\n"],"mappings":"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"}
|