@weasel-js/core 1.11.1 → 1.13.0-rc.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 +275 -0
- package/dist/WeaselProvider.js +1 -1
- package/dist/affordances/pathAnchors.js +10 -6
- package/dist/affordances/pathAnchors.js.map +1 -1
- package/dist/animation/poseTarget.js +46 -15
- package/dist/animation/poseTarget.js.map +1 -1
- package/dist/animation/poseTransition.js +3 -3
- package/dist/animation/poseTransition.js.map +1 -1
- package/dist/animation/reflow.js +3 -6
- package/dist/animation/reflow.js.map +1 -1
- package/dist/animation/rig/bindRig.js +2 -2
- package/dist/animation/rig/bindRig.js.map +1 -1
- package/dist/animation/rig/skinBinding.js +2 -2
- package/dist/animation/rig/skinBinding.js.map +1 -1
- package/dist/barrel/canvas.js +5 -1
- package/dist/barrel/chrome.js +3 -1
- package/dist/barrel/paint.js +4 -4
- package/dist/barrel/paths.js +9 -4
- package/dist/barrel/renderer.js +3 -2
- package/dist/barrel/routing.js +4 -4
- package/dist/barrel/scene.js +7 -1
- package/dist/barrel/text.js +2 -2
- package/dist/barrel/viewport.js +6 -2
- package/dist/canvas/Canvas.js +2 -1
- 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 +5 -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 +2 -0
- 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/canvasPassThrough.js +2 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/props.js.map +1 -1
- package/dist/canvas/SceneCanvas/scenePointQuery.js +27 -5
- package/dist/canvas/SceneCanvas/scenePointQuery.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/useCanvasDevtoolsRoot.js +1 -0
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +10 -2
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.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 +17 -2
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -0
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
- package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
- package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +7 -4
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +30 -12
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/ViewMarker.js +35 -0
- package/dist/canvas/ViewMarker.js.map +1 -0
- package/dist/canvas/accentColor.js +26 -3
- package/dist/canvas/accentColor.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +14 -20
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/copiesAbove.js +1 -1
- package/dist/canvas/copiesAbove.js.map +1 -1
- package/dist/canvas/defaultDrawOne.js +11 -6
- package/dist/canvas/defaultDrawOne.js.map +1 -1
- package/dist/canvas/deps/areaSelect.js +5 -2
- package/dist/canvas/deps/areaSelect.js.map +1 -1
- package/dist/canvas/deps/booleans.js +3 -2
- package/dist/canvas/deps/booleans.js.map +1 -1
- package/dist/canvas/deps/enteredGroup.js +81 -0
- package/dist/canvas/deps/enteredGroup.js.map +1 -0
- package/dist/canvas/deps/hitTestArea.js +28 -16
- package/dist/canvas/deps/hitTestArea.js.map +1 -1
- package/dist/canvas/deps/insert.js +10 -38
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/insertSite.js +39 -0
- package/dist/canvas/deps/insertSite.js.map +1 -0
- package/dist/canvas/deps/lassoSelect.js +6 -3
- package/dist/canvas/deps/lassoSelect.js.map +1 -1
- package/dist/canvas/deps/pathEditCommits.js +3 -7
- package/dist/canvas/deps/pathEditCommits.js.map +1 -1
- package/dist/canvas/deps/pathLeaf.js +1 -1
- package/dist/canvas/deps/pathLeaf.js.map +1 -1
- package/dist/canvas/deps/slice.js +2 -1
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/derivedPath.js +14 -6
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/doubleClickTarget.js +9 -11
- package/dist/canvas/doubleClickTarget.js.map +1 -1
- package/dist/canvas/drawnBounds.js +26 -9
- package/dist/canvas/drawnBounds.js.map +1 -1
- package/dist/canvas/inheritedPaints.js +55 -0
- package/dist/canvas/inheritedPaints.js.map +1 -0
- package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
- package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/nodePaint.js +7 -34
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
- package/dist/canvas/nodeShapes/pathPainter.js +7 -7
- 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 +9 -9
- 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/ownClip.js +24 -0
- package/dist/canvas/ownClip.js.map +1 -0
- package/dist/canvas/paintCull.js.map +1 -1
- package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
- package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
- package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
- package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
- package/dist/canvas/pickReach.js +3 -3
- package/dist/canvas/pickReach.js.map +1 -1
- package/dist/canvas/pickWalk.js +17 -91
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/poseCascade.js +1 -1
- package/dist/canvas/poseCascade.js.map +1 -1
- package/dist/canvas/publishPreviews.js +97 -0
- package/dist/canvas/publishPreviews.js.map +1 -0
- package/dist/canvas/renderSceneToPixels.js +1 -1
- package/dist/canvas/renderSceneToPixels.js.map +1 -1
- package/dist/canvas/sceneAdapter.js +7 -5
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +17 -10
- package/dist/canvas/sceneClipboard.js.map +1 -1
- package/dist/canvas/scenePickReads.js +37 -0
- package/dist/canvas/scenePickReads.js.map +1 -0
- package/dist/canvas/scenePickSource.js +74 -0
- package/dist/canvas/scenePickSource.js.map +1 -0
- package/dist/canvas/sceneSlotWiring.js +9 -3
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +9 -4
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/shownNodes.js +15 -0
- package/dist/canvas/shownNodes.js.map +1 -0
- package/dist/canvas/surfaceContribution.js +12 -8
- package/dist/canvas/surfaceContribution.js.map +1 -1
- package/dist/canvas/useCanvasView.js +4 -2
- package/dist/canvas/useCanvasView.js.map +1 -1
- package/dist/canvas/useLayerStack.js +6 -2
- package/dist/canvas/useLayerStack.js.map +1 -1
- package/dist/canvas/useSceneAdapter.js +5 -3
- package/dist/canvas/useSceneAdapter.js.map +1 -1
- package/dist/canvas/useViewAim.js +121 -0
- package/dist/canvas/useViewAim.js.map +1 -0
- 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 +2 -2
- package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
- package/dist/clone.d.ts +3 -3
- package/dist/core/isPlainObject.js +2 -2
- package/dist/core/layers/layerGroup.js.map +1 -1
- package/dist/core/pages/pageEdits.js +2 -3
- package/dist/core/pages/pageEdits.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.map +1 -1
- package/dist/core/scene/arrivalWindow.js +1 -1
- package/dist/core/scene/arrivalWindow.js.map +1 -1
- package/dist/core/scene/clipNothing.js +10 -0
- package/dist/core/scene/clipNothing.js.map +1 -0
- package/dist/core/scene/declaredFieldSetters.js +44 -0
- package/dist/core/scene/declaredFieldSetters.js.map +1 -0
- package/dist/core/scene/depMemo.js +14 -3
- package/dist/core/scene/depMemo.js.map +1 -1
- package/dist/core/scene/dependents.js +9 -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/documentOf.js +43 -0
- package/dist/core/scene/documentOf.js.map +1 -0
- package/dist/core/scene/effectivePose.js +44 -15
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/fieldEdits.js +17 -14
- 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/historyForm.js +4 -4
- package/dist/core/scene/historyForm.js.map +1 -1
- package/dist/core/scene/inheritedPaint.js +97 -0
- package/dist/core/scene/inheritedPaint.js.map +1 -0
- package/dist/core/scene/kitOps.js +3 -23
- package/dist/core/scene/kitOps.js.map +1 -1
- package/dist/core/scene/kitRegistry.js +5 -0
- package/dist/core/scene/kitRegistry.js.map +1 -1
- package/dist/core/scene/layoutPass.js +1 -2
- package/dist/core/scene/layoutPass.js.map +1 -1
- package/dist/core/scene/nodeAsShown.js +72 -0
- package/dist/core/scene/nodeAsShown.js.map +1 -0
- package/dist/core/scene/nodeFnFields.js +2 -10
- package/dist/core/scene/nodeFnFields.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +21 -8
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/nodeStore.js +2 -4
- package/dist/core/scene/nodeStore.js.map +1 -1
- package/dist/core/scene/pathEffects/blend.js.map +1 -1
- package/dist/core/scene/pathEffects/derive.js +10 -4
- 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/foundHidden.js +35 -0
- package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
- package/dist/core/scene/pathEffects/textAlong.js +95 -0
- package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
- package/dist/core/scene/plyStacks.js +89 -0
- package/dist/core/scene/plyStacks.js.map +1 -0
- package/dist/core/scene/poseHold.js +42 -24
- package/dist/core/scene/poseHold.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +35 -38
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/posePlies.js +59 -0
- package/dist/core/scene/posePlies.js.map +1 -0
- package/dist/core/scene/reconcileSpecs.js +6 -15
- package/dist/core/scene/reconcileSpecs.js.map +1 -1
- package/dist/core/scene/renderOrder.js +10 -4
- package/dist/core/scene/renderOrder.js.map +1 -1
- package/dist/core/scene/sameValue.js +17 -0
- package/dist/core/scene/sameValue.js.map +1 -0
- package/dist/core/scene/scene.js +3 -3
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneEdits.js +8 -0
- package/dist/core/scene/sceneEdits.js.map +1 -1
- package/dist/core/scene/sceneState.js +1 -2
- package/dist/core/scene/sceneState.js.map +1 -1
- package/dist/core/scene/serialization.js +45 -9
- package/dist/core/scene/serialization.js.map +1 -1
- package/dist/core/scene/staleReplay.js +5 -2
- package/dist/core/scene/staleReplay.js.map +1 -1
- package/dist/core/scene/statedFields.js +4 -2
- package/dist/core/scene/statedFields.js.map +1 -1
- package/dist/core/scene/treeEdits.js +2 -4
- package/dist/core/scene/treeEdits.js.map +1 -1
- package/dist/core/scene/treeOrder.js +3 -1
- package/dist/core/scene/treeOrder.js.map +1 -1
- package/dist/core/scene/treeReads.js +1 -2
- package/dist/core/scene/treeReads.js.map +1 -1
- package/dist/core/scene/treeShape.js +13 -0
- package/dist/core/scene/treeShape.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/useScene.js +1 -1
- package/dist/core/scene/useScene.js.map +1 -1
- package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-CNCb7aTY.d.ts} +3 -2
- package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-B1RS-IJx.d.ts} +39 -18
- package/dist/devtools/handlers.js +1 -1
- package/dist/devtools/handlers.js.map +1 -1
- package/dist/devtools/protocol.js.map +1 -1
- package/dist/devtools/registry.js +4 -4
- package/dist/devtools/registry.js.map +1 -1
- package/dist/devtools-protocol.d.ts +4 -3
- package/dist/devtools.d.ts +3 -2
- package/dist/features/measure/measureContribution.js +4 -2
- package/dist/features/measure/measureContribution.js.map +1 -1
- package/dist/features/measure/measureHit.js +5 -3
- package/dist/features/measure/measureHit.js.map +1 -1
- package/dist/features/measure/measurement.js +1 -1
- package/dist/features/measure/measurement.js.map +1 -1
- package/dist/features/modes/modeLayers.js +14 -7
- package/dist/features/modes/modeLayers.js.map +1 -1
- package/dist/features/modes/modeTriggers.js +10 -6
- package/dist/features/modes/modeTriggers.js.map +1 -1
- package/dist/features/pathEffects/effectSources.js +69 -0
- package/dist/features/pathEffects/effectSources.js.map +1 -0
- package/dist/features/pathEffects/readSource.js +18 -2
- 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 +53 -0
- package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
- package/dist/features/paths/editablePath.js +1 -1
- package/dist/features/paths/editablePath.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/sceneIndex.js +1 -1
- package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
- package/dist/features/selection/enteredGroupFrame.js +65 -0
- package/dist/features/selection/enteredGroupFrame.js.map +1 -0
- package/dist/features/selection/enteredGroupScope.js +44 -0
- package/dist/features/selection/enteredGroupScope.js.map +1 -0
- package/dist/features/text/sceneTextDrafts.js +64 -0
- package/dist/features/text/sceneTextDrafts.js.map +1 -0
- package/dist/features/text/textHitAt.js +34 -3
- package/dist/features/text/textHitAt.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +23 -19
- 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/index.d.ts +709 -258
- package/dist/index.js +33 -12
- package/dist/interactions/actions/admitsChild.js +11 -0
- package/dist/interactions/actions/admitsChild.js.map +1 -0
- 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/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 +7 -4
- 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 +33 -11
- package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
- package/dist/interactions/actions/defaults/createPaintAction.js +5 -2
- package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
- package/dist/interactions/actions/defaults/delete.js +28 -14
- package/dist/interactions/actions/defaults/delete.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +11 -8
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/enterGroup.js +88 -0
- package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
- package/dist/interactions/actions/defaults/enterPathEdit.js +20 -10
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
- package/dist/interactions/actions/defaults/enterTextEdit.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 +26 -20
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +10 -4
- 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 +8 -2
- 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 +15 -8
- package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
- package/dist/interactions/actions/defaults/selectDeep.js +35 -0
- package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
- 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/drawnBoxes.js +2 -1
- package/dist/interactions/actions/drawnBoxes.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/action.js +7 -4
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/flip/action.js +8 -2
- package/dist/interactions/actions/flip/action.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +1 -0
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/parent.js +7 -5
- package/dist/interactions/actions/insert/parent.js.map +1 -1
- package/dist/interactions/actions/insert/placeAction.js +3 -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 +2 -2
- package/dist/interactions/actions/insert/session.js.map +1 -1
- package/dist/interactions/actions/move/action.js +7 -4
- package/dist/interactions/actions/move/action.js.map +1 -1
- package/dist/interactions/actions/move/commit/reparent.js +1 -1
- package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
- package/dist/interactions/actions/move/gestureAdapter.js +6 -7
- package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
- package/dist/interactions/actions/move/layout/exits.js +1 -1
- package/dist/interactions/actions/move/layout/exits.js.map +1 -1
- package/dist/interactions/actions/move/session.js +4 -4
- package/dist/interactions/actions/move/session.js.map +1 -1
- 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/pathCommands/pathNodes.js +14 -5
- package/dist/interactions/actions/pathCommands/pathNodes.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/selectionSubtree.js +12 -7
- package/dist/interactions/actions/selectionSubtree.js.map +1 -1
- package/dist/interactions/actions/transform-again/action.js +8 -5
- package/dist/interactions/actions/transform-again/action.js.map +1 -1
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
- package/dist/json.d.ts +4 -0
- package/dist/json.js +3 -0
- package/dist/kitSettingsSchema.js +8 -1
- package/dist/kitSettingsSchema.js.map +1 -1
- package/dist/{math-CrJULaZI.d.ts → math-C_elShP_.d.ts} +7 -1
- package/dist/math.d.ts +1 -1
- package/dist/math.js +2 -1
- package/dist/move.d.ts +4 -4
- package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
- package/dist/resize.d.ts +4 -4
- package/dist/test-seams.d.ts +6 -6
- 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/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/pressTarget.js +22 -0
- package/dist/tools/builtin/pressTarget.js.map +1 -0
- 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 +6 -5
- package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
- package/dist/tools/builtin/select/collapseDeferred.js +40 -0
- package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
- package/dist/tools/builtin/select/groupEntry.js +82 -0
- package/dist/tools/builtin/select/groupEntry.js.map +1 -0
- 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 +74 -55
- 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/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
- package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
- package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
- package/dist/util/paint.js +1 -3
- package/dist/util/paint.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +29 -25
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"areaSelect.js","names":["hitTestAreaShared"],"sources":["../../../src/canvas/deps/areaSelect.ts"],"sourcesContent":["/**\n * `useAreaSelectDepSource` — wires the `areaSelect` dep consumed by\n * `areaSelectAction`. The dep exposes:\n * - `hitTestArea(bounds)` — silhouette-aware scan over `scene.renderOrderNodes()`\n * (rect poses by AABB; polygon poses by the kernel polygon-overlap test).\n * - `getSelection` / `setSelection` — passthrough to the kit selection api.\n *\n * Built once per render and stabilised by `useDepSource` (which reads via a\n * ref internally), so callers can pass fresh selection/scene refs without\n * triggering re-registration.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { AreaSelectDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { hitTestArea as hitTestAreaShared, regionPickOptions } from './hitTestArea';\n\nexport function useAreaSelectDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n descriptor?: PoseDescriptor<unknown>,\n /** The painted alpha the surface's layers apply, which every view shares. */\n alphaOf?: (id: string) => number,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const descriptorRef = useLatest(descriptor);\n const alphaOfRef = useLatest(alphaOf);\n\n useDepSource('areaSelect', (): AreaSelectDep => {\n const sc = sceneRef.current;\n const s = selectionRef.current;\n const d = descriptorRef.current;\n const a = alphaOfRef.current;\n return {\n hitTestArea: (bounds, view) =>\n hitTestAreaShared(sc, bounds, regionPickOptions(view, a), d),\n getSelection: () => s.current as NodeId[],\n setSelection: (ids) => s.set(ids),\n };\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,SAAgB,uBACd,OACA,WACA,YAEA,
|
|
1
|
+
{"version":3,"file":"areaSelect.js","names":["hitTestAreaShared"],"sources":["../../../src/canvas/deps/areaSelect.ts"],"sourcesContent":["/**\n * `useAreaSelectDepSource` — wires the `areaSelect` dep consumed by\n * `areaSelectAction`. The dep exposes:\n * - `hitTestArea(bounds)` — silhouette-aware scan over `scene.renderOrderNodes()`\n * (rect poses by AABB; polygon poses by the kernel polygon-overlap test).\n * - `getSelection` / `setSelection` — passthrough to the kit selection api.\n *\n * Built once per render and stabilised by `useDepSource` (which reads via a\n * ref internally), so callers can pass fresh selection/scene refs without\n * triggering re-registration.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { AreaSelectDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { hitTestArea as hitTestAreaShared, regionPickOptions } from './hitTestArea';\n\nexport function useAreaSelectDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n descriptor?: PoseDescriptor<unknown>,\n /** The painted alpha the surface's layers apply, which every view shares. */\n alphaOf?: (id: string) => number,\n /** Whether a node can be reached at all: the canvas's one answer, which picking reads too. */\n reachable?: (id: string) => boolean,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const descriptorRef = useLatest(descriptor);\n const alphaOfRef = useLatest(alphaOf);\n const reachableRef = useLatest(reachable);\n\n useDepSource('areaSelect', (): AreaSelectDep => {\n const sc = sceneRef.current;\n const s = selectionRef.current;\n const d = descriptorRef.current;\n const a = alphaOfRef.current;\n const r = reachableRef.current;\n const keep = (ids: NodeId[]): NodeId[] => (r ? ids.filter((id) => r(id)) : ids);\n return {\n hitTestArea: (bounds, view) =>\n keep(hitTestAreaShared(sc, bounds, regionPickOptions(view, a), d)),\n getSelection: () => s.current as NodeId[],\n setSelection: (ids) => s.set(ids),\n };\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,SAAgB,uBACd,OACA,WACA,YAEA,SAEA,WACM;CACN,MAAM,WAAW,UAAU,KAAK;CAChC,MAAM,eAAe,UAAU,SAAS;CACxC,MAAM,gBAAgB,UAAU,UAAU;CAC1C,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,eAAe,UAAU,SAAS;CAExC,aAAa,oBAAmC;EAC9C,MAAM,KAAK,SAAS;EACpB,MAAM,IAAI,aAAa;EACvB,MAAM,IAAI,cAAc;EACxB,MAAM,IAAI,WAAW;EACrB,MAAM,IAAI,aAAa;EACvB,MAAM,QAAQ,QAA6B,IAAI,IAAI,QAAQ,OAAO,EAAE,EAAE,CAAC,IAAI;EAC3E,OAAO;GACL,cAAc,QAAQ,SACpB,KAAKA,YAAkB,IAAI,QAAQ,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC;GACnE,oBAAoB,EAAE;GACtB,eAAe,QAAQ,EAAE,IAAI,GAAG;EAClC;CACF,CAAC;AACH"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { poseDescriptorForNode } from "../../core/geometry/poseDescriptor.js";
|
|
2
2
|
import { AUTO_POSE_DESCRIPTOR } from "../../core/geometry/autoPoseDescriptor.js";
|
|
3
|
+
import { sceneInheritedPaintOf } from "../inheritedPaints.js";
|
|
3
4
|
import { pathPaintOf } from "../nodeShapes/nodePaint.js";
|
|
4
5
|
import { asNodeId } from "../../core/scene/types.js";
|
|
5
6
|
import { sceneMutators } from "../../core/scene/sceneMutators.js";
|
|
@@ -60,7 +61,7 @@ function useBooleansAdapterSource(scene, selection, adapter, poseDescriptor) {
|
|
|
60
61
|
},
|
|
61
62
|
getPaint: (id) => {
|
|
62
63
|
const leaf = leafOf(id);
|
|
63
|
-
return leaf ? pathPaintOf(leaf.node.data) : void 0;
|
|
64
|
+
return leaf ? pathPaintOf(leaf.node.data, sceneInheritedPaintOf(sceneRef.current)(leaf.node)) : void 0;
|
|
64
65
|
},
|
|
65
66
|
compareZ: (a, b) => {
|
|
66
67
|
const order = [...sceneRef.current.renderOrder()];
|
|
@@ -81,7 +82,7 @@ function useBooleansAdapterSource(scene, selection, adapter, poseDescriptor) {
|
|
|
81
82
|
};
|
|
82
83
|
return {
|
|
83
84
|
id: sceneRef.current.mintId(),
|
|
84
|
-
kind: "
|
|
85
|
+
kind: "mark",
|
|
85
86
|
layer: leaf.node.layer,
|
|
86
87
|
parent: leaf.node.parent ?? null,
|
|
87
88
|
pose: leaf.frame.local(sourceId, stored.pose),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"booleans.js","names":[],"sources":["../../../src/canvas/deps/booleans.ts"],"sourcesContent":["/**\n * The kit `booleansAdapter` dep: what the Pathfinder actions, and any tool\n * reading the dep, work on over a canvas's own path leaves.\n *\n * `<SceneCanvas>` publishes it over its own scene. A consumer replaces it with\n * `useBooleansAdapter` from under the canvas — for text operands, say, which\n * this one does not read; the newest registration wins, and a canvas child\n * registers after the canvas's own.\n */\nimport { useMemo } from 'react';\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport { pathInWorld } from '@weasel-js/geom';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId, type Scene } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport { scenePoseFrame } from 'interactions/actions/poseFrame';\nimport type { BooleansAdapter } from 'interactions/actions/booleans/booleans';\nimport { pathPaintOf, type PathPaint } from '../nodeShapes/nodePaint';\nimport { drawnPose, leafPath, storePiece } from './pathLeaf';\nimport type { OpsApplier } from './slice';\n\n/**\n * Publish a `booleansAdapter` over `scene`'s path leaves (`data.path` drawn\n * through the pose's box). A result is minted from the source it stands in\n * for: its layer, parent, frame, and data, with the result's path, and the\n * result's paint in place of the source's when the operation gives one.\n */\nexport function useBooleansAdapterSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n poseDescriptor?: PoseDescriptor<unknown>,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n const descriptorRef = useLatest(poseDescriptor);\n\n const booleans = useMemo<BooleansAdapter>(() => {\n const leafOf = (id: string) => {\n const sc = sceneRef.current;\n const node = sc.get(asNodeId(id));\n const path = node ? leafPath(sc, node) : null;\n if (!node || !path) return null;\n const frame = scenePoseFrame(sc);\n const g = poseDescriptorForNode(descriptorRef.current ?? AUTO_POSE_DESCRIPTOR, node);\n const drawn = drawnPose(g, frame.world(node.id));\n return drawn ? { node, path, drawn, frame } : null;\n };\n const tree = () => sceneMutators(sceneRef.current);\n return {\n getNode: (id) => tree().getNode(id),\n getParent: (id) => tree().getParent(id),\n getChildren: (parentId) => tree().getChildren(parentId),\n setChildOrder: (parentId, ids) => tree().setChildOrder(parentId, ids),\n getSelection: () => selectionRef.current.get(),\n getWorldPath: (id) => {\n const leaf = leafOf(id);\n return leaf ? pathInWorld(leaf.path, leaf.drawn.box) : undefined;\n },\n getPaint: (id) => {\n const leaf = leafOf(id);\n return leaf
|
|
1
|
+
{"version":3,"file":"booleans.js","names":[],"sources":["../../../src/canvas/deps/booleans.ts"],"sourcesContent":["/**\n * The kit `booleansAdapter` dep: what the Pathfinder actions, and any tool\n * reading the dep, work on over a canvas's own path leaves.\n *\n * `<SceneCanvas>` publishes it over its own scene. A consumer replaces it with\n * `useBooleansAdapter` from under the canvas — for text operands, say, which\n * this one does not read; the newest registration wins, and a canvas child\n * registers after the canvas's own.\n */\nimport { sceneInheritedPaintOf } from 'canvas/inheritedPaints';\nimport { useMemo } from 'react';\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport { pathInWorld } from '@weasel-js/geom';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId, type Scene } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport { scenePoseFrame } from 'interactions/actions/poseFrame';\nimport type { BooleansAdapter } from 'interactions/actions/booleans/booleans';\nimport { pathPaintOf, type PathPaint } from '../nodeShapes/nodePaint';\nimport { drawnPose, leafPath, storePiece } from './pathLeaf';\nimport type { OpsApplier } from './slice';\n\n/**\n * Publish a `booleansAdapter` over `scene`'s path leaves (`data.path` drawn\n * through the pose's box). A result is minted from the source it stands in\n * for: its layer, parent, frame, and data, with the result's path, and the\n * result's paint in place of the source's when the operation gives one.\n */\nexport function useBooleansAdapterSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n poseDescriptor?: PoseDescriptor<unknown>,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n const descriptorRef = useLatest(poseDescriptor);\n\n const booleans = useMemo<BooleansAdapter>(() => {\n const leafOf = (id: string) => {\n const sc = sceneRef.current;\n const node = sc.get(asNodeId(id));\n const path = node ? leafPath(sc, node) : null;\n if (!node || !path) return null;\n const frame = scenePoseFrame(sc);\n const g = poseDescriptorForNode(descriptorRef.current ?? AUTO_POSE_DESCRIPTOR, node);\n const drawn = drawnPose(g, frame.world(node.id));\n return drawn ? { node, path, drawn, frame } : null;\n };\n const tree = () => sceneMutators(sceneRef.current);\n return {\n getNode: (id) => tree().getNode(id),\n getParent: (id) => tree().getParent(id),\n getChildren: (parentId) => tree().getChildren(parentId),\n setChildOrder: (parentId, ids) => tree().setChildOrder(parentId, ids),\n getSelection: () => selectionRef.current.get(),\n getWorldPath: (id) => {\n const leaf = leafOf(id);\n return leaf ? pathInWorld(leaf.path, leaf.drawn.box) : undefined;\n },\n getPaint: (id) => {\n const leaf = leafOf(id);\n return leaf\n ? pathPaintOf(leaf.node.data as Parameters<typeof pathPaintOf>[0], sceneInheritedPaintOf(sceneRef.current)(leaf.node))\n : undefined;\n },\n compareZ: (a, b) => {\n const order = [...sceneRef.current.renderOrder()];\n return order.indexOf(asNodeId(a)) - order.indexOf(asNodeId(b));\n },\n createPathNode: (path, _op, sourceId, paint?: PathPaint) => {\n const leaf = leafOf(sourceId);\n if (!leaf) throw new Error(`[booleansAdapter] no path leaf behind source ${sourceId}`);\n const stored = storePiece(leaf.drawn, path);\n const { fill: _fill, stroke: _stroke, ...rest } = (leaf.node.data ?? {}) as Record<string, unknown>;\n const data = paint ? { ...rest, ...paint, path: stored.path } : { ...(leaf.node.data as object), path: stored.path };\n return {\n id: sceneRef.current.mintId(),\n kind: 'mark',\n layer: leaf.node.layer,\n parent: leaf.node.parent ?? null,\n pose: leaf.frame.local(sourceId, stored.pose),\n data,\n };\n },\n applyOps: (ops, label) => adapterRef.current.applyOps(ops, label),\n };\n }, [sceneRef, selectionRef, adapterRef, descriptorRef]);\n\n useDepSource('booleansAdapter', () => booleans);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,yBACd,OACA,WACA,SACA,gBACM;CACN,MAAM,WAAW,UAAU,KAAK;CAChC,MAAM,eAAe,UAAU,SAAS;CACxC,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,gBAAgB,UAAU,cAAc;CAE9C,MAAM,WAAW,cAA+B;EAC9C,MAAM,UAAU,OAAe;GAC7B,MAAM,KAAK,SAAS;GACpB,MAAM,OAAO,GAAG,IAAI,SAAS,EAAE,CAAC;GAChC,MAAM,OAAO,OAAO,SAAS,IAAI,IAAI,IAAI;GACzC,IAAI,CAAC,QAAQ,CAAC,MAAM,OAAO;GAC3B,MAAM,QAAQ,eAAe,EAAE;GAC/B,MAAM,IAAI,sBAAsB,cAAc,WAAW,sBAAsB,IAAI;GACnF,MAAM,QAAQ,UAAU,GAAG,MAAM,MAAM,KAAK,EAAE,CAAC;GAC/C,OAAO,QAAQ;IAAE;IAAM;IAAM;IAAO;GAAM,IAAI;EAChD;EACA,MAAM,aAAa,cAAc,SAAS,OAAO;EACjD,OAAO;GACL,UAAU,OAAO,KAAK,CAAC,CAAC,QAAQ,EAAE;GAClC,YAAY,OAAO,KAAK,CAAC,CAAC,UAAU,EAAE;GACtC,cAAc,aAAa,KAAK,CAAC,CAAC,YAAY,QAAQ;GACtD,gBAAgB,UAAU,QAAQ,KAAK,CAAC,CAAC,cAAc,UAAU,GAAG;GACpE,oBAAoB,aAAa,QAAQ,IAAI;GAC7C,eAAe,OAAO;IACpB,MAAM,OAAO,OAAO,EAAE;IACtB,OAAO,OAAO,YAAY,KAAK,MAAM,KAAK,MAAM,GAAG,IAAI;GACzD;GACA,WAAW,OAAO;IAChB,MAAM,OAAO,OAAO,EAAE;IACtB,OAAO,OACH,YAAY,KAAK,KAAK,MAA2C,sBAAsB,SAAS,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,IACnH;GACN;GACA,WAAW,GAAG,MAAM;IAClB,MAAM,QAAQ,CAAC,GAAG,SAAS,QAAQ,YAAY,CAAC;IAChD,OAAO,MAAM,QAAQ,SAAS,CAAC,CAAC,IAAI,MAAM,QAAQ,SAAS,CAAC,CAAC;GAC/D;GACA,iBAAiB,MAAM,KAAK,UAAU,UAAsB;IAC1D,MAAM,OAAO,OAAO,QAAQ;IAC5B,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,gDAAgD,UAAU;IACrF,MAAM,SAAS,WAAW,KAAK,OAAO,IAAI;IAC1C,MAAM,EAAE,MAAM,OAAO,QAAQ,SAAS,GAAG,SAAU,KAAK,KAAK,QAAQ,CAAC;IACtE,MAAM,OAAO,QAAQ;KAAE,GAAG;KAAM,GAAG;KAAO,MAAM,OAAO;IAAK,IAAI;KAAE,GAAI,KAAK,KAAK;KAAiB,MAAM,OAAO;IAAK;IACnH,OAAO;KACL,IAAI,SAAS,QAAQ,OAAO;KAC5B,MAAM;KACN,OAAO,KAAK,KAAK;KACjB,QAAQ,KAAK,KAAK,UAAU;KAC5B,MAAM,KAAK,MAAM,MAAM,UAAU,OAAO,IAAI;KAC5C;IACF;GACF;GACA,WAAW,KAAK,UAAU,WAAW,QAAQ,SAAS,KAAK,KAAK;EAClE;CACF,GAAG;EAAC;EAAU;EAAc;EAAY;CAAa,CAAC;CAEtD,aAAa,yBAAyB,QAAQ;AAChD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useEffect, useMemo } from "react";
|
|
2
|
+
import { useDepRegistry, useDepSource } from "@weasel-js/routing/react";
|
|
3
|
+
|
|
4
|
+
//#region src/canvas/deps/enteredGroup.ts
|
|
5
|
+
/**
|
|
6
|
+
* The entered group: the group a press resolves inside of. Session state beside
|
|
7
|
+
* the selection, not scene data, so it is never saved and never undone.
|
|
8
|
+
*
|
|
9
|
+
* It remembers the entered group and the groups above it. `get()` answers the
|
|
10
|
+
* innermost of those still in the scene, so deleting the group lands on its
|
|
11
|
+
* parent, and an undo that brings it back lands on it again.
|
|
12
|
+
*/
|
|
13
|
+
/** Whether `id` is reachable while `group` is entered: inside it, and not the group itself. With
|
|
14
|
+
* nothing entered every node is. Picking, region selects and select-all all ask this. */
|
|
15
|
+
function reachableInGroup(scene, group, id) {
|
|
16
|
+
return group === null || scene.ancestorsOf(id).includes(group);
|
|
17
|
+
}
|
|
18
|
+
/** An entered-group store over `scene`, with nothing entered. */
|
|
19
|
+
function createEnteredGroup(scene) {
|
|
20
|
+
let chain = [];
|
|
21
|
+
let announced = null;
|
|
22
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
23
|
+
const isGroup = (id) => scene.get(id)?.kind === "group";
|
|
24
|
+
const get = () => chain.find(isGroup) ?? null;
|
|
25
|
+
const groupsFrom = (id) => [id, ...scene.ancestorsOf(id)].filter(isGroup);
|
|
26
|
+
const announce = () => {
|
|
27
|
+
announced = get();
|
|
28
|
+
for (const fn of [...listeners]) fn();
|
|
29
|
+
};
|
|
30
|
+
let stopScene = null;
|
|
31
|
+
const watchScene = () => scene.subscribe(() => {
|
|
32
|
+
if (get() !== announced) announce();
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
get,
|
|
36
|
+
enter(id) {
|
|
37
|
+
if (!isGroup(id)) return;
|
|
38
|
+
chain = groupsFrom(id);
|
|
39
|
+
announce();
|
|
40
|
+
},
|
|
41
|
+
leave() {
|
|
42
|
+
const here = get();
|
|
43
|
+
if (here === null) return;
|
|
44
|
+
chain = groupsFrom(here).slice(1);
|
|
45
|
+
announce();
|
|
46
|
+
},
|
|
47
|
+
leaveAll() {
|
|
48
|
+
if (get() === null) return;
|
|
49
|
+
chain = [];
|
|
50
|
+
announce();
|
|
51
|
+
},
|
|
52
|
+
subscribe(fn) {
|
|
53
|
+
if (listeners.size === 0) {
|
|
54
|
+
announced = get();
|
|
55
|
+
stopScene = watchScene();
|
|
56
|
+
}
|
|
57
|
+
listeners.add(fn);
|
|
58
|
+
return () => {
|
|
59
|
+
listeners.delete(fn);
|
|
60
|
+
if (listeners.size === 0) {
|
|
61
|
+
stopScene?.();
|
|
62
|
+
stopScene = null;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** One entered-group store for the life of the scene. */
|
|
69
|
+
function useEnteredGroup(scene) {
|
|
70
|
+
return useMemo(() => createEnteredGroup(scene), [scene]);
|
|
71
|
+
}
|
|
72
|
+
function useEnteredGroupApiSource(dep) {
|
|
73
|
+
useDepSource("enteredGroup", () => dep);
|
|
74
|
+
useDepSource("insertInto", (below) => ({ get: () => below()?.get() ?? dep.get() }));
|
|
75
|
+
const registry = useDepRegistry();
|
|
76
|
+
useEffect(() => dep.subscribe(() => registry.changed?.()), [dep, registry]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
export { createEnteredGroup, reachableInGroup, useEnteredGroup, useEnteredGroupApiSource };
|
|
81
|
+
//# sourceMappingURL=enteredGroup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enteredGroup.js","names":[],"sources":["../../../src/canvas/deps/enteredGroup.ts"],"sourcesContent":["/**\n * The entered group: the group a press resolves inside of. Session state beside\n * the selection, not scene data, so it is never saved and never undone.\n *\n * It remembers the entered group and the groups above it. `get()` answers the\n * innermost of those still in the scene, so deleting the group lands on its\n * parent, and an undo that brings it back lands on it again.\n */\nimport { useEffect, useMemo } from 'react';\nimport { useDepRegistry, useDepSource } from '@weasel-js/routing/react';\nimport type { NodeId, Scene } from 'core/scene/types';\n\n/** The group a press resolves inside of, held beside the selection; `<SceneCanvas enteredGroup>` takes one. */\nexport interface EnteredGroupApi {\n get(): NodeId | null;\n enter(id: NodeId): void;\n /** Moves to the nearest group above the current one, or to none. */\n leave(): void;\n /** Leaves every entered group at once. */\n leaveAll(): void;\n subscribe(fn: () => void): () => void;\n}\n\n/** Whether `id` is reachable while `group` is entered: inside it, and not the group itself. With\n * nothing entered every node is. Picking, region selects and select-all all ask this. */\nexport function reachableInGroup(\n scene: Pick<Scene<unknown, string, unknown>, 'ancestorsOf'>,\n group: NodeId | null,\n id: string,\n): boolean {\n return group === null || scene.ancestorsOf(id as NodeId).includes(group);\n}\n\ntype SceneReads = Pick<Scene<unknown, string, unknown>, 'get' | 'ancestorsOf' | 'subscribe'>;\n\n/** An entered-group store over `scene`, with nothing entered. */\nexport function createEnteredGroup(scene: SceneReads): EnteredGroupApi {\n let chain: NodeId[] = [];\n let announced: NodeId | null = null;\n const listeners = new Set<() => void>();\n\n const isGroup = (id: NodeId) => scene.get(id)?.kind === 'group';\n const get = (): NodeId | null => chain.find(isGroup) ?? null;\n const groupsFrom = (id: NodeId): NodeId[] => [id, ...scene.ancestorsOf(id)].filter(isGroup);\n\n const announce = () => {\n announced = get();\n for (const fn of [...listeners]) fn();\n };\n\n let stopScene: (() => void) | null = null;\n const watchScene = () => scene.subscribe(() => {\n if (get() !== announced) announce();\n });\n\n return {\n get,\n enter(id) {\n if (!isGroup(id)) return;\n chain = groupsFrom(id);\n announce();\n },\n leave() {\n const here = get();\n if (here === null) return;\n chain = groupsFrom(here).slice(1);\n announce();\n },\n leaveAll() {\n if (get() === null) return;\n chain = [];\n announce();\n },\n subscribe(fn) {\n if (listeners.size === 0) {\n announced = get();\n stopScene = watchScene();\n }\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n if (listeners.size === 0) { stopScene?.(); stopScene = null; }\n };\n },\n };\n}\n\n/** One entered-group store for the life of the scene. */\nexport function useEnteredGroup(scene: SceneReads): EnteredGroupApi {\n return useMemo(() => createEnteredGroup(scene), [scene]);\n}\n\nexport function useEnteredGroupApiSource(dep: EnteredGroupApi): void {\n useDepSource('enteredGroup', () => dep);\n // What is drawn or pasted while a group is entered lands in it, unless a source\n // registered under this one, such as a symmetry group, names a container.\n useDepSource('insertInto', (below) => ({ get: () => below()?.get() ?? dep.get() }));\n const registry = useDepRegistry();\n useEffect(() => dep.subscribe(() => registry.changed?.()), [dep, registry]);\n}\n"],"mappings":";;;;;;;;;;;;;;AAyBA,SAAgB,iBACd,OACA,OACA,IACS;CACT,OAAO,UAAU,QAAQ,MAAM,YAAY,EAAY,CAAC,CAAC,SAAS,KAAK;AACzE;;AAKA,SAAgB,mBAAmB,OAAoC;CACrE,IAAI,QAAkB,CAAC;CACvB,IAAI,YAA2B;CAC/B,MAAM,4BAAY,IAAI,IAAgB;CAEtC,MAAM,WAAW,OAAe,MAAM,IAAI,EAAE,CAAC,EAAE,SAAS;CACxD,MAAM,YAA2B,MAAM,KAAK,OAAO,KAAK;CACxD,MAAM,cAAc,OAAyB,CAAC,IAAI,GAAG,MAAM,YAAY,EAAE,CAAC,CAAC,CAAC,OAAO,OAAO;CAE1F,MAAM,iBAAiB;EACrB,YAAY,IAAI;EAChB,KAAK,MAAM,MAAM,CAAC,GAAG,SAAS,GAAG,GAAG;CACtC;CAEA,IAAI,YAAiC;CACrC,MAAM,mBAAmB,MAAM,gBAAgB;EAC7C,IAAI,IAAI,MAAM,WAAW,SAAS;CACpC,CAAC;CAED,OAAO;EACL;EACA,MAAM,IAAI;GACR,IAAI,CAAC,QAAQ,EAAE,GAAG;GAClB,QAAQ,WAAW,EAAE;GACrB,SAAS;EACX;EACA,QAAQ;GACN,MAAM,OAAO,IAAI;GACjB,IAAI,SAAS,MAAM;GACnB,QAAQ,WAAW,IAAI,CAAC,CAAC,MAAM,CAAC;GAChC,SAAS;EACX;EACA,WAAW;GACT,IAAI,IAAI,MAAM,MAAM;GACpB,QAAQ,CAAC;GACT,SAAS;EACX;EACA,UAAU,IAAI;GACZ,IAAI,UAAU,SAAS,GAAG;IACxB,YAAY,IAAI;IAChB,YAAY,WAAW;GACzB;GACA,UAAU,IAAI,EAAE;GAChB,aAAa;IACX,UAAU,OAAO,EAAE;IACnB,IAAI,UAAU,SAAS,GAAG;KAAE,YAAY;KAAG,YAAY;IAAM;GAC/D;EACF;CACF;AACF;;AAGA,SAAgB,gBAAgB,OAAoC;CAClE,OAAO,cAAc,mBAAmB,KAAK,GAAG,CAAC,KAAK,CAAC;AACzD;AAEA,SAAgB,yBAAyB,KAA4B;CACnE,aAAa,sBAAsB,GAAG;CAGtC,aAAa,eAAe,WAAW,EAAE,WAAW,MAAM,CAAC,EAAE,IAAI,KAAK,IAAI,IAAI,EAAE,EAAE;CAClF,MAAM,WAAW,eAAe;CAChC,gBAAgB,IAAI,gBAAgB,SAAS,UAAU,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC;AAC5E"}
|
|
@@ -2,9 +2,10 @@ import { rectToPlane, toPlane } from "../../core/viewport/parallax.js";
|
|
|
2
2
|
import { poseDescriptorForNode, visualBoundsViaDescriptor } from "../../core/geometry/poseDescriptor.js";
|
|
3
3
|
import { AUTO_POSE_DESCRIPTOR } from "../../core/geometry/autoPoseDescriptor.js";
|
|
4
4
|
import { findShapeInk, findShapeSilhouette } from "../NodeShape.js";
|
|
5
|
-
import { pickWalk, scenePickSource } from "../pickWalk.js";
|
|
6
5
|
import { polygonRegion, poseOutline, rectRegion, regionBoundsOf, regionOf, regionTakes } from "../../core/geometry/regionHit.js";
|
|
7
|
-
import {
|
|
6
|
+
import { scenePickSource } from "../scenePickSource.js";
|
|
7
|
+
import { pickWalk } from "../pickWalk.js";
|
|
8
|
+
import { applyToPoint, boundsOfPath, pathIntersectsRect } from "@weasel-js/geom";
|
|
8
9
|
|
|
9
10
|
//#region src/canvas/deps/hitTestArea.ts
|
|
10
11
|
/** The pick options a region dep hands the shared walk: the asking view's
|
|
@@ -38,26 +39,37 @@ function asInked(filled, shape) {
|
|
|
38
39
|
openFill: "none"
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
|
-
/** The walk every region query shares: skip
|
|
42
|
+
/** The walk every region query shares: skip groups unless asked, gate on
|
|
42
43
|
* the clip, and let `regionTakes` decide each node. */
|
|
43
44
|
function walkArea(scene, region, mode, opts, descriptor, query) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const src = scenePickSource(scene, {
|
|
46
|
+
...opts,
|
|
47
|
+
index: { descriptor }
|
|
48
|
+
});
|
|
49
|
+
return pickWalk(src, regionPickQuery(region, mode, descriptor, query));
|
|
50
|
+
}
|
|
51
|
+
/** The query a marquee or a lasso over `r` asks of each node. */
|
|
52
|
+
function regionPickQuery(r, mode, descriptor, query) {
|
|
53
|
+
return {
|
|
54
|
+
includeGroups: query.includeGroups === true,
|
|
55
|
+
broad: { box: r.bounds },
|
|
56
|
+
clipAdmits: (clip, node, pose, derived) => {
|
|
47
57
|
const g = poseDescriptorForNode(descriptor, node);
|
|
48
|
-
return pathIntersectsRect(clip, visualBoundsViaDescriptor(pose, g)) && pathIntersectsRect(clip, r.bounds);
|
|
58
|
+
return pathIntersectsRect(clip, derived ? boundsOfPath(derived) : visualBoundsViaDescriptor(pose, g)) && pathIntersectsRect(clip, r.bounds);
|
|
49
59
|
},
|
|
50
|
-
hits: (node, pose) => {
|
|
60
|
+
hits: (node, pose, derived, inherited) => {
|
|
51
61
|
const g = poseDescriptorForNode(descriptor, node);
|
|
52
|
-
const b = regionBoundsOf(node, pose, g);
|
|
62
|
+
const b = derived ? boundsOfPath(derived) : regionBoundsOf(node, pose, g);
|
|
53
63
|
let ink;
|
|
54
|
-
const filled = () => ink ??= findShapeInk(node, pose)?.filled !== false;
|
|
55
|
-
return regionTakes(r, mode, asInked(filled, pose), b, () => asInked(filled, findShapeSilhouette(node, pose) ?? poseOutline(pose, b)), filled);
|
|
64
|
+
const filled = () => ink ??= findShapeInk(node, pose, { inherited })?.filled !== false;
|
|
65
|
+
return regionTakes(r, mode, asInked(filled, pose), b, () => asInked(filled, findShapeSilhouette(node, pose, { derivedPath: derived }) ?? derived ?? poseOutline(pose, b)), filled);
|
|
56
66
|
},
|
|
57
|
-
inPlane: (m) =>
|
|
58
|
-
inCopy: (m) =>
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
inPlane: (m) => regionPickQuery(regionInPlane(r, m), mode, descriptor, query),
|
|
68
|
+
inCopy: (m) => ({
|
|
69
|
+
...regionPickQuery(regionInCopy(r, m), mode, descriptor, query),
|
|
70
|
+
broad: void 0
|
|
71
|
+
})
|
|
72
|
+
};
|
|
61
73
|
}
|
|
62
74
|
/** `r` carried into a plane's world. The map is axis-aligned, so a rect stays
|
|
63
75
|
* one; a flipped axis reverses the winding, which no region test reads. */
|
|
@@ -92,5 +104,5 @@ function regionInCopy(r, m) {
|
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
//#endregion
|
|
95
|
-
export { hitTestArea, hitTestLassoPolygon, regionPickOptions };
|
|
107
|
+
export { hitTestArea, hitTestLassoPolygon, regionPickOptions, regionPickQuery };
|
|
96
108
|
//# sourceMappingURL=hitTestArea.js.map
|
|
@@ -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":"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"}
|
|
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, boundsOfPath, 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 group and its members\n * selects the same ink twice. A bare-adapter consumer, which has nothing\n * to fold members back into their group, asks for them. */\n includeGroups?: 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 groups 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 src = scenePickSource(scene, { ...opts, index: { descriptor } });\n return pickWalk<unknown>(src, regionPickQuery(region, mode, descriptor, query)) as NodeId[];\n}\n\n/** The query a marquee or a lasso over `r` asks of each node. */\nexport function regionPickQuery(\n r: SelectRegion,\n mode: LassoHitMode,\n descriptor: PoseDescriptor<unknown>,\n query: RegionQueryOptions,\n): PickQuery<unknown> {\n return {\n includeGroups: query.includeGroups === true,\n broad: { box: r.bounds },\n clipAdmits: (clip, node, pose, derived) => {\n const g = poseDescriptorForNode(descriptor, node);\n return pathIntersectsRect(clip, derived ? boundsOfPath(derived) : 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 // A derived node's pose is a placeholder: its path is the extent and the fallback outline.\n const b = derived ? boundsOfPath(derived) : 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, { derivedPath: derived }) ?? derived ?? poseOutline(pose, b)), filled);\n },\n inPlane: (m) => regionPickQuery(regionInPlane(r, m), mode, descriptor, query),\n // A copy is tested by every walk, so the query carried into one asks no index.\n inCopy: (m) => ({ ...regionPickQuery(regionInCopy(r, m), mode, descriptor, query), broad: undefined }),\n };\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,14 +1,13 @@
|
|
|
1
|
-
import { composeWorldPose, composesFrames } from "../../core/scene/composeFrame.js";
|
|
2
|
-
import { definesFrame } from "../../core/scene/effectivePose.js";
|
|
3
|
-
import { cycleFill, solid, strokeOf } from "../../util/paint.js";
|
|
4
1
|
import { insertPreviewExtent } from "../insertPreviewExtent.js";
|
|
2
|
+
import { insertSite } from "./insertSite.js";
|
|
5
3
|
import { minSizeOf } from "../../core/scene/minSize.js";
|
|
6
|
-
import { insertParent } from "../../interactions/actions/insert/parent.js";
|
|
7
4
|
import { createInsertOp } from "../../core/ops/create.js";
|
|
5
|
+
import { cycleFill, solid, strokeOf } from "../../util/paint.js";
|
|
8
6
|
import { stickyData } from "../../tools/builtin/sticky/stickyNode.js";
|
|
9
7
|
import { ellipsePath, linePath, polygonFromPoints, rectPath, regularPolygonPath, schneiderFit, starPath } from "@weasel-js/geom";
|
|
10
8
|
import { useRef } from "react";
|
|
11
9
|
import { useLatest } from "@weasel-js/react";
|
|
10
|
+
import { DEFAULT_TEXT_FILL } from "@weasel-js/text";
|
|
12
11
|
import { useDepRegistry, useDepSource } from "@weasel-js/routing/react";
|
|
13
12
|
|
|
14
13
|
//#region src/canvas/deps/insert.ts
|
|
@@ -69,7 +68,10 @@ function boxKindData(kind, bounds, extras, painted) {
|
|
|
69
68
|
...e.closed ? {} : { fill: null }
|
|
70
69
|
};
|
|
71
70
|
}
|
|
72
|
-
case "text": return {
|
|
71
|
+
case "text": return {
|
|
72
|
+
text: extras.text ?? "",
|
|
73
|
+
fill: DEFAULT_TEXT_FILL
|
|
74
|
+
};
|
|
73
75
|
case "sticky": return stickyData(extras);
|
|
74
76
|
case "image": {
|
|
75
77
|
const e = extras;
|
|
@@ -81,36 +83,6 @@ function boxKindData(kind, bounds, extras, painted) {
|
|
|
81
83
|
default: return null;
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Where an insert lands: the container the `insertInto` dep names, on that container's
|
|
86
|
-
* layer, with the pose made local to it when the scene composes frames —
|
|
87
|
-
* or the top of the tree, on the first layer, when it names no container.
|
|
88
|
-
*/
|
|
89
|
-
function insertSite(sc, into) {
|
|
90
|
-
const parentId = insertParent(sc, into);
|
|
91
|
-
if (parentId === null) return {
|
|
92
|
-
parent: null,
|
|
93
|
-
layer: sc.layers[0]?.id ?? "default",
|
|
94
|
-
local: (p) => p
|
|
95
|
-
};
|
|
96
|
-
const parent = sc.get(parentId);
|
|
97
|
-
const composition = sc.composition;
|
|
98
|
-
if (composition === void 0 || !composesFrames(composition)) return {
|
|
99
|
-
parent: parent.id,
|
|
100
|
-
layer: parent.layer,
|
|
101
|
-
local: (p) => p
|
|
102
|
-
};
|
|
103
|
-
const frame = composeWorldPose({
|
|
104
|
-
getPose: (id) => sc.get(id).pose,
|
|
105
|
-
getParent: (id) => sc.get(id)?.parent ?? null,
|
|
106
|
-
definesFrame: (id) => definesFrame(sc.get(id) ?? {})
|
|
107
|
-
}, parent.id, composition.compose);
|
|
108
|
-
return {
|
|
109
|
-
parent: parent.id,
|
|
110
|
-
layer: parent.layer,
|
|
111
|
-
local: (p) => composition.decompose(frame, p)
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
86
|
function useInsertDepSource(scene, adapter, factories, paint = paletteInsertPaint) {
|
|
115
87
|
const sceneRef = useLatest(scene);
|
|
116
88
|
const adapterRef = useLatest(adapter);
|
|
@@ -123,7 +95,7 @@ function useInsertDepSource(scene, adapter, factories, paint = paletteInsertPain
|
|
|
123
95
|
useDepSource("insert", () => {
|
|
124
96
|
const sc = sceneRef.current;
|
|
125
97
|
const ad = adapterRef.current;
|
|
126
|
-
const site = () => insertSite(sc, registry.get("insertInto")?.get());
|
|
98
|
+
const site = () => insertSite(sc, registry.get("insertInto")?.get(), registry.get("canDropInto"));
|
|
127
99
|
const build = (bounds, extras) => {
|
|
128
100
|
const kind = extras.kind;
|
|
129
101
|
const { parent, layer, local } = site();
|
|
@@ -146,7 +118,7 @@ function useInsertDepSource(scene, adapter, factories, paint = paletteInsertPain
|
|
|
146
118
|
ops: [createInsertOp({
|
|
147
119
|
node: {
|
|
148
120
|
id,
|
|
149
|
-
kind: "
|
|
121
|
+
kind: "mark",
|
|
150
122
|
layer,
|
|
151
123
|
parent,
|
|
152
124
|
pose: local(built.pose ?? pose),
|
|
@@ -223,7 +195,7 @@ function useInsertDepSource(scene, adapter, factories, paint = paletteInsertPain
|
|
|
223
195
|
ops: [createInsertOp({
|
|
224
196
|
node: {
|
|
225
197
|
id,
|
|
226
|
-
kind: "
|
|
198
|
+
kind: "mark",
|
|
227
199
|
layer,
|
|
228
200
|
parent,
|
|
229
201
|
pose: local(pose),
|
|
@@ -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 { 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 { 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 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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"}
|
|
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 { insertSite } from './insertSite';\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 { 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\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(), registry.get('canDropInto'));\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: 'mark', 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: 'mark', 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,39 @@
|
|
|
1
|
+
import { composeWorldPose, composesFrames } from "../../core/scene/composeFrame.js";
|
|
2
|
+
import { definesFrame } from "../../core/scene/effectivePose.js";
|
|
3
|
+
import { insertParent } from "../../interactions/actions/insert/parent.js";
|
|
4
|
+
|
|
5
|
+
//#region src/canvas/deps/insertSite.ts
|
|
6
|
+
/**
|
|
7
|
+
* Where an insert lands: the container the `insertInto` dep names, on that container's
|
|
8
|
+
* layer, with the pose made local to it when the scene composes frames —
|
|
9
|
+
* or the top of the tree, on the first layer, when it names no container.
|
|
10
|
+
*/
|
|
11
|
+
function insertSite(sc, into, canDropInto) {
|
|
12
|
+
const parentId = insertParent(sc, into, canDropInto);
|
|
13
|
+
if (parentId === null) return {
|
|
14
|
+
parent: null,
|
|
15
|
+
layer: sc.layers[0]?.id ?? "default",
|
|
16
|
+
local: (p) => p
|
|
17
|
+
};
|
|
18
|
+
const parent = sc.get(parentId);
|
|
19
|
+
const composition = sc.composition;
|
|
20
|
+
if (composition === void 0 || !composesFrames(composition)) return {
|
|
21
|
+
parent: parent.id,
|
|
22
|
+
layer: parent.layer,
|
|
23
|
+
local: (p) => p
|
|
24
|
+
};
|
|
25
|
+
const frame = composeWorldPose({
|
|
26
|
+
getPose: (id) => sc.get(id).pose,
|
|
27
|
+
getParent: (id) => sc.get(id)?.parent ?? null,
|
|
28
|
+
definesFrame: (id) => definesFrame(sc.get(id) ?? {})
|
|
29
|
+
}, parent.id, composition.compose);
|
|
30
|
+
return {
|
|
31
|
+
parent: parent.id,
|
|
32
|
+
layer: parent.layer,
|
|
33
|
+
local: (p) => composition.decompose(frame, p)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { insertSite };
|
|
39
|
+
//# sourceMappingURL=insertSite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insertSite.js","names":[],"sources":["../../../src/canvas/deps/insertSite.ts"],"sourcesContent":["import type { Scene, NodeId } from 'core/scene/types';\nimport type { CanDropIntoDep } from 'interactions/actions/layoutDeps';\nimport { insertParent } from 'interactions/actions/insert/parent';\nimport { composesFrames, composeWorldPose } from 'core/scene/composeFrame';\nimport { definesFrame } from 'core/scene/effectivePose';\n\n/** Where a new node lands: its parent, its layer, and how a world pose becomes its stored one. */\nexport interface InsertSite {\n parent: NodeId | null;\n layer: string;\n local: (world: unknown) => unknown;\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 */\nexport function insertSite(\n sc: Pick<Scene<unknown, string, unknown>, 'get' | 'layers' | 'composition'>,\n into: string | null | undefined,\n canDropInto?: CanDropIntoDep,\n): InsertSite {\n const parentId = insertParent(sc, into, canDropInto);\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"],"mappings":";;;;;;;;;;AAkBA,SAAgB,WACd,IACA,MACA,aACY;CACZ,MAAM,WAAW,aAAa,IAAI,MAAM,WAAW;CACnD,IAAI,aAAa,MACf,OAAO;EAAE,QAAQ;EAAM,OAAQ,GAAG,OAAO,EAAE,EAAE,MAAM;EAAsB,QAAQ,MAAM;CAAE;CAE3F,MAAM,SAAS,GAAG,IAAI,QAAQ;CAC9B,MAAM,cAAc,GAAG;CACvB,IAAI,gBAAgB,UAAa,CAAC,eAAe,WAAW,GAC1D,OAAO;EAAE,QAAQ,OAAO;EAAI,OAAO,OAAO;EAAO,QAAQ,MAAM;CAAE;CAEnE,MAAM,QAAQ,iBAAiB;EAC7B,UAAU,OAAO,GAAG,IAAI,EAAY,CAAC,CAAE;EACvC,YAAY,OAAO,GAAG,IAAI,EAAY,CAAC,EAAE,UAAU;EACnD,eAAe,OAAO,aAAa,GAAG,IAAI,EAAY,KAAK,CAAC,CAAC;CAC/D,GAAG,OAAO,IAAI,YAAY,OAAO;CACjC,OAAO;EAAE,QAAQ,OAAO;EAAI,OAAO,OAAO;EAAO,QAAQ,MAAM,YAAY,UAAU,OAAO,CAAC;CAAE;AACjG"}
|
|
@@ -8,19 +8,22 @@ import { useDepSource } from "@weasel-js/routing/react";
|
|
|
8
8
|
* `lassoSelectAction`: `hitTestLasso` tests the lasso polygon itself, and
|
|
9
9
|
* `hitTestArea` is the same silhouette-aware rect test `areaSelect` uses.
|
|
10
10
|
*/
|
|
11
|
-
function useLassoSelectDepSource(scene, selection, descriptor, alphaOf) {
|
|
11
|
+
function useLassoSelectDepSource(scene, selection, descriptor, alphaOf, reachable) {
|
|
12
12
|
const sceneRef = useLatest(scene);
|
|
13
13
|
const selectionRef = useLatest(selection);
|
|
14
14
|
const descriptorRef = useLatest(descriptor);
|
|
15
15
|
const alphaOfRef = useLatest(alphaOf);
|
|
16
|
+
const reachableRef = useLatest(reachable);
|
|
16
17
|
useDepSource("lassoSelect", () => {
|
|
17
18
|
const sc = sceneRef.current;
|
|
18
19
|
const s = selectionRef.current;
|
|
19
20
|
const d = descriptorRef.current;
|
|
20
21
|
const a = alphaOfRef.current;
|
|
22
|
+
const r = reachableRef.current;
|
|
23
|
+
const keep = (ids) => r ? ids.filter((id) => r(id)) : ids;
|
|
21
24
|
return {
|
|
22
|
-
hitTestLasso: (polygon, mode, view) => hitTestLassoPolygon(sc, polygon, mode, regionPickOptions(view, a), d),
|
|
23
|
-
hitTestArea: (bounds, view) => hitTestArea(sc, bounds, regionPickOptions(view, a), d),
|
|
25
|
+
hitTestLasso: (polygon, mode, view) => keep(hitTestLassoPolygon(sc, polygon, mode, regionPickOptions(view, a), d)),
|
|
26
|
+
hitTestArea: (bounds, view) => keep(hitTestArea(sc, bounds, regionPickOptions(view, a), d)),
|
|
24
27
|
getSelection: () => s.current,
|
|
25
28
|
setSelection: (ids) => s.set(ids)
|
|
26
29
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lassoSelect.js","names":["hitTestAreaShared"],"sources":["../../../src/canvas/deps/lassoSelect.ts"],"sourcesContent":["/**\n * `useLassoSelectDepSource` — wires the `lassoSelect` dep consumed by\n * `lassoSelectAction`: `hitTestLasso` tests the lasso polygon itself, and\n * `hitTestArea` is the same silhouette-aware rect test `areaSelect` uses.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { LassoSelectDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { hitTestArea as hitTestAreaShared, hitTestLassoPolygon, regionPickOptions } from './hitTestArea';\n\nexport function useLassoSelectDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n descriptor?: PoseDescriptor<unknown>,\n /** The painted alpha the surface's layers apply, which every view shares. */\n alphaOf?: (id: string) => number,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const descriptorRef = useLatest(descriptor);\n const alphaOfRef = useLatest(alphaOf);\n\n useDepSource('lassoSelect', (): LassoSelectDep => {\n const sc = sceneRef.current;\n const s = selectionRef.current;\n const d = descriptorRef.current;\n const a = alphaOfRef.current;\n return {\n hitTestLasso: (polygon, mode, view) =>\n hitTestLassoPolygon(sc, polygon, mode, regionPickOptions(view, a), d),\n hitTestArea: (bounds, view) =>\n hitTestAreaShared(sc, bounds, regionPickOptions(view, a), d),\n getSelection: () => s.current as NodeId[],\n setSelection: (ids) => s.set(ids as NodeId[]),\n };\n });\n}\n"],"mappings":";;;;;;;;;;AAaA,SAAgB,wBACd,OACA,WACA,YAEA,
|
|
1
|
+
{"version":3,"file":"lassoSelect.js","names":["hitTestAreaShared"],"sources":["../../../src/canvas/deps/lassoSelect.ts"],"sourcesContent":["/**\n * `useLassoSelectDepSource` — wires the `lassoSelect` dep consumed by\n * `lassoSelectAction`: `hitTestLasso` tests the lasso polygon itself, and\n * `hitTestArea` is the same silhouette-aware rect test `areaSelect` uses.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { LassoSelectDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { hitTestArea as hitTestAreaShared, hitTestLassoPolygon, regionPickOptions } from './hitTestArea';\n\nexport function useLassoSelectDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n descriptor?: PoseDescriptor<unknown>,\n /** The painted alpha the surface's layers apply, which every view shares. */\n alphaOf?: (id: string) => number,\n /** Whether a node can be reached at all: the canvas's one answer, which picking reads too. */\n reachable?: (id: string) => boolean,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const descriptorRef = useLatest(descriptor);\n const alphaOfRef = useLatest(alphaOf);\n const reachableRef = useLatest(reachable);\n\n useDepSource('lassoSelect', (): LassoSelectDep => {\n const sc = sceneRef.current;\n const s = selectionRef.current;\n const d = descriptorRef.current;\n const a = alphaOfRef.current;\n const r = reachableRef.current;\n const keep = (ids: NodeId[]): NodeId[] => (r ? ids.filter((id) => r(id)) : ids);\n return {\n hitTestLasso: (polygon, mode, view) =>\n keep(hitTestLassoPolygon(sc, polygon, mode, regionPickOptions(view, a), d)),\n hitTestArea: (bounds, view) =>\n keep(hitTestAreaShared(sc, bounds, regionPickOptions(view, a), d)),\n getSelection: () => s.current as NodeId[],\n setSelection: (ids) => s.set(ids as NodeId[]),\n };\n });\n}\n"],"mappings":";;;;;;;;;;AAaA,SAAgB,wBACd,OACA,WACA,YAEA,SAEA,WACM;CACN,MAAM,WAAW,UAAU,KAAK;CAChC,MAAM,eAAe,UAAU,SAAS;CACxC,MAAM,gBAAgB,UAAU,UAAU;CAC1C,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,eAAe,UAAU,SAAS;CAExC,aAAa,qBAAqC;EAChD,MAAM,KAAK,SAAS;EACpB,MAAM,IAAI,aAAa;EACvB,MAAM,IAAI,cAAc;EACxB,MAAM,IAAI,WAAW;EACrB,MAAM,IAAI,aAAa;EACvB,MAAM,QAAQ,QAA6B,IAAI,IAAI,QAAQ,OAAO,EAAE,EAAE,CAAC,IAAI;EAC3E,OAAO;GACL,eAAe,SAAS,MAAM,SAC5B,KAAK,oBAAoB,IAAI,SAAS,MAAM,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC;GAC5E,cAAc,QAAQ,SACpB,KAAKA,YAAkB,IAAI,QAAQ,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC;GACnE,oBAAoB,EAAE;GACtB,eAAe,QAAQ,EAAE,IAAI,GAAe;EAC9C;CACF,CAAC;AACH"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sceneDependentsOf } from "../../core/scene/dependents.js";
|
|
1
2
|
import { createTransformOp } from "../../core/ops/transform.js";
|
|
2
3
|
import { createSetDataOp } from "../../core/ops/setData.js";
|
|
3
4
|
import { createDeleteOp } from "../../core/ops/delete.js";
|
|
@@ -7,11 +8,6 @@ import { withoutGeometryBoundData } from "../../features/paths/pathEditExtension
|
|
|
7
8
|
import { writtenBy } from "../../core/ops/writtenBy.js";
|
|
8
9
|
|
|
9
10
|
//#region src/canvas/deps/pathEditCommits.ts
|
|
10
|
-
/** The nodes that list `id` in their `dependsOn`, directly or through others. */
|
|
11
|
-
function dependentsOf(sc, id) {
|
|
12
|
-
if (!sc.get(id)) return [];
|
|
13
|
-
return sc.removalClosure([id]).filter((d) => d !== id && !sc.ancestorsOf(d).includes(id));
|
|
14
|
-
}
|
|
15
11
|
/** The scene as an extension's `finishEdit` reads it. */
|
|
16
12
|
function finishCtx(sc, id, extensions) {
|
|
17
13
|
return {
|
|
@@ -25,7 +21,7 @@ function finishCtx(sc, id, extensions) {
|
|
|
25
21
|
} : null;
|
|
26
22
|
},
|
|
27
23
|
path: (nid) => pathEditPathOf(sc.get(nid), extensions),
|
|
28
|
-
dependents: (nid) =>
|
|
24
|
+
dependents: (nid) => sceneDependentsOf(sc, nid)
|
|
29
25
|
};
|
|
30
26
|
}
|
|
31
27
|
/** `o` laid over what `others` already holds for its node. */
|
|
@@ -131,7 +127,7 @@ function transformOthers(sc, extensions, after) {
|
|
|
131
127
|
node: nodeAfter,
|
|
132
128
|
path: (id) => pathEditPathOf(nodeAfter(id) ?? void 0, extensions),
|
|
133
129
|
before: (id) => pathEditPathOf(sc.get(id), extensions),
|
|
134
|
-
dependents: (id) =>
|
|
130
|
+
dependents: (id) => sceneDependentsOf(sc, id)
|
|
135
131
|
};
|
|
136
132
|
for (const ext of finishing) {
|
|
137
133
|
const done = ext.finishTransform(ctx);
|