@weasel-js/core 1.13.0 → 1.14.1
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 +326 -0
- package/README.md +8 -12
- package/dist/affordances/hitAffordanceRegions.js +1 -1
- package/dist/affordances/hitAffordanceRegions.js.map +1 -1
- package/dist/affordances/pathAnchors.js.map +1 -1
- package/dist/animation/clipTargets.js +1 -1
- package/dist/animation/clipTargets.js.map +1 -1
- package/dist/animation/poseTarget.js +5 -32
- package/dist/animation/poseTarget.js.map +1 -1
- package/dist/{autoPoseDescriptor-CZ1eXbzY.d.ts → autoPoseDescriptor-C25QVb0W.d.ts} +1 -1
- package/dist/barrel/canvas.js +8 -8
- package/dist/barrel/chrome.js +9 -9
- package/dist/barrel/input.js +1 -1
- package/dist/barrel/interactions.js +3 -3
- package/dist/barrel/paint.js +2 -2
- package/dist/barrel/paths.js +19 -17
- package/dist/barrel/renderer.js +11 -7
- package/dist/barrel/routing.js +4 -1
- package/dist/barrel/scene.js +16 -12
- package/dist/barrel/text.js +5 -5
- package/dist/barrel/viewport.js +8 -8
- package/dist/canvas/Canvas.js +2 -2
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +20 -14
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/GestureReason.js +64 -0
- package/dist/canvas/GestureReason.js.map +1 -0
- package/dist/canvas/GestureReason.module.js +7 -0
- package/dist/canvas/GestureReason.module.js.map +1 -0
- package/dist/canvas/LeanSceneCanvas.js +3 -1
- package/dist/canvas/LeanSceneCanvas.js.map +1 -1
- package/dist/canvas/NodeShape.js +2 -0
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/PickHud.js +7 -3
- package/dist/canvas/PickHud.js.map +1 -1
- package/dist/canvas/SceneCanvas/CanvasKitProviders.js +1 -1
- package/dist/canvas/SceneCanvas/CanvasKitProviders.js.map +1 -1
- package/dist/canvas/SceneCanvas/DevHuds.js +2 -2
- package/dist/canvas/SceneCanvas/DevHuds.js.map +1 -1
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +4 -2
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js.map +1 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js +3 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
- package/dist/canvas/SceneCanvas/brushOverlay.js.map +1 -1
- package/dist/canvas/SceneCanvas/builtinToolSets.js +17 -9
- package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +75 -19
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +7 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultTextEditor.js +108 -0
- package/dist/canvas/SceneCanvas/defaultTextEditor.js.map +1 -0
- package/dist/canvas/SceneCanvas/describePresets.js +47 -0
- package/dist/canvas/SceneCanvas/describePresets.js.map +1 -0
- package/dist/canvas/SceneCanvas/features.js +57 -16
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/mergeLayers.js +10 -1
- package/dist/canvas/SceneCanvas/mergeLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/namedPresets.js +52 -24
- package/dist/canvas/SceneCanvas/namedPresets.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/arrange.js +35 -10
- package/dist/canvas/SceneCanvas/presets/arrange.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/baseline.js +9 -0
- package/dist/canvas/SceneCanvas/presets/baseline.js.map +1 -0
- package/dist/canvas/SceneCanvas/presets/chrome.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/composites.js +43 -0
- package/dist/canvas/SceneCanvas/presets/composites.js.map +1 -0
- package/dist/canvas/SceneCanvas/presets/edit.js +79 -26
- package/dist/canvas/SceneCanvas/presets/edit.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/ingest.js +1 -0
- package/dist/canvas/SceneCanvas/presets/ingest.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/paths.js +58 -16
- package/dist/canvas/SceneCanvas/presets/paths.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/select.js +4 -2
- package/dist/canvas/SceneCanvas/presets/select.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/tools.js +24 -0
- package/dist/canvas/SceneCanvas/presets/tools.js.map +1 -0
- package/dist/canvas/SceneCanvas/presets/transform.js +4 -0
- package/dist/canvas/SceneCanvas/presets/transform.js.map +1 -1
- package/dist/canvas/SceneCanvas/presets/view.js +3 -1
- package/dist/canvas/SceneCanvas/presets/view.js.map +1 -1
- package/dist/canvas/SceneCanvas/props.js.map +1 -1
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +1 -1
- package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/toolRegistry.js +5 -4
- package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +26 -10
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useContributionRoles.js +15 -2
- package/dist/canvas/SceneCanvas/useContributionRoles.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js +5 -4
- package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +1 -1
- package/dist/canvas/SceneCanvas/useNodeAtPoint.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +9 -8
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js +2 -2
- package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +4 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneChrome.js +7 -4
- package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +9 -9
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
- package/dist/canvas/SceneCanvas/useScenePicking.js +4 -7
- package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneRedrawSources.js +2 -1
- package/dist/canvas/SceneCanvas/useSceneRedrawSources.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +6 -6
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +2 -2
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js +1 -1
- package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSurfaceViewInputs.js.map +1 -1
- package/dist/canvas/SceneCanvas/useToolKeybindings.js +2 -2
- package/dist/canvas/SceneCanvas/useToolKeybindings.js.map +1 -1
- package/dist/canvas/SceneCanvas/useViewportActions.js +5 -2
- package/dist/canvas/SceneCanvas/useViewportActions.js.map +1 -1
- package/dist/canvas/SceneCanvas/worldPointAt.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +22 -11
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SceneViewCanvas.js +1 -0
- package/dist/canvas/SceneViewCanvas.js.map +1 -1
- package/dist/canvas/ViewMarker.js.map +1 -1
- package/dist/canvas/accentColor.js +14 -1
- package/dist/canvas/accentColor.js.map +1 -1
- package/dist/canvas/affordanceAt.js +2 -2
- package/dist/canvas/affordanceAt.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +5 -3
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/deps/editAnchors.js +4 -4
- package/dist/canvas/deps/editAnchors.js.map +1 -1
- package/dist/canvas/deps/enteredGroup.js +14 -5
- package/dist/canvas/deps/enteredGroup.js.map +1 -1
- package/dist/canvas/deps/ingestion.js +5 -1
- package/dist/canvas/deps/ingestion.js.map +1 -1
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/insertSite.js +5 -3
- package/dist/canvas/deps/insertSite.js.map +1 -1
- package/dist/canvas/deps/nodeAtPoint.js +1 -12
- package/dist/canvas/deps/nodeAtPoint.js.map +1 -1
- package/dist/canvas/deps/pathEditCommits.js +3 -3
- package/dist/canvas/deps/pathEditCommits.js.map +1 -1
- package/dist/canvas/deps/slice.js +16 -2
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/deps/snap.js +5 -5
- package/dist/canvas/deps/snap.js.map +1 -1
- package/dist/canvas/deps/textEdit.js +5 -6
- package/dist/canvas/deps/textEdit.js.map +1 -1
- package/dist/canvas/derivedPath.js +8 -3
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/getNodeAtPoint.js +2 -2
- package/dist/canvas/getNodeAtPoint.js.map +1 -1
- package/dist/canvas/insertPreviewExtent.js.map +1 -1
- package/dist/canvas/layerSlots.js.map +1 -1
- package/dist/canvas/nodeShapes/measurePainter.js +1 -1
- package/dist/canvas/nodeShapes/measurePainter.js.map +1 -1
- package/dist/canvas/nodeShapes/strokedBounds.js +5 -3
- package/dist/canvas/nodeShapes/strokedBounds.js.map +1 -1
- package/dist/canvas/overlayRoleStrokes.js +6 -0
- package/dist/canvas/overlayRoleStrokes.js.map +1 -1
- package/dist/canvas/pickIndex/scenePickIndex.js +10 -12
- package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -1
- package/dist/canvas/sceneAdapter.js +10 -19
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +17 -4
- package/dist/canvas/sceneClipboard.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +3 -2
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/surfaceContribution.js.map +1 -1
- package/dist/canvas/useCanvasPaint.js +10 -4
- package/dist/canvas/useCanvasPaint.js.map +1 -1
- package/dist/canvas/useHitObserver.js +1 -1
- package/dist/canvas/useHitObserver.js.map +1 -1
- package/dist/canvas/useViewAim.js +1 -1
- package/dist/canvas/useViewAim.js.map +1 -1
- package/dist/canvas/useViewHelpers.js +8 -5
- package/dist/canvas/useViewHelpers.js.map +1 -1
- package/dist/canvas/useViewNesting.js +39 -12
- package/dist/canvas/useViewNesting.js.map +1 -1
- package/dist/canvas/viewInputs.js.map +1 -1
- package/dist/canvas/viewMarkers.js +2 -2
- package/dist/canvas/viewMarkers.js.map +1 -1
- package/dist/canvas/viewRegistry.js +2 -2
- package/dist/canvas/viewRegistry.js.map +1 -1
- package/dist/canvas/viewSnap.js +67 -0
- package/dist/canvas/viewSnap.js.map +1 -0
- package/dist/clipboard.d.ts +6 -12
- package/dist/{clone-qb2e1F5p.d.ts → clone-Bz4iqGfE.d.ts} +10 -5
- package/dist/clone.d.ts +5 -5
- package/dist/core/geometry/booleanOfPaths.js +23 -0
- package/dist/core/geometry/booleanOfPaths.js.map +1 -0
- package/dist/core/geometry/pathPoseDescriptor.js +12 -9
- package/dist/core/geometry/pathPoseDescriptor.js.map +1 -1
- package/dist/core/geometry/poseDescriptor.js +12 -3
- package/dist/core/geometry/poseDescriptor.js.map +1 -1
- package/dist/core/geometry/targetFrame.js +2 -13
- package/dist/core/geometry/targetFrame.js.map +1 -1
- package/dist/core/layers/render.js +2 -1
- package/dist/core/layers/render.js.map +1 -1
- package/dist/core/layers/retain.js +5 -3
- package/dist/core/layers/retain.js.map +1 -1
- package/dist/core/ops/setPath.js +3 -0
- package/dist/core/ops/setPath.js.map +1 -1
- package/dist/core/paint/meshKindLazy.js.map +1 -1
- package/dist/core/paint/paintKindEntry.js.map +1 -1
- package/dist/core/paint/paintKinds.js +8 -1
- package/dist/core/paint/paintKinds.js.map +1 -1
- package/dist/core/scene/NodeProperties.js.map +1 -1
- package/dist/core/scene/NodeRouting.js.map +1 -1
- package/dist/core/scene/booleanGroup.js +33 -0
- package/dist/core/scene/booleanGroup.js.map +1 -0
- package/dist/core/scene/booleanOfChildren.js +53 -0
- package/dist/core/scene/booleanOfChildren.js.map +1 -0
- package/dist/core/scene/carryDescendants.js +18 -6
- package/dist/core/scene/carryDescendants.js.map +1 -1
- package/dist/core/scene/composeFrame.js +42 -5
- package/dist/core/scene/composeFrame.js.map +1 -1
- package/dist/core/scene/composePose.js +35 -16
- package/dist/core/scene/composePose.js.map +1 -1
- package/dist/core/scene/depMemo.js +36 -18
- package/dist/core/scene/depMemo.js.map +1 -1
- package/dist/core/scene/derivedParts.js +2 -23
- package/dist/core/scene/derivedParts.js.map +1 -1
- package/dist/core/scene/effectivePose.js +10 -0
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/historyForm.js +5 -4
- package/dist/core/scene/historyForm.js.map +1 -1
- package/dist/core/scene/kitOps.js +5 -71
- 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/layerOps.js +82 -0
- package/dist/core/scene/layerOps.js.map +1 -0
- package/dist/core/scene/layoutPass.js +14 -12
- package/dist/core/scene/layoutPass.js.map +1 -1
- package/dist/core/scene/lockGuard.js +9 -3
- package/dist/core/scene/lockGuard.js.map +1 -1
- package/dist/core/scene/nestedHit.js +5 -5
- package/dist/core/scene/nestedHit.js.map +1 -1
- package/dist/core/scene/nodeFlags.js +8 -2
- package/dist/core/scene/nodeFlags.js.map +1 -1
- package/dist/core/scene/nodeGates.js +31 -2
- package/dist/core/scene/nodeGates.js.map +1 -1
- package/dist/core/scene/pathEffects/effects.js +22 -3
- package/dist/core/scene/pathEffects/effects.js.map +1 -1
- package/dist/core/scene/pathEffects/textAlong.js +16 -3
- package/dist/core/scene/pathEffects/textAlong.js.map +1 -1
- package/dist/core/scene/pathEffects/textAlongBands.js +65 -0
- package/dist/core/scene/pathEffects/textAlongBands.js.map +1 -0
- package/dist/core/scene/reconcileSpecs.js +1 -1
- package/dist/core/scene/reconcileSpecs.js.map +1 -1
- package/dist/core/scene/scene.js +11 -5
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneEdits.js +5 -2
- package/dist/core/scene/sceneEdits.js.map +1 -1
- package/dist/core/scene/scenePoseAdapter.js +10 -1
- package/dist/core/scene/scenePoseAdapter.js.map +1 -1
- package/dist/core/scene/scrollOffsets.js +93 -0
- package/dist/core/scene/scrollOffsets.js.map +1 -0
- package/dist/core/scene/serialization.js +15 -5
- package/dist/core/scene/serialization.js.map +1 -1
- package/dist/core/scene/statedFields.js +2 -1
- package/dist/core/scene/statedFields.js.map +1 -1
- package/dist/core/scene/strategyView.js +35 -0
- package/dist/core/scene/strategyView.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/useScene.js +2 -0
- package/dist/core/scene/useScene.js.map +1 -1
- package/dist/core/scene/worldSave.js +87 -0
- package/dist/core/scene/worldSave.js.map +1 -0
- package/dist/core/selection/selectionTarget.js +1 -1
- package/dist/core/selection/selectionTarget.js.map +1 -1
- package/dist/{cornerHandles-CNCb7aTY.d.ts → cornerHandles-DpmQM_z5.d.ts} +1 -1
- package/dist/{depSchema-CpIQzKWQ.d.ts → depSchema-C3BwxitB.d.ts} +119 -99
- package/dist/devtools-protocol.d.ts +2 -2
- package/dist/devtools.d.ts +2 -2
- package/dist/{features → domains}/chrome-caps/buildChromeCtx.js +1 -1
- package/dist/domains/chrome-caps/buildChromeCtx.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/conditions.js +1 -1
- package/dist/domains/chrome-caps/conditions.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/defaults.js +1 -1
- package/dist/domains/chrome-caps/defaults.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/resolve.js +1 -1
- package/dist/domains/chrome-caps/resolve.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/useChromeCaps.js +1 -1
- package/dist/domains/chrome-caps/useChromeCaps.js.map +1 -0
- package/dist/{features → domains}/chrome-caps/useHoverTracking.js +1 -1
- package/dist/domains/chrome-caps/useHoverTracking.js.map +1 -0
- package/dist/{features → domains}/ingestion/contentHandlers.js +20 -3
- package/dist/domains/ingestion/contentHandlers.js.map +1 -0
- package/dist/domains/ingestion/ghost.js +56 -0
- package/dist/domains/ingestion/ghost.js.map +1 -0
- package/dist/{features → domains}/ingestion/imageHandler.js +1 -1
- package/dist/domains/ingestion/imageHandler.js.map +1 -0
- package/dist/domains/ingestion/kitHandlers.js +22 -0
- package/dist/domains/ingestion/kitHandlers.js.map +1 -0
- package/dist/{features → domains}/ingestion/openFilePicker.js +1 -1
- package/dist/domains/ingestion/openFilePicker.js.map +1 -0
- package/dist/{features → domains}/ingestion/placement.js +1 -1
- package/dist/domains/ingestion/placement.js.map +1 -0
- package/dist/{features → domains}/ingestion/svgHandler.js +1 -1
- package/dist/domains/ingestion/svgHandler.js.map +1 -0
- package/dist/{features → domains}/ingestion/textHandler.js +9 -7
- package/dist/domains/ingestion/textHandler.js.map +1 -0
- package/dist/{features → domains}/ingestion/useCanvasIngestion.js +13 -15
- package/dist/domains/ingestion/useCanvasIngestion.js.map +1 -0
- package/dist/{features → domains}/ingestion/weaselJsonHandler.js +1 -1
- package/dist/domains/ingestion/weaselJsonHandler.js.map +1 -0
- package/dist/{features → domains}/longPress/longPressFeedback.js +1 -1
- package/dist/domains/longPress/longPressFeedback.js.map +1 -0
- package/dist/{features → domains}/measure/actionIds.js +1 -1
- package/dist/domains/measure/actionIds.js.map +1 -0
- package/dist/{features → domains}/measure/measureCommands.js +1 -1
- package/dist/domains/measure/measureCommands.js.map +1 -0
- package/dist/{features → domains}/measure/measureContribution.js +1 -1
- package/dist/domains/measure/measureContribution.js.map +1 -0
- package/dist/{features → domains}/measure/measureEdits.js +1 -1
- package/dist/domains/measure/measureEdits.js.map +1 -0
- package/dist/{features → domains}/measure/measureGestures.js +4 -7
- package/dist/domains/measure/measureGestures.js.map +1 -0
- package/dist/{features → domains}/measure/measureHit.js +1 -1
- package/dist/domains/measure/measureHit.js.map +1 -0
- package/dist/{features → domains}/measure/measureLayer.js +1 -1
- package/dist/domains/measure/measureLayer.js.map +1 -0
- package/dist/{features → domains}/measure/measurement.js +1 -1
- package/dist/domains/measure/measurement.js.map +1 -0
- package/dist/{features → domains}/measure/measurements.js +2 -2
- package/dist/domains/measure/measurements.js.map +1 -0
- package/dist/{features → domains}/modes/modeLayers.js +1 -1
- package/dist/domains/modes/modeLayers.js.map +1 -0
- package/dist/{features → domains}/modes/modeTriggers.js +3 -6
- package/dist/domains/modes/modeTriggers.js.map +1 -0
- package/dist/{features → domains}/pathEffects/effectSources.js +1 -1
- package/dist/domains/pathEffects/effectSources.js.map +1 -0
- package/dist/{features → domains}/pathEffects/index.js +1 -1
- package/dist/domains/pathEffects/index.js.map +1 -0
- package/dist/{features → domains}/pathEffects/readSource.js +1 -1
- package/dist/domains/pathEffects/readSource.js.map +1 -0
- package/dist/{features → domains}/pathEffects/textItems.js +11 -2
- package/dist/domains/pathEffects/textItems.js.map +1 -0
- package/dist/{features → domains}/pathEffects/useEffectSourcesLayer.js +1 -1
- package/dist/domains/pathEffects/useEffectSourcesLayer.js.map +1 -0
- package/dist/{features → domains}/paths/anchorData.js +1 -1
- package/dist/domains/paths/anchorData.js.map +1 -0
- package/dist/{features → domains}/paths/anchorMarkers.js +9 -7
- package/dist/domains/paths/anchorMarkers.js.map +1 -0
- package/dist/{features → domains}/paths/editablePath.js +1 -1
- package/dist/domains/paths/editablePath.js.map +1 -0
- package/dist/{features → domains}/paths/index.js +3 -3
- package/dist/{features → domains}/paths/originProjection.js +1 -1
- package/dist/domains/paths/originProjection.js.map +1 -0
- package/dist/{features → domains}/paths/pathEditExtension.js +1 -1
- package/dist/domains/paths/pathEditExtension.js.map +1 -0
- package/dist/{features → domains}/paths/pathEditState.js +1 -1
- package/dist/domains/paths/pathEditState.js.map +1 -0
- package/dist/{features → domains}/paths/pathEditingOverlayLayer.js +1 -1
- package/dist/domains/paths/pathEditingOverlayLayer.js.map +1 -0
- package/dist/{features → domains}/paths/pathLayer.js +1 -1
- package/dist/domains/paths/pathLayer.js.map +1 -0
- package/dist/{features → domains}/paths/pathRepresentation.js +1 -1
- package/dist/domains/paths/pathRepresentation.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/glyphs.js +1 -1
- package/dist/domains/paths/pathSnap/glyphs.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/pathSnapSource.js +1 -1
- package/dist/domains/paths/pathSnap/pathSnapSource.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/sceneIndex.js +1 -1
- package/dist/domains/paths/pathSnap/sceneIndex.js.map +1 -0
- package/dist/{features → domains}/paths/pathSnap/snapGeometry.js +3 -2
- package/dist/domains/paths/pathSnap/snapGeometry.js.map +1 -0
- package/dist/{features → domains}/paths/penPreviewLayer.js +1 -1
- package/dist/domains/paths/penPreviewLayer.js.map +1 -0
- package/dist/{features → domains}/paths/shapeGenerator.js +6 -4
- package/dist/domains/paths/shapeGenerator.js.map +1 -0
- package/dist/{features → domains}/paths/strokeOutline.js +1 -1
- package/dist/domains/paths/strokeOutline.js.map +1 -0
- package/dist/{features → domains}/paths/useAnchorFields.js +1 -1
- package/dist/domains/paths/useAnchorFields.js.map +1 -0
- package/dist/{features → domains}/paths/usePathEdit.js +1 -1
- package/dist/domains/paths/usePathEdit.js.map +1 -0
- package/dist/{features → domains}/poseRun/usePoseRun.js +1 -1
- package/dist/domains/poseRun/usePoseRun.js.map +1 -0
- package/dist/{features → domains}/selection/enteredGroupFrame.js +49 -10
- package/dist/domains/selection/enteredGroupFrame.js.map +1 -0
- package/dist/{features → domains}/selection/enteredGroupScope.js +1 -1
- package/dist/domains/selection/enteredGroupScope.js.map +1 -0
- package/dist/domains/selection/enteredOperands.js +29 -0
- package/dist/domains/selection/enteredOperands.js.map +1 -0
- package/dist/{features → domains}/selection/handleCommands.js +1 -1
- package/dist/domains/selection/handleCommands.js.map +1 -0
- package/dist/{features → domains}/selection/overlay.js +1 -1
- package/dist/domains/selection/overlay.js.map +1 -0
- package/dist/{features → domains}/selection/selectionPose.js +1 -1
- package/dist/domains/selection/selectionPose.js.map +1 -0
- package/dist/{features → domains}/snap/angle2d.js +1 -1
- package/dist/domains/snap/angle2d.js.map +1 -0
- package/dist/{features → domains}/snap/debugCandidates.js +1 -1
- package/dist/domains/snap/debugCandidates.js.map +1 -0
- package/dist/{features → domains}/snap/gather.js +1 -1
- package/dist/domains/snap/gather.js.map +1 -0
- package/dist/{features → domains}/snap/inputSubspace.js +1 -1
- package/dist/domains/snap/inputSubspace.js.map +1 -0
- package/dist/{features → domains}/snap/lazySession.js +1 -1
- package/dist/domains/snap/lazySession.js.map +1 -0
- package/dist/{features → domains}/snap/marks.js +1 -1
- package/dist/domains/snap/marks.js.map +1 -0
- package/dist/{features → domains}/snap/outcomeStore.js +1 -1
- package/dist/domains/snap/outcomeStore.js.map +1 -0
- package/dist/{features → domains}/snap/pointSnap.js +1 -1
- package/dist/domains/snap/pointSnap.js.map +1 -0
- package/dist/{features → domains}/snap/probes2d.js +1 -1
- package/dist/domains/snap/probes2d.js.map +1 -0
- package/dist/{features → domains}/snap/readoutDraw.js +1 -1
- package/dist/domains/snap/readoutDraw.js.map +1 -0
- package/dist/{features → domains}/snap/readoutLayer.js +1 -1
- package/dist/domains/snap/readoutLayer.js.map +1 -0
- package/dist/{features → domains}/snap/registry.js +1 -1
- package/dist/domains/snap/registry.js.map +1 -0
- package/dist/{features → domains}/snap/resizeBox.js +1 -1
- package/dist/domains/snap/resizeBox.js.map +1 -0
- package/dist/{features → domains}/snap/roles.js +1 -1
- package/dist/domains/snap/roles.js.map +1 -0
- package/dist/{features → domains}/snap/rotationSource.js +1 -1
- package/dist/domains/snap/rotationSource.js.map +1 -0
- package/dist/{features → domains}/snap/sceneReads.js +24 -3
- package/dist/domains/snap/sceneReads.js.map +1 -0
- package/dist/{features → domains}/snap/sceneSession2d.js +3 -2
- package/dist/domains/snap/sceneSession2d.js.map +1 -0
- package/dist/{features → domains}/snap/screen.js +1 -1
- package/dist/domains/snap/screen.js.map +1 -0
- package/dist/{features → domains}/snap/session2d.js +3 -2
- package/dist/domains/snap/session2d.js.map +1 -0
- package/dist/{features → domains}/snap/spanSnap.js +1 -1
- package/dist/domains/snap/spanSnap.js.map +1 -0
- package/dist/{features → domains}/snap/stepAngles.js +1 -1
- package/dist/domains/snap/stepAngles.js.map +1 -0
- package/dist/{features → domains}/text/editingDrawOne.js +4 -4
- package/dist/domains/text/editingDrawOne.js.map +1 -0
- package/dist/domains/text/hostTextEditors.js +24 -0
- package/dist/domains/text/hostTextEditors.js.map +1 -0
- package/dist/{features → domains}/text/index.js +1 -1
- package/dist/{features → domains}/text/sceneTextDrafts.js +1 -1
- package/dist/domains/text/sceneTextDrafts.js.map +1 -0
- package/dist/{features → domains}/text/textHitAt.js +18 -2
- package/dist/domains/text/textHitAt.js.map +1 -0
- package/dist/{features → domains}/text/textLayer.js +1 -1
- package/dist/domains/text/textLayer.js.map +1 -0
- package/dist/{features → domains}/text/useSceneTextEdit.js +11 -3
- package/dist/domains/text/useSceneTextEdit.js.map +1 -0
- package/dist/{features → domains}/viewports/aim.js +1 -1
- package/dist/domains/viewports/aim.js.map +1 -0
- package/dist/{features → domains}/viewports/viewMarker.js +23 -5
- package/dist/domains/viewports/viewMarker.js.map +1 -0
- package/dist/{features → domains}/viewports/viewMarkerDrag.js +3 -3
- package/dist/domains/viewports/viewMarkerDrag.js.map +1 -0
- package/dist/{features → domains}/viewports/viewResolver.js +1 -1
- package/dist/domains/viewports/viewResolver.js.map +1 -0
- package/dist/{features → domains}/viewports/viewportLayer.js +1 -1
- package/dist/domains/viewports/viewportLayer.js.map +1 -0
- package/dist/icons.table.js +1 -1
- package/dist/icons.table.js.map +1 -1
- package/dist/icons.table.json +106 -6
- package/dist/index.d.ts +1441 -1050
- package/dist/index.js +90 -77
- package/dist/interactions/actions/admitsChild.js +6 -2
- package/dist/interactions/actions/admitsChild.js.map +1 -1
- package/dist/interactions/actions/align/align.js +1 -4
- package/dist/interactions/actions/align/align.js.map +1 -1
- package/dist/interactions/actions/booleans/booleanGroupAction.js +66 -0
- package/dist/interactions/actions/booleans/booleanGroupAction.js.map +1 -0
- package/dist/interactions/actions/booleans/keymap.js +52 -0
- package/dist/interactions/actions/booleans/keymap.js.map +1 -0
- package/dist/interactions/actions/booleans/operands.js +1 -1
- package/dist/interactions/actions/booleans/pieces.js +4 -11
- package/dist/interactions/actions/booleans/pieces.js.map +1 -1
- package/dist/interactions/actions/clipboard/action.js +3 -7
- package/dist/interactions/actions/clipboard/action.js.map +1 -1
- package/dist/interactions/actions/clipboard/clipboardOps.js +1 -1
- package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
- package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +6 -6
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
- package/dist/interactions/actions/defaults/containerScroll.js +170 -0
- package/dist/interactions/actions/defaults/containerScroll.js.map +1 -0
- package/dist/interactions/actions/defaults/contextMenu.js +2 -5
- package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
- package/dist/interactions/actions/defaults/delete.js +1 -1
- package/dist/interactions/actions/defaults/delete.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +1 -3
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/enterGroup.js +7 -12
- package/dist/interactions/actions/defaults/enterGroup.js.map +1 -1
- package/dist/interactions/actions/defaults/enterPathEdit.js +4 -8
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +12 -24
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +18 -10
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +3 -3
- package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
- package/dist/interactions/actions/defaults/nudgeSteps.js +3 -21
- package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -1
- package/dist/interactions/actions/defaults/pathEditMarks.js.map +1 -1
- package/dist/interactions/actions/defaults/selectDeep.js +2 -2
- package/dist/interactions/actions/defaults/selectDeep.js.map +1 -1
- package/dist/interactions/actions/distribute/distribute.js.map +1 -1
- package/dist/interactions/actions/dropVerdict.js +9 -0
- package/dist/interactions/actions/dropVerdict.js.map +1 -0
- package/dist/interactions/actions/edit-anchors/action.js +2 -2
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/extensionDrags.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/geometry.js +8 -6
- package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/handleDrag.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/preview.js +1 -1
- package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
- package/dist/interactions/actions/flip/action.js +3 -2
- package/dist/interactions/actions/flip/action.js.map +1 -1
- package/dist/interactions/actions/geometryProjection.js +8 -3
- package/dist/interactions/actions/geometryProjection.js.map +1 -1
- package/dist/interactions/actions/gestureFrames.js +1 -0
- package/dist/interactions/actions/gestureFrames.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +6 -2
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/frame.js +1 -1
- package/dist/interactions/actions/insert/frame.js.map +1 -1
- package/dist/interactions/actions/insert/placeAction.js +6 -8
- package/dist/interactions/actions/insert/placeAction.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 +6 -15
- package/dist/interactions/actions/move/action.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/layoutDrop.js +12 -2
- package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/reparentOnDrop.js +29 -10
- package/dist/interactions/actions/move/behaviors/reparentOnDrop.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +42 -6
- package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
- package/dist/interactions/actions/move/capture.js +4 -8
- package/dist/interactions/actions/move/capture.js.map +1 -1
- package/dist/interactions/actions/move/commit/index.js.map +1 -1
- package/dist/interactions/actions/move/commit/layoutDrop.js +4 -3
- package/dist/interactions/actions/move/commit/layoutDrop.js.map +1 -1
- package/dist/interactions/actions/move/commit/layoutRelease.js +15 -9
- package/dist/interactions/actions/move/commit/layoutRelease.js.map +1 -1
- package/dist/interactions/actions/move/commit/reparent.js +19 -13
- package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
- package/dist/interactions/actions/move/delta.js +51 -5
- package/dist/interactions/actions/move/delta.js.map +1 -1
- package/dist/interactions/actions/move/frame.js +8 -4
- package/dist/interactions/actions/move/frame.js.map +1 -1
- package/dist/interactions/actions/move/layout/container.js +19 -9
- package/dist/interactions/actions/move/layout/container.js.map +1 -1
- package/dist/interactions/actions/move/layout/dragged.js +2 -2
- package/dist/interactions/actions/move/layout/dragged.js.map +1 -1
- package/dist/interactions/actions/move/layout/dropPass.js +36 -18
- package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
- package/dist/interactions/actions/move/layout/exits.js +2 -2
- package/dist/interactions/actions/move/layout/exits.js.map +1 -1
- package/dist/interactions/actions/move/layout/glide.js +5 -5
- package/dist/interactions/actions/move/layout/glide.js.map +1 -1
- package/dist/interactions/actions/move/layout/view.js +55 -0
- package/dist/interactions/actions/move/layout/view.js.map +1 -0
- package/dist/interactions/actions/move/refusal.js +52 -0
- package/dist/interactions/actions/move/refusal.js.map +1 -0
- package/dist/interactions/actions/move/session.js +9 -8
- package/dist/interactions/actions/move/session.js.map +1 -1
- package/dist/interactions/actions/move/translate.js +10 -4
- package/dist/interactions/actions/move/translate.js.map +1 -1
- package/dist/interactions/actions/moveNodes.js +21 -5
- package/dist/interactions/actions/moveNodes.js.map +1 -1
- package/dist/interactions/actions/pathCommands/pathNodes.js +3 -3
- package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
- package/dist/interactions/actions/planeInput.js +14 -13
- package/dist/interactions/actions/planeInput.js.map +1 -1
- package/dist/interactions/actions/poseFrame.js +21 -8
- package/dist/interactions/actions/poseFrame.js.map +1 -1
- package/dist/interactions/actions/resize/action.js +1 -1
- package/dist/interactions/actions/resize/action.js.map +1 -1
- package/dist/interactions/actions/resize/aspect.js +1 -1
- package/dist/interactions/actions/resize/aspect.js.map +1 -1
- package/dist/interactions/actions/resize/commit.js +10 -1
- package/dist/interactions/actions/resize/commit.js.map +1 -1
- package/dist/interactions/actions/resize/frame.js +3 -2
- package/dist/interactions/actions/resize/frame.js.map +1 -1
- package/dist/interactions/actions/resize/held.js +44 -0
- package/dist/interactions/actions/resize/held.js.map +1 -0
- package/dist/interactions/actions/resize/session.js +3 -1
- package/dist/interactions/actions/resize/session.js.map +1 -1
- package/dist/interactions/actions/resolveOverlays.js +9 -0
- package/dist/interactions/actions/resolveOverlays.js.map +1 -1
- package/dist/interactions/actions/resolvePreviews.js +23 -7
- package/dist/interactions/actions/resolvePreviews.js.map +1 -1
- package/dist/interactions/actions/rotate/action.js +1 -1
- package/dist/interactions/actions/rotate/action.js.map +1 -1
- package/dist/interactions/actions/rotate/frame.js +1 -1
- package/dist/interactions/actions/rotate/frame.js.map +1 -1
- package/dist/interactions/actions/rotate/session.js +2 -2
- package/dist/interactions/actions/rotate/session.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +2 -4
- package/dist/interactions/actions/selectionSubtree.js.map +1 -1
- package/dist/interactions/actions/spatialReference.js +4 -9
- package/dist/interactions/actions/spatialReference.js.map +1 -1
- package/dist/interactions/gestures/dragRadial/dragRadial.js.map +1 -1
- package/dist/interactions/gestures/dragRect/dragRect.js.map +1 -1
- package/dist/kitSettingsSchema.js +2 -2
- package/dist/kitSettingsSchema.js.map +1 -1
- package/dist/{math-C_elShP_.d.ts → math-B_unOnF4.d.ts} +2 -2
- package/dist/math.d.ts +3 -3
- package/dist/move.d.ts +6 -6
- package/dist/{options-ClEm3SEl.d.ts → options-DAjkhbcs.d.ts} +5 -5
- package/dist/{poseDescriptor-DN2yfnq_.d.ts → poseDescriptor-CvSxVhCd.d.ts} +17 -4
- package/dist/resize.d.ts +6 -6
- package/dist/style.css +14 -0
- package/dist/test-seams.d.ts +11 -15
- package/dist/test-seams.js +1 -1
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js +3 -6
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
- package/dist/tools/builtin/line/useLineTool.js.map +1 -1
- package/dist/tools/builtin/measure/useMeasureTool.js +2 -2
- package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
- package/dist/tools/builtin/pen/penHandle.js +2 -2
- package/dist/tools/builtin/pen/penHandle.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +13 -19
- package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
- package/dist/tools/builtin/select/groupEntry.js +1 -1
- package/dist/tools/builtin/select/groupEntry.js.map +1 -1
- package/dist/tools/builtin/select/selectionContributions.js +1 -1
- package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +7 -7
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/slice/useSliceTool.js +5 -8
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +1 -1
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/{types-49uYJN3j.d.ts → types-A_sNJYRd.d.ts} +8 -0
- package/dist/{types-BEdRIhNc.d.ts → types-DW14eu9B.d.ts} +131 -36
- package/dist/{types-p2alu-R1.d.ts → types-VCtfchp-.d.ts} +4 -6
- package/dist/util/color.js +12 -14
- package/dist/util/color.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -24
- package/dist/canvas/poseCascade.js +0 -65
- package/dist/canvas/poseCascade.js.map +0 -1
- package/dist/features/chrome-caps/buildChromeCtx.js.map +0 -1
- package/dist/features/chrome-caps/conditions.js.map +0 -1
- package/dist/features/chrome-caps/defaults.js.map +0 -1
- package/dist/features/chrome-caps/resolve.js.map +0 -1
- package/dist/features/chrome-caps/useChromeCaps.js.map +0 -1
- package/dist/features/chrome-caps/useHoverTracking.js.map +0 -1
- package/dist/features/ingestion/contentHandlers.js.map +0 -1
- package/dist/features/ingestion/imageHandler.js.map +0 -1
- package/dist/features/ingestion/openFilePicker.js.map +0 -1
- package/dist/features/ingestion/placement.js.map +0 -1
- package/dist/features/ingestion/registerKitHandlers.js +0 -43
- package/dist/features/ingestion/registerKitHandlers.js.map +0 -1
- package/dist/features/ingestion/svgHandler.js.map +0 -1
- package/dist/features/ingestion/textHandler.js.map +0 -1
- package/dist/features/ingestion/useCanvasIngestion.js.map +0 -1
- package/dist/features/ingestion/weaselJsonHandler.js.map +0 -1
- package/dist/features/longPress/longPressFeedback.js.map +0 -1
- package/dist/features/measure/actionIds.js.map +0 -1
- package/dist/features/measure/measureCommands.js.map +0 -1
- package/dist/features/measure/measureContribution.js.map +0 -1
- package/dist/features/measure/measureEdits.js.map +0 -1
- package/dist/features/measure/measureGestures.js.map +0 -1
- package/dist/features/measure/measureHit.js.map +0 -1
- package/dist/features/measure/measureLayer.js.map +0 -1
- package/dist/features/measure/measurement.js.map +0 -1
- package/dist/features/measure/measurements.js.map +0 -1
- package/dist/features/modes/modeLayers.js.map +0 -1
- package/dist/features/modes/modeTriggers.js.map +0 -1
- package/dist/features/pathEffects/effectSources.js.map +0 -1
- package/dist/features/pathEffects/index.js.map +0 -1
- package/dist/features/pathEffects/readSource.js.map +0 -1
- package/dist/features/pathEffects/textItems.js.map +0 -1
- package/dist/features/pathEffects/useEffectSourcesLayer.js.map +0 -1
- package/dist/features/paths/anchorData.js.map +0 -1
- package/dist/features/paths/anchorMarkers.js.map +0 -1
- package/dist/features/paths/editablePath.js.map +0 -1
- package/dist/features/paths/originProjection.js.map +0 -1
- package/dist/features/paths/pathEditExtension.js.map +0 -1
- package/dist/features/paths/pathEditState.js.map +0 -1
- package/dist/features/paths/pathEditingOverlayLayer.js.map +0 -1
- package/dist/features/paths/pathLayer.js.map +0 -1
- package/dist/features/paths/pathRepresentation.js.map +0 -1
- package/dist/features/paths/pathSnap/glyphs.js.map +0 -1
- package/dist/features/paths/pathSnap/pathSnapSource.js.map +0 -1
- package/dist/features/paths/pathSnap/sceneIndex.js.map +0 -1
- package/dist/features/paths/pathSnap/snapGeometry.js.map +0 -1
- package/dist/features/paths/penPreviewLayer.js.map +0 -1
- package/dist/features/paths/shapeGenerator.js.map +0 -1
- package/dist/features/paths/strokeOutline.js.map +0 -1
- package/dist/features/paths/useAnchorFields.js.map +0 -1
- package/dist/features/paths/usePathEdit.js.map +0 -1
- package/dist/features/poseRun/usePoseRun.js.map +0 -1
- package/dist/features/selection/enteredGroupFrame.js.map +0 -1
- package/dist/features/selection/enteredGroupScope.js.map +0 -1
- package/dist/features/selection/handleCommands.js.map +0 -1
- package/dist/features/selection/overlay.js.map +0 -1
- package/dist/features/selection/selectionPose.js.map +0 -1
- package/dist/features/snap/angle2d.js.map +0 -1
- package/dist/features/snap/debugCandidates.js.map +0 -1
- package/dist/features/snap/gather.js.map +0 -1
- package/dist/features/snap/inputSubspace.js.map +0 -1
- package/dist/features/snap/lazySession.js.map +0 -1
- package/dist/features/snap/marks.js.map +0 -1
- package/dist/features/snap/outcomeStore.js.map +0 -1
- package/dist/features/snap/pointSnap.js.map +0 -1
- package/dist/features/snap/probes2d.js.map +0 -1
- package/dist/features/snap/readoutDraw.js.map +0 -1
- package/dist/features/snap/readoutLayer.js.map +0 -1
- package/dist/features/snap/registry.js.map +0 -1
- package/dist/features/snap/resizeBox.js.map +0 -1
- package/dist/features/snap/roles.js.map +0 -1
- package/dist/features/snap/rotationSource.js.map +0 -1
- package/dist/features/snap/sceneReads.js.map +0 -1
- package/dist/features/snap/sceneSession2d.js.map +0 -1
- package/dist/features/snap/screen.js.map +0 -1
- package/dist/features/snap/session2d.js.map +0 -1
- package/dist/features/snap/spanSnap.js.map +0 -1
- package/dist/features/snap/stepAngles.js.map +0 -1
- package/dist/features/text/editingDrawOne.js.map +0 -1
- package/dist/features/text/sceneTextDrafts.js.map +0 -1
- package/dist/features/text/textHitAt.js.map +0 -1
- package/dist/features/text/textLayer.js.map +0 -1
- package/dist/features/text/useSceneTextEdit.js.map +0 -1
- package/dist/features/viewports/aim.js.map +0 -1
- package/dist/features/viewports/viewMarker.js.map +0 -1
- package/dist/features/viewports/viewMarkerDrag.js.map +0 -1
- package/dist/features/viewports/viewResolver.js.map +0 -1
- package/dist/features/viewports/viewportLayer.js.map +0 -1
- /package/dist/{features → domains}/chrome-caps/index.js +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//#region src/core/scene/layerOps.ts
|
|
2
|
+
function setParallax(record, to) {
|
|
3
|
+
if (to === void 0) delete record.parallax;
|
|
4
|
+
else record.parallax = to;
|
|
5
|
+
}
|
|
6
|
+
/** Register the layer op kinds against `env`'s layer stack. */
|
|
7
|
+
function registerLayerOps(env) {
|
|
8
|
+
const { state, requireLayerIndex, rebuildLayerIndex } = env;
|
|
9
|
+
function registerKitOp(kind, handler) {
|
|
10
|
+
env.register(kind, handler);
|
|
11
|
+
}
|
|
12
|
+
registerKitOp("kit:setLayerVisible", {
|
|
13
|
+
apply: (p) => {
|
|
14
|
+
state.layers[requireLayerIndex(p.layer)].visible = p.to;
|
|
15
|
+
if (!p.to) env.narrowSelection();
|
|
16
|
+
},
|
|
17
|
+
revert: (p) => {
|
|
18
|
+
state.layers[requireLayerIndex(p.layer)].visible = p.from;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
registerKitOp("kit:setLayerLocked", {
|
|
22
|
+
apply: (p) => {
|
|
23
|
+
state.layers[requireLayerIndex(p.layer)].locked = p.to;
|
|
24
|
+
if (p.to) env.narrowSelection();
|
|
25
|
+
},
|
|
26
|
+
revert: (p) => {
|
|
27
|
+
state.layers[requireLayerIndex(p.layer)].locked = p.from;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
registerKitOp("kit:setLayerParallax", {
|
|
31
|
+
apply: (p) => {
|
|
32
|
+
setParallax(state.layers[requireLayerIndex(p.layer)], p.to);
|
|
33
|
+
},
|
|
34
|
+
revert: (p) => {
|
|
35
|
+
setParallax(state.layers[requireLayerIndex(p.layer)], p.from);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
registerKitOp("kit:addLayer", {
|
|
39
|
+
apply: (p) => {
|
|
40
|
+
state.layers.splice(p.index, 0, { ...p.record });
|
|
41
|
+
rebuildLayerIndex();
|
|
42
|
+
},
|
|
43
|
+
revert: (p) => {
|
|
44
|
+
state.layers.splice(p.index, 1);
|
|
45
|
+
rebuildLayerIndex();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
registerKitOp("kit:removeLayer", {
|
|
49
|
+
apply: (p) => {
|
|
50
|
+
state.layers.splice(p.index, 1);
|
|
51
|
+
rebuildLayerIndex();
|
|
52
|
+
},
|
|
53
|
+
revert: (p) => {
|
|
54
|
+
state.layers.splice(p.index, 0, { ...p.record });
|
|
55
|
+
rebuildLayerIndex();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
registerKitOp("kit:renameLayer", {
|
|
59
|
+
apply: (p) => {
|
|
60
|
+
state.layers[requireLayerIndex(p.layer)].name = p.to;
|
|
61
|
+
},
|
|
62
|
+
revert: (p) => {
|
|
63
|
+
state.layers[requireLayerIndex(p.layer)].name = p.from;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
/** Finds the layer by id, not by where the payload last left it: moves of
|
|
67
|
+
* one layer coalesce, and the entry then reverts the first payload from
|
|
68
|
+
* wherever the last one put the layer. */
|
|
69
|
+
function placeLayer(layer, index) {
|
|
70
|
+
const [rec] = state.layers.splice(requireLayerIndex(layer), 1);
|
|
71
|
+
state.layers.splice(index, 0, rec);
|
|
72
|
+
rebuildLayerIndex();
|
|
73
|
+
}
|
|
74
|
+
registerKitOp("kit:moveLayer", {
|
|
75
|
+
apply: (p) => placeLayer(p.layer, p.to),
|
|
76
|
+
revert: (p) => placeLayer(p.layer, p.from)
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { registerLayerOps };
|
|
82
|
+
//# sourceMappingURL=layerOps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layerOps.js","names":[],"sources":["../../../src/core/scene/layerOps.ts"],"sourcesContent":["/**\n * The kit's layer op kinds: visibility, lock, parallax, and adding, removing,\n * renaming, and moving a layer.\n */\nimport type { ParallaxOpts } from 'core/viewport/parallax';\nimport type { KitOpsEnv } from './kitOps';\nimport type { LayerRecord, RegisteredOp, UserLayerRecord } from './types';\n\nfunction setParallax(record: { parallax?: ParallaxOpts }, to: ParallaxOpts | undefined): void {\n if (to === undefined) delete record.parallax;\n else record.parallax = to;\n}\n\n/** Register the layer op kinds against `env`'s layer stack. */\nexport function registerLayerOps<TData, TLayer extends string, TPose>(\n env: Pick<KitOpsEnv<TData, TLayer, TPose>, 'state' | 'register' | 'requireLayerIndex' | 'rebuildLayerIndex' | 'narrowSelection'>,\n): void {\n const { state, requireLayerIndex, rebuildLayerIndex } = env;\n function registerKitOp<P>(kind: string, handler: RegisteredOp<P>): void {\n env.register(kind, handler as RegisteredOp<unknown>);\n }\n\n registerKitOp<{ layer: TLayer; from: boolean; to: boolean }>('kit:setLayerVisible', {\n apply: (p) => { state.layers[requireLayerIndex(p.layer)].visible = p.to; if (!p.to) env.narrowSelection(); },\n revert: (p) => { state.layers[requireLayerIndex(p.layer)].visible = p.from; },\n });\n\n registerKitOp<{ layer: TLayer; from: boolean; to: boolean }>('kit:setLayerLocked', {\n apply: (p) => { state.layers[requireLayerIndex(p.layer)].locked = p.to; if (p.to) env.narrowSelection(); },\n revert: (p) => { state.layers[requireLayerIndex(p.layer)].locked = p.from; },\n });\n\n registerKitOp<{ layer: TLayer; from?: ParallaxOpts; to?: ParallaxOpts }>('kit:setLayerParallax', {\n apply: (p) => { setParallax(state.layers[requireLayerIndex(p.layer)], p.to); },\n revert: (p) => { setParallax(state.layers[requireLayerIndex(p.layer)], p.from); },\n });\n\n registerKitOp<{ record: UserLayerRecord<TLayer>; index: number }>('kit:addLayer', {\n apply: (p) => { state.layers.splice(p.index, 0, { ...p.record }); rebuildLayerIndex(); },\n revert: (p) => { state.layers.splice(p.index, 1); rebuildLayerIndex(); },\n });\n\n registerKitOp<{ record: LayerRecord<TLayer>; index: number }>('kit:removeLayer', {\n apply: (p) => { state.layers.splice(p.index, 1); rebuildLayerIndex(); },\n revert: (p) => { state.layers.splice(p.index, 0, { ...p.record } as LayerRecord<TLayer>); rebuildLayerIndex(); },\n });\n\n registerKitOp<{ layer: TLayer; from: string; to: string }>('kit:renameLayer', {\n apply: (p) => { (state.layers[requireLayerIndex(p.layer)] as UserLayerRecord<TLayer>).name = p.to; },\n revert: (p) => { (state.layers[requireLayerIndex(p.layer)] as UserLayerRecord<TLayer>).name = p.from; },\n });\n\n /** Finds the layer by id, not by where the payload last left it: moves of\n * one layer coalesce, and the entry then reverts the first payload from\n * wherever the last one put the layer. */\n function placeLayer(layer: TLayer, index: number): void {\n const [rec] = state.layers.splice(requireLayerIndex(layer), 1);\n state.layers.splice(index, 0, rec);\n rebuildLayerIndex();\n }\n\n registerKitOp<{ layer: TLayer; from: number; to: number }>('kit:moveLayer', {\n apply: (p) => placeLayer(p.layer, p.to),\n revert: (p) => placeLayer(p.layer, p.from),\n });\n}\n"],"mappings":";AAQA,SAAS,YAAY,QAAqC,IAAoC;CAC5F,IAAI,OAAO,QAAW,OAAO,OAAO;MAC/B,OAAO,WAAW;AACzB;;AAGA,SAAgB,iBACd,KACM;CACN,MAAM,EAAE,OAAO,mBAAmB,sBAAsB;CACxD,SAAS,cAAiB,MAAc,SAAgC;EACtE,IAAI,SAAS,MAAM,OAAgC;CACrD;CAEA,cAA6D,uBAAuB;EAClF,QAAQ,MAAM;GAAE,MAAM,OAAO,kBAAkB,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE;GAAI,IAAI,CAAC,EAAE,IAAI,IAAI,gBAAgB;EAAG;EAC3G,SAAS,MAAM;GAAE,MAAM,OAAO,kBAAkB,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE;EAAM;CAC9E,CAAC;CAED,cAA6D,sBAAsB;EACjF,QAAQ,MAAM;GAAE,MAAM,OAAO,kBAAkB,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE;GAAI,IAAI,EAAE,IAAI,IAAI,gBAAgB;EAAG;EACzG,SAAS,MAAM;GAAE,MAAM,OAAO,kBAAkB,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE;EAAM;CAC7E,CAAC;CAED,cAAyE,wBAAwB;EAC/F,QAAQ,MAAM;GAAE,YAAY,MAAM,OAAO,kBAAkB,EAAE,KAAK,IAAI,EAAE,EAAE;EAAG;EAC7E,SAAS,MAAM;GAAE,YAAY,MAAM,OAAO,kBAAkB,EAAE,KAAK,IAAI,EAAE,IAAI;EAAG;CAClF,CAAC;CAED,cAAkE,gBAAgB;EAChF,QAAQ,MAAM;GAAE,MAAM,OAAO,OAAO,EAAE,OAAO,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC;GAAG,kBAAkB;EAAG;EACvF,SAAS,MAAM;GAAE,MAAM,OAAO,OAAO,EAAE,OAAO,CAAC;GAAG,kBAAkB;EAAG;CACzE,CAAC;CAED,cAA8D,mBAAmB;EAC/E,QAAQ,MAAM;GAAE,MAAM,OAAO,OAAO,EAAE,OAAO,CAAC;GAAG,kBAAkB;EAAG;EACtE,SAAS,MAAM;GAAE,MAAM,OAAO,OAAO,EAAE,OAAO,GAAG,EAAE,GAAG,EAAE,OAAO,CAAwB;GAAG,kBAAkB;EAAG;CACjH,CAAC;CAED,cAA2D,mBAAmB;EAC5E,QAAQ,MAAM;GAAE,MAAO,OAAO,kBAAkB,EAAE,KAAK,EAAE,CAA6B,OAAO,EAAE;EAAI;EACnG,SAAS,MAAM;GAAE,MAAO,OAAO,kBAAkB,EAAE,KAAK,EAAE,CAA6B,OAAO,EAAE;EAAM;CACxG,CAAC;;;;CAKD,SAAS,WAAW,OAAe,OAAqB;EACtD,MAAM,CAAC,OAAO,MAAM,OAAO,OAAO,kBAAkB,KAAK,GAAG,CAAC;EAC7D,MAAM,OAAO,OAAO,OAAO,GAAG,GAAG;EACjC,kBAAkB;CACpB;CAEA,cAA2D,iBAAiB;EAC1E,QAAQ,MAAM,WAAW,EAAE,OAAO,EAAE,EAAE;EACtC,SAAS,MAAM,WAAW,EAAE,OAAO,EAAE,IAAI;CAC3C,CAAC;AACH"}
|
|
@@ -2,6 +2,7 @@ import { composeWorldPose, rebaseLocalPose } from "./composeFrame.js";
|
|
|
2
2
|
import { samePoseValue } from "./poseSnapshot.js";
|
|
3
3
|
import { definesFrame, documentPose } from "./effectivePose.js";
|
|
4
4
|
import { carriedDescendants } from "./carryDescendants.js";
|
|
5
|
+
import { strategyView } from "./strategyView.js";
|
|
5
6
|
import { asRectPose } from "./kitRegistry.js";
|
|
6
7
|
|
|
7
8
|
//#region src/core/scene/layoutPass.ts
|
|
@@ -16,8 +17,10 @@ import { asRectPose } from "./kitRegistry.js";
|
|
|
16
17
|
* resized, had a child moved, or gained or lost children with neither hook —
|
|
17
18
|
* is put back in its resting arrangement, `childPoses`.
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* A strategy sees the container upright: an upright container and its
|
|
21
|
+
* children in world, and a turned one in its own frame, placed where its
|
|
22
|
+
* unturned box sits, so children are laid out along the container's axes.
|
|
23
|
+
* Each result is taken back to world and rebased into its node's parent frame
|
|
21
24
|
* after. A child already where the strategy puts it is left out, so a pass
|
|
22
25
|
* that changes nothing writes nothing. A child the pass moves takes its own
|
|
23
26
|
* descendants along (`carriedDescendants`). A container this pass resizes is
|
|
@@ -72,21 +75,20 @@ function runLayoutPass(source, layoutOf, arrivals, changed, frame, departures =
|
|
|
72
75
|
if (id !== current && layoutOf(id) !== null && resized(frame, id, before, pose)) queue.push(id);
|
|
73
76
|
};
|
|
74
77
|
let current = null;
|
|
75
|
-
/** The container and its children as the strategy sees them,
|
|
76
|
-
*
|
|
78
|
+
/** The container and its children as the strategy sees them, with every
|
|
79
|
+
* write this pass has made so far folded in. */
|
|
77
80
|
const frameOf = (containerId) => {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
if (bounds === null) return null;
|
|
81
|
+
const view = strategyView(world(containerId), source.composition, (p) => frame.bounds(p, containerId), (p, from, to) => frame.remap(p, from, to, containerId));
|
|
82
|
+
if (view === null) return null;
|
|
81
83
|
const children = source.childrenOf(containerId).map((cid) => ({
|
|
82
84
|
id: cid,
|
|
83
|
-
pose: world(cid)
|
|
85
|
+
pose: view.toStrategy(world(cid))
|
|
84
86
|
}));
|
|
85
87
|
return {
|
|
86
|
-
|
|
88
|
+
...view,
|
|
87
89
|
container: {
|
|
88
90
|
id: containerId,
|
|
89
|
-
bounds
|
|
91
|
+
bounds: view.bounds
|
|
90
92
|
},
|
|
91
93
|
children
|
|
92
94
|
};
|
|
@@ -94,14 +96,14 @@ function runLayoutPass(source, layoutOf, arrivals, changed, frame, departures =
|
|
|
94
96
|
const apply = (containerId, at, result) => {
|
|
95
97
|
if (result.bounds) {
|
|
96
98
|
const node = source.get(containerId);
|
|
97
|
-
const sized = frame.remap(at.
|
|
99
|
+
const sized = at.toWorld(frame.remap(at.upright, at.container.bounds, result.bounds, containerId));
|
|
98
100
|
write(containerId, rebaseLocalPose(pa, sized, node.parent, compose, decompose));
|
|
99
101
|
}
|
|
100
102
|
for (const [cid, pose] of result.poses) {
|
|
101
103
|
const id = cid;
|
|
102
104
|
if (source.get(id)?.parent !== containerId) continue;
|
|
103
105
|
const before = frame.bounds(world(id), id);
|
|
104
|
-
write(id, rebaseLocalPose(pa, pose, containerId, compose, decompose));
|
|
106
|
+
write(id, rebaseLocalPose(pa, at.toWorld(pose), containerId, compose, decompose));
|
|
105
107
|
carry(id, before);
|
|
106
108
|
}
|
|
107
109
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layoutPass.js","names":[],"sources":["../../../src/core/scene/layoutPass.ts"],"sourcesContent":["/**\n * The one computation behind a scene's container layouts: given the\n * containers one edit changed, the pose writes their layouts ask for.\n *\n * A container that lost children and whose strategy has `depart` rearranges\n * the rest through it (and may shrink). One that gained children and whose\n * strategy has `arrive` then places them through it (and may refuse, or\n * grow). Any other changed container — one that reordered children, was\n * resized, had a child moved, or gained or lost children with neither hook —\n * is put back in its resting arrangement, `childPoses`.\n *\n * `LayoutStrategy` is world-framed, so each pose is composed to world before\n * a strategy sees it and each result is rebased into its node's parent frame\n * after. A child already where the strategy puts it is left out, so a pass\n * that changes nothing writes nothing. A child the pass moves takes its own\n * descendants along (`carriedDescendants`). A container this pass resizes is\n * arranged in turn, so nested layouts settle in the same edit.\n */\nimport {\n composeWorldPose,\n rebaseLocalPose,\n type PoseAdapter,\n type PoseComposition,\n} from './composeFrame';\nimport type { ContainerBounds, LayoutArrival, LayoutChild, LayoutStrategy } from '../../layout/types';\nimport { carriedDescendants } from './carryDescendants';\nimport { definesFrame, documentPose, type PoseSource, type PosedNode } from './effectivePose';\nimport { asRectPose } from './kitRegistry';\nimport { samePoseValue } from './poseSnapshot';\nimport type { LayoutFrame, NodeId } from './types';\n\n/** What the pass reads a scene through. */\nexport interface LayoutPassSource<TPose> extends PoseSource<TPose> {\n get(id: NodeId): (PosedNode<TPose> & { parent: NodeId | null }) | undefined;\n readonly composition: PoseComposition<TPose>;\n}\n\n/** How the pass measures and resizes poses, with every default filled. */\nexport interface ResolvedLayoutFrame<TPose> {\n bounds(pose: TPose, id: NodeId): ContainerBounds | null;\n remap(pose: TPose, from: ContainerBounds, to: ContainerBounds, id: NodeId): TPose;\n}\n\n/** The pose writes one edit's layouts ask for, or `null` when a strategy\n * refused the children that joined it. */\nexport type LayoutPassResult<TPose> = Map<NodeId, TPose> | null;\n\nexport function resolveLayoutFrame<TPose>(frame: LayoutFrame<TPose> | undefined): ResolvedLayoutFrame<TPose> {\n return {\n bounds: frame?.bounds ?? ((pose) => {\n const r = asRectPose(pose);\n return r === null ? null : { x: r.x, y: r.y, width: r.width, height: r.height };\n }),\n remap: frame?.remap ?? ((pose, _from, to) => ({ ...(pose as object), ...to }) as TPose),\n };\n}\n\n/** Whether a node going from `from` to `to` changed size. */\nexport function resized<TPose>(frame: ResolvedLayoutFrame<TPose>, id: NodeId, from: TPose, to: TPose): boolean {\n const a = frame.bounds(from, id);\n const b = frame.bounds(to, id);\n if (a === null || b === null) return a !== b;\n return a.width !== b.width || a.height !== b.height;\n}\n\n/** How many times one pass may arrange a container before giving up on it —\n * only nested layouts that keep resizing each other get close. */\nconst MAX_PASSES = 16;\n\nexport function runLayoutPass<TPose>(\n source: LayoutPassSource<TPose>,\n layoutOf: (id: NodeId) => LayoutStrategy<TPose> | null,\n arrivals: ReadonlyMap<NodeId, readonly NodeId[]>,\n changed: ReadonlySet<NodeId>,\n frame: ResolvedLayoutFrame<TPose>,\n departures: ReadonlyMap<NodeId, readonly NodeId[]> = new Map(),\n): LayoutPassResult<TPose> {\n // Poses already decided in this pass, so a container resized here is the\n // frame its children are rebased into.\n const out = new Map<NodeId, TPose>();\n const poseOf = (id: NodeId): TPose => out.get(id) ?? documentPose(source, source.get(id)!);\n const pa: PoseAdapter<TPose> = {\n getPose: (id) => poseOf(id as NodeId),\n getParent: (id) => source.get(id as NodeId)?.parent ?? null,\n definesFrame: (id) => {\n const node = source.get(id as NodeId);\n return node === undefined || definesFrame(node);\n },\n };\n const { compose, decompose } = source.composition;\n const world = (id: NodeId): TPose => composeWorldPose(pa, id, compose);\n\n const queue: NodeId[] = [...new Set<NodeId>([...arrivals.keys(), ...changed])];\n const passes = new Map<NodeId, number>();\n const write = (id: NodeId, pose: TPose): void => {\n const before = poseOf(id);\n if (samePoseValue(before, pose)) return;\n out.set(id, pose);\n // A container its own `depart` or `arrive` resized is arranged already.\n if (id !== current && layoutOf(id) !== null\n && resized(frame, id, before, pose)) queue.push(id);\n };\n let current: NodeId | null = null;\n\n /** The container and its children as the strategy sees them, in world,\n * with every write this pass has made so far folded in. */\n const frameOf = (containerId: NodeId) => {\n const pose = world(containerId);\n const bounds = frame.bounds(pose, containerId);\n if (bounds === null) return null;\n const children: LayoutChild<TPose>[] = source.childrenOf(containerId).map((cid) => ({\n id: cid as string,\n pose: world(cid),\n }));\n return { world: pose, container: { id: containerId as string, bounds }, children };\n };\n const apply = (\n containerId: NodeId,\n at: NonNullable<ReturnType<typeof frameOf>>,\n result: LayoutArrival<TPose>,\n ): void => {\n if (result.bounds) {\n const node = source.get(containerId)!;\n const sized = frame.remap(at.world, at.container.bounds, result.bounds, containerId);\n write(containerId, rebaseLocalPose(pa, sized, node.parent, compose, decompose));\n }\n for (const [cid, pose] of result.poses) {\n const id = cid as NodeId;\n if (source.get(id)?.parent !== containerId) continue;\n const before = frame.bounds(world(id), id);\n write(id, rebaseLocalPose(pa, pose, containerId, compose, decompose));\n carry(id, before);\n }\n };\n /** Moves `id`'s descendants by however far its write just moved it from `before`. */\n const carry = (id: NodeId, before: ContainerBounds | null): void => {\n const after = frame.bounds(world(id), id);\n if (before === null || after === null) return;\n const dx = after.x - before.x;\n const dy = after.y - before.y;\n if (dx === 0 && dy === 0) return;\n const carried = carriedDescendants(\n { childrenOf: (cid) => source.childrenOf(cid), poses: pa, composition: source.composition },\n id,\n (pose, cid) => {\n const b = frame.bounds(pose, cid);\n return b === null ? null : frame.remap(pose, b, { ...b, x: b.x + dx, y: b.y + dy }, cid);\n },\n );\n for (const [cid, pose] of carried) write(cid, pose);\n };\n\n while (queue.length > 0) {\n const containerId = queue.shift()!;\n current = containerId;\n const n = (passes.get(containerId) ?? 0) + 1;\n passes.set(containerId, n);\n if (n > MAX_PASSES) continue;\n const layout = layoutOf(containerId);\n if (layout === null || source.get(containerId) === undefined) continue;\n const joined = n === 1 ? arrivals.get(containerId) : undefined;\n const left = n === 1 ? departures.get(containerId) : undefined;\n let arranged = false;\n if (left !== undefined && layout.depart) {\n const at = frameOf(containerId);\n if (at === null) continue;\n const joinedSet = new Set<string>(joined ?? []);\n const staying = at.children.filter((c) => !joinedSet.has(c.id));\n apply(containerId, at, layout.depart(at.container, staying, new Set<string>(left)));\n arranged = true;\n }\n if (joined !== undefined && layout.arrive) {\n const at = frameOf(containerId);\n if (at === null) continue;\n const result = layout.arrive(at.container, at.children, new Set<string>(joined));\n if (result === null) return null;\n apply(containerId, at, result);\n } else if (!arranged || joined !== undefined) {\n const at = frameOf(containerId);\n if (at === null) continue;\n apply(containerId, at, { poses: layout.childPoses(at.container, at.children) });\n }\n }\n return out;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"layoutPass.js","names":[],"sources":["../../../src/core/scene/layoutPass.ts"],"sourcesContent":["/**\n * The one computation behind a scene's container layouts: given the\n * containers one edit changed, the pose writes their layouts ask for.\n *\n * A container that lost children and whose strategy has `depart` rearranges\n * the rest through it (and may shrink). One that gained children and whose\n * strategy has `arrive` then places them through it (and may refuse, or\n * grow). Any other changed container — one that reordered children, was\n * resized, had a child moved, or gained or lost children with neither hook —\n * is put back in its resting arrangement, `childPoses`.\n *\n * A strategy sees the container upright: an upright container and its\n * children in world, and a turned one in its own frame, placed where its\n * unturned box sits, so children are laid out along the container's axes.\n * Each result is taken back to world and rebased into its node's parent frame\n * after. A child already where the strategy puts it is left out, so a pass\n * that changes nothing writes nothing. A child the pass moves takes its own\n * descendants along (`carriedDescendants`). A container this pass resizes is\n * arranged in turn, so nested layouts settle in the same edit.\n */\nimport {\n composeWorldPose,\n rebaseLocalPose,\n type PoseAdapter,\n type PoseComposition,\n} from './composeFrame';\nimport type { ContainerBounds, LayoutArrival, LayoutChild, LayoutStrategy } from '../../layout/types';\nimport { carriedDescendants } from './carryDescendants';\nimport { definesFrame, documentPose, type PoseSource, type PosedNode } from './effectivePose';\nimport { asRectPose } from './kitRegistry';\nimport { samePoseValue } from './poseSnapshot';\nimport { strategyView } from './strategyView';\nimport type { LayoutFrame, NodeId } from './types';\n\n/** What the pass reads a scene through. */\nexport interface LayoutPassSource<TPose> extends PoseSource<TPose> {\n get(id: NodeId): (PosedNode<TPose> & { parent: NodeId | null }) | undefined;\n readonly composition: PoseComposition<TPose>;\n}\n\n/** How the pass measures and resizes poses, with every default filled. */\nexport interface ResolvedLayoutFrame<TPose> {\n bounds(pose: TPose, id: NodeId): ContainerBounds | null;\n remap(pose: TPose, from: ContainerBounds, to: ContainerBounds, id: NodeId): TPose;\n}\n\n/** The pose writes one edit's layouts ask for, or `null` when a strategy\n * refused the children that joined it. */\nexport type LayoutPassResult<TPose> = Map<NodeId, TPose> | null;\n\nexport function resolveLayoutFrame<TPose>(frame: LayoutFrame<TPose> | undefined): ResolvedLayoutFrame<TPose> {\n return {\n bounds: frame?.bounds ?? ((pose) => {\n const r = asRectPose(pose);\n return r === null ? null : { x: r.x, y: r.y, width: r.width, height: r.height };\n }),\n remap: frame?.remap ?? ((pose, _from, to) => ({ ...(pose as object), ...to }) as TPose),\n };\n}\n\n/** Whether a node going from `from` to `to` changed size. */\nexport function resized<TPose>(frame: ResolvedLayoutFrame<TPose>, id: NodeId, from: TPose, to: TPose): boolean {\n const a = frame.bounds(from, id);\n const b = frame.bounds(to, id);\n if (a === null || b === null) return a !== b;\n return a.width !== b.width || a.height !== b.height;\n}\n\n/** How many times one pass may arrange a container before giving up on it —\n * only nested layouts that keep resizing each other get close. */\nconst MAX_PASSES = 16;\n\nexport function runLayoutPass<TPose>(\n source: LayoutPassSource<TPose>,\n layoutOf: (id: NodeId) => LayoutStrategy<TPose> | null,\n arrivals: ReadonlyMap<NodeId, readonly NodeId[]>,\n changed: ReadonlySet<NodeId>,\n frame: ResolvedLayoutFrame<TPose>,\n departures: ReadonlyMap<NodeId, readonly NodeId[]> = new Map(),\n): LayoutPassResult<TPose> {\n // Poses already decided in this pass, so a container resized here is the\n // frame its children are rebased into.\n const out = new Map<NodeId, TPose>();\n const poseOf = (id: NodeId): TPose => out.get(id) ?? documentPose(source, source.get(id)!);\n const pa: PoseAdapter<TPose> = {\n getPose: (id) => poseOf(id as NodeId),\n getParent: (id) => source.get(id as NodeId)?.parent ?? null,\n definesFrame: (id) => {\n const node = source.get(id as NodeId);\n return node === undefined || definesFrame(node);\n },\n };\n const { compose, decompose } = source.composition;\n const world = (id: NodeId): TPose => composeWorldPose(pa, id, compose);\n\n const queue: NodeId[] = [...new Set<NodeId>([...arrivals.keys(), ...changed])];\n const passes = new Map<NodeId, number>();\n const write = (id: NodeId, pose: TPose): void => {\n const before = poseOf(id);\n if (samePoseValue(before, pose)) return;\n out.set(id, pose);\n // A container its own `depart` or `arrive` resized is arranged already.\n if (id !== current && layoutOf(id) !== null\n && resized(frame, id, before, pose)) queue.push(id);\n };\n let current: NodeId | null = null;\n\n /** The container and its children as the strategy sees them, with every\n * write this pass has made so far folded in. */\n const frameOf = (containerId: NodeId) => {\n const view = strategyView(\n world(containerId),\n source.composition,\n (p) => frame.bounds(p, containerId),\n (p, from, to) => frame.remap(p, from, to, containerId),\n );\n if (view === null) return null;\n const children: LayoutChild<TPose>[] = source.childrenOf(containerId).map((cid) => ({\n id: cid as string,\n pose: view.toStrategy(world(cid)),\n }));\n return { ...view, container: { id: containerId as string, bounds: view.bounds }, children };\n };\n const apply = (\n containerId: NodeId,\n at: NonNullable<ReturnType<typeof frameOf>>,\n result: LayoutArrival<TPose>,\n ): void => {\n if (result.bounds) {\n const node = source.get(containerId)!;\n const sized = at.toWorld(frame.remap(at.upright, at.container.bounds, result.bounds, containerId));\n write(containerId, rebaseLocalPose(pa, sized, node.parent, compose, decompose));\n }\n for (const [cid, pose] of result.poses) {\n const id = cid as NodeId;\n if (source.get(id)?.parent !== containerId) continue;\n const before = frame.bounds(world(id), id);\n write(id, rebaseLocalPose(pa, at.toWorld(pose), containerId, compose, decompose));\n carry(id, before);\n }\n };\n /** Moves `id`'s descendants by however far its write just moved it from `before`. */\n const carry = (id: NodeId, before: ContainerBounds | null): void => {\n const after = frame.bounds(world(id), id);\n if (before === null || after === null) return;\n const dx = after.x - before.x;\n const dy = after.y - before.y;\n if (dx === 0 && dy === 0) return;\n const carried = carriedDescendants(\n { childrenOf: (cid) => source.childrenOf(cid), poses: pa, composition: source.composition },\n id,\n (pose, cid) => {\n const b = frame.bounds(pose, cid);\n return b === null ? null : frame.remap(pose, b, { ...b, x: b.x + dx, y: b.y + dy }, cid);\n },\n );\n for (const [cid, pose] of carried) write(cid, pose);\n };\n\n while (queue.length > 0) {\n const containerId = queue.shift()!;\n current = containerId;\n const n = (passes.get(containerId) ?? 0) + 1;\n passes.set(containerId, n);\n if (n > MAX_PASSES) continue;\n const layout = layoutOf(containerId);\n if (layout === null || source.get(containerId) === undefined) continue;\n const joined = n === 1 ? arrivals.get(containerId) : undefined;\n const left = n === 1 ? departures.get(containerId) : undefined;\n let arranged = false;\n if (left !== undefined && layout.depart) {\n const at = frameOf(containerId);\n if (at === null) continue;\n const joinedSet = new Set<string>(joined ?? []);\n const staying = at.children.filter((c) => !joinedSet.has(c.id));\n apply(containerId, at, layout.depart(at.container, staying, new Set<string>(left)));\n arranged = true;\n }\n if (joined !== undefined && layout.arrive) {\n const at = frameOf(containerId);\n if (at === null) continue;\n const result = layout.arrive(at.container, at.children, new Set<string>(joined));\n if (result === null) return null;\n apply(containerId, at, result);\n } else if (!arranged || joined !== undefined) {\n const at = frameOf(containerId);\n if (at === null) continue;\n apply(containerId, at, { poses: layout.childPoses(at.container, at.children) });\n }\n }\n return out;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { nodeGates } from "./nodeGates.js";
|
|
1
|
+
import { isNodeHidden, isNodeLocked, nodeGates } from "./nodeGates.js";
|
|
2
2
|
|
|
3
3
|
//#region src/core/scene/lockGuard.ts
|
|
4
4
|
/**
|
|
@@ -7,16 +7,19 @@ import { nodeGates } from "./nodeGates.js";
|
|
|
7
7
|
*/
|
|
8
8
|
function createLockGuard(state) {
|
|
9
9
|
let bypass = 0;
|
|
10
|
+
/** Whether any node has carried `locked` since the scene was made. It is
|
|
11
|
+
* never cleared, so it can only err toward climbing. */
|
|
12
|
+
let nodeLockSeen = false;
|
|
10
13
|
const source = {
|
|
11
14
|
get layers() {
|
|
12
15
|
return state.layers;
|
|
13
16
|
},
|
|
14
17
|
get: (id) => state.nodes.get(id)
|
|
15
18
|
};
|
|
16
|
-
const isLocked = (id) =>
|
|
19
|
+
const isLocked = (id) => (nodeLockSeen || state.layers.some((l) => l.locked)) && isNodeLocked(source, id);
|
|
17
20
|
return {
|
|
18
21
|
isLocked,
|
|
19
|
-
isHidden: (id) =>
|
|
22
|
+
isHidden: (id) => isNodeHidden(source, id),
|
|
20
23
|
refuse(id, verb) {
|
|
21
24
|
if (bypass > 0 || !isLocked(id)) return;
|
|
22
25
|
throw new Error(`Scene: cannot ${verb} "${id}" — it is locked`);
|
|
@@ -29,6 +32,9 @@ function createLockGuard(state) {
|
|
|
29
32
|
const gates = nodeGates(source);
|
|
30
33
|
return ids.some((id) => gates.locked(id) || gates.hidden(id)) ? ids.filter((id) => !gates.locked(id) && !gates.hidden(id)) : ids;
|
|
31
34
|
},
|
|
35
|
+
noteWritten(node) {
|
|
36
|
+
if (node.locked === true) nodeLockSeen = true;
|
|
37
|
+
},
|
|
32
38
|
lifted(fn) {
|
|
33
39
|
bypass++;
|
|
34
40
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lockGuard.js","names":[],"sources":["../../../src/core/scene/lockGuard.ts"],"sourcesContent":["/**\n * The scene's lock guard: what a lock refuses, what the selection may hold,\n * and how a caller that is restoring an already-legal state gets past it.\n */\nimport { nodeGates } from './nodeGates';\nimport type { Node, NodeId } from './types';\n\n/** What the guard reads of a scene's state. */\nexport interface LockGuardState<TData, TLayer extends string, TPose> {\n nodes: Map<NodeId, Node<TData, TLayer, TPose>>;\n layers: { id: string; visible: boolean; locked: boolean }[];\n layerIndex: Map<TLayer, number>;\n}\n\nexport interface LockGuard<TLayer extends string> {\n /** See `Scene.isLocked`. */\n isLocked(id: NodeId): boolean;\n /** See `Scene.isHidden`. */\n isHidden(id: NodeId): boolean;\n /** Throw when `id` is locked and the guard is not lifted. */\n refuse(id: NodeId, verb: string): void;\n /** Throw when `layer` is locked and the guard is not lifted. */\n refuseLayer(layer: TLayer, verb: string): void;\n /** `ids` less the locked and hidden ones: the scene's selection never\n * holds either, whoever writes it. */\n reachable(ids: readonly NodeId[]): readonly NodeId[];\n /** Run `fn` with the guard lifted: `unlocked()`, `removeLayer`, and every\n * replay — undo, redo, a journal stepping, a rollback — which restores a\n * state that was legal when it was recorded. */\n lifted<T>(fn: () => T): T;\n}\n\nexport function createLockGuard<TData, TLayer extends string, TPose>(\n state: LockGuardState<TData, TLayer, TPose>,\n): LockGuard<TLayer> {\n let bypass = 0;\n\n const source = { get layers() { return state.layers; }, get: (id: NodeId) => state.nodes.get(id) };\n const isLocked = (id: NodeId): boolean
|
|
1
|
+
{"version":3,"file":"lockGuard.js","names":[],"sources":["../../../src/core/scene/lockGuard.ts"],"sourcesContent":["/**\n * The scene's lock guard: what a lock refuses, what the selection may hold,\n * and how a caller that is restoring an already-legal state gets past it.\n */\nimport { isNodeHidden, isNodeLocked, nodeGates } from './nodeGates';\nimport type { Node, NodeId } from './types';\n\n/** What the guard reads of a scene's state. */\nexport interface LockGuardState<TData, TLayer extends string, TPose> {\n nodes: Map<NodeId, Node<TData, TLayer, TPose>>;\n layers: { id: string; visible: boolean; locked: boolean }[];\n layerIndex: Map<TLayer, number>;\n}\n\nexport interface LockGuard<TLayer extends string> {\n /** See `Scene.isLocked`. */\n isLocked(id: NodeId): boolean;\n /** See `Scene.isHidden`. */\n isHidden(id: NodeId): boolean;\n /** Throw when `id` is locked and the guard is not lifted. */\n refuse(id: NodeId, verb: string): void;\n /** Throw when `layer` is locked and the guard is not lifted. */\n refuseLayer(layer: TLayer, verb: string): void;\n /** `ids` less the locked and hidden ones: the scene's selection never\n * holds either, whoever writes it. */\n reachable(ids: readonly NodeId[]): readonly NodeId[];\n /** Tell the guard `node` was just written. Until some node carries\n * `locked`, and no layer is locked, nothing is locked and a check need\n * not climb. Every write that can set the flag calls this. */\n noteWritten(node: { readonly locked?: boolean }): void;\n /** Run `fn` with the guard lifted: `unlocked()`, `removeLayer`, and every\n * replay — undo, redo, a journal stepping, a rollback — which restores a\n * state that was legal when it was recorded. */\n lifted<T>(fn: () => T): T;\n}\n\nexport function createLockGuard<TData, TLayer extends string, TPose>(\n state: LockGuardState<TData, TLayer, TPose>,\n): LockGuard<TLayer> {\n let bypass = 0;\n /** Whether any node has carried `locked` since the scene was made. It is\n * never cleared, so it can only err toward climbing. */\n let nodeLockSeen = false;\n\n const source = { get layers() { return state.layers; }, get: (id: NodeId) => state.nodes.get(id) };\n const isLocked = (id: NodeId): boolean =>\n (nodeLockSeen || state.layers.some((l) => l.locked)) && isNodeLocked(source, id);\n\n return {\n isLocked,\n isHidden: (id) => isNodeHidden(source, id),\n refuse(id, verb) {\n if (bypass > 0 || !isLocked(id)) return;\n throw new Error(`Scene: cannot ${verb} \"${id}\" — it is locked`);\n },\n refuseLayer(layer, verb) {\n if (bypass > 0 || !state.layers[state.layerIndex.get(layer) ?? -1]?.locked) return;\n throw new Error(`Scene: cannot ${verb} layer \"${layer}\" — it is locked`);\n },\n reachable(ids) {\n const gates = nodeGates(source);\n return ids.some((id) => gates.locked(id) || gates.hidden(id))\n ? ids.filter((id) => !gates.locked(id) && !gates.hidden(id))\n : ids;\n },\n noteWritten(node) {\n if (node.locked === true) nodeLockSeen = true;\n },\n lifted(fn) {\n bypass++;\n try {\n return fn();\n } finally {\n bypass--;\n }\n },\n };\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,gBACd,OACmB;CACnB,IAAI,SAAS;;;CAGb,IAAI,eAAe;CAEnB,MAAM,SAAS;EAAE,IAAI,SAAS;GAAE,OAAO,MAAM;EAAQ;EAAG,MAAM,OAAe,MAAM,MAAM,IAAI,EAAE;CAAE;CACjG,MAAM,YAAY,QACf,gBAAgB,MAAM,OAAO,MAAM,MAAM,EAAE,MAAM,MAAM,aAAa,QAAQ,EAAE;CAEjF,OAAO;EACL;EACA,WAAW,OAAO,aAAa,QAAQ,EAAE;EACzC,OAAO,IAAI,MAAM;GACf,IAAI,SAAS,KAAK,CAAC,SAAS,EAAE,GAAG;GACjC,MAAM,IAAI,MAAM,iBAAiB,KAAK,IAAI,GAAG,iBAAiB;EAChE;EACA,YAAY,OAAO,MAAM;GACvB,IAAI,SAAS,KAAK,CAAC,MAAM,OAAO,MAAM,WAAW,IAAI,KAAK,KAAK,GAAG,EAAE,QAAQ;GAC5E,MAAM,IAAI,MAAM,iBAAiB,KAAK,UAAU,MAAM,iBAAiB;EACzE;EACA,UAAU,KAAK;GACb,MAAM,QAAQ,UAAU,MAAM;GAC9B,OAAO,IAAI,MAAM,OAAO,MAAM,OAAO,EAAE,KAAK,MAAM,OAAO,EAAE,CAAC,IACxD,IAAI,QAAQ,OAAO,CAAC,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,OAAO,EAAE,CAAC,IACzD;EACN;EACA,YAAY,MAAM;GAChB,IAAI,KAAK,WAAW,MAAM,eAAe;EAC3C;EACA,OAAO,IAAI;GACT;GACA,IAAI;IACF,OAAO,GAAG;GACZ,UAAU;IACR;GACF;EACF;CACF;AACF"}
|
|
@@ -19,7 +19,7 @@ function nestedHitTester(adapter, opts) {
|
|
|
19
19
|
}
|
|
20
20
|
return chain;
|
|
21
21
|
};
|
|
22
|
-
const hitLeaf = (wx, wy) => {
|
|
22
|
+
const hitLeaf = ({ x: wx, y: wy }) => {
|
|
23
23
|
const objs = adapter.getNodes();
|
|
24
24
|
for (let i = objs.length - 1; i >= 0; i--) {
|
|
25
25
|
const o = objs[i];
|
|
@@ -31,12 +31,12 @@ function nestedHitTester(adapter, opts) {
|
|
|
31
31
|
}
|
|
32
32
|
return null;
|
|
33
33
|
};
|
|
34
|
-
const pickOutermost = (
|
|
35
|
-
const leaf = hitLeaf(
|
|
34
|
+
const pickOutermost = (world) => {
|
|
35
|
+
const leaf = hitLeaf(world);
|
|
36
36
|
return leaf === null ? null : ancestorChain(leaf)[0];
|
|
37
37
|
};
|
|
38
|
-
const pickBest = (
|
|
39
|
-
const leaf = hitLeaf(
|
|
38
|
+
const pickBest = (world, alt, selection) => {
|
|
39
|
+
const leaf = hitLeaf(world);
|
|
40
40
|
if (leaf === null) return null;
|
|
41
41
|
const chain = ancestorChain(leaf);
|
|
42
42
|
if (!alt) return chain[0];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nestedHit.js","names":[],"sources":["../../../src/core/scene/nestedHit.ts"],"sourcesContent":["import { worldPoseLookup } from 'core/scene/composePose';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\n\n/** @internal */\ninterface HitAdapter<TNode extends { id: string }, TPose> {\n getNode: (id: string) => TNode | undefined;\n getNodes: () => TNode[];\n getPose: (id: string) => TPose;\n getParent: (id: string) => string | null;\n}\n\n/** Options for `nestedHitTester`. */\nexport interface NestedHitOpts<TNode extends { id: string }, TPose> {\n /** Compose a child's local pose into world coords given its parent's world\n * pose. Same shape as `composeRectPose` (the default expectation). */\n composePose: (parent: TPose, child: TPose) => TPose;\n /** How to read world poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** Predicate for \"this object is a nesting parent body\". The leaf scan\n * skips objects for which this returns true so a click on a parent's\n * painted body resolves to a child leaf, not the parent itself. Default:\n * never (treat every object as hittable). */\n isGroup?: (id: string, obj: TNode | undefined) => boolean;\n}\n\n/** Hit-test entry points for a nested scene: one that always picks the\n * outermost ancestor, and one that steps deeper on alt-click. */\nexport interface NestedHitTester {\n /** Outermost-ancestor pick. Suitable as the chrome-level `pickEvery`: a\n * casual click selects the whole top-level ancestor. Returns `null` on\n * empty space. */\n pickOutermost: (
|
|
1
|
+
{"version":3,"file":"nestedHit.js","names":[],"sources":["../../../src/core/scene/nestedHit.ts"],"sourcesContent":["import { worldPoseLookup } from 'core/scene/composePose';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport type { WorldPoint } from '@weasel-js/routing';\n\n/** @internal */\ninterface HitAdapter<TNode extends { id: string }, TPose> {\n getNode: (id: string) => TNode | undefined;\n getNodes: () => TNode[];\n getPose: (id: string) => TPose;\n getParent: (id: string) => string | null;\n}\n\n/** Options for `nestedHitTester`. */\nexport interface NestedHitOpts<TNode extends { id: string }, TPose> {\n /** Compose a child's local pose into world coords given its parent's world\n * pose. Same shape as `composeRectPose` (the default expectation). */\n composePose: (parent: TPose, child: TPose) => TPose;\n /** How to read world poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** Predicate for \"this object is a nesting parent body\". The leaf scan\n * skips objects for which this returns true so a click on a parent's\n * painted body resolves to a child leaf, not the parent itself. Default:\n * never (treat every object as hittable). */\n isGroup?: (id: string, obj: TNode | undefined) => boolean;\n}\n\n/** Hit-test entry points for a nested scene: one that always picks the\n * outermost ancestor, and one that steps deeper on alt-click. */\nexport interface NestedHitTester {\n /** Outermost-ancestor pick. Suitable as the chrome-level `pickEvery`: a\n * casual click selects the whole top-level ancestor. Returns `null` on\n * empty space. */\n pickOutermost: (world: WorldPoint) => string | null;\n /** Alt-aware selection-update pick. Without `alt`, returns the outermost\n * ancestor (same as `pickOutermost`). With `alt`, returns one level\n * deeper than the deepest currently-selected ancestor in the leaf's\n * chain — repeated alt-clicks step ancestor → descendant → leaf. With\n * `alt` and nothing in the chain selected, jumps straight to the leaf.\n * Plug into `useSelectTool({ pickBest })`. */\n pickBest: (world: WorldPoint, alt: boolean, selection: readonly string[]) => string | null;\n}\n\n/** Build hit-testers that respect containers: a plain click selects the\n * top-level ancestor, and alt-clicking descends one level at a time toward\n * the leaf actually under the pointer. */\nexport function nestedHitTester<TNode extends { id: string }, TPose>(\n adapter: HitAdapter<TNode, TPose>,\n opts: NestedHitOpts<TNode, TPose>,\n): NestedHitTester {\n const d = (opts.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const isGroup = opts.isGroup ?? (() => false);\n const worldOf = worldPoseLookup(adapter, opts.composePose);\n\n const ancestorChain = (id: string): string[] => {\n const chain: string[] = [];\n let cur: string | null = id;\n while (cur !== null) {\n chain.unshift(cur);\n cur = adapter.getParent(cur);\n }\n return chain;\n };\n\n const hitLeaf = ({ x: wx, y: wy }: WorldPoint): string | null => {\n const objs = adapter.getNodes();\n for (let i = objs.length - 1; i >= 0; i--) {\n const o = objs[i];\n if (isGroup(o.id, o)) continue;\n const w = worldOf(o.id);\n if (!w) continue;\n const b = poseDescriptorForNode(d, o).getBounds(w);\n if (wx >= b.x && wx <= b.x + b.width && wy >= b.y && wy <= b.y + b.height) {\n return o.id;\n }\n }\n return null;\n };\n\n const pickOutermost = (world: WorldPoint): string | null => {\n const leaf = hitLeaf(world);\n return leaf === null ? null : ancestorChain(leaf)[0];\n };\n\n const pickBest = (world: WorldPoint, alt: boolean, selection: readonly string[]): string | null => {\n const leaf = hitLeaf(world);\n if (leaf === null) return null;\n const chain = ancestorChain(leaf);\n if (!alt) return chain[0];\n for (let i = chain.length - 1; i >= 0; i--) {\n if (selection.includes(chain[i])) {\n return chain[Math.min(i + 1, chain.length - 1)];\n }\n }\n return chain[chain.length - 1];\n };\n\n return { pickOutermost, pickBest };\n}\n"],"mappings":";;;;;;;;AA8CA,SAAgB,gBACd,SACA,MACiB;CACjB,MAAM,IAAK,KAAK,kBAAkB;CAClC,MAAM,UAAU,KAAK,kBAAkB;CACvC,MAAM,UAAU,gBAAgB,SAAS,KAAK,WAAW;CAEzD,MAAM,iBAAiB,OAAyB;EAC9C,MAAM,QAAkB,CAAC;EACzB,IAAI,MAAqB;EACzB,OAAO,QAAQ,MAAM;GACnB,MAAM,QAAQ,GAAG;GACjB,MAAM,QAAQ,UAAU,GAAG;EAC7B;EACA,OAAO;CACT;CAEA,MAAM,WAAW,EAAE,GAAG,IAAI,GAAG,SAAoC;EAC/D,MAAM,OAAO,QAAQ,SAAS;EAC9B,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;GACzC,MAAM,IAAI,KAAK;GACf,IAAI,QAAQ,EAAE,IAAI,CAAC,GAAG;GACtB,MAAM,IAAI,QAAQ,EAAE,EAAE;GACtB,IAAI,CAAC,GAAG;GACR,MAAM,IAAI,sBAAsB,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;GACjD,IAAI,MAAM,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,QACjE,OAAO,EAAE;EAEb;EACA,OAAO;CACT;CAEA,MAAM,iBAAiB,UAAqC;EAC1D,MAAM,OAAO,QAAQ,KAAK;EAC1B,OAAO,SAAS,OAAO,OAAO,cAAc,IAAI,CAAC,CAAC;CACpD;CAEA,MAAM,YAAY,OAAmB,KAAc,cAAgD;EACjG,MAAM,OAAO,QAAQ,KAAK;EAC1B,IAAI,SAAS,MAAM,OAAO;EAC1B,MAAM,QAAQ,cAAc,IAAI;EAChC,IAAI,CAAC,KAAK,OAAO,MAAM;EACvB,KAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KACrC,IAAI,UAAU,SAAS,MAAM,EAAE,GAC7B,OAAO,MAAM,KAAK,IAAI,IAAI,GAAG,MAAM,SAAS,CAAC;EAGjD,OAAO,MAAM,MAAM,SAAS;CAC9B;CAEA,OAAO;EAAE;EAAe;CAAS;AACnC"}
|
|
@@ -15,12 +15,18 @@ function createNodeFlags(env) {
|
|
|
15
15
|
for (const flag of ["hidden", "locked"]) env.register(KIND[flag], {
|
|
16
16
|
apply: (p) => {
|
|
17
17
|
const { id, to } = p;
|
|
18
|
-
|
|
18
|
+
const node = env.requireNode(id);
|
|
19
|
+
write(node, flag, to);
|
|
20
|
+
env.noteWritten(node);
|
|
21
|
+
if (flag === "hidden") env.invalidateDependents(id);
|
|
19
22
|
if (to) env.narrowSelection();
|
|
20
23
|
},
|
|
21
24
|
revert: (p) => {
|
|
22
25
|
const { id, from } = p;
|
|
23
|
-
|
|
26
|
+
const node = env.requireNode(id);
|
|
27
|
+
write(node, flag, from);
|
|
28
|
+
env.noteWritten(node);
|
|
29
|
+
if (flag === "hidden") env.invalidateDependents(id);
|
|
24
30
|
}
|
|
25
31
|
});
|
|
26
32
|
const set = (flag, label) => (id, to) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeFlags.js","names":[],"sources":["../../../src/core/scene/nodeFlags.ts"],"sourcesContent":["/**\n * A node's own `hidden` and `locked` flags: the kit ops that write them, and\n * the scene methods that record those ops. What the flags mean to every\n * reader is `nodeGates`.\n */\nimport type { Node, NodeId, RegisteredOp, Scene } from './types';\n\n/** The two per-node flags. */\nexport type NodeFlag = 'hidden' | 'locked';\n\nexport interface NodeFlagsEnv<TData, TLayer extends string, TPose> {\n register(kind: string, handler: RegisteredOp<unknown>): void;\n requireNode(id: NodeId): Node<TData, TLayer, TPose>;\n record(kind: string, payload: unknown, label: string): void;\n /** Drop from the selection whatever is now hidden or locked. */\n narrowSelection(): void;\n}\n\ninterface FlagPayload { id: NodeId; from: boolean; to: boolean }\n\n/** Stored only while set, so a snapshot of an unflagged node carries neither. */\nfunction write(node: { hidden?: boolean; locked?: boolean }, flag: NodeFlag, to: boolean): void {\n if (to) node[flag] = true;\n else delete node[flag];\n}\n\nconst KIND: Record<NodeFlag, string> = { hidden: 'kit:setHidden', locked: 'kit:setLocked' };\n\n/** Register `kit:setHidden` and `kit:setLocked`, and answer with the scene\n * methods that record them. Neither is refused by a lock: like a layer's,\n * a node's lock is what lifts it. */\nexport function createNodeFlags<TData, TLayer extends string, TPose>(\n env: NodeFlagsEnv<TData, TLayer, TPose>,\n): Pick<Scene<TData, TLayer, TPose>, 'setHidden' | 'setLocked'> {\n for (const flag of ['hidden', 'locked'] as const) {\n env.register(KIND[flag], {\n apply: (p) => {\n const { id, to } = p as FlagPayload;\n
|
|
1
|
+
{"version":3,"file":"nodeFlags.js","names":[],"sources":["../../../src/core/scene/nodeFlags.ts"],"sourcesContent":["/**\n * A node's own `hidden` and `locked` flags: the kit ops that write them, and\n * the scene methods that record those ops. What the flags mean to every\n * reader is `nodeGates`.\n */\nimport type { Node, NodeId, RegisteredOp, Scene } from './types';\n\n/** The two per-node flags. */\nexport type NodeFlag = 'hidden' | 'locked';\n\nexport interface NodeFlagsEnv<TData, TLayer extends string, TPose> {\n register(kind: string, handler: RegisteredOp<unknown>): void;\n requireNode(id: NodeId): Node<TData, TLayer, TPose>;\n record(kind: string, payload: unknown, label: string): void;\n /** Drop from the selection whatever is now hidden or locked. */\n narrowSelection(): void;\n /** Drop what derives from `id`: a derivation may leave a hidden node out. */\n invalidateDependents(id: NodeId): void;\n /** See `LockGuard.noteWritten`. */\n noteWritten(node: Node<TData, TLayer, TPose>): void;\n}\n\ninterface FlagPayload { id: NodeId; from: boolean; to: boolean }\n\n/** Stored only while set, so a snapshot of an unflagged node carries neither. */\nfunction write(node: { hidden?: boolean; locked?: boolean }, flag: NodeFlag, to: boolean): void {\n if (to) node[flag] = true;\n else delete node[flag];\n}\n\nconst KIND: Record<NodeFlag, string> = { hidden: 'kit:setHidden', locked: 'kit:setLocked' };\n\n/** Register `kit:setHidden` and `kit:setLocked`, and answer with the scene\n * methods that record them. Neither is refused by a lock: like a layer's,\n * a node's lock is what lifts it. */\nexport function createNodeFlags<TData, TLayer extends string, TPose>(\n env: NodeFlagsEnv<TData, TLayer, TPose>,\n): Pick<Scene<TData, TLayer, TPose>, 'setHidden' | 'setLocked'> {\n for (const flag of ['hidden', 'locked'] as const) {\n env.register(KIND[flag], {\n apply: (p) => {\n const { id, to } = p as FlagPayload;\n const node = env.requireNode(id);\n write(node, flag, to);\n env.noteWritten(node);\n if (flag === 'hidden') env.invalidateDependents(id);\n if (to) env.narrowSelection();\n },\n revert: (p) => {\n const { id, from } = p as FlagPayload;\n const node = env.requireNode(id);\n write(node, flag, from);\n env.noteWritten(node);\n if (flag === 'hidden') env.invalidateDependents(id);\n },\n });\n }\n\n const set = (flag: NodeFlag, label: string) => (id: NodeId, to: boolean): void => {\n const from = env.requireNode(id)[flag] === true;\n if (from === to) return;\n env.record(KIND[flag], { id, from, to } satisfies FlagPayload, label);\n };\n\n return { setHidden: set('hidden', 'setHidden'), setLocked: set('locked', 'setLocked') };\n}\n"],"mappings":";;AAyBA,SAAS,MAAM,MAA8C,MAAgB,IAAmB;CAC9F,IAAI,IAAI,KAAK,QAAQ;MAChB,OAAO,KAAK;AACnB;AAEA,IAAM,OAAiC;CAAE,QAAQ;CAAiB,QAAQ;AAAgB;;;;AAK1F,SAAgB,gBACd,KAC8D;CAC9D,KAAK,MAAM,QAAQ,CAAC,UAAU,QAAQ,GACpC,IAAI,SAAS,KAAK,OAAO;EACvB,QAAQ,MAAM;GACZ,MAAM,EAAE,IAAI,OAAO;GACnB,MAAM,OAAO,IAAI,YAAY,EAAE;GAC/B,MAAM,MAAM,MAAM,EAAE;GACpB,IAAI,YAAY,IAAI;GACpB,IAAI,SAAS,UAAU,IAAI,qBAAqB,EAAE;GAClD,IAAI,IAAI,IAAI,gBAAgB;EAC9B;EACA,SAAS,MAAM;GACb,MAAM,EAAE,IAAI,SAAS;GACrB,MAAM,OAAO,IAAI,YAAY,EAAE;GAC/B,MAAM,MAAM,MAAM,IAAI;GACtB,IAAI,YAAY,IAAI;GACpB,IAAI,SAAS,UAAU,IAAI,qBAAqB,EAAE;EACpD;CACF,CAAC;CAGH,MAAM,OAAO,MAAgB,WAAmB,IAAY,OAAsB;EAChF,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC,CAAC,UAAU;EAC3C,IAAI,SAAS,IAAI;EACjB,IAAI,OAAO,KAAK,OAAO;GAAE;GAAI;GAAM;EAAG,GAAyB,KAAK;CACtE;CAEA,OAAO;EAAE,WAAW,IAAI,UAAU,WAAW;EAAG,WAAW,IAAI,UAAU,WAAW;CAAE;AACxF"}
|
|
@@ -44,13 +44,42 @@ function nodeGates(scene) {
|
|
|
44
44
|
locked: inherited("locked", lockedLayers)
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
+
/** Whether `test` holds on the node at `id` or any ancestor, without the
|
|
48
|
+
* allocation {@link nodeGates} spends on a walk: the scene's lock guard asks
|
|
49
|
+
* this on every mutation. A parent cycle answers false, as the walk's does. */
|
|
50
|
+
function climbs(scene, id, test) {
|
|
51
|
+
let seen = null;
|
|
52
|
+
let hops = 0;
|
|
53
|
+
for (let at = id; at !== null;) {
|
|
54
|
+
const node = scene.get(at);
|
|
55
|
+
if (node === void 0) return false;
|
|
56
|
+
if (++hops > 64) {
|
|
57
|
+
seen ??= /* @__PURE__ */ new Set();
|
|
58
|
+
if (seen.has(at)) return false;
|
|
59
|
+
seen.add(at);
|
|
60
|
+
}
|
|
61
|
+
if (test(node)) return true;
|
|
62
|
+
at = node.parent ?? null;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
/** The layers flagged `flag`, or null when none is: the usual answer, which
|
|
67
|
+
* lets a climb skip the layer test at every hop. */
|
|
68
|
+
function flaggedLayers(scene, flag) {
|
|
69
|
+
let out = null;
|
|
70
|
+
for (const l of scene.layers ?? []) if (flag === "locked" ? l.locked === true : l.visible === false) (out ??= /* @__PURE__ */ new Set()).add(l.id);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
47
73
|
/** {@link NodeGates.hidden} for one id. */
|
|
48
74
|
function isNodeHidden(scene, id) {
|
|
49
|
-
|
|
75
|
+
const node = scene.get(id);
|
|
76
|
+
if (node === void 0) return false;
|
|
77
|
+
return flaggedLayers(scene, "hidden")?.has(node.layer) === true || climbs(scene, id, hidesSubtree);
|
|
50
78
|
}
|
|
51
79
|
/** {@link NodeGates.locked} for one id. */
|
|
52
80
|
function isNodeLocked(scene, id) {
|
|
53
|
-
|
|
81
|
+
const layers = flaggedLayers(scene, "locked");
|
|
82
|
+
return climbs(scene, id, layers === null ? (node) => node.locked === true : (node) => node.locked === true || layers.has(node.layer));
|
|
54
83
|
}
|
|
55
84
|
|
|
56
85
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeGates.js","names":[],"sources":["../../../src/core/scene/nodeGates.ts"],"sourcesContent":["/**\n * Whether a node is shown, and whether it is in reach — the one place either\n * question is answered. Painting, picking, area selection, select-all,\n * snapping, export, and the scene's own lock guard all ask here.\n *\n * - **Hidden**: its own layer is not visible, or it or any ancestor carries\n * `hidden`. A layer hides only the nodes tagged to it; a hidden node hides\n * its whole subtree, as SVG's `display=\"none\"` does.\n * - **Locked**: it or any ancestor carries `locked`, or sits on a locked\n * layer. A lock covers the whole subtree, whatever layers it spans.\n */\nimport type { NodeId } from './types';\n\n/** The node fields the gates read. A partial scene stand-in may leave `parent` out. */\nexport interface GatedNode {\n readonly layer: string;\n readonly parent?: string | null;\n readonly hidden?: boolean;\n readonly locked?: boolean;\n}\n\n/** The layer fields the gates read. Only an explicit `visible: false` hides. */\nexport interface GatedLayer {\n readonly id: string;\n readonly visible?: boolean;\n readonly locked?: boolean;\n}\n\n/** What the gates read of a scene. A `Scene` is one. */\nexport interface GateSource {\n readonly layers?: readonly GatedLayer[];\n get(id: NodeId): GatedNode | undefined;\n}\n\n/** Whether `node`'s own flag hides it and its subtree. A walk from the roots\n * down prunes where this holds instead of asking {@link NodeGates.hidden}\n * per node; the two agree because this is the flag that answer climbs for. */\nexport function hidesSubtree(node: { readonly hidden?: boolean }): boolean {\n return node.hidden === true;\n}\n\n/** Both answers, for one read of a scene. */\nexport interface NodeGates {\n /** Whether `id` is not drawn. An id not in the scene is not hidden. */\n hidden(id: string): boolean;\n /** Whether `id` is out of reach. An id not in the scene is not locked. */\n locked(id: string): boolean;\n}\n\n/**\n * The gates over `scene` as it stands now. Ancestor answers are remembered,\n * so a walk over every node climbs each chain once: build one per walk, and\n * a fresh one after an edit.\n */\nexport function nodeGates(scene: GateSource): NodeGates {\n const hiddenLayers = new Set<string>();\n const lockedLayers = new Set<string>();\n for (const l of scene.layers ?? []) {\n if (l.visible === false) hiddenLayers.add(l.id);\n if (l.locked === true) lockedLayers.add(l.id);\n }\n\n /** Whether `flag` holds on the node at `id` or any ancestor, the node's\n * layer counting when `layers` holds it. */\n const inherited = (flag: 'hidden' | 'locked', layers: Set<string> | null) => {\n const memo = new Map<string, boolean>();\n const climb = (id: string, seen: Set<string>): boolean => {\n const known = memo.get(id);\n if (known !== undefined) return known;\n const node = scene.get(id as NodeId);\n if (node === undefined || seen.has(id)) return false;\n seen.add(id);\n const parent = node.parent ?? null;\n const out = (flag === 'hidden' ? hidesSubtree(node) : node.locked === true)\n || (layers !== null && layers.has(node.layer))\n || (parent !== null && climb(parent, seen));\n memo.set(id, out);\n return out;\n };\n return (id: string) => climb(id, new Set());\n };\n\n const flaggedHidden = inherited('hidden', null);\n const locked = inherited('locked', lockedLayers);\n return {\n hidden(id) {\n const node = scene.get(id as NodeId);\n if (node === undefined) return false;\n return hiddenLayers.has(node.layer) || flaggedHidden(id);\n },\n locked,\n };\n}\n\n/** {@link NodeGates.hidden} for one id. */\nexport function isNodeHidden(scene: GateSource, id: NodeId): boolean {\n return
|
|
1
|
+
{"version":3,"file":"nodeGates.js","names":[],"sources":["../../../src/core/scene/nodeGates.ts"],"sourcesContent":["/**\n * Whether a node is shown, and whether it is in reach — the one place either\n * question is answered. Painting, picking, area selection, select-all,\n * snapping, export, and the scene's own lock guard all ask here.\n *\n * - **Hidden**: its own layer is not visible, or it or any ancestor carries\n * `hidden`. A layer hides only the nodes tagged to it; a hidden node hides\n * its whole subtree, as SVG's `display=\"none\"` does.\n * - **Locked**: it or any ancestor carries `locked`, or sits on a locked\n * layer. A lock covers the whole subtree, whatever layers it spans.\n */\nimport type { NodeId } from './types';\n\n/** The node fields the gates read. A partial scene stand-in may leave `parent` out. */\nexport interface GatedNode {\n readonly layer: string;\n readonly parent?: string | null;\n readonly hidden?: boolean;\n readonly locked?: boolean;\n}\n\n/** The layer fields the gates read. Only an explicit `visible: false` hides. */\nexport interface GatedLayer {\n readonly id: string;\n readonly visible?: boolean;\n readonly locked?: boolean;\n}\n\n/** What the gates read of a scene. A `Scene` is one. */\nexport interface GateSource {\n readonly layers?: readonly GatedLayer[];\n get(id: NodeId): GatedNode | undefined;\n}\n\n/** Whether `node`'s own flag hides it and its subtree. A walk from the roots\n * down prunes where this holds instead of asking {@link NodeGates.hidden}\n * per node; the two agree because this is the flag that answer climbs for. */\nexport function hidesSubtree(node: { readonly hidden?: boolean }): boolean {\n return node.hidden === true;\n}\n\n/** Both answers, for one read of a scene. */\nexport interface NodeGates {\n /** Whether `id` is not drawn. An id not in the scene is not hidden. */\n hidden(id: string): boolean;\n /** Whether `id` is out of reach. An id not in the scene is not locked. */\n locked(id: string): boolean;\n}\n\n/**\n * The gates over `scene` as it stands now. Ancestor answers are remembered,\n * so a walk over every node climbs each chain once: build one per walk, and\n * a fresh one after an edit.\n */\nexport function nodeGates(scene: GateSource): NodeGates {\n const hiddenLayers = new Set<string>();\n const lockedLayers = new Set<string>();\n for (const l of scene.layers ?? []) {\n if (l.visible === false) hiddenLayers.add(l.id);\n if (l.locked === true) lockedLayers.add(l.id);\n }\n\n /** Whether `flag` holds on the node at `id` or any ancestor, the node's\n * layer counting when `layers` holds it. */\n const inherited = (flag: 'hidden' | 'locked', layers: Set<string> | null) => {\n const memo = new Map<string, boolean>();\n const climb = (id: string, seen: Set<string>): boolean => {\n const known = memo.get(id);\n if (known !== undefined) return known;\n const node = scene.get(id as NodeId);\n if (node === undefined || seen.has(id)) return false;\n seen.add(id);\n const parent = node.parent ?? null;\n const out = (flag === 'hidden' ? hidesSubtree(node) : node.locked === true)\n || (layers !== null && layers.has(node.layer))\n || (parent !== null && climb(parent, seen));\n memo.set(id, out);\n return out;\n };\n return (id: string) => climb(id, new Set());\n };\n\n const flaggedHidden = inherited('hidden', null);\n const locked = inherited('locked', lockedLayers);\n return {\n hidden(id) {\n const node = scene.get(id as NodeId);\n if (node === undefined) return false;\n return hiddenLayers.has(node.layer) || flaggedHidden(id);\n },\n locked,\n };\n}\n\n/** Whether `test` holds on the node at `id` or any ancestor, without the\n * allocation {@link nodeGates} spends on a walk: the scene's lock guard asks\n * this on every mutation. A parent cycle answers false, as the walk's does. */\nfunction climbs(scene: GateSource, id: string, test: (node: GatedNode) => boolean): boolean {\n let seen: Set<string> | null = null;\n let hops = 0;\n for (let at: string | null = id; at !== null; ) {\n const node = scene.get(at as NodeId);\n if (node === undefined) return false;\n if (++hops > 64) {\n seen ??= new Set();\n if (seen.has(at)) return false;\n seen.add(at);\n }\n if (test(node)) return true;\n at = node.parent ?? null;\n }\n return false;\n}\n\n/** The layers flagged `flag`, or null when none is: the usual answer, which\n * lets a climb skip the layer test at every hop. */\nfunction flaggedLayers(scene: GateSource, flag: 'locked' | 'hidden'): Set<string> | null {\n let out: Set<string> | null = null;\n for (const l of scene.layers ?? []) {\n if (flag === 'locked' ? l.locked === true : l.visible === false) (out ??= new Set()).add(l.id);\n }\n return out;\n}\n\n/** {@link NodeGates.hidden} for one id. */\nexport function isNodeHidden(scene: GateSource, id: NodeId): boolean {\n const node = scene.get(id);\n if (node === undefined) return false;\n return flaggedLayers(scene, 'hidden')?.has(node.layer) === true || climbs(scene, id, hidesSubtree);\n}\n\n/** {@link NodeGates.locked} for one id. */\nexport function isNodeLocked(scene: GateSource, id: NodeId): boolean {\n const layers = flaggedLayers(scene, 'locked');\n return climbs(scene, id, layers === null\n ? (node) => node.locked === true\n : (node) => node.locked === true || layers.has(node.layer));\n}\n"],"mappings":";;;;AAqCA,SAAgB,aAAa,MAA8C;CACzE,OAAO,KAAK,WAAW;AACzB;;;;;;AAeA,SAAgB,UAAU,OAA8B;CACtD,MAAM,+BAAe,IAAI,IAAY;CACrC,MAAM,+BAAe,IAAI,IAAY;CACrC,KAAK,MAAM,KAAK,MAAM,UAAU,CAAC,GAAG;EAClC,IAAI,EAAE,YAAY,OAAO,aAAa,IAAI,EAAE,EAAE;EAC9C,IAAI,EAAE,WAAW,MAAM,aAAa,IAAI,EAAE,EAAE;CAC9C;;;CAIA,MAAM,aAAa,MAA2B,WAA+B;EAC3E,MAAM,uBAAO,IAAI,IAAqB;EACtC,MAAM,SAAS,IAAY,SAA+B;GACxD,MAAM,QAAQ,KAAK,IAAI,EAAE;GACzB,IAAI,UAAU,QAAW,OAAO;GAChC,MAAM,OAAO,MAAM,IAAI,EAAY;GACnC,IAAI,SAAS,UAAa,KAAK,IAAI,EAAE,GAAG,OAAO;GAC/C,KAAK,IAAI,EAAE;GACX,MAAM,SAAS,KAAK,UAAU;GAC9B,MAAM,OAAO,SAAS,WAAW,aAAa,IAAI,IAAI,KAAK,WAAW,SAChE,WAAW,QAAQ,OAAO,IAAI,KAAK,KAAK,KACxC,WAAW,QAAQ,MAAM,QAAQ,IAAI;GAC3C,KAAK,IAAI,IAAI,GAAG;GAChB,OAAO;EACT;EACA,QAAQ,OAAe,MAAM,oBAAI,IAAI,IAAI,CAAC;CAC5C;CAEA,MAAM,gBAAgB,UAAU,UAAU,IAAI;CAE9C,OAAO;EACL,OAAO,IAAI;GACT,MAAM,OAAO,MAAM,IAAI,EAAY;GACnC,IAAI,SAAS,QAAW,OAAO;GAC/B,OAAO,aAAa,IAAI,KAAK,KAAK,KAAK,cAAc,EAAE;EACzD;EACA,QAPa,UAAU,UAAU,YAOjC;CACF;AACF;;;;AAKA,SAAS,OAAO,OAAmB,IAAY,MAA6C;CAC1F,IAAI,OAA2B;CAC/B,IAAI,OAAO;CACX,KAAK,IAAI,KAAoB,IAAI,OAAO,OAAQ;EAC9C,MAAM,OAAO,MAAM,IAAI,EAAY;EACnC,IAAI,SAAS,QAAW,OAAO;EAC/B,IAAI,EAAE,OAAO,IAAI;GACf,yBAAS,IAAI,IAAI;GACjB,IAAI,KAAK,IAAI,EAAE,GAAG,OAAO;GACzB,KAAK,IAAI,EAAE;EACb;EACA,IAAI,KAAK,IAAI,GAAG,OAAO;EACvB,KAAK,KAAK,UAAU;CACtB;CACA,OAAO;AACT;;;AAIA,SAAS,cAAc,OAAmB,MAA+C;CACvF,IAAI,MAA0B;CAC9B,KAAK,MAAM,KAAK,MAAM,UAAU,CAAC,GAC/B,IAAI,SAAS,WAAW,EAAE,WAAW,OAAO,EAAE,YAAY,OAAO,CAAC,wBAAQ,IAAI,IAAI,EAAA,CAAG,IAAI,EAAE,EAAE;CAE/F,OAAO;AACT;;AAGA,SAAgB,aAAa,OAAmB,IAAqB;CACnE,MAAM,OAAO,MAAM,IAAI,EAAE;CACzB,IAAI,SAAS,QAAW,OAAO;CAC/B,OAAO,cAAc,OAAO,QAAQ,CAAC,EAAE,IAAI,KAAK,KAAK,MAAM,QAAQ,OAAO,OAAO,IAAI,YAAY;AACnG;;AAGA,SAAgB,aAAa,OAAmB,IAAqB;CACnE,MAAM,SAAS,cAAc,OAAO,QAAQ;CAC5C,OAAO,OAAO,OAAO,IAAI,WAAW,QAC/B,SAAS,KAAK,WAAW,QACzB,SAAS,KAAK,WAAW,QAAQ,OAAO,IAAI,KAAK,KAAK,CAAC;AAC9D"}
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
import { blendParts } from "./blend.js";
|
|
2
2
|
import { textAlongParts, wholeItem } from "./textAlong.js";
|
|
3
|
-
import { bendAlongPath, repeatAlongPath } from "@weasel-js/geom";
|
|
3
|
+
import { bendAlongPath, puckerBloatPath, repeatAlongPath, roughenPath, zigzagPath } from "@weasel-js/geom";
|
|
4
4
|
|
|
5
5
|
//#region src/core/scene/pathEffects/effects.ts
|
|
6
6
|
/**
|
|
7
7
|
* The path effects, each as the parts it paints from its sources: a blend's
|
|
8
8
|
* steps between two shapes, a shape bent along a spine, copies of a shape
|
|
9
|
-
* along a path, text laid along a path
|
|
9
|
+
* along a path, text laid along a path, and one shape roughened, zigzagged,
|
|
10
|
+
* or puckered and bloated.
|
|
10
11
|
*/
|
|
11
12
|
/** The parts `effect` paints from `sources`, in its sources' order, or
|
|
12
13
|
* `null` when a source it needs is missing — a blend's spine among them,
|
|
13
14
|
* where it names one. */
|
|
14
15
|
function effectParts(effect, sources) {
|
|
15
16
|
const [a, b] = sources;
|
|
16
|
-
if (!a
|
|
17
|
+
if (!a) return null;
|
|
18
|
+
switch (effect.kind) {
|
|
19
|
+
case "roughen": return [{
|
|
20
|
+
path: roughenPath(a.world, effect),
|
|
21
|
+
fill: a.fill,
|
|
22
|
+
stroke: a.stroke
|
|
23
|
+
}];
|
|
24
|
+
case "zigzag": return [{
|
|
25
|
+
path: zigzagPath(a.world, effect),
|
|
26
|
+
fill: a.fill,
|
|
27
|
+
stroke: a.stroke
|
|
28
|
+
}];
|
|
29
|
+
case "puckerBloat": return [{
|
|
30
|
+
path: puckerBloatPath(a.world, effect),
|
|
31
|
+
fill: a.fill,
|
|
32
|
+
stroke: a.stroke
|
|
33
|
+
}];
|
|
34
|
+
}
|
|
35
|
+
if (!b) return null;
|
|
17
36
|
switch (effect.kind) {
|
|
18
37
|
case "blend": {
|
|
19
38
|
if (sources.length < 3) return blendParts(effect, a, b);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effects.js","names":[],"sources":["../../../../src/core/scene/pathEffects/effects.ts"],"sourcesContent":["/**\n * The path effects, each as the parts it paints from its sources: a blend's\n * steps between two shapes, a shape bent along a spine, copies of a shape\n * along a path, text laid along a path.\n */\nimport {\n bendAlongPath, repeatAlongPath, type BendAlongPathOptions, type RepeatAlongPathOptions,\n} from '@weasel-js/geom';\nimport type { DerivedPart } from '../derivedParts';\nimport { blendParts, type BlendEffect } from './blend';\nimport type { EffectSource, PathEffectOptions } from './source';\nimport { textAlongParts, wholeItem, type TextAlongEffect } from './textAlong';\n\nexport type { BlendEffect, PathEffectOptions, TextAlongEffect };\n\n/** A shape bent along a spine. Its sources are `[shape, spine]`. */\nexport interface BendAlongEffect extends BendAlongPathOptions, PathEffectOptions {\n kind: 'bendAlong';\n}\n\n/** Copies of a shape along a path. Its sources are `[shape, path]`. */\nexport interface RepeatAlongEffect extends Omit<RepeatAlongPathOptions, 'flattenTolerance'>, PathEffectOptions {\n kind: 'repeatAlong';\n}\n\n/** A path effect, as a node's `data.pathEffect` holds it. */\nexport type PathEffect
|
|
1
|
+
{"version":3,"file":"effects.js","names":[],"sources":["../../../../src/core/scene/pathEffects/effects.ts"],"sourcesContent":["/**\n * The path effects, each as the parts it paints from its sources: a blend's\n * steps between two shapes, a shape bent along a spine, copies of a shape\n * along a path, text laid along a path, and one shape roughened, zigzagged,\n * or puckered and bloated.\n */\nimport {\n bendAlongPath, puckerBloatPath, repeatAlongPath, roughenPath, zigzagPath, type BendAlongPathOptions,\n type PuckerBloatPathOptions, type RepeatAlongPathOptions, type RoughenPathOptions, type ZigzagPathOptions,\n} from '@weasel-js/geom';\nimport type { DerivedPart } from '../derivedParts';\nimport { blendParts, type BlendEffect } from './blend';\nimport type { EffectSource, PathEffectOptions } from './source';\nimport { textAlongParts, wholeItem, type TextAlongEffect } from './textAlong';\n\nexport type { BlendEffect, PathEffectOptions, TextAlongEffect };\n\n/** A shape bent along a spine. Its sources are `[shape, spine]`. */\nexport interface BendAlongEffect extends BendAlongPathOptions, PathEffectOptions {\n kind: 'bendAlong';\n}\n\n/** Copies of a shape along a path. Its sources are `[shape, path]`. */\nexport interface RepeatAlongEffect extends Omit<RepeatAlongPathOptions, 'flattenTolerance'>, PathEffectOptions {\n kind: 'repeatAlong';\n}\n\n/** A shape with every point of its outline moved at random. Its source is `[shape]`. */\nexport interface RoughenEffect extends RoughenPathOptions, PathEffectOptions {\n kind: 'roughen';\n}\n\n/** A shape with its outline pushed alternately to either side. Its source is `[shape]`. */\nexport interface ZigzagEffect extends ZigzagPathOptions, PathEffectOptions {\n kind: 'zigzag';\n}\n\n/** A shape with its anchors pulled toward its middle and its handles pushed\n * away, or the reverse. Its source is `[shape]`. */\nexport interface PuckerBloatEffect extends PuckerBloatPathOptions, PathEffectOptions {\n kind: 'puckerBloat';\n}\n\n/** A path effect, as a node's `data.pathEffect` holds it. */\nexport type PathEffect =\n | BlendEffect | BendAlongEffect | RepeatAlongEffect | TextAlongEffect | RoughenEffect | ZigzagEffect | PuckerBloatEffect;\n\n/** The parts `effect` paints from `sources`, in its sources' order, or\n * `null` when a source it needs is missing — a blend's spine among them,\n * where it names one. */\nexport function effectParts(effect: PathEffect, sources: readonly (EffectSource | null)[]): DerivedPart[] | null {\n const [a, b] = sources;\n if (!a) return null;\n switch (effect.kind) {\n case 'roughen':\n return [{ path: roughenPath(a.world, effect), fill: a.fill, stroke: a.stroke }];\n case 'zigzag':\n return [{ path: zigzagPath(a.world, effect), fill: a.fill, stroke: a.stroke }];\n case 'puckerBloat':\n return [{ path: puckerBloatPath(a.world, effect), fill: a.fill, stroke: a.stroke }];\n }\n if (!b) return null;\n switch (effect.kind) {\n case 'blend': {\n if (sources.length < 3) return blendParts(effect, a, b);\n const spine = sources[2];\n return spine ? blendParts(effect, a, b, spine.world) : null;\n }\n case 'bendAlong': {\n const path = bendAlongPath(a.world, b.world, effect);\n return path === null ? null : [{ path, fill: a.fill, stroke: a.stroke }];\n }\n case 'repeatAlong':\n return repeatAlongPath(a.world, b.world, effect).map((path) => ({ path, fill: a.fill, stroke: a.stroke }));\n case 'textAlong':\n return textAlongParts(effect, a.items?.() ?? [wholeItem(a)], b.world);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAkDA,SAAgB,YAAY,QAAoB,SAAiE;CAC/G,MAAM,CAAC,GAAG,KAAK;CACf,IAAI,CAAC,GAAG,OAAO;CACf,QAAQ,OAAO,MAAf;EACE,KAAK,WACH,OAAO,CAAC;GAAE,MAAM,YAAY,EAAE,OAAO,MAAM;GAAG,MAAM,EAAE;GAAM,QAAQ,EAAE;EAAO,CAAC;EAChF,KAAK,UACH,OAAO,CAAC;GAAE,MAAM,WAAW,EAAE,OAAO,MAAM;GAAG,MAAM,EAAE;GAAM,QAAQ,EAAE;EAAO,CAAC;EAC/E,KAAK,eACH,OAAO,CAAC;GAAE,MAAM,gBAAgB,EAAE,OAAO,MAAM;GAAG,MAAM,EAAE;GAAM,QAAQ,EAAE;EAAO,CAAC;CACtF;CACA,IAAI,CAAC,GAAG,OAAO;CACf,QAAQ,OAAO,MAAf;EACE,KAAK,SAAS;GACZ,IAAI,QAAQ,SAAS,GAAG,OAAO,WAAW,QAAQ,GAAG,CAAC;GACtD,MAAM,QAAQ,QAAQ;GACtB,OAAO,QAAQ,WAAW,QAAQ,GAAG,GAAG,MAAM,KAAK,IAAI;EACzD;EACA,KAAK,aAAa;GAChB,MAAM,OAAO,cAAc,EAAE,OAAO,EAAE,OAAO,MAAM;GACnD,OAAO,SAAS,OAAO,OAAO,CAAC;IAAE;IAAM,MAAM,EAAE;IAAM,QAAQ,EAAE;GAAO,CAAC;EACzE;EACA,KAAK,eACH,OAAO,gBAAgB,EAAE,OAAO,EAAE,OAAO,MAAM,CAAC,CAAC,KAAK,UAAU;GAAE;GAAM,MAAM,EAAE;GAAM,QAAQ,EAAE;EAAO,EAAE;EAC3G,KAAK,aACH,OAAO,eAAe,QAAQ,EAAE,QAAQ,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,KAAK;CACxE;AACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bandParts } from "./textAlongBands.js";
|
|
1
2
|
import { boundsOfPath, multiply, pathShape, placeAlongRun, rotate, runLength, trackRun, transformPath, translate } from "@weasel-js/geom";
|
|
2
3
|
|
|
3
4
|
//#region src/core/scene/pathEffects/textAlong.ts
|
|
@@ -27,25 +28,37 @@ function textAlongParts(effect, items, path) {
|
|
|
27
28
|
side: effect.side ?? "auto",
|
|
28
29
|
about
|
|
29
30
|
});
|
|
31
|
+
const shift = finiteOr(effect.shift, 0);
|
|
30
32
|
const places = placeAlongRun(run, lengths, {
|
|
31
33
|
gap,
|
|
32
34
|
align: "center",
|
|
33
35
|
at: run.reversed ? L - c : c,
|
|
34
|
-
shift
|
|
36
|
+
shift,
|
|
35
37
|
wrap: closed
|
|
36
38
|
});
|
|
37
39
|
const center = effect.anchor === "center";
|
|
38
40
|
const out = [];
|
|
41
|
+
const laid = [];
|
|
39
42
|
const firstStart = places[0].center - lengths[0] / 2;
|
|
40
43
|
places.forEach((place, i) => {
|
|
41
|
-
if (dropped(place, closed ? firstStart : null, L))
|
|
44
|
+
if (dropped(place, closed ? firstStart : null, L)) {
|
|
45
|
+
laid.push(null);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
42
48
|
const item = items[i];
|
|
43
|
-
const
|
|
49
|
+
const lift = center ? item.middle : 0;
|
|
50
|
+
const m = itemFrame(place, item.length, lift);
|
|
44
51
|
for (const part of item.parts) out.push({
|
|
45
52
|
...part,
|
|
46
53
|
path: transformPath(part.path, m)
|
|
47
54
|
});
|
|
55
|
+
laid.push({
|
|
56
|
+
item,
|
|
57
|
+
start: place.center - item.length / 2,
|
|
58
|
+
lift
|
|
59
|
+
});
|
|
48
60
|
});
|
|
61
|
+
if (items.some((it) => it.bands !== void 0 && it.bands.length > 0)) out.push(...bandParts(laid, run, shift));
|
|
49
62
|
return out;
|
|
50
63
|
}
|
|
51
64
|
/** `source`'s whole outline as one item: its bounds' left at x = 0 and their vertical middle on the anchor line. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textAlong.js","names":[],"sources":["../../../../src/core/scene/pathEffects/textAlong.ts"],"sourcesContent":["/**\n * Text along a path: a source's items — a text node's grapheme clusters, or a\n * whole shape as one item — laid end to end along a path, each turned to it.\n */\nimport {\n boundsOfPath, multiply, pathShape, placeAlongRun, rotate, runLength, trackRun, transformPath, translate,\n type Affine, type Path, type RunAlign, type RunPlace, type TrackSide,\n} from '@weasel-js/geom';\nimport type { DerivedPart } from '../derivedParts';\nimport type { EffectItem, EffectSource, PathEffectOptions } from './source';\n\n/**\n * Text (or any shape) laid along a path, item by item. Its sources are `[text, path]`. Only the text's first laid line\n * follows the path; a color glyph keeps its advance and paints nothing
|
|
1
|
+
{"version":3,"file":"textAlong.js","names":[],"sources":["../../../../src/core/scene/pathEffects/textAlong.ts"],"sourcesContent":["/**\n * Text along a path: a source's items — a text node's grapheme clusters, or a\n * whole shape as one item — laid end to end along a path, each turned to it.\n */\nimport {\n boundsOfPath, multiply, pathShape, placeAlongRun, rotate, runLength, trackRun, transformPath, translate,\n type Affine, type Path, type RunAlign, type RunPlace, type TrackSide,\n} from '@weasel-js/geom';\nimport type { DerivedPart } from '../derivedParts';\nimport type { EffectItem, EffectSource, PathEffectOptions } from './source';\nimport { bandParts, type LaidItem } from './textAlongBands';\n\n/**\n * Text (or any shape) laid along a path, item by item. Its sources are `[text, path]`. Only the text's first laid line\n * follows the path; a color glyph keeps its advance and paints nothing. An item's bands — underline, strikethrough and\n * overline — join their alike neighbors' and bend with the path as one ribbon, while the glyphs stay rigid.\n * On an open path an item whose middle falls past either end is not drawn; on a closed one the text wraps past the\n * seam, and an item a whole lap on from the first item's start is not drawn.\n */\nexport interface TextAlongEffect extends PathEffectOptions {\n kind: 'textAlong';\n /** Which part of the text sits at `start`. Default `'start'`. */\n align?: RunAlign;\n /** Where the text sits, as a fraction 0..1 of the path's length from its first point. Default 0. */\n start?: number;\n /** How far the anchor line sits off the path, toward the glyphs' up, in world units. Default 0. */\n shift?: number;\n /** What sits on the path: the glyphs' baseline, or (`'center'`) their x-height middle. Default `'baseline'`. */\n anchor?: 'baseline' | 'center';\n /** Text longer than its room is cut at the path's end (`'clip'`), or has the gaps between items squeezed until it\n * fits (`'fit'`). Default `'clip'`. */\n overflow?: 'clip' | 'fit';\n /** Which side of the path the glyphs stand on; see `TrackSide`. Default `'auto'`, whichever reads upright. Turned over,\n * the text keeps the stretch of path it had, and on an open path drops whichever letters fall off it. */\n side?: TrackSide;\n}\n\nconst LEAD: Record<RunAlign, number> = { start: 0, center: 0.5, end: 1 };\n\n/** The parts `items` paint laid along `path` as `effect` says; `[]` when there is nothing to lay or nowhere to lay it. */\nexport function textAlongParts(effect: TextAlongEffect, items: readonly EffectItem[], path: Path): DerivedPart[] {\n const shape = pathShape(path);\n if (shape === null || !(shape.length > 0) || items.length === 0) return [];\n const L = shape.length;\n const closed = shape.closed;\n const lengths = items.map((it) => it.length);\n const k = LEAD[effect.align ?? 'start'];\n const at = clamp01(effect.start ?? 0) * L;\n const gap = effect.overflow === 'fit' ? fitGap(lengths, roomAt(at, L, k, closed)) : 0;\n const total = runLength(lengths, gap);\n const c = at + (0.5 - k) * total;\n const about = closed ? ((c / L) % 1 + 1) % 1 : clamp01(c / L);\n const run = trackRun(shape, 0, 1, { side: effect.side ?? 'auto', about });\n const shift = finiteOr(effect.shift, 0);\n const places = placeAlongRun(run, lengths, { gap, align: 'center', at: run.reversed ? L - c : c, shift, wrap: closed });\n const center = effect.anchor === 'center';\n const out: DerivedPart[] = [];\n const laid: (LaidItem | null)[] = [];\n const firstStart = places[0]!.center - lengths[0]! / 2;\n places.forEach((place, i) => {\n // By center (SVG textPath's rule), not by RunPlace.clipped, which judges by extent.\n if (dropped(place, closed ? firstStart : null, L)) {\n laid.push(null);\n return;\n }\n const item = items[i]!;\n const lift = center ? item.middle : 0;\n const m = itemFrame(place, item.length, lift);\n for (const part of item.parts) out.push({ ...part, path: transformPath(part.path, m) });\n laid.push({ item, start: place.center - item.length / 2, lift });\n });\n if (items.some((it) => it.bands !== undefined && it.bands.length > 0)) out.push(...bandParts(laid, run, shift));\n return out;\n}\n\n/** `source`'s whole outline as one item: its bounds' left at x = 0 and their vertical middle on the anchor line. */\nexport function wholeItem(source: EffectSource): EffectItem {\n const b = boundsOfPath(source.world);\n return {\n length: b.width,\n middle: 0,\n parts: [{\n path: transformPath(source.world, translate(-b.x, -(b.y + b.height / 2))),\n fill: source.fill,\n stroke: source.stroke,\n }],\n };\n}\n\n/** SVG `textPath`'s rule: an item is not drawn once its middle is past an open path's ends, or on a closed path a whole\n * lap or more on from the first item's start. Judged within a hair of rounding. */\nfunction dropped(place: RunPlace, firstStart: number | null, L: number): boolean {\n const hair = 1e-9 * Math.max(1, L);\n if (firstStart !== null) return place.center - firstStart >= L - hair;\n return place.center < -hair || place.center > L + hair;\n}\n\n/** How much path the text has before it runs off, from `at`, with `k` of it laid back behind `at`. */\nfunction roomAt(at: number, L: number, k: number, closed: boolean): number {\n if (closed) return L;\n return k === 0 ? L - at : k === 1 ? at : 2 * Math.min(at, L - at);\n}\n\n/** The gap that makes `lengths` fill `room`, or 0 when they fit already. Never so negative that an item's step back\n * from the one before it (its length plus the gap) goes below zero, so items keep their order in too short a room. */\nfunction fitGap(lengths: readonly number[], room: number): number {\n const total = runLength(lengths, 0);\n if (!(total > room) || lengths.length < 2) return 0;\n let floor = -Infinity;\n for (let i = 1; i < lengths.length; i++) floor = Math.max(floor, -(lengths[i - 1]! + lengths[i]!) / 2);\n return Math.max((room - total) / (lengths.length - 1), floor);\n}\n\n/** Item frame to world: its middle on `place`, turned to it, its anchor line `lift` above y = 0 brought onto the path. */\nfunction itemFrame(place: RunPlace, length: number, lift: number): Affine {\n const turn = multiply(rotate((place.angle * Math.PI) / 180), translate(-length / 2, lift));\n return multiply(translate(place.point.x, place.point.y), turn);\n}\n\nconst clamp01 = (v: number): number => (v > 1 ? 1 : v > 0 ? v : 0);\nconst finiteOr = (v: number | undefined, d: number): number => (v !== undefined && Number.isFinite(v) ? v : d);\n"],"mappings":"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"}
|