@weasel-js/core 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +300 -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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenePickIndex.js","names":[],"sources":["../../../src/canvas/pickIndex/scenePickIndex.ts"],"sourcesContent":["/**\n * The per-scene pick index: one BVH per scene layer over every node's broad\n * box (`nodeBroadBox`), keyed by the node's place in the render order it was\n * built from. A pick after anything it read can have changed walks the scene\n * and only records that state; the next pick to find the scene unchanged\n * builds, so a scene edited before every pick never pays for a build. Nodes it\n * cannot box conservatively are on `always` and are tested by every walk.\n */\nimport { createBvh, type Bvh } from '@weasel-js/geom/nd';\nimport { markerGeneration } from '@weasel-js/stroke';\nimport { getNodeShapes } from 'canvas/NodeShape';\nimport { inheritedPaintOf, inheritsPaint } from 'core/scene/inheritedPaint';\nimport { nodesAsShown } from 'core/scene/nodeAsShown';\nimport { nodeMemoGeneration } from 'core/scene/nodeMemo';\nimport type { Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { scenePaintedPoseOf } from '../derivedPath';\nimport type { PickBroad, PickCandidate } from '../pickWalk';\nimport { sceneParentsOf, sceneShownPathOf } from '../scenePickReads';\nimport { nodeBroadBox, type Box2 } from './nodeBroadBox';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype Order = readonly PickCandidate<unknown>[];\n\nexport interface ScenePickIndex {\n /** The layers holding indexed nodes; `undefined` for nodes with none. */\n readonly layers: readonly (string | undefined)[];\n readonly maxOutsetPx: number;\n}\n\nlet builds = 0;\n\n/** How many indexes have been built, for tests that pin when one rebuilds. */\nexport function pickIndexBuildCount(): number {\n return builds;\n}\n\ninterface Cached {\n version: number;\n overrides: number;\n memo: number;\n markers: number;\n /** Null until a second pick finds the same state. */\n index: BuiltIndex | null;\n}\n\nconst CACHE = new WeakMap<object, WeakMap<PoseDescriptor<unknown>, Cached>>();\n\n/** Past this share of the scene the index costs more than the walk it narrows. */\nconst MAX_SHARE = 0.5;\n\nclass BuiltIndex implements ScenePickIndex {\n readonly layers: readonly (string | undefined)[];\n /** The render order the candidates are read out of. */\n private order: Order;\n /** Build position to position in `order`, or null while they are the same array. */\n private remap: Int32Array | null = null;\n private readonly marks: Uint8Array;\n private found: Int32Array;\n\n constructor(\n private readonly built: Order,\n private readonly trees: Map<string | undefined, Bvh<number>>,\n private readonly always: Int32Array,\n readonly maxOutsetPx: number,\n ) {\n this.layers = [...trees.keys()];\n this.order = built;\n this.marks = new Uint8Array(built.length);\n this.found = new Int32Array(Math.max(16, always.length));\n }\n\n /** Follow a new render order array the scene hands back for the same state. */\n rankBy(order: Order): void {\n if (order === this.order) return;\n this.order = order;\n if (order === this.built) {\n this.remap = null;\n return;\n }\n const rank = new Map<string, number>();\n for (let i = 0; i < order.length; i++) rank.set(order[i].id, i);\n const remap = new Int32Array(this.built.length);\n for (let i = 0; i < remap.length; i++) remap[i] = rank.get(this.built[i].id) ?? -1;\n this.remap = remap;\n }\n\n /** The nodes the boxes reach, in render order, or null when that is most of the scene. */\n candidates(broads: readonly (PickBroad | null)[]): Order | null {\n const limit = Math.floor(this.order.length * MAX_SHARE);\n let count = 0;\n const add = (built: number): boolean => {\n const at = this.remap === null ? built : this.remap[built];\n if (at < 0) return true;\n if (count === this.found.length) {\n const next = new Int32Array(count * 2);\n next.set(this.found);\n this.found = next;\n }\n this.found[count++] = at;\n return count <= limit;\n };\n for (const i of this.always) if (!add(i)) return null;\n for (let l = 0; l < this.layers.length; l++) {\n const broad = broads[l];\n const tree = this.trees.get(this.layers[l]);\n if (broad === null || tree === undefined) continue;\n const { x, y, width, height } = broad.box;\n const k = broad.pxPerWorld;\n const by = this.maxOutsetPx > 0 && k !== undefined ? (k > 0 ? this.maxOutsetPx / k : Infinity) : 0;\n let over = false;\n tree.queryBox([x - by, y - by, x + width + by, y + height + by], (i) => {\n if (add(i)) return true;\n over = true;\n return false;\n });\n if (over) return null;\n }\n return this.readOut(count);\n }\n\n private readOut(count: number): Order {\n const { order, found } = this;\n const out: PickCandidate<unknown>[] = new Array(count);\n // A handful sorts faster than a pass over every mark.\n if (count * 32 < order.length) {\n const ranks = found.subarray(0, count).sort();\n for (let i = 0; i < count; i++) out[i] = order[ranks[i]];\n return out;\n }\n const marks = this.marks.length >= order.length ? this.marks : new Uint8Array(order.length);\n for (let i = 0; i < count; i++) marks[found[i]] = 1;\n let n = 0;\n for (let i = 0; n < count && i < order.length; i++) {\n if (marks[i] === 0) continue;\n marks[i] = 0;\n out[n++] = order[i];\n }\n return out;\n }\n}\n\nfunction hasCopyingAncestor(parents: Order): boolean {\n return parents.some((p) => typeof p.copiesFromPose === 'function');\n}\n\nfunction build(scene: AnyScene, descriptor: PoseDescriptor<unknown>, order: Order): BuiltIndex {\n builds++;\n const poseOf = scenePaintedPoseOf(scene);\n const pathOf = sceneShownPathOf(scene);\n const parentsOf = sceneParentsOf(scene);\n const entries = new Map<string | undefined, [number, Box2][]>();\n const always: number[] = [];\n let maxOutsetPx = 0;\n for (let i = 0; i < order.length; i++) {\n const node = order[i];\n const parents = parentsOf(node);\n // Also hit wherever an ancestor paints a copy of it, which its own box does not cover.\n if (hasCopyingAncestor(parents)) {\n always.push(i);\n continue;\n }\n const box: Box2 = [0, 0, 0, 0];\n let outsetPx: number;\n try {\n const inherited = inheritsPaint(node) ? inheritedPaintOf(node, parents) : undefined;\n outsetPx = nodeBroadBox(node, poseOf(node as never), pathOf(node), inherited, descriptor, box);\n } catch {\n always.push(i);\n continue;\n }\n if (!Number.isFinite(outsetPx) || !box.every(Number.isFinite)) {\n always.push(i);\n continue;\n }\n if (outsetPx > maxOutsetPx) maxOutsetPx = outsetPx;\n let list = entries.get(node.layer);\n if (list === undefined) entries.set(node.layer, (list = []));\n list.push([i, box]);\n }\n const trees = new Map<string | undefined, Bvh<number>>();\n for (const [layer, list] of entries) {\n const tree = createBvh<number>(2, list);\n // The tree drops an inverted box: test that node the old way.\n if (tree.size !== list.length) {\n for (const [i, box] of list) if (!(box[0] <= box[2] && box[1] <= box[3])) always.push(i);\n }\n trees.set(layer, tree);\n }\n return new BuiltIndex(order, trees, Int32Array.from(always), maxOutsetPx);\n}\n\n/** The index for `scene` read through `descriptor`, or null when this pick walks instead. */\nexport function scenePickIndex(scene: AnyScene, descriptor: PoseDescriptor<unknown>): ScenePickIndex | null {\n return current(scene, descriptor);\n}\n\nfunction current(scene: AnyScene, descriptor: PoseDescriptor<unknown>): BuiltIndex | null {\n // Registers the built-in painters first, which bumps the memo generation.\n getNodeShapes();\n const version = scene.getVersion();\n const overrides = scene.overrides.getGeneration();\n const memo = nodeMemoGeneration();\n const markers = markerGeneration();\n let perScene = CACHE.get(scene);\n if (perScene === undefined) CACHE.set(scene, (perScene = new WeakMap()));\n const hit = perScene.get(descriptor);\n // Boxed as shown, so a data override that grows a node's ink is met where it is painted.\n const order = nodesAsShown(scene.overrides, scene.renderOrderNodes()) as unknown as Order;\n if (hit && hit.version === version && hit.overrides === overrides && hit.memo === memo && hit.markers === markers) {\n if (hit.index === null) hit.index = build(scene, descriptor, order);\n else hit.index.rankBy(order);\n return hit.index;\n }\n perScene.set(descriptor, { version, overrides, memo, markers, index: null });\n return null;\n}\n\n/** What `PickSource.candidates` answers for a scene source with an index. */\nexport function indexedCandidates(\n scene: AnyScene,\n descriptor: PoseDescriptor<unknown>,\n broadFor: (layer: string | undefined) => PickBroad | null | undefined,\n): Order | null {\n const index = current(scene, descriptor);\n if (index === null) return null;\n const broads: (PickBroad | null)[] = [];\n for (const layer of index.layers) {\n const broad = broadFor(layer);\n if (broad === undefined) return null;\n if (broad !== null && Number.isNaN(broad.box.x + broad.box.y + broad.box.width + broad.box.height)) return null;\n broads.push(broad);\n }\n return index.candidates(broads);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scenePickIndex.js","names":[],"sources":["../../../src/canvas/pickIndex/scenePickIndex.ts"],"sourcesContent":["/**\n * The per-scene pick index: one BVH per scene layer over every node's broad\n * box (`nodeBroadBox`), keyed by the node's place in the render order it was\n * built from. A pick after anything it read can have changed walks the scene\n * and only records that state; the next pick to find the scene unchanged\n * builds, so a scene edited before every pick never pays for a build. Nodes it\n * cannot box conservatively are on `always` and are tested by every walk.\n */\nimport { createBvh, createSettledCache, type Bvh, type SettledCache } from '@weasel-js/geom/nd';\nimport { markerGeneration } from '@weasel-js/stroke';\nimport { getNodeShapes } from 'canvas/NodeShape';\nimport { inheritedPaintOf, inheritsPaint } from 'core/scene/inheritedPaint';\nimport { nodesAsShown } from 'core/scene/nodeAsShown';\nimport { nodeMemoGeneration } from 'core/scene/nodeMemo';\nimport type { Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { scenePaintedPoseOf } from '../derivedPath';\nimport type { PickBroad, PickCandidate } from '../pickWalk';\nimport { sceneParentsOf, sceneShownPathOf } from '../scenePickReads';\nimport { nodeBroadBox, type Box2 } from './nodeBroadBox';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype Order = readonly PickCandidate<unknown>[];\n\nexport interface ScenePickIndex {\n /** The layers holding indexed nodes; `undefined` for nodes with none. */\n readonly layers: readonly (string | undefined)[];\n readonly maxOutsetPx: number;\n}\n\nlet builds = 0;\n\n/** How many indexes have been built, for tests that pin when one rebuilds. */\nexport function pickIndexBuildCount(): number {\n return builds;\n}\n\nconst CACHE = new WeakMap<object, WeakMap<PoseDescriptor<unknown>, SettledCache<BuiltIndex>>>();\n\n/** Past this share of the scene the index costs more than the walk it narrows. */\nconst MAX_SHARE = 0.5;\n\nclass BuiltIndex implements ScenePickIndex {\n readonly layers: readonly (string | undefined)[];\n /** The render order the candidates are read out of. */\n private order: Order;\n /** Build position to position in `order`, or null while they are the same array. */\n private remap: Int32Array | null = null;\n private readonly marks: Uint8Array;\n private found: Int32Array;\n\n constructor(\n private readonly built: Order,\n private readonly trees: Map<string | undefined, Bvh<number>>,\n private readonly always: Int32Array,\n readonly maxOutsetPx: number,\n ) {\n this.layers = [...trees.keys()];\n this.order = built;\n this.marks = new Uint8Array(built.length);\n this.found = new Int32Array(Math.max(16, always.length));\n }\n\n /** Follow a new render order array the scene hands back for the same state. */\n rankBy(order: Order): void {\n if (order === this.order) return;\n this.order = order;\n if (order === this.built) {\n this.remap = null;\n return;\n }\n const rank = new Map<string, number>();\n for (let i = 0; i < order.length; i++) rank.set(order[i].id, i);\n const remap = new Int32Array(this.built.length);\n for (let i = 0; i < remap.length; i++) remap[i] = rank.get(this.built[i].id) ?? -1;\n this.remap = remap;\n }\n\n /** The nodes the boxes reach, in render order, or null when that is most of the scene. */\n candidates(broads: readonly (PickBroad | null)[]): Order | null {\n const limit = Math.floor(this.order.length * MAX_SHARE);\n let count = 0;\n const add = (built: number): boolean => {\n const at = this.remap === null ? built : this.remap[built];\n if (at < 0) return true;\n if (count === this.found.length) {\n const next = new Int32Array(count * 2);\n next.set(this.found);\n this.found = next;\n }\n this.found[count++] = at;\n return count <= limit;\n };\n for (const i of this.always) if (!add(i)) return null;\n for (let l = 0; l < this.layers.length; l++) {\n const broad = broads[l];\n const tree = this.trees.get(this.layers[l]);\n if (broad === null || tree === undefined) continue;\n const { x, y, width, height } = broad.box;\n const k = broad.pxPerWorld;\n const by = this.maxOutsetPx > 0 && k !== undefined ? (k > 0 ? this.maxOutsetPx / k : Infinity) : 0;\n let over = false;\n tree.queryBox([x - by, y - by, x + width + by, y + height + by], (i) => {\n if (add(i)) return true;\n over = true;\n return false;\n });\n if (over) return null;\n }\n return this.readOut(count);\n }\n\n private readOut(count: number): Order {\n const { order, found } = this;\n const out: PickCandidate<unknown>[] = new Array(count);\n // A handful sorts faster than a pass over every mark.\n if (count * 32 < order.length) {\n const ranks = found.subarray(0, count).sort();\n for (let i = 0; i < count; i++) out[i] = order[ranks[i]];\n return out;\n }\n const marks = this.marks.length >= order.length ? this.marks : new Uint8Array(order.length);\n for (let i = 0; i < count; i++) marks[found[i]] = 1;\n let n = 0;\n for (let i = 0; n < count && i < order.length; i++) {\n if (marks[i] === 0) continue;\n marks[i] = 0;\n out[n++] = order[i];\n }\n return out;\n }\n}\n\nfunction hasCopyingAncestor(parents: Order): boolean {\n return parents.some((p) => typeof p.copiesFromPose === 'function');\n}\n\nfunction build(scene: AnyScene, descriptor: PoseDescriptor<unknown>, order: Order): BuiltIndex {\n builds++;\n const poseOf = scenePaintedPoseOf(scene);\n const pathOf = sceneShownPathOf(scene);\n const parentsOf = sceneParentsOf(scene);\n const entries = new Map<string | undefined, [number, Box2][]>();\n const always: number[] = [];\n let maxOutsetPx = 0;\n for (let i = 0; i < order.length; i++) {\n const node = order[i];\n const parents = parentsOf(node);\n // Also hit wherever an ancestor paints a copy of it, which its own box does not cover.\n if (hasCopyingAncestor(parents)) {\n always.push(i);\n continue;\n }\n const box: Box2 = [0, 0, 0, 0];\n let outsetPx: number;\n try {\n const inherited = inheritsPaint(node) ? inheritedPaintOf(node, parents) : undefined;\n outsetPx = nodeBroadBox(node, poseOf(node as never), pathOf(node), inherited, descriptor, box);\n } catch {\n always.push(i);\n continue;\n }\n if (!Number.isFinite(outsetPx) || !box.every(Number.isFinite)) {\n always.push(i);\n continue;\n }\n if (outsetPx > maxOutsetPx) maxOutsetPx = outsetPx;\n let list = entries.get(node.layer);\n if (list === undefined) entries.set(node.layer, (list = []));\n list.push([i, box]);\n }\n const trees = new Map<string | undefined, Bvh<number>>();\n for (const [layer, list] of entries) {\n const tree = createBvh<number>(2, list);\n // The tree drops an inverted box: test that node the old way.\n if (tree.size !== list.length) {\n for (const [i, box] of list) if (!(box[0] <= box[2] && box[1] <= box[3])) always.push(i);\n }\n trees.set(layer, tree);\n }\n return new BuiltIndex(order, trees, Int32Array.from(always), maxOutsetPx);\n}\n\n/** The index for `scene` read through `descriptor`, or null when this pick walks instead. */\nexport function scenePickIndex(scene: AnyScene, descriptor: PoseDescriptor<unknown>): ScenePickIndex | null {\n return current(scene, descriptor);\n}\n\nfunction current(scene: AnyScene, descriptor: PoseDescriptor<unknown>): BuiltIndex | null {\n // Registers the built-in painters first, which bumps the memo generation.\n getNodeShapes();\n const version = scene.getVersion();\n const overrides = scene.overrides.getGeneration();\n // Boxed where each node is painted, which a container's scroll moves.\n const scrolled = scene.scroll?.getGeneration() ?? 0;\n const memo = nodeMemoGeneration();\n const markers = markerGeneration();\n let perScene = CACHE.get(scene);\n if (perScene === undefined) CACHE.set(scene, (perScene = new WeakMap()));\n let cache = perScene.get(descriptor);\n if (cache === undefined) perScene.set(descriptor, (cache = createSettledCache()));\n // Boxed as shown, so a data override that grows a node's ink is met where it is painted.\n const order = nodesAsShown(scene.overrides, scene.renderOrderNodes()) as unknown as Order;\n const index = cache.read([version, overrides, scrolled, memo, markers], () => build(scene, descriptor, order));\n index?.rankBy(order);\n return index;\n}\n\n/** What `PickSource.candidates` answers for a scene source with an index. */\nexport function indexedCandidates(\n scene: AnyScene,\n descriptor: PoseDescriptor<unknown>,\n broadFor: (layer: string | undefined) => PickBroad | null | undefined,\n): Order | null {\n const index = current(scene, descriptor);\n if (index === null) return null;\n const broads: (PickBroad | null)[] = [];\n for (const layer of index.layers) {\n const broad = broadFor(layer);\n if (broad === undefined) return null;\n if (broad !== null && Number.isNaN(broad.box.x + broad.box.y + broad.box.width + broad.box.height)) return null;\n broads.push(broad);\n }\n return index.candidates(broads);\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { composeWorldPose, composesFrames, frameAtOrAbove } from "../core/scene/composeFrame.js";
|
|
1
|
+
import { composeWorldPose, composesFrames, frameAtOrAbove, memoizeCompose } from "../core/scene/composeFrame.js";
|
|
2
2
|
import { definesFrame, effectivePose } from "../core/scene/effectivePose.js";
|
|
3
3
|
import { poseDescriptorForNode, translatePoseViaDescriptor, visualBoundsViaDescriptor } from "../core/geometry/poseDescriptor.js";
|
|
4
|
+
import { scrolledFrames } from "../core/scene/scrollOffsets.js";
|
|
4
5
|
import { AUTO_POSE_DESCRIPTOR } from "../core/geometry/autoPoseDescriptor.js";
|
|
5
6
|
import { asNodeId } from "../core/scene/types.js";
|
|
6
7
|
import { nodeGates } from "../core/scene/nodeGates.js";
|
|
@@ -9,19 +10,13 @@ import { applyOpsTo } from "../core/applyOps.js";
|
|
|
9
10
|
import { hitTestArea, hitTestLassoPolygon, regionPickOptions } from "./deps/hitTestArea.js";
|
|
10
11
|
import { insertSite } from "./deps/insertSite.js";
|
|
11
12
|
import { scenePoseTarget } from "../animation/poseTarget.js";
|
|
12
|
-
import { cascadingSetPose, containerCascade } from "./poseCascade.js";
|
|
13
13
|
import { pasteSceneSnapshot, snapshotSceneSelection } from "./sceneClipboard.js";
|
|
14
14
|
|
|
15
15
|
//#region src/canvas/sceneAdapter.ts
|
|
16
16
|
/** Derive the adapter for a scene. Reads go straight to the scene; writes are
|
|
17
17
|
* ops applied through its history, so anything done through the adapter is
|
|
18
|
-
* undoable.
|
|
19
|
-
*
|
|
20
|
-
* Moving a container carries its contents either way the scene composes
|
|
21
|
-
* poses. Under a frame (`scene.composition`) the one pose written moves them.
|
|
22
|
-
* Under the absolute-pose model `setPose` on a container translates every
|
|
23
|
-
* descendant by the same delta, and a batch that poses descendants too undoes
|
|
24
|
-
* exactly only when it goes through this adapter's `applyOps`. */
|
|
18
|
+
* undoable. A container's pose is a frame its children are stored in, so the
|
|
19
|
+
* one pose written moves its contents. */
|
|
25
20
|
function sceneToAdapter(scene, options = {}) {
|
|
26
21
|
const sel = options.selection;
|
|
27
22
|
const getSelection = sel?.getSelection ?? sel?.get ?? (() => []);
|
|
@@ -31,15 +26,15 @@ function sceneToAdapter(scene, options = {}) {
|
|
|
31
26
|
const translate = (p, dx, dy, node) => translatePoseViaDescriptor(p, dx, dy, node === void 0 ? d : poseDescriptorForNode(d, node));
|
|
32
27
|
const composition = scene.composition;
|
|
33
28
|
const composes = composesFrames(composition);
|
|
29
|
+
const compose = memoizeCompose(composition.compose);
|
|
30
|
+
const childFrame = composes ? scrolledFrames(scene) : void 0;
|
|
34
31
|
const pickOpts = {};
|
|
35
32
|
const pickOptsFor = (view) => view ? {
|
|
36
33
|
...pickOpts,
|
|
37
34
|
...regionPickOptions(view, void 0)
|
|
38
35
|
} : pickOpts;
|
|
39
|
-
const cascade = composes ? null : { setPose: cascadingSetPose(scene, d) };
|
|
40
36
|
const clipboardEnv = {
|
|
41
37
|
scene,
|
|
42
|
-
composes,
|
|
43
38
|
worldPoseOf: (id) => adapter.getWorldPose(id),
|
|
44
39
|
frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),
|
|
45
40
|
poseBounds,
|
|
@@ -64,15 +59,11 @@ function sceneToAdapter(scene, options = {}) {
|
|
|
64
59
|
},
|
|
65
60
|
getWorldPose(id) {
|
|
66
61
|
if (!composes) return adapter.getPose(id);
|
|
67
|
-
return composeWorldPose(adapter, id,
|
|
68
|
-
},
|
|
69
|
-
...composes ? { composePose: composition.compose } : {},
|
|
70
|
-
...cascade ? { setPose: (id, pose) => cascade.setPose(asNodeId(id), pose) } : {},
|
|
71
|
-
poseTarget(id, descriptor) {
|
|
72
|
-
if (cascade === null || scene.get(asNodeId(id))?.kind !== "group") return scenePoseTarget(scene, id, descriptor ?? d);
|
|
73
|
-
const carried = containerCascade(scene, asNodeId(id), d, (pose) => adapter.setPose(id, pose));
|
|
74
|
-
return scenePoseTarget(scene, id, descriptor ?? d, carried);
|
|
62
|
+
return composeWorldPose(adapter, id, compose);
|
|
75
63
|
},
|
|
64
|
+
...composes ? { composePose: compose } : {},
|
|
65
|
+
...childFrame ? { childFrame } : {},
|
|
66
|
+
poseTarget: (id, descriptor) => scenePoseTarget(scene, id, descriptor ?? d),
|
|
76
67
|
getLayers: () => scene.layers.map((l) => ({
|
|
77
68
|
id: l.id,
|
|
78
69
|
visible: l.visible,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sceneAdapter.js","names":[],"sources":["../../src/canvas/sceneAdapter.ts"],"sourcesContent":["/**\n * Synthesize a `<Canvas>`-compatible adapter from a `Scene` primitive.\n * `getPose` / `setPose` return / write **local** coordinates (relative to\n * parent); world composition is the renderer's job via `composeWorldPose`.\n */\nimport type {\n AreaSelectAdapter,\n ClipboardSnapshot,\n InsertAdapter,\n PasteCtx,\n LassoHitMode,\n LassoSelectAdapter,\n RegionPickView,\n LayerEnumerableAdapter,\n MoveAdapter,\n ResizeAdapter,\n RotateAdapter,\n} from 'core/adapters/types';\nimport type { LayoutStrategy } from '../layout/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene } from 'core/scene/types';\nimport { definesFrame, effectivePose } from 'core/scene/effectivePose';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId } from 'core/scene/types';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport { applyOpsTo } from 'core/applyOps';\nimport { hitTestArea, hitTestLassoPolygon, regionPickOptions } from 'canvas/deps/hitTestArea';\nimport type { ScenePickSourceOptions } from 'canvas/pickWalk';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { composesFrames, composeWorldPose, frameAtOrAbove } from 'core/scene/composePose';\nimport { insertSite } from './deps/insertSite';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport { scenePoseTarget } from '../animation/poseTarget';\nimport { cascadingSetPose, containerCascade } from './poseCascade';\nimport { pasteSceneSnapshot, snapshotSceneSelection, type SceneClipboardEnv } from './sceneClipboard';\n\n/** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /\n * `setSelection`. Matches `useSelection().adapterMethods` plus an imperative\n * read; pass `useSelection()` itself or `selection.adapterMethods`. */\nexport interface SceneAdapterSelection {\n get?(): string[];\n set?(ids: string[]): void;\n getSelection?(): string[];\n setSelection?(ids: string[]): void;\n}\n\n/** Sibling z-order surface used by reorder ops. Inlined here (rather than\n * pulled from `core/ops/reorder` to avoid import cycles); the `null` parent\n * channel addresses the root sibling list. */\n/** @internal */\ninterface ReorderAdapter {\n getChildren(parentId: string | null): string[];\n setChildOrder(parentId: string | null, ids: string[]): void;\n}\n\n/** Everything the interaction layer can ask of a scene, as one type: the\n * move, resize, rotate, selection, layer and reorder contracts combined.\n * `<SceneCanvas>` synthesizes one of these, which is why most consumers never\n * touch an adapter directly. */\nexport type SceneCanvasAdapter<TData, TLayer extends string, TPose> =\n & MoveAdapter<Node<TData, TLayer, TPose>, TPose>\n & ResizeAdapter<Node<TData, TLayer, TPose>, TPose>\n & RotateAdapter<Node<TData, TLayer, TPose>, TPose>\n & AreaSelectAdapter\n & LassoSelectAdapter\n & LayerEnumerableAdapter<TLayer>\n & ReorderAdapter\n & Partial<InsertAdapter<Node<TData, TLayer, TPose>>>\n // Optional on the underlying adapter contracts, but unconditionally\n // provided by a scene-backed adapter.\n & {\n getParent(id: string): string | null;\n getLayers: NonNullable<LayerEnumerableAdapter<TLayer>['getLayers']>;\n /** The node's pose with every ancestor's frame folded in. Equal to\n * `getPose` unless the scene composes poses. */\n getWorldPose(id: string): TPose;\n /** Whether `id`'s pose is a frame its children are expressed in — see\n * `definesFrame`. */\n definesFrame(id: string): boolean;\n /** Present only when a composing strategy is configured; the render\n * walk feature-detects it. */\n composePose?(parent: TPose, child: TPose): TPose;\n getSelection(): string[];\n setSelection(ids: string[]): void;\n insertNode(node: Node<TData, TLayer, TPose>, index?: number): void;\n removeNode(id: string): void;\n /** Replace a node's `data` — what a `setData` op writes through. */\n setData(id: string, data: TData): void;\n /** Swap a container's layout — what a `setLayout` op writes through. */\n setLayout(id: string, layout: LayoutStrategy<TPose> | string | null): void;\n /** Move a node to another layer — what a `setLayer` op writes through. */\n setLayer(id: string, layer: TLayer): void;\n /** Retarget a derived node's sources — what a `setDependsOn` op writes through. */\n setDependsOn(id: string, dependsOn: readonly string[] | 'children' | undefined): void;\n /** Hide or show a node — what a `setHidden` op writes through. */\n setHidden(id: string, hidden: boolean): void;\n /** Lock or unlock a node — what a `setLocked` op writes through. */\n setLocked(id: string, locked: boolean): void;\n getRemovalClosure(ids: readonly string[]): string[];\n applyOps(ops: Op[], label?: string): void;\n snapshotSelection(ids: string[]): ClipboardSnapshot;\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[];\n getPasteOffset(clipboard: ClipboardSnapshot): { dx: number; dy: number };\n };\n\n/** Optional extras for the synthesized adapter. Pass `commitInsert` to wire\n * the insert gesture into a Scene-owned canvas; the returned object becomes\n * a leaf on the layer named by `layer` (default `'default'`). */\nexport interface SceneToAdapterOptions<TData, TLayer extends string, TPose> {\n /** Factory for new objects. Returning `null` aborts the insert. */\n commitInsert?: (bounds: Bounds) => {\n pose: TPose;\n data: TData;\n id?: string;\n } | null;\n /** Layer to place inserted nodes on. Defaults to the trivial-form layer. */\n insertLayer?: TLayer;\n /** Selection source: the result of `useSelection()` (or its\n * `adapterMethods`). When omitted, `getSelection` returns `[]` and\n * `setSelection` is a noop, so the marquee gesture updates nothing. */\n selection?: SceneAdapterSelection;\n /** How to read and rewrite this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** The container a paste lands in, read at each paste: `<SceneCanvas>` passes the `insertInto` dep's. */\n pasteInto?: () => string | null;\n}\n\n/** Derive the adapter for a scene. Reads go straight to the scene; writes are\n * ops applied through its history, so anything done through the adapter is\n * undoable.\n *\n * Moving a container carries its contents either way the scene composes\n * poses. Under a frame (`scene.composition`) the one pose written moves them.\n * Under the absolute-pose model `setPose` on a container translates every\n * descendant by the same delta, and a batch that poses descendants too undoes\n * exactly only when it goes through this adapter's `applyOps`. */\nexport function sceneToAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const sel = options.selection;\n const getSelection = sel?.getSelection ?? sel?.get ?? (() => [] as string[]);\n const setSelection = sel?.setSelection ?? sel?.set ?? (() => {});\n const d = (options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const poseBounds = (p: TPose, node: unknown): Bounds =>\n visualBoundsViaDescriptor(p, poseDescriptorForNode(d, node));\n const translate = (p: TPose, dx: number, dy: number, node?: unknown): TPose =>\n translatePoseViaDescriptor(\n p, dx, dy, node === undefined ? d : poseDescriptorForNode(d, node),\n );\n\n const composition = scene.composition;\n const composes = composesFrames(composition);\n const pickOpts: ScenePickSourceOptions<unknown> = {};\n // The asking view's layer gate and camera, over the adapter's own options.\n const pickOptsFor = (view: RegionPickView | undefined): ScenePickSourceOptions<unknown> =>\n (view ? { ...pickOpts, ...regionPickOptions(view, undefined) } : pickOpts);\n\n const cascade = composes ? null : { setPose: cascadingSetPose(scene, d) };\n\n const clipboardEnv: SceneClipboardEnv<TData, TLayer, TPose> = {\n scene,\n composes,\n worldPoseOf: (id) => adapter.getWorldPose(id),\n frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),\n poseBounds,\n translate, ...(options.pasteInto ? { landing: () => insertSite(scene as never, options.pasteInto!()) } : {}),\n };\n\n const adapter: SceneCanvasAdapter<TData, TLayer, TPose> = {\n ...sceneMutators(scene),\n // What is shown, where the mutators' other users read the document:\n // the nodes not hidden, and the effective pose.\n getNodes() {\n const gates = nodeGates(scene);\n return scene.renderOrderNodes().filter((n) => !gates.hidden(n.id));\n },\n getPose(id) {\n const nid = asNodeId(id);\n const n = scene.get(nid);\n if (!n) throw new Error(`sceneToAdapter: unknown node \"${id}\"`);\n return effectivePose(scene, n);\n },\n definesFrame(id) {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n getWorldPose(id) {\n if (!composes) return adapter.getPose(id);\n return composeWorldPose(adapter, id, composition.compose);\n },\n // Only present when it would do something: the render walk skips the fold\n // entirely when this is absent, which is the absolute-pose path.\n ...(composes ? { composePose: composition.compose } : {}),\n ...(cascade ? { setPose: (id: string, pose: TPose) => cascade.setPose(asNodeId(id), pose) } : {}),\n poseTarget(id, descriptor) {\n if (cascade === null || scene.get(asNodeId(id))?.kind !== 'group') {\n return scenePoseTarget(scene, id, descriptor ?? d);\n }\n // Its descendants hold poses of their own: a commit moves them, and they are shown beside it.\n const carried = containerCascade(scene, asNodeId(id), d, (pose) => adapter.setPose(id, pose));\n return scenePoseTarget(scene, id, descriptor ?? d, carried);\n },\n getLayers: () => scene.layers.map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n subscribeLayers: (listener) => scene.subscribe(listener),\n getLayout: (id) => scene.layoutOf(asNodeId(id)),\n // With a label the ops are one batch checkpoint; without one they are\n // applied transiently, with no checkpoint (the AreaSelectAdapter contract).\n applyOps(ops: Op[], label?: string) {\n if (label !== undefined) {\n scene.applyBatch(ops, label, this);\n } else {\n applyOpsTo(this, ops);\n }\n },\n getSelection,\n setSelection,\n // The live marquee's and lasso's own hit-tests. Groups come back:\n // this adapter's consumer has nothing to fold children into them.\n hitTestArea(rect: Bounds, view?: RegionPickView) {\n return hitTestArea(\n scene,\n rect,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n hitTestLasso(polygon, mode: LassoHitMode, view?: RegionPickView) {\n return hitTestLassoPolygon(\n scene,\n polygon,\n mode,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n // A pure factory: it constructs a Node spec but does NOT touch the scene.\n // The `InsertOp` → `adapter.insertNode` → `scene.add` path is the sole\n // insertion path. Calling `scene.add` here too would double-insert, and\n // the second add throws on id collision.\n ...(options.commitInsert\n ? {\n commitInsert: (bounds: { x: number; y: number; width: number; height: number }) => {\n const created = options.commitInsert!(bounds);\n if (!created) return null;\n const layer = (options.insertLayer ?? ('default' as TLayer));\n const id = created.id === undefined ? scene.mintId() : asNodeId(created.id);\n return {\n kind: 'mark',\n id,\n layer,\n pose: created.pose,\n data: created.data,\n parent: null,\n } as Node<TData, TLayer, TPose>;\n },\n }\n : {}),\n // Clipboard seam (`useClipboardOps` / the paste ingestion path).\n snapshotSelection(ids: string[]): ClipboardSnapshot {\n return snapshotSceneSelection(clipboardEnv, ids);\n },\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[] {\n return pasteSceneSnapshot(clipboardEnv, clipboard, offset, ctx);\n },\n // Constant cascade: the hook re-snapshots the just-created items, so\n // repeated pastes stack +12/+12 each time.\n getPasteOffset(): { dx: number; dy: number } {\n return { dx: 12, dy: 12 };\n },\n };\n\n return adapter;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sceneAdapter.js","names":[],"sources":["../../src/canvas/sceneAdapter.ts"],"sourcesContent":["/**\n * Synthesize a `<Canvas>`-compatible adapter from a `Scene` primitive.\n * `getPose` / `setPose` return / write **local** coordinates (relative to\n * parent); world composition is the renderer's job via `composeWorldPose`.\n */\nimport type {\n AreaSelectAdapter,\n ClipboardSnapshot,\n InsertAdapter,\n PasteCtx,\n LassoHitMode,\n LassoSelectAdapter,\n RegionPickView,\n LayerEnumerableAdapter,\n MoveAdapter,\n ResizeAdapter,\n RotateAdapter,\n} from 'core/adapters/types';\nimport type { LayoutStrategy } from '../layout/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene } from 'core/scene/types';\nimport { definesFrame, effectivePose } from 'core/scene/effectivePose';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId } from 'core/scene/types';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport { applyOpsTo } from 'core/applyOps';\nimport { hitTestArea, hitTestLassoPolygon, regionPickOptions } from 'canvas/deps/hitTestArea';\nimport type { ScenePickSourceOptions } from 'canvas/pickWalk';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { composesFrames, composeWorldPose, frameAtOrAbove } from 'core/scene/composePose';\nimport { memoizeCompose } from 'core/scene/composeFrame';\nimport { scrolledFrames } from 'core/scene/scrollOffsets';\nimport { insertSite } from './deps/insertSite';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport { scenePoseTarget } from '../animation/poseTarget';\nimport { pasteSceneSnapshot, snapshotSceneSelection, type SceneClipboardEnv } from './sceneClipboard';\n\n/** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /\n * `setSelection`. Matches `useSelection().adapterMethods` plus an imperative\n * read; pass `useSelection()` itself or `selection.adapterMethods`. */\nexport interface SceneAdapterSelection {\n get?(): string[];\n set?(ids: string[]): void;\n getSelection?(): string[];\n setSelection?(ids: string[]): void;\n}\n\n/** Sibling z-order surface used by reorder ops. Inlined here (rather than\n * pulled from `core/ops/reorder` to avoid import cycles); the `null` parent\n * channel addresses the root sibling list. */\n/** @internal */\ninterface ReorderAdapter {\n getChildren(parentId: string | null): string[];\n setChildOrder(parentId: string | null, ids: string[]): void;\n}\n\n/** Everything the interaction layer can ask of a scene, as one type: the\n * move, resize, rotate, selection, layer and reorder contracts combined.\n * `<SceneCanvas>` synthesizes one of these, which is why most consumers never\n * touch an adapter directly. */\nexport type SceneCanvasAdapter<TData, TLayer extends string, TPose> =\n & MoveAdapter<Node<TData, TLayer, TPose>, TPose>\n & ResizeAdapter<Node<TData, TLayer, TPose>, TPose>\n & RotateAdapter<Node<TData, TLayer, TPose>, TPose>\n & AreaSelectAdapter\n & LassoSelectAdapter\n & LayerEnumerableAdapter<TLayer>\n & ReorderAdapter\n & Partial<InsertAdapter<Node<TData, TLayer, TPose>>>\n // Optional on the underlying adapter contracts, but unconditionally\n // provided by a scene-backed adapter.\n & {\n getParent(id: string): string | null;\n getLayers: NonNullable<LayerEnumerableAdapter<TLayer>['getLayers']>;\n /** The node's pose with every ancestor's frame folded in. Equal to\n * `getPose` unless the scene composes poses. */\n getWorldPose(id: string): TPose;\n /** Whether `id`'s pose is a frame its children are expressed in — see\n * `definesFrame`. */\n definesFrame(id: string): boolean;\n /** Present only when a composing strategy is configured; the render\n * walk feature-detects it. */\n composePose?(parent: TPose, child: TPose): TPose;\n /** The frame a container gives its children, scrolled by the scene's\n * `scroll` offset — see `PoseAdapter.childFrame`. Present only when the\n * composition can scroll. */\n childFrame?(id: string, world: TPose): TPose;\n getSelection(): string[];\n setSelection(ids: string[]): void;\n insertNode(node: Node<TData, TLayer, TPose>, index?: number): void;\n removeNode(id: string): void;\n /** Replace a node's `data` — what a `setData` op writes through. */\n setData(id: string, data: TData): void;\n /** Swap a container's layout — what a `setLayout` op writes through. */\n setLayout(id: string, layout: LayoutStrategy<TPose> | string | null): void;\n /** Move a node to another layer — what a `setLayer` op writes through. */\n setLayer(id: string, layer: TLayer): void;\n /** Retarget a derived node's sources — what a `setDependsOn` op writes through. */\n setDependsOn(id: string, dependsOn: readonly string[] | 'children' | undefined): void;\n /** Hide or show a node — what a `setHidden` op writes through. */\n setHidden(id: string, hidden: boolean): void;\n /** Lock or unlock a node — what a `setLocked` op writes through. */\n setLocked(id: string, locked: boolean): void;\n getRemovalClosure(ids: readonly string[]): string[];\n applyOps(ops: Op[], label?: string): void;\n snapshotSelection(ids: string[]): ClipboardSnapshot;\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[];\n getPasteOffset(clipboard: ClipboardSnapshot): { dx: number; dy: number };\n };\n\n/** Optional extras for the synthesized adapter. Pass `commitInsert` to wire\n * the insert gesture into a Scene-owned canvas; the returned object becomes\n * a leaf on the layer named by `layer` (default `'default'`). */\nexport interface SceneToAdapterOptions<TData, TLayer extends string, TPose> {\n /** Factory for new objects. Returning `null` aborts the insert. */\n commitInsert?: (bounds: Bounds) => {\n pose: TPose;\n data: TData;\n id?: string;\n } | null;\n /** Layer to place inserted nodes on. Defaults to the trivial-form layer. */\n insertLayer?: TLayer;\n /** Selection source: the result of `useSelection()` (or its\n * `adapterMethods`). When omitted, `getSelection` returns `[]` and\n * `setSelection` is a noop, so the marquee gesture updates nothing. */\n selection?: SceneAdapterSelection;\n /** How to read and rewrite this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** The container a paste lands in, read at each paste: `<SceneCanvas>` passes the `insertInto` dep's. */\n pasteInto?: () => string | null;\n}\n\n/** Derive the adapter for a scene. Reads go straight to the scene; writes are\n * ops applied through its history, so anything done through the adapter is\n * undoable. A container's pose is a frame its children are stored in, so the\n * one pose written moves its contents. */\nexport function sceneToAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const sel = options.selection;\n const getSelection = sel?.getSelection ?? sel?.get ?? (() => [] as string[]);\n const setSelection = sel?.setSelection ?? sel?.set ?? (() => {});\n const d = (options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const poseBounds = (p: TPose, node: unknown): Bounds =>\n visualBoundsViaDescriptor(p, poseDescriptorForNode(d, node));\n const translate = (p: TPose, dx: number, dy: number, node?: unknown): TPose =>\n translatePoseViaDescriptor(p, dx, dy, node === undefined ? d : poseDescriptorForNode(d, node));\n\n const composition = scene.composition;\n const composes = composesFrames(composition);\n const compose = memoizeCompose(composition.compose);\n const childFrame = composes ? scrolledFrames(scene) : undefined;\n const pickOpts: ScenePickSourceOptions<unknown> = {};\n // The asking view's layer gate and camera, over the adapter's own options.\n const pickOptsFor = (view: RegionPickView | undefined): ScenePickSourceOptions<unknown> =>\n (view ? { ...pickOpts, ...regionPickOptions(view, undefined) } : pickOpts);\n\n const clipboardEnv: SceneClipboardEnv<TData, TLayer, TPose> = {\n scene,\n worldPoseOf: (id) => adapter.getWorldPose(id),\n frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),\n poseBounds,\n translate, ...(options.pasteInto ? { landing: () => insertSite(scene as never, options.pasteInto!()) } : {}),\n };\n\n const adapter: SceneCanvasAdapter<TData, TLayer, TPose> = {\n ...sceneMutators(scene),\n // What is shown, where the mutators' other users read the document:\n // the nodes not hidden, and the effective pose.\n getNodes() {\n const gates = nodeGates(scene);\n return scene.renderOrderNodes().filter((n) => !gates.hidden(n.id));\n },\n getPose(id) {\n const nid = asNodeId(id);\n const n = scene.get(nid);\n if (!n) throw new Error(`sceneToAdapter: unknown node \"${id}\"`);\n return effectivePose(scene, n);\n },\n definesFrame(id) {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n getWorldPose(id) {\n if (!composes) return adapter.getPose(id);\n return composeWorldPose(adapter, id, compose);\n },\n // Only present when it would do something: the render walk skips the fold\n // entirely when this is absent, which is the absolute-pose path.\n ...(composes ? { composePose: compose } : {}),\n ...(childFrame ? { childFrame } : {}),\n poseTarget: (id, descriptor) => scenePoseTarget(scene, id, descriptor ?? d),\n getLayers: () => scene.layers.map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n subscribeLayers: (listener) => scene.subscribe(listener),\n getLayout: (id) => scene.layoutOf(asNodeId(id)),\n // With a label the ops are one batch checkpoint; without one they are\n // applied transiently, with no checkpoint (the AreaSelectAdapter contract).\n applyOps(ops: Op[], label?: string) {\n if (label !== undefined) {\n scene.applyBatch(ops, label, this);\n } else {\n applyOpsTo(this, ops);\n }\n },\n getSelection,\n setSelection,\n // The live marquee's and lasso's own hit-tests. Groups come back:\n // this adapter's consumer has nothing to fold children into them.\n hitTestArea(rect: Bounds, view?: RegionPickView) {\n return hitTestArea(\n scene,\n rect,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n hitTestLasso(polygon, mode: LassoHitMode, view?: RegionPickView) {\n return hitTestLassoPolygon(\n scene,\n polygon,\n mode,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n // A pure factory: it constructs a Node spec but does NOT touch the scene.\n // The `InsertOp` → `adapter.insertNode` → `scene.add` path is the sole\n // insertion path. Calling `scene.add` here too would double-insert, and\n // the second add throws on id collision.\n ...(options.commitInsert\n ? {\n commitInsert: (bounds: { x: number; y: number; width: number; height: number }) => {\n const created = options.commitInsert!(bounds);\n if (!created) return null;\n const layer = (options.insertLayer ?? ('default' as TLayer));\n const id = created.id === undefined ? scene.mintId() : asNodeId(created.id);\n return {\n kind: 'mark',\n id,\n layer,\n pose: created.pose,\n data: created.data,\n parent: null,\n } as Node<TData, TLayer, TPose>;\n },\n }\n : {}),\n // Clipboard seam (`useClipboardOps` / the paste ingestion path).\n snapshotSelection(ids: string[]): ClipboardSnapshot {\n return snapshotSceneSelection(clipboardEnv, ids);\n },\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[] {\n return pasteSceneSnapshot(clipboardEnv, clipboard, offset, ctx);\n },\n // Constant cascade: the hook re-snapshots the just-created items, so\n // repeated pastes stack +12/+12 each time.\n getPasteOffset(): { dx: number; dy: number } {\n return { dx: 12, dy: 12 };\n },\n };\n\n return adapter;\n}\n"],"mappings":"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"}
|
|
@@ -18,7 +18,6 @@ function snapshotSceneSelection(env, ids) {
|
|
|
18
18
|
const taken = /* @__PURE__ */ new Set();
|
|
19
19
|
const items = [];
|
|
20
20
|
const poseFor = (n, isRoot) => {
|
|
21
|
-
if (!env.composes) return n.pose;
|
|
22
21
|
if (isRoot) return env.worldPoseOf(n.id);
|
|
23
22
|
const frame = env.frameAtOrAbove(n.parent);
|
|
24
23
|
return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);
|
|
@@ -43,8 +42,23 @@ function snapshotSceneSelection(env, ids) {
|
|
|
43
42
|
});
|
|
44
43
|
if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);
|
|
45
44
|
};
|
|
45
|
+
const covered = (id) => idSet.has(id) || scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a));
|
|
46
|
+
const carrying = /* @__PURE__ */ new Set();
|
|
47
|
+
const carrySources = (id) => {
|
|
48
|
+
if (carrying.has(id)) return;
|
|
49
|
+
carrying.add(id);
|
|
50
|
+
const n = scene.get(asNodeId(id));
|
|
51
|
+
if (!n) return;
|
|
52
|
+
for (const source of sourcesOf(n)) {
|
|
53
|
+
if (taken.has(source) || covered(source) || scene.get(source)?.hidden !== true) continue;
|
|
54
|
+
carrySources(source);
|
|
55
|
+
capture(source, true);
|
|
56
|
+
}
|
|
57
|
+
if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) carrySources(cid);
|
|
58
|
+
};
|
|
46
59
|
for (const id of ids) {
|
|
47
60
|
if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;
|
|
61
|
+
carrySources(id);
|
|
48
62
|
capture(id, true);
|
|
49
63
|
}
|
|
50
64
|
return { items };
|
|
@@ -118,7 +132,6 @@ function pasteSceneSnapshot(env, clipboard, offset, ctx) {
|
|
|
118
132
|
const isRoot = (item) => item.parent === null || !idMap.has(item.parent);
|
|
119
133
|
const byId = new Map(src.map((item) => [item.id, item]));
|
|
120
134
|
const ridesPastedFrame = (item) => {
|
|
121
|
-
if (!env.composes) return false;
|
|
122
135
|
let p = item.parent === null ? void 0 : byId.get(item.parent);
|
|
123
136
|
while (p !== void 0) {
|
|
124
137
|
if (definesFrame(p)) return true;
|
|
@@ -138,8 +151,8 @@ function pasteSceneSnapshot(env, clipboard, offset, ctx) {
|
|
|
138
151
|
if (b.y < minY) minY = b.y;
|
|
139
152
|
}
|
|
140
153
|
if (Number.isFinite(minX) && Number.isFinite(minY)) {
|
|
141
|
-
dx = ctx.dropPoint.
|
|
142
|
-
dy = ctx.dropPoint.
|
|
154
|
+
dx = ctx.dropPoint.x - minX;
|
|
155
|
+
dy = ctx.dropPoint.y - minY;
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
158
|
const site = env.landing?.();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sceneClipboard.js","names":[],"sources":["../../src/canvas/sceneClipboard.ts"],"sourcesContent":["/**\n * The scene adapter's clipboard seam: what a copy captures and what a paste\n * mints from it.\n *\n * `pasteSceneSnapshot` is a pure factory — it mints fresh nodes and never\n * touches the scene. Insertion happens through the caller's InsertOps (one per\n * created node, parents before children).\n *\n * A snapshot item carries its node's function fields twice: live, for a paste\n * in the same session, and as the scene's registry keys, for a snapshot that\n * crossed JSON on the way back.\n */\nimport type { ClipboardSnapshot, PasteCtx, PasteOmission } from 'core/adapters/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { asNodeId } from 'core/scene/types';\nimport { hasChildren } from 'core/scene/treeShape';\nimport { sourcesOf } from 'core/scene/dependents';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport {\n NODE_FN_FIELDS, createNodeFnFields, fnFieldsOfNode, fnKeysOf, type FnBearingNode,\n} from 'core/scene/nodeFnFields';\nimport { statedFields } from 'core/scene/statedFields';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport type { InsertSite } from './deps/insertSite';\n\n/** What the seam needs from the adapter it serves. */\nexport interface SceneClipboardEnv<TData, TLayer extends string, TPose> {\n scene: Scene<TData, TLayer, TPose>;\n /** Whether a stored pose is relative to its container's frame. */\n composes: boolean;\n worldPoseOf(id: string): TPose;\n /** The nearest frame-defining container at or above `id`. */\n frameAtOrAbove(id: string | null): string | null;\n poseBounds(pose: TPose, node: unknown): Bounds;\n translate(pose: TPose, dx: number, dy: number, node?: unknown): TPose;\n /** Where the pasted cluster's roots land; absent, or naming no parent, they keep their layer at the top of the tree. */\n landing?: () => InsertSite;\n}\n\n// Spread-level copy per the clipboard contract: mutating a snapshot's\n// pose/data/children must not touch the scene.\nfunction copyField<T>(value: T): T {\n if (Array.isArray(value)) return [...value] as T;\n if (value !== null && typeof value === 'object') return { ...(value as object) } as T;\n return value;\n}\n\ntype Item<TData, TLayer extends string, TPose> =\n Node<TData, TLayer, TPose> & Record<string, unknown>;\n\nexport function snapshotSceneSelection<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n ids: string[],\n): ClipboardSnapshot {\n const { scene } = env;\n const fnFields = createNodeFnFields<TPose>(scene.registry as never);\n const idSet = new Set(ids);\n const taken = new Set<string>();\n const items: Node<TData, TLayer, TPose>[] = [];\n // A stored pose is relative to its frame, and a node whose frame is not\n // copied with it lands somewhere else entirely on paste. Capture those in\n // world coordinates: every root, and under an envelope root, the nodes\n // sharing the frame the root left behind.\n const poseFor = (n: Node<TData, TLayer, TPose>, isRoot: boolean): TPose => {\n if (!env.composes) return n.pose;\n if (isRoot) return env.worldPoseOf(n.id);\n const frame = env.frameAtOrAbove(n.parent);\n return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);\n };\n\n // DFS from one snapshot root: push the node, then its subtree —\n // parents-before-children, so paste can re-insert in array order.\n const capture = (id: string, isRoot = false): void => {\n if (taken.has(id)) return;\n const n = scene.get(asNodeId(id));\n if (!n) return;\n taken.add(id);\n items.push({\n kind: n.kind,\n id: n.id,\n layer: n.layer,\n parent: n.parent,\n pose: copyField(poseFor(n, isRoot)),\n data: copyField(n.data),\n ...(n.children !== undefined ? { children: [...n.children] } : {}),\n ...statedFields(n),\n ...(n.dependsOn !== undefined ? { dependsOn: copyField(n.dependsOn) } : {}),\n ...fnFieldsOfNode(n),\n ...fnKeysOf(fnFields, n as unknown as FnBearingNode, 'jsonKey'),\n } as Node<TData, TLayer, TPose>);\n if (hasChildren(n)) {\n for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);\n }\n };\n for (const id of ids) {\n // Dedupe: an id whose ancestor is also selected is already covered\n // by that ancestor's subtree walk.\n if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;\n capture(id, true);\n }\n return { items };\n}\n\n// JSON drops a function, and turns a layout strategy into an object with no\n// methods on it: neither is something a pasted node can run.\nconst isLive = (value: unknown): boolean =>\n typeof value === 'function'\n || (typeof value === 'object' && value !== null\n && Object.values(value).some((member) => typeof member === 'function'));\n\n/**\n * The item's function fields: for each, what its registry key names in this\n * scene, else the live one it carries, else nothing.\n */\nfunction restoredFnFields<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n item: Item<TData, TLayer, TPose>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const registry = scene.registry as Record<string, Record<string, unknown> | undefined>;\n for (const spec of NODE_FN_FIELDS) {\n const key = item[spec.jsonKey];\n const registered = typeof key === 'string' ? registry[spec.field]?.[key] : undefined;\n const fn = registered ?? (isLive(item[spec.field]) ? item[spec.field] : undefined);\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out;\n}\n\n/**\n * Which items a paste leaves out, and why. A node deriving from an id list is\n * pasted only with everything it depends on, since a copy still naming the\n * originals would follow them rather than the copies beside it; and only with\n * a derivation to run, since without one it is a placeholder that paints\n * nothing. Leaving one out can strand another, so this runs to a fixed point.\n */\nfunction omissions<TData, TLayer extends string, TPose>(\n src: readonly Item<TData, TLayer, TPose>[],\n fns: ReadonlyMap<string, Record<string, unknown>>,\n): Map<string, PasteOmission> {\n const out = new Map<string, PasteOmission>();\n const present = new Set(src.map((item) => item.id as string));\n for (let changed = true; changed;) {\n changed = false;\n for (const item of src) {\n if (out.has(item.id)) continue;\n let omission: PasteOmission | undefined;\n const deps = sourcesOf(item);\n const missing = deps.filter((dep) => !present.has(dep));\n const fn = fns.get(item.id)!;\n if (item.parent !== null && out.has(item.parent)) {\n omission = { id: item.id, reason: 'container-not-pasted' };\n } else if (missing.length > 0) {\n omission = { id: item.id, reason: 'dependency-not-copied', missing };\n } else if (deps.length > 0 && fn.derivePath === undefined && fn.derivePose === undefined) {\n omission = { id: item.id, reason: 'derivation-not-registered' };\n }\n if (omission === undefined) continue;\n out.set(item.id, omission);\n present.delete(item.id);\n changed = true;\n }\n }\n return out;\n}\n\nexport function pasteSceneSnapshot<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n): Node<TData, TLayer, TPose>[] {\n const all = clipboard.items as Item<TData, TLayer, TPose>[];\n if (all.length === 0) return [];\n const fns = new Map(all.map((item) => [item.id as string, restoredFnFields(env.scene, item)]));\n const omitted = omissions(all, fns);\n if (ctx?.onOmit) for (const item of all) {\n const omission = omitted.get(item.id);\n if (omission) ctx.onOmit(omission);\n }\n const src = omitted.size === 0 ? all : all.filter((item) => !omitted.has(item.id));\n\n const idMap = new Map<string, NodeId>();\n for (const item of src) idMap.set(item.id, env.scene.mintId());\n // Items whose captured parent isn't part of the snapshot become roots\n // of the pasted cluster.\n const isRoot = (item: Node<TData, TLayer, TPose>): boolean =>\n item.parent === null || !idMap.has(item.parent);\n // Under a composing strategy a node inside a pasted frame rides it, and\n // offsetting it as well would move it twice.\n const byId = new Map(src.map((item) => [item.id as string, item]));\n const ridesPastedFrame = (item: Node<TData, TLayer, TPose>): boolean => {\n if (!env.composes) return false;\n let p = item.parent === null ? undefined : byId.get(item.parent);\n while (p !== undefined) {\n if (definesFrame(p)) return true;\n p = p.parent === null ? undefined : byId.get(p.parent);\n }\n return false;\n };\n // Without composition poses are absolute, so the delta applies to EVERY\n // pasted node.\n let dx = offset.dx;\n let dy = offset.dy;\n if (ctx?.dropPoint) {\n // Drop-point policy: land the cluster origin (min corner of the root\n // items' bounds) on the drop point; `offset` is ignored.\n let minX = Infinity;\n let minY = Infinity;\n for (const item of src) {\n if (!isRoot(item)) continue;\n const b = env.poseBounds(item.pose, item);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n }\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\n dx = ctx.dropPoint.worldX - minX;\n dy = ctx.dropPoint.worldY - minY;\n }\n }\n const site = env.landing?.();\n const into = site?.parent != null ? site : null;\n const placed = (item: Item<TData, TLayer, TPose>): TPose => {\n if (ridesPastedFrame(item)) return copyField(item.pose);\n const moved = env.translate(copyField(item.pose), dx, dy, item);\n return into && isRoot(item) ? into.local(moved) as TPose : moved;\n };\n // `src` is parents-before-children and this keeps it, so children re-attach\n // to freshly inserted parents when the caller's InsertOps apply in order.\n return src.map((item) => ({\n id: idMap.get(item.id)!,\n kind: item.kind,\n layer: into && isRoot(item) ? into.layer as TLayer : item.layer,\n parent: isRoot(item) ? into?.parent ?? null : idMap.get(item.parent as string)!,\n pose: placed(item),\n data: copyField(item.data),\n ...statedFields(item),\n ...(item.children !== undefined\n ? { children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)!) }\n : {}),\n ...(item.dependsOn === 'children'\n ? { dependsOn: 'children' as const }\n : sourcesOf(item).length > 0\n ? { dependsOn: sourcesOf(item).map((dep) => idMap.get(dep)!) }\n : {}),\n ...fns.get(item.id),\n } as Node<TData, TLayer, TPose>));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sceneClipboard.js","names":[],"sources":["../../src/canvas/sceneClipboard.ts"],"sourcesContent":["/**\n * The scene adapter's clipboard seam: what a copy captures and what a paste\n * mints from it.\n *\n * `pasteSceneSnapshot` is a pure factory — it mints fresh nodes and never\n * touches the scene. Insertion happens through the caller's InsertOps (one per\n * created node, parents before children).\n *\n * A snapshot item carries its node's function fields twice: live, for a paste\n * in the same session, and as the scene's registry keys, for a snapshot that\n * crossed JSON on the way back.\n */\nimport type { ClipboardSnapshot, PasteCtx, PasteOmission } from 'core/adapters/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { asNodeId } from 'core/scene/types';\nimport { hasChildren } from 'core/scene/treeShape';\nimport { sourcesOf } from 'core/scene/dependents';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport {\n NODE_FN_FIELDS, createNodeFnFields, fnFieldsOfNode, fnKeysOf, type FnBearingNode,\n} from 'core/scene/nodeFnFields';\nimport { statedFields } from 'core/scene/statedFields';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport type { InsertSite } from './deps/insertSite';\n\n/** What the seam needs from the adapter it serves. */\nexport interface SceneClipboardEnv<TData, TLayer extends string, TPose> {\n scene: Scene<TData, TLayer, TPose>;\n worldPoseOf(id: string): TPose;\n /** The nearest frame-defining container at or above `id`. */\n frameAtOrAbove(id: string | null): string | null;\n poseBounds(pose: TPose, node: unknown): Bounds;\n translate(pose: TPose, dx: number, dy: number, node?: unknown): TPose;\n /** Where the pasted cluster's roots land; absent, or naming no parent, they keep their layer at the top of the tree. */\n landing?: () => InsertSite;\n}\n\n// Spread-level copy per the clipboard contract: mutating a snapshot's\n// pose/data/children must not touch the scene.\nfunction copyField<T>(value: T): T {\n if (Array.isArray(value)) return [...value] as T;\n if (value !== null && typeof value === 'object') return { ...(value as object) } as T;\n return value;\n}\n\ntype Item<TData, TLayer extends string, TPose> =\n Node<TData, TLayer, TPose> & Record<string, unknown>;\n\nexport function snapshotSceneSelection<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n ids: string[],\n): ClipboardSnapshot {\n const { scene } = env;\n const fnFields = createNodeFnFields<TPose>(scene.registry as never);\n const idSet = new Set(ids);\n const taken = new Set<string>();\n const items: Node<TData, TLayer, TPose>[] = [];\n // A stored pose is relative to its frame, and a node whose frame is not\n // copied with it lands somewhere else entirely on paste. Capture those in\n // world coordinates: every root, and under an envelope root, the nodes\n // sharing the frame the root left behind.\n const poseFor = (n: Node<TData, TLayer, TPose>, isRoot: boolean): TPose => {\n if (isRoot) return env.worldPoseOf(n.id);\n const frame = env.frameAtOrAbove(n.parent);\n return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);\n };\n\n // DFS from one snapshot root: push the node, then its subtree —\n // parents-before-children, so paste can re-insert in array order.\n const capture = (id: string, isRoot = false): void => {\n if (taken.has(id)) return;\n const n = scene.get(asNodeId(id));\n if (!n) return;\n taken.add(id);\n items.push({\n kind: n.kind,\n id: n.id,\n layer: n.layer,\n parent: n.parent,\n pose: copyField(poseFor(n, isRoot)),\n data: copyField(n.data),\n ...(n.children !== undefined ? { children: [...n.children] } : {}),\n ...statedFields(n),\n ...(n.dependsOn !== undefined ? { dependsOn: copyField(n.dependsOn) } : {}),\n ...fnFieldsOfNode(n),\n ...fnKeysOf(fnFields, n as unknown as FnBearingNode, 'jsonKey'),\n } as Node<TData, TLayer, TPose>);\n if (hasChildren(n)) {\n for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);\n }\n };\n const covered = (id: string): boolean =>\n idSet.has(id) || scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a));\n // A hidden node cannot be selected, so one that a copied node derives from\n // comes with it, ahead of it: without it the paste would leave the copy out.\n const carrying = new Set<string>();\n const carrySources = (id: string): void => {\n if (carrying.has(id)) return;\n carrying.add(id);\n const n = scene.get(asNodeId(id));\n if (!n) return;\n for (const source of sourcesOf(n)) {\n if (taken.has(source) || covered(source) || scene.get(source)?.hidden !== true) continue;\n carrySources(source);\n capture(source, true);\n }\n if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) carrySources(cid);\n };\n for (const id of ids) {\n // Dedupe: an id whose ancestor is also selected is already covered\n // by that ancestor's subtree walk.\n if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;\n carrySources(id);\n capture(id, true);\n }\n return { items };\n}\n\n// JSON drops a function, and turns a layout strategy into an object with no\n// methods on it: neither is something a pasted node can run.\nconst isLive = (value: unknown): boolean =>\n typeof value === 'function'\n || (typeof value === 'object' && value !== null\n && Object.values(value).some((member) => typeof member === 'function'));\n\n/**\n * The item's function fields: for each, what its registry key names in this\n * scene, else the live one it carries, else nothing.\n */\nfunction restoredFnFields<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n item: Item<TData, TLayer, TPose>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const registry = scene.registry as Record<string, Record<string, unknown> | undefined>;\n for (const spec of NODE_FN_FIELDS) {\n const key = item[spec.jsonKey];\n const registered = typeof key === 'string' ? registry[spec.field]?.[key] : undefined;\n const fn = registered ?? (isLive(item[spec.field]) ? item[spec.field] : undefined);\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out;\n}\n\n/**\n * Which items a paste leaves out, and why. A node deriving from an id list is\n * pasted only with everything it depends on, since a copy still naming the\n * originals would follow them rather than the copies beside it; and only with\n * a derivation to run, since without one it is a placeholder that paints\n * nothing. Leaving one out can strand another, so this runs to a fixed point.\n */\nfunction omissions<TData, TLayer extends string, TPose>(\n src: readonly Item<TData, TLayer, TPose>[],\n fns: ReadonlyMap<string, Record<string, unknown>>,\n): Map<string, PasteOmission> {\n const out = new Map<string, PasteOmission>();\n const present = new Set(src.map((item) => item.id as string));\n for (let changed = true; changed;) {\n changed = false;\n for (const item of src) {\n if (out.has(item.id)) continue;\n let omission: PasteOmission | undefined;\n const deps = sourcesOf(item);\n const missing = deps.filter((dep) => !present.has(dep));\n const fn = fns.get(item.id)!;\n if (item.parent !== null && out.has(item.parent)) {\n omission = { id: item.id, reason: 'container-not-pasted' };\n } else if (missing.length > 0) {\n omission = { id: item.id, reason: 'dependency-not-copied', missing };\n } else if (deps.length > 0 && fn.derivePath === undefined && fn.derivePose === undefined) {\n omission = { id: item.id, reason: 'derivation-not-registered' };\n }\n if (omission === undefined) continue;\n out.set(item.id, omission);\n present.delete(item.id);\n changed = true;\n }\n }\n return out;\n}\n\nexport function pasteSceneSnapshot<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n): Node<TData, TLayer, TPose>[] {\n const all = clipboard.items as Item<TData, TLayer, TPose>[];\n if (all.length === 0) return [];\n const fns = new Map(all.map((item) => [item.id as string, restoredFnFields(env.scene, item)]));\n const omitted = omissions(all, fns);\n if (ctx?.onOmit) for (const item of all) {\n const omission = omitted.get(item.id);\n if (omission) ctx.onOmit(omission);\n }\n const src = omitted.size === 0 ? all : all.filter((item) => !omitted.has(item.id));\n\n const idMap = new Map<string, NodeId>();\n for (const item of src) idMap.set(item.id, env.scene.mintId());\n // Items whose captured parent isn't part of the snapshot become roots\n // of the pasted cluster.\n const isRoot = (item: Node<TData, TLayer, TPose>): boolean =>\n item.parent === null || !idMap.has(item.parent);\n // A node inside a pasted frame rides it, and offsetting it as well would\n // move it twice.\n const byId = new Map(src.map((item) => [item.id as string, item]));\n const ridesPastedFrame = (item: Node<TData, TLayer, TPose>): boolean => {\n let p = item.parent === null ? undefined : byId.get(item.parent);\n while (p !== undefined) {\n if (definesFrame(p)) return true;\n p = p.parent === null ? undefined : byId.get(p.parent);\n }\n return false;\n };\n let dx = offset.dx;\n let dy = offset.dy;\n if (ctx?.dropPoint) {\n // Drop-point policy: land the cluster origin (min corner of the root\n // items' bounds) on the drop point; `offset` is ignored.\n let minX = Infinity;\n let minY = Infinity;\n for (const item of src) {\n if (!isRoot(item)) continue;\n const b = env.poseBounds(item.pose, item);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n }\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\n dx = ctx.dropPoint.x - minX;\n dy = ctx.dropPoint.y - minY;\n }\n }\n const site = env.landing?.();\n const into = site?.parent != null ? site : null;\n const placed = (item: Item<TData, TLayer, TPose>): TPose => {\n if (ridesPastedFrame(item)) return copyField(item.pose);\n const moved = env.translate(copyField(item.pose), dx, dy, item);\n return into && isRoot(item) ? into.local(moved) as TPose : moved;\n };\n // `src` is parents-before-children and this keeps it, so children re-attach\n // to freshly inserted parents when the caller's InsertOps apply in order.\n return src.map((item) => ({\n id: idMap.get(item.id)!,\n kind: item.kind,\n layer: into && isRoot(item) ? into.layer as TLayer : item.layer,\n parent: isRoot(item) ? into?.parent ?? null : idMap.get(item.parent as string)!,\n pose: placed(item),\n data: copyField(item.data),\n ...statedFields(item),\n ...(item.children !== undefined\n ? { children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)!) }\n : {}),\n ...(item.dependsOn === 'children'\n ? { dependsOn: 'children' as const }\n : sourcesOf(item).length > 0\n ? { dependsOn: sourcesOf(item).map((dep) => idMap.get(dep)!) }\n : {}),\n ...fns.get(item.id),\n } as Node<TData, TLayer, TPose>));\n}\n"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ import { viewToMat3 as viewToMat3$1 } from "@weasel-js/geom";
|
|
|
19
19
|
* adapter's own reads, under this view's layer list.
|
|
20
20
|
*/
|
|
21
21
|
function sceneAsHierarchy(scene, layers = {}) {
|
|
22
|
-
const { getNode, getChildren, getPose, composePose } = sceneToAdapter(scene);
|
|
22
|
+
const { getNode, getChildren, getPose, composePose, childFrame } = sceneToAdapter(scene);
|
|
23
23
|
return {
|
|
24
24
|
getLayers: () => paintedSceneLayers(scene.layers, layers).map((l) => ({
|
|
25
25
|
id: l.id,
|
|
@@ -29,7 +29,8 @@ function sceneAsHierarchy(scene, layers = {}) {
|
|
|
29
29
|
getNode,
|
|
30
30
|
getChildren,
|
|
31
31
|
getPose,
|
|
32
|
-
...composePose ? { composePose } : {}
|
|
32
|
+
...composePose ? { composePose } : {},
|
|
33
|
+
...childFrame ? { childFrame } : {}
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sceneViewRender.js","names":["viewToMat3"],"sources":["../../src/canvas/sceneViewRender.ts"],"sourcesContent":["/**\n * `renderSceneToCanvas` — substrate for detached, read-only scene views\n * (`<SceneViewCanvas>` / `<MinimapCanvas>`). Given a `<canvas>`, a `Scene`,\n * a `View`, and a per-node `drawOne`, render one frame.\n */\n\nimport type { WeaselRenderer } from '@weasel-js/renderer';\nimport { cachedCanvasRenderer, paintCanvas, resolveDpr } from './canvasRenderer';\nimport { viewToMat3 } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { View } from '../core/viewport/view';\nimport type { Node, NodeId, Scene } from '../core/scene/types';\nimport { wrapNodeOutput } from './wrapNodeOutput';\nimport { buildSceneTree, type HierarchicalAdapter } from './buildSceneTree';\nimport { sceneToAdapter } from './sceneAdapter';\nimport { withInheritedPaintBounds, withInheritedPaints } from './inheritedPaints';\nimport { withDerivedPaths, sceneDerivedPathOf } from './derivedPath';\nimport { withColorOverrides } from './colorOverrides';\nimport type { ColorOverrideRegistry } from '@weasel-js/animator';\nimport type { SceneViewDrawOne } from './NodeShape';\nimport { paintedSceneLayers } from './sceneLayerPaint';\nimport { cullDrawCommands } from '@weasel-js/renderer';\nimport { paintMissesView, type PaintBoundsFn } from './paintCull';\nimport { paintBoundsFor } from './defaultDrawOne';\nimport { nodeAsShown, shownLookup } from 'core/scene/nodeAsShown';\nimport { withShownNode } from './shownNodes';\n\nexport type { SceneViewDrawOne } from './NodeShape';\n\n/**\n * Which scene layers a detached view paints, on top of each layer's own\n * `visible` flag. Keyed the way `<SceneCanvas>` keys its scene layers —\n * `scene:<layerId>` — so the map a main canvas takes serves its minimap too.\n */\nexport interface SceneViewLayers {\n /** `false` hides that layer in this view. It cannot show one the scene hides. */\n layerVisibility?: Record<string, boolean>;\n /** Paint order, bottom first. A listed order is the whole list: a scene\n * layer left out of it is not painted. */\n layerOrder?: readonly string[];\n}\n\n/** The view size `buildSceneViewCommands` culls to, the density it will be\n * painted at, and how it bounds a node's paint before painting it. */\nexport interface SceneViewCull<TData, TLayer extends string, TPose> {\n width: number;\n height: number;\n dpr: number;\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n}\n\n/** What to draw into an existing canvas: the scene, the view, and the same\n * painting hooks `<SceneCanvas>` takes. */\nexport interface RenderSceneToCanvasArgs<TData, TLayer extends string, TPose> {\n /** The DOM canvas to paint into. The first call on a given canvas creates\n * its renderer and later calls reuse it;\n * `releaseCanvasRenderer(canvas)` frees it. */\n canvas: HTMLCanvasElement;\n scene: Scene<TData, TLayer, TPose>;\n /** Camera view applied to scene and `extraCommands` alike. */\n view: View;\n /** CSS-pixel size. The drawing buffer is set to `width × dpr` and\n * `height × dpr`; the element's CSS size is the caller's to set. */\n width: number;\n height: number;\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>;\n /** Commands appended after the scene-node output, wrapped in the same view\n * transform as scene commands. */\n extraCommands?: DrawCommand[];\n /** Per-id alpha multiplier, mirroring `<SceneCanvas>`'s scene-slot\n * `alphaFor`. Defaults to `() => 1`. */\n alphaFor?: (id: string) => number;\n /** Per-view layer visibility. See {@link SceneViewLayers}. */\n layerVisibility?: SceneViewLayers['layerVisibility'];\n /** Per-view layer order. See {@link SceneViewLayers}. */\n layerOrder?: SceneViewLayers['layerOrder'];\n /** Animated vertex colors to paint, typically an animator's\n * `colorOverrides` — see `NodePaintCtx.vertexColors`. */\n colorOverrides?: ColorOverrideRegistry;\n /** Skip what cannot reach the `width × height` view: nodes `paintBounds`\n * places outside it are not painted, and scene commands that cannot reach\n * it are dropped before the renderer — the scene slot's `cull`. Off by\n * default. `extraCommands` are never culled. */\n cull?: boolean;\n /** Where `drawOne`'s output for a node can reach, as on the scene slot.\n * Defaults to `defaultPaintBounds` when `drawOne` is `defaultDrawOne`. */\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n /** Device-pixel ratio. Defaults to `window.devicePixelRatio || 1` when\n * available, otherwise 1. */\n dpr?: number;\n}\n\n/** Test seam: the renderer `renderSceneToCanvas` created for a canvas. Not\n * re-exported from the barrel. */\nexport function __getCachedRendererForTest(\n canvas: HTMLCanvasElement,\n): WeaselRenderer | undefined {\n return cachedCanvasRenderer(canvas);\n}\n\n/**\n * Present a `Scene` as the reading surface `buildSceneTree` walks: the scene\n * adapter's own reads, under this view's layer list.\n */\nfunction sceneAsHierarchy<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n layers: SceneViewLayers = {},\n): HierarchicalAdapter<Node<TData, TLayer, TPose>, TPose> {\n const { getNode, getChildren, getPose, composePose } = sceneToAdapter(scene);\n return {\n getLayers: () =>\n paintedSceneLayers(scene.layers, layers)\n .map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n getNode,\n getChildren,\n getPose,\n ...(composePose ? { composePose } : {}),\n };\n}\n\n/**\n * Build the DrawCommand list that `renderSceneToCanvas` would dispatch, in\n * screen space. Pure — no GL, no DOM. Walks the scene through `buildSceneTree`,\n * the same walk `<SceneCanvas>` uses, which is what keeps hidden layers\n * unpainted and container clips applied here.\n *\n * Each node's commands go through `wrapNodeOutput` — pose rotation, then the\n * `alphaFor` multiplier — so a `drawOne` that rotates its own output rotates\n * twice; emit unrotated geometry and let the pose drive it.\n *\n * Output is one group per **visible** layer, in scene-layer order (or\n * `layers.layerOrder`), each holding one group per node on that layer.\n */\nexport function buildSceneViewCommands<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n view: View,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n extraCommands?: ReadonlyArray<DrawCommand>,\n alphaFor?: (id: string) => number,\n /** This view's own layer visibility and order. */\n layers?: SceneViewLayers,\n /** Animated vertex colors to paint, typically an animator's `colorOverrides`. */\n colorOverrides?: ColorOverrideRegistry,\n /** Cull the scene to this CSS-pixel view size — see\n * `RenderSceneToCanvasArgs.cull`. Omit to paint every node. */\n cull?: SceneViewCull<TData, TLayer, TPose>,\n): DrawCommand[] {\n const shown = shownLookup(scene);\n const derivedOnly = withDerivedPaths(scene, withInheritedPaints(shown, drawOne));\n const derived = colorOverrides ? withColorOverrides(colorOverrides, derivedOnly) : derivedOnly;\n const derivedPathAt = sceneDerivedPathOf(scene);\n const derivedPathOf = (node: Node<TData, TLayer, TPose>) => derivedPathAt(nodeAsShown(scene.overrides, node));\n const wrappedDrawOne = (\n node: Node<TData, TLayer, TPose>,\n pose: TPose,\n v: View,\n ): DrawCommand[] => {\n const overrideAlpha = scene.overrides.read(node.id as NodeId)?.alpha ?? 1;\n return wrapNodeOutput(\n derived(nodeAsShown(scene.overrides, node), pose, v),\n pose,\n (alphaFor ? alphaFor(node.id) : 1) * overrideAlpha,\n );\n };\n\n const hierarchy = sceneAsHierarchy(scene, layers);\n\n const cullRect = cull ? { x: 0, y: 0, width: cull.width, height: cull.height } : undefined;\n const culled = cullRect && cull?.paintBounds\n ? paintMissesView(withShownNode(scene.overrides, withInheritedPaintBounds(shown, cull.paintBounds)), view, cullRect)\n : undefined;\n const tree: DrawCommand[] = buildSceneTree(\n hierarchy as Parameters<typeof buildSceneTree>[0],\n wrappedDrawOne as unknown as Parameters<typeof buildSceneTree>[1],\n view,\n undefined,\n derivedPathOf as unknown as Parameters<typeof buildSceneTree>[4],\n culled as Parameters<typeof buildSceneTree>[5],\n );\n const children = cull && cullRect ? cullDrawCommands(tree, viewToMat3(view), cullRect, cull.dpr) : tree;\n if (extraCommands && extraCommands.length > 0) {\n for (const cmd of extraCommands) children.push(cmd);\n }\n return [{\n kind: 'group',\n transform: viewToMat3(view),\n children,\n }];\n}\n\n/**\n * The commands `renderSceneToCanvas` paints for one frame — everything in\n * `args` but the canvas. `<SceneViewCanvas>` paints these through its own\n * renderer lease.\n */\nexport function sceneViewFrame<TData, TLayer extends string, TPose>(\n args: Omit<RenderSceneToCanvasArgs<TData, TLayer, TPose>, 'canvas'>,\n): DrawCommand[] {\n const {\n scene, view, width, height, drawOne, extraCommands, alphaFor, layerVisibility, layerOrder,\n colorOverrides, cull, dpr,\n } = args;\n return buildSceneViewCommands(\n scene, view, drawOne, extraCommands, alphaFor, { layerVisibility, layerOrder }, colorOverrides,\n cull ? { width, height, dpr: resolveDpr(dpr), paintBounds: args.paintBounds ?? paintBoundsFor(drawOne) } : undefined,\n );\n}\n\n/**\n * Render one frame of `scene` at `view` into `canvas`. Where no WebGL2\n * context is available (jsdom) the call paints nothing. The canvas's renderer\n * lives until `releaseCanvasRenderer(canvas)`.\n */\nexport function renderSceneToCanvas<TData, TLayer extends string, TPose>(\n args: RenderSceneToCanvasArgs<TData, TLayer, TPose>,\n): void {\n const { canvas, view, width, height, dpr } = args;\n paintCanvas(canvas, sceneViewFrame(args), view, { width, height, dpr });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwGA,SAAS,iBACP,OACA,SAA0B,CAAC,GAC6B;CACxD,MAAM,EAAE,SAAS,aAAa,SAAS,gBAAgB,eAAe,KAAK;CAC3E,OAAO;EACL,iBACE,mBAAmB,MAAM,QAAQ,MAAM,CAAC,CACrC,KAAK,OAAO;GAAE,IAAI,EAAE;GAAI,SAAS,EAAE;GAAS,UAAU,EAAE;EAAS,EAAE;EACxE;EACA;EACA;EACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;CACvC;AACF;;;;;;;;;;;;;;AAeA,SAAgB,uBACd,OACA,MACA,SACA,eACA,UAEA,QAEA,gBAGA,MACe;CACf,MAAM,QAAQ,YAAY,KAAK;CAC/B,MAAM,cAAc,iBAAiB,OAAO,oBAAoB,OAAO,OAAO,CAAC;CAC/E,MAAM,UAAU,iBAAiB,mBAAmB,gBAAgB,WAAW,IAAI;CACnF,MAAM,gBAAgB,mBAAmB,KAAK;CAC9C,MAAM,iBAAiB,SAAqC,cAAc,YAAY,MAAM,WAAW,IAAI,CAAC;CAC5G,MAAM,kBACJ,MACA,MACA,MACkB;EAClB,MAAM,gBAAgB,MAAM,UAAU,KAAK,KAAK,EAAY,CAAC,EAAE,SAAS;EACxE,OAAO,eACL,QAAQ,YAAY,MAAM,WAAW,IAAI,GAAG,MAAM,CAAC,GACnD,OACC,WAAW,SAAS,KAAK,EAAE,IAAI,KAAK,aACvC;CACF;CAEA,MAAM,YAAY,iBAAiB,OAAO,MAAM;CAEhD,MAAM,WAAW,OAAO;EAAE,GAAG;EAAG,GAAG;EAAG,OAAO,KAAK;EAAO,QAAQ,KAAK;CAAO,IAAI;CACjF,MAAM,SAAS,YAAY,MAAM,cAC7B,gBAAgB,cAAc,MAAM,WAAW,yBAAyB,OAAO,KAAK,WAAW,CAAC,GAAG,MAAM,QAAQ,IACjH;CACJ,MAAM,OAAsB,eAC1B,WACA,gBACA,MACA,QACA,eACA,MACF;CACA,MAAM,WAAW,QAAQ,WAAW,iBAAiB,MAAMA,aAAW,IAAI,GAAG,UAAU,KAAK,GAAG,IAAI;CACnG,IAAI,iBAAiB,cAAc,SAAS,GAC1C,KAAK,MAAM,OAAO,eAAe,SAAS,KAAK,GAAG;CAEpD,OAAO,CAAC;EACN,MAAM;EACN,WAAWA,aAAW,IAAI;EAC1B;CACF,CAAC;AACH;;;;;;AAOA,SAAgB,eACd,MACe;CACf,MAAM,EACJ,OAAO,MAAM,OAAO,QAAQ,SAAS,eAAe,UAAU,iBAAiB,YAC/E,gBAAgB,MAAM,QACpB;CACJ,OAAO,uBACL,OAAO,MAAM,SAAS,eAAe,UAAU;EAAE;EAAiB;CAAW,GAAG,gBAChF,OAAO;EAAE;EAAO;EAAQ,KAAK,WAAW,GAAG;EAAG,aAAa,KAAK,eAAe,eAAe,OAAO;CAAE,IAAI,MAC7G;AACF;;;;;;AAOA,SAAgB,oBACd,MACM;CACN,MAAM,EAAE,QAAQ,MAAM,OAAO,QAAQ,QAAQ;CAC7C,YAAY,QAAQ,eAAe,IAAI,GAAG,MAAM;EAAE;EAAO;EAAQ;CAAI,CAAC;AACxE"}
|
|
1
|
+
{"version":3,"file":"sceneViewRender.js","names":["viewToMat3"],"sources":["../../src/canvas/sceneViewRender.ts"],"sourcesContent":["/**\n * `renderSceneToCanvas` — substrate for detached, read-only scene views\n * (`<SceneViewCanvas>` / `<MinimapCanvas>`). Given a `<canvas>`, a `Scene`,\n * a `View`, and a per-node `drawOne`, render one frame.\n */\n\nimport type { WeaselRenderer } from '@weasel-js/renderer';\nimport { cachedCanvasRenderer, paintCanvas, resolveDpr } from './canvasRenderer';\nimport { viewToMat3 } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { View } from '../core/viewport/view';\nimport type { Node, NodeId, Scene } from '../core/scene/types';\nimport { wrapNodeOutput } from './wrapNodeOutput';\nimport { buildSceneTree, type HierarchicalAdapter } from './buildSceneTree';\nimport { sceneToAdapter } from './sceneAdapter';\nimport { withInheritedPaintBounds, withInheritedPaints } from './inheritedPaints';\nimport { withDerivedPaths, sceneDerivedPathOf } from './derivedPath';\nimport { withColorOverrides } from './colorOverrides';\nimport type { ColorOverrideRegistry } from '@weasel-js/animator';\nimport type { SceneViewDrawOne } from './NodeShape';\nimport { paintedSceneLayers } from './sceneLayerPaint';\nimport { cullDrawCommands } from '@weasel-js/renderer';\nimport { paintMissesView, type PaintBoundsFn } from './paintCull';\nimport { paintBoundsFor } from './defaultDrawOne';\nimport { nodeAsShown, shownLookup } from 'core/scene/nodeAsShown';\nimport { withShownNode } from './shownNodes';\n\nexport type { SceneViewDrawOne } from './NodeShape';\n\n/**\n * Which scene layers a detached view paints, on top of each layer's own\n * `visible` flag. Keyed the way `<SceneCanvas>` keys its scene layers —\n * `scene:<layerId>` — so the map a main canvas takes serves its minimap too.\n */\nexport interface SceneViewLayers {\n /** `false` hides that layer in this view. It cannot show one the scene hides. */\n layerVisibility?: Record<string, boolean>;\n /** Paint order, bottom first. A listed order is the whole list: a scene\n * layer left out of it is not painted. */\n layerOrder?: readonly string[];\n}\n\n/** The view size `buildSceneViewCommands` culls to, the density it will be\n * painted at, and how it bounds a node's paint before painting it. */\nexport interface SceneViewCull<TData, TLayer extends string, TPose> {\n width: number;\n height: number;\n dpr: number;\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n}\n\n/** What to draw into an existing canvas: the scene, the view, and the same\n * painting hooks `<SceneCanvas>` takes. */\nexport interface RenderSceneToCanvasArgs<TData, TLayer extends string, TPose> {\n /** The DOM canvas to paint into. The first call on a given canvas creates\n * its renderer and later calls reuse it;\n * `releaseCanvasRenderer(canvas)` frees it. */\n canvas: HTMLCanvasElement;\n scene: Scene<TData, TLayer, TPose>;\n /** Camera view applied to scene and `extraCommands` alike. */\n view: View;\n /** CSS-pixel size. The drawing buffer is set to `width × dpr` and\n * `height × dpr`; the element's CSS size is the caller's to set. */\n width: number;\n height: number;\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>;\n /** Commands appended after the scene-node output, wrapped in the same view\n * transform as scene commands. */\n extraCommands?: DrawCommand[];\n /** Per-id alpha multiplier, mirroring `<SceneCanvas>`'s scene-slot\n * `alphaFor`. Defaults to `() => 1`. */\n alphaFor?: (id: string) => number;\n /** Per-view layer visibility. See {@link SceneViewLayers}. */\n layerVisibility?: SceneViewLayers['layerVisibility'];\n /** Per-view layer order. See {@link SceneViewLayers}. */\n layerOrder?: SceneViewLayers['layerOrder'];\n /** Animated vertex colors to paint, typically an animator's\n * `colorOverrides` — see `NodePaintCtx.vertexColors`. */\n colorOverrides?: ColorOverrideRegistry;\n /** Skip what cannot reach the `width × height` view: nodes `paintBounds`\n * places outside it are not painted, and scene commands that cannot reach\n * it are dropped before the renderer — the scene slot's `cull`. Off by\n * default. `extraCommands` are never culled. */\n cull?: boolean;\n /** Where `drawOne`'s output for a node can reach, as on the scene slot.\n * Defaults to `defaultPaintBounds` when `drawOne` is `defaultDrawOne`. */\n paintBounds?: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>;\n /** Device-pixel ratio. Defaults to `window.devicePixelRatio || 1` when\n * available, otherwise 1. */\n dpr?: number;\n}\n\n/** Test seam: the renderer `renderSceneToCanvas` created for a canvas. Not\n * re-exported from the barrel. */\nexport function __getCachedRendererForTest(\n canvas: HTMLCanvasElement,\n): WeaselRenderer | undefined {\n return cachedCanvasRenderer(canvas);\n}\n\n/**\n * Present a `Scene` as the reading surface `buildSceneTree` walks: the scene\n * adapter's own reads, under this view's layer list.\n */\nfunction sceneAsHierarchy<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n layers: SceneViewLayers = {},\n): HierarchicalAdapter<Node<TData, TLayer, TPose>, TPose> {\n const { getNode, getChildren, getPose, composePose, childFrame } = sceneToAdapter(scene);\n return {\n getLayers: () =>\n paintedSceneLayers(scene.layers, layers)\n .map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n getNode,\n getChildren,\n getPose,\n ...(composePose ? { composePose } : {}),\n ...(childFrame ? { childFrame } : {}),\n };\n}\n\n/**\n * Build the DrawCommand list that `renderSceneToCanvas` would dispatch, in\n * screen space. Pure — no GL, no DOM. Walks the scene through `buildSceneTree`,\n * the same walk `<SceneCanvas>` uses, which is what keeps hidden layers\n * unpainted and container clips applied here.\n *\n * Each node's commands go through `wrapNodeOutput` — pose rotation, then the\n * `alphaFor` multiplier — so a `drawOne` that rotates its own output rotates\n * twice; emit unrotated geometry and let the pose drive it.\n *\n * Output is one group per **visible** layer, in scene-layer order (or\n * `layers.layerOrder`), each holding one group per node on that layer.\n */\nexport function buildSceneViewCommands<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n view: View,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n extraCommands?: ReadonlyArray<DrawCommand>,\n alphaFor?: (id: string) => number,\n /** This view's own layer visibility and order. */\n layers?: SceneViewLayers,\n /** Animated vertex colors to paint, typically an animator's `colorOverrides`. */\n colorOverrides?: ColorOverrideRegistry,\n /** Cull the scene to this CSS-pixel view size — see\n * `RenderSceneToCanvasArgs.cull`. Omit to paint every node. */\n cull?: SceneViewCull<TData, TLayer, TPose>,\n): DrawCommand[] {\n const shown = shownLookup(scene);\n const derivedOnly = withDerivedPaths(scene, withInheritedPaints(shown, drawOne));\n const derived = colorOverrides ? withColorOverrides(colorOverrides, derivedOnly) : derivedOnly;\n const derivedPathAt = sceneDerivedPathOf(scene);\n const derivedPathOf = (node: Node<TData, TLayer, TPose>) => derivedPathAt(nodeAsShown(scene.overrides, node));\n const wrappedDrawOne = (\n node: Node<TData, TLayer, TPose>,\n pose: TPose,\n v: View,\n ): DrawCommand[] => {\n const overrideAlpha = scene.overrides.read(node.id as NodeId)?.alpha ?? 1;\n return wrapNodeOutput(\n derived(nodeAsShown(scene.overrides, node), pose, v),\n pose,\n (alphaFor ? alphaFor(node.id) : 1) * overrideAlpha,\n );\n };\n\n const hierarchy = sceneAsHierarchy(scene, layers);\n\n const cullRect = cull ? { x: 0, y: 0, width: cull.width, height: cull.height } : undefined;\n const culled = cullRect && cull?.paintBounds\n ? paintMissesView(withShownNode(scene.overrides, withInheritedPaintBounds(shown, cull.paintBounds)), view, cullRect)\n : undefined;\n const tree: DrawCommand[] = buildSceneTree(\n hierarchy as Parameters<typeof buildSceneTree>[0],\n wrappedDrawOne as unknown as Parameters<typeof buildSceneTree>[1],\n view,\n undefined,\n derivedPathOf as unknown as Parameters<typeof buildSceneTree>[4],\n culled as Parameters<typeof buildSceneTree>[5],\n );\n const children = cull && cullRect ? cullDrawCommands(tree, viewToMat3(view), cullRect, cull.dpr) : tree;\n if (extraCommands && extraCommands.length > 0) {\n for (const cmd of extraCommands) children.push(cmd);\n }\n return [{\n kind: 'group',\n transform: viewToMat3(view),\n children,\n }];\n}\n\n/**\n * The commands `renderSceneToCanvas` paints for one frame — everything in\n * `args` but the canvas. `<SceneViewCanvas>` paints these through its own\n * renderer lease.\n */\nexport function sceneViewFrame<TData, TLayer extends string, TPose>(\n args: Omit<RenderSceneToCanvasArgs<TData, TLayer, TPose>, 'canvas'>,\n): DrawCommand[] {\n const {\n scene, view, width, height, drawOne, extraCommands, alphaFor, layerVisibility, layerOrder,\n colorOverrides, cull, dpr,\n } = args;\n return buildSceneViewCommands(\n scene, view, drawOne, extraCommands, alphaFor, { layerVisibility, layerOrder }, colorOverrides,\n cull ? { width, height, dpr: resolveDpr(dpr), paintBounds: args.paintBounds ?? paintBoundsFor(drawOne) } : undefined,\n );\n}\n\n/**\n * Render one frame of `scene` at `view` into `canvas`. Where no WebGL2\n * context is available (jsdom) the call paints nothing. The canvas's renderer\n * lives until `releaseCanvasRenderer(canvas)`.\n */\nexport function renderSceneToCanvas<TData, TLayer extends string, TPose>(\n args: RenderSceneToCanvasArgs<TData, TLayer, TPose>,\n): void {\n const { canvas, view, width, height, dpr } = args;\n paintCanvas(canvas, sceneViewFrame(args), view, { width, height, dpr });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwGA,SAAS,iBACP,OACA,SAA0B,CAAC,GAC6B;CACxD,MAAM,EAAE,SAAS,aAAa,SAAS,aAAa,eAAe,eAAe,KAAK;CACvF,OAAO;EACL,iBACE,mBAAmB,MAAM,QAAQ,MAAM,CAAC,CACrC,KAAK,OAAO;GAAE,IAAI,EAAE;GAAI,SAAS,EAAE;GAAS,UAAU,EAAE;EAAS,EAAE;EACxE;EACA;EACA;EACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;EACrC,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;CACrC;AACF;;;;;;;;;;;;;;AAeA,SAAgB,uBACd,OACA,MACA,SACA,eACA,UAEA,QAEA,gBAGA,MACe;CACf,MAAM,QAAQ,YAAY,KAAK;CAC/B,MAAM,cAAc,iBAAiB,OAAO,oBAAoB,OAAO,OAAO,CAAC;CAC/E,MAAM,UAAU,iBAAiB,mBAAmB,gBAAgB,WAAW,IAAI;CACnF,MAAM,gBAAgB,mBAAmB,KAAK;CAC9C,MAAM,iBAAiB,SAAqC,cAAc,YAAY,MAAM,WAAW,IAAI,CAAC;CAC5G,MAAM,kBACJ,MACA,MACA,MACkB;EAClB,MAAM,gBAAgB,MAAM,UAAU,KAAK,KAAK,EAAY,CAAC,EAAE,SAAS;EACxE,OAAO,eACL,QAAQ,YAAY,MAAM,WAAW,IAAI,GAAG,MAAM,CAAC,GACnD,OACC,WAAW,SAAS,KAAK,EAAE,IAAI,KAAK,aACvC;CACF;CAEA,MAAM,YAAY,iBAAiB,OAAO,MAAM;CAEhD,MAAM,WAAW,OAAO;EAAE,GAAG;EAAG,GAAG;EAAG,OAAO,KAAK;EAAO,QAAQ,KAAK;CAAO,IAAI;CACjF,MAAM,SAAS,YAAY,MAAM,cAC7B,gBAAgB,cAAc,MAAM,WAAW,yBAAyB,OAAO,KAAK,WAAW,CAAC,GAAG,MAAM,QAAQ,IACjH;CACJ,MAAM,OAAsB,eAC1B,WACA,gBACA,MACA,QACA,eACA,MACF;CACA,MAAM,WAAW,QAAQ,WAAW,iBAAiB,MAAMA,aAAW,IAAI,GAAG,UAAU,KAAK,GAAG,IAAI;CACnG,IAAI,iBAAiB,cAAc,SAAS,GAC1C,KAAK,MAAM,OAAO,eAAe,SAAS,KAAK,GAAG;CAEpD,OAAO,CAAC;EACN,MAAM;EACN,WAAWA,aAAW,IAAI;EAC1B;CACF,CAAC;AACH;;;;;;AAOA,SAAgB,eACd,MACe;CACf,MAAM,EACJ,OAAO,MAAM,OAAO,QAAQ,SAAS,eAAe,UAAU,iBAAiB,YAC/E,gBAAgB,MAAM,QACpB;CACJ,OAAO,uBACL,OAAO,MAAM,SAAS,eAAe,UAAU;EAAE;EAAiB;CAAW,GAAG,gBAChF,OAAO;EAAE;EAAO;EAAQ,KAAK,WAAW,GAAG;EAAG,aAAa,KAAK,eAAe,eAAe,OAAO;CAAE,IAAI,MAC7G;AACF;;;;;;AAOA,SAAgB,oBACd,MACM;CACN,MAAM,EAAE,QAAQ,MAAM,OAAO,QAAQ,QAAQ;CAC7C,YAAY,QAAQ,eAAe,IAAI,GAAG,MAAM;EAAE;EAAO;EAAQ;CAAI,CAAC;AACxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surfaceContribution.js","names":["mergeContributions","mergeRoutingContributions"],"sources":["../../src/canvas/surfaceContribution.ts"],"sourcesContent":["/**\n * The canvas's unit of extension: a routing `Contribution` plus the roles only\n * a surface can install — views on it, and whatever else needs the mounted\n * canvas.\n */\nimport { mergeContributions as mergeRoutingContributions } from '@weasel-js/routing';\nimport type { DepName, DepSchema } from 'interactions/actions/depSchema';\nimport type { Contribution } from '../tools/overlayBinding';\nimport type { CanvasExtensionApi } from './canvasExtension';\nimport type { CanvasViewProps } from './CanvasView';\nimport type { ViewMarkerProps } from '../
|
|
1
|
+
{"version":3,"file":"surfaceContribution.js","names":["mergeContributions","mergeRoutingContributions"],"sources":["../../src/canvas/surfaceContribution.ts"],"sourcesContent":["/**\n * The canvas's unit of extension: a routing `Contribution` plus the roles only\n * a surface can install — views on it, and whatever else needs the mounted\n * canvas.\n */\nimport { mergeContributions as mergeRoutingContributions } from '@weasel-js/routing';\nimport type { DepName, DepSchema } from 'interactions/actions/depSchema';\nimport type { Contribution } from '../tools/overlayBinding';\nimport type { CanvasExtensionApi } from './canvasExtension';\nimport type { CanvasViewProps } from './CanvasView';\nimport type { ViewMarkerProps } from '../domains/viewports/viewMarker';\nimport type { View } from '../core/viewport/view';\nimport type { KitRequirements } from './kitRequirements';\n\n/** Reads the surface's deps from inside `SurfaceContribution.attach`. */\nexport interface ContributionDepReader {\n get<K extends DepName>(name: K): DepSchema[K] | undefined;\n}\n\n/** What {@link SurfaceContribution.beforePaint} and `afterPaint` are handed. */\nexport interface ContributionFrameCtx {\n /** The frame's timestamp, on `performance.now()`'s clock. Both phases of one\n * frame see the same value. */\n readonly time: number;\n /** The surface camera this frame paints with. */\n readonly view: View;\n /** Ask for another frame. A hook animating something calls this every frame\n * it still has work for, and stops calling it to let the loop go idle. */\n requestFrame(): void;\n /** The surface's deps, as `attach` reads them. */\n readonly deps: ContributionDepReader;\n}\n\n/**\n * A feature a surface installs in one step. Every role is optional: `bindings`,\n * `actions`, `deps`, `overlay` and `presentation` from `Contribution`, plus\n * the ones below. Installing it installs every role it declares, and removing\n * it removes them.\n */\nexport interface SurfaceContribution extends Contribution {\n /** Views this entry adds to the surface, as `<SceneCanvas views>` takes. */\n views?: readonly CanvasViewProps[];\n /** Markers this entry draws, as `<ViewMarker>` elements would. */\n markers?: readonly ViewMarkerProps[];\n /**\n * Runs once the surface mounts; returns its teardown. For what the roles\n * above cannot say — a registered (hit-testable) layer, a subscription.\n *\n * Keyed by the function's identity, so a contribution built once (in a\n * `useMemo`, or at module scope) attaches once however often the host\n * re-renders.\n */\n attach?: (api: CanvasExtensionApi, deps: ContributionDepReader) => () => void;\n /**\n * Runs on every frame the surface paints, before the paint — for state the\n * entry's layers read, advanced once per frame. Hooks run in the order the\n * surface lists its entries (registered tools, then `ambient` in array\n * order), and one that throws is reported and skipped without stopping the\n * frame.\n */\n beforePaint?: (ctx: ContributionFrameCtx) => void;\n /** Runs after a frame's pixels have landed, in the same order as\n * {@link beforePaint}. A frame that painted nothing runs no `afterPaint`. */\n afterPaint?: (ctx: ContributionFrameCtx) => void;\n /**\n * The kit versions this entry was written against, as semver ranges:\n * `{ core: '^1.7' }`. A mismatch warns in development, or throws where the\n * surface is set to `versionCheck=\"strict\"`.\n */\n requires?: KitRequirements;\n}\n\n/**\n * Concatenate contribution bundles into one registry, preserving order. Throws\n * on a duplicate entry id, dep name, view id or marker id — two features\n * answering for the same name is a bug in whichever was added second.\n */\nexport function mergeContributions<T extends SurfaceContribution>(\n ...bundles: readonly (readonly T[])[]\n): T[] {\n const out = mergeRoutingContributions<T>(...bundles);\n claimIds(out, 'view', (entry) => entry.views ?? []);\n claimIds(out, 'marker', (entry) => entry.markers ?? []);\n return out;\n}\n\nfunction claimIds<T extends SurfaceContribution>(\n entries: readonly T[], kind: 'view' | 'marker', of: (entry: T) => readonly { id: string }[],\n): void {\n const owners = new Map<string, string>();\n for (const entry of entries) {\n for (const { id } of of(entry)) {\n const owner = owners.get(id);\n if (owner !== undefined) {\n throw new Error(\n `mergeContributions: ${kind} \"${id}\" is added by both \"${owner}\" and \"${entry.id}\". `\n + `${kind === 'view' ? 'View' : 'Marker'} ids are unique per surface; rename one.`,\n );\n }\n owners.set(id, entry.id);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA6EA,SAAgBA,qBACd,GAAG,SACE;CACL,MAAM,MAAMC,mBAA6B,GAAG,OAAO;CACnD,SAAS,KAAK,SAAS,UAAU,MAAM,SAAS,CAAC,CAAC;CAClD,SAAS,KAAK,WAAW,UAAU,MAAM,WAAW,CAAC,CAAC;CACtD,OAAO;AACT;AAEA,SAAS,SACP,SAAuB,MAAyB,IAC1C;CACN,MAAM,yBAAS,IAAI,IAAoB;CACvC,KAAK,MAAM,SAAS,SAClB,KAAK,MAAM,EAAE,QAAQ,GAAG,KAAK,GAAG;EAC9B,MAAM,QAAQ,OAAO,IAAI,EAAE;EAC3B,IAAI,UAAU,QACZ,MAAM,IAAI,MACR,uBAAuB,KAAK,IAAI,GAAG,sBAAsB,MAAM,SAAS,MAAM,GAAG,KAC5E,SAAS,SAAS,SAAS,SAAS,yCAC3C;EAEF,OAAO,IAAI,IAAI,MAAM,EAAE;CACzB;AAEJ"}
|
|
@@ -3,6 +3,7 @@ import { drawLayers } from "../core/layers/render.js";
|
|
|
3
3
|
import { resolveDpr } from "./canvasRenderer.js";
|
|
4
4
|
import { beginDebugFrame } from "../debug/useDebugSink.js";
|
|
5
5
|
import { NO_LAYER_VISIBILITY } from "./useExtraLayers.js";
|
|
6
|
+
import { ViewSnap } from "./viewSnap.js";
|
|
6
7
|
import { viewToMat3 } from "@weasel-js/renderer";
|
|
7
8
|
import { useCallback, useEffect, useInsertionEffect, useRef } from "react";
|
|
8
9
|
import { useLatest } from "@weasel-js/react";
|
|
@@ -32,10 +33,11 @@ function retainPolicy(retain) {
|
|
|
32
33
|
* leased renderer to whichever canvas pixels go to and draws the layer stack through the
|
|
33
34
|
* current view. The layer command cache lives as long as the canvas.
|
|
34
35
|
*/
|
|
35
|
-
function useCanvasPaint(paintRef, lease, target, frame, markPainted) {
|
|
36
|
+
function useCanvasPaint(paintRef, lease, target, frame, markPainted, requestRedraw) {
|
|
36
37
|
const { paintTargetRef, ownCanvasRef, paintRectRef } = target;
|
|
37
38
|
const { inputsRef, viewRef, gateRef, helpersRef } = frame;
|
|
38
39
|
const layerCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
40
|
+
const snapRef = useRef(new ViewSnap());
|
|
39
41
|
const paint = useCallback(() => {
|
|
40
42
|
const c = paintTargetRef.current ?? ownCanvasRef.current;
|
|
41
43
|
if (!c) return false;
|
|
@@ -55,17 +57,20 @@ function useCanvasPaint(paintRef, lease, target, frame, markPainted) {
|
|
|
55
57
|
shaders: paintShaders
|
|
56
58
|
});
|
|
57
59
|
if (!renderer) return false;
|
|
58
|
-
const
|
|
60
|
+
const dpr = resolveDpr(dprIn);
|
|
61
|
+
const view = snapRef.current.draw(viewRef.current, Math.round(w * dpr) / w, retain !== false && retain?.whole !== false);
|
|
59
62
|
const { layerVisibility: vis, layerOrder: order } = gateRef.current;
|
|
60
63
|
const spans = debugFrame?.time();
|
|
61
64
|
const commands = drawLayers(paintLayers, helpersRef.current, vis ?? NO_LAYER_VISIBILITY, order, view, {
|
|
62
65
|
width: w,
|
|
63
66
|
height: h,
|
|
64
|
-
dpr
|
|
67
|
+
dpr
|
|
65
68
|
}, layerCacheRef.current, void 0, groups, spans, retainPolicy(retain));
|
|
66
69
|
renderer.render(commands, viewToMat3(view), spans ? { spans } : void 0);
|
|
67
|
-
|
|
70
|
+
const stats = renderer.lastFrameStats();
|
|
71
|
+
debugFrame?.end(stats);
|
|
68
72
|
markPainted();
|
|
73
|
+
if (snapRef.current.owesFrame(stats.retained.declined)) requestRedraw();
|
|
69
74
|
return true;
|
|
70
75
|
}, [
|
|
71
76
|
gateRef,
|
|
@@ -76,6 +81,7 @@ function useCanvasPaint(paintRef, lease, target, frame, markPainted) {
|
|
|
76
81
|
ownCanvasRef,
|
|
77
82
|
paintRectRef,
|
|
78
83
|
paintTargetRef,
|
|
84
|
+
requestRedraw,
|
|
79
85
|
viewRef
|
|
80
86
|
]);
|
|
81
87
|
useInsertionEffect(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanvasPaint.js","names":[],"sources":["../../src/canvas/useCanvasPaint.ts"],"sourcesContent":["import { useCallback, useEffect, useInsertionEffect, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { viewToMat3 } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport {\n DEFAULT_LAYER_RETAIN, drawLayers, type LayerCommandCache, type LayerRetainPolicy, type RenderLayer,\n} from 'core/layers/render';\nimport type { CanvasDebugSink } from '../debug/types';\nimport { beginDebugFrame } from '../debug/useDebugSink';\nimport { resolveDpr, type CanvasRendererLease } from './canvasRenderer';\nimport type { CanvasProps } from './canvasProps';\nimport type { CanvasElements } from './useCanvasElements';\nimport { NO_LAYER_VISIBILITY, type LayerGate } from './useExtraLayers';\n\n/** What a frame's paint reads that a React render owns. */\nexport interface PaintInputs extends Pick<CanvasProps<unknown>, 'dpr' | 'layerGroups' | 'shaders' | 'flattenTolerance' | 'imageMinification' | 'retain'> {\n layers: RenderLayer<unknown>[];\n width: number;\n height: number;\n debugSink: CanvasDebugSink | null;\n frameStats: boolean;\n}\n\n/** What a frame's paint reads that changes outside a render. */\nexport interface PaintFrame {\n inputsRef: { readonly current: PaintInputs };\n viewRef: { readonly current: View };\n gateRef: { readonly current: LayerGate };\n helpersRef: { readonly current: unknown };\n}\n\n/**\n * Which content version the last paint showed. `markPainted` records the current one, read\n * from `contentVersion`'s latest committed value.\n */\nexport function usePaintedVersion(contentVersion: (() => number) | undefined): {\n getPaintedVersion: () => number;\n markPainted: () => void;\n} {\n const contentVersionRef = useLatest(contentVersion);\n const paintedVersionRef = useRef(0);\n const getPaintedVersion = useCallback(() => paintedVersionRef.current, []);\n const markPainted = useCallback(() => {\n paintedVersionRef.current = contentVersionRef.current?.() ?? 0;\n }, [contentVersionRef]);\n return { getPaintedVersion, markPainted };\n}\n\nfunction retainPolicy(retain: PaintInputs['retain']): LayerRetainPolicy | false {\n if (retain === false) return false;\n const minCommands = retain?.minCommands;\n return minCommands === undefined ? DEFAULT_LAYER_RETAIN : { minCommands };\n}\n\n/**\n * Publish the canvas's paint into `paintRef`, which the frame loop calls. The paint binds the\n * leased renderer to whichever canvas pixels go to and draws the layer stack through the\n * current view. The layer command cache lives as long as the canvas.\n */\nexport function useCanvasPaint(\n paintRef: { current: () => boolean },\n lease: CanvasRendererLease,\n target: Pick<CanvasElements, 'paintTargetRef' | 'ownCanvasRef' | 'paintRectRef'>,\n frame: PaintFrame,\n markPainted: () => void,\n): void {\n const { paintTargetRef, ownCanvasRef, paintRectRef } = target;\n const { inputsRef, viewRef, gateRef, helpersRef } = frame;\n const layerCacheRef = useRef<LayerCommandCache>(new Map());\n\n const paint = useCallback((): boolean => {\n const c = paintTargetRef.current ?? ownCanvasRef.current;\n if (!c) return false;\n const rect = paintRectRef.current;\n const {\n layers: paintLayers, width: w, height: h, debugSink: sink, frameStats: timed,\n dpr: dprIn,\n flattenTolerance: paintFlattenTolerance,\n layerGroups: groups, shaders: paintShaders, retain, imageMinification,\n } = inputsRef.current;\n if (w === 0 || h === 0) return false;\n\n const debugFrame = beginDebugFrame(sink, timed, paintLayers);\n\n const renderer = lease.bind(c, { width: w, height: h, dpr: dprIn }, {\n tile: rect,\n flattenTolerance: paintFlattenTolerance,\n retainBudget: retain ? retain.budget : undefined,\n imageMinification,\n shaders: paintShaders,\n });\n // No WebGL2 (jsdom), or a context that is lost for now: nothing to paint.\n if (!renderer) return false;\n\n const view = viewRef.current;\n const { layerVisibility: vis, layerOrder: order } = gateRef.current;\n const spans = debugFrame?.time();\n const commands = drawLayers(\n paintLayers,\n helpersRef.current,\n vis ?? NO_LAYER_VISIBILITY,\n order,\n view,\n { width: w, height: h, dpr
|
|
1
|
+
{"version":3,"file":"useCanvasPaint.js","names":[],"sources":["../../src/canvas/useCanvasPaint.ts"],"sourcesContent":["import { useCallback, useEffect, useInsertionEffect, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { viewToMat3 } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport {\n DEFAULT_LAYER_RETAIN, drawLayers, type LayerCommandCache, type LayerRetainPolicy, type RenderLayer,\n} from 'core/layers/render';\nimport type { CanvasDebugSink } from '../debug/types';\nimport { beginDebugFrame } from '../debug/useDebugSink';\nimport { resolveDpr, type CanvasRendererLease } from './canvasRenderer';\nimport type { CanvasProps } from './canvasProps';\nimport type { CanvasElements } from './useCanvasElements';\nimport { NO_LAYER_VISIBILITY, type LayerGate } from './useExtraLayers';\nimport { ViewSnap } from './viewSnap';\n\n/** What a frame's paint reads that a React render owns. */\nexport interface PaintInputs extends Pick<CanvasProps<unknown>, 'dpr' | 'layerGroups' | 'shaders' | 'flattenTolerance' | 'imageMinification' | 'retain'> {\n layers: RenderLayer<unknown>[];\n width: number;\n height: number;\n debugSink: CanvasDebugSink | null;\n frameStats: boolean;\n}\n\n/** What a frame's paint reads that changes outside a render. */\nexport interface PaintFrame {\n inputsRef: { readonly current: PaintInputs };\n viewRef: { readonly current: View };\n gateRef: { readonly current: LayerGate };\n helpersRef: { readonly current: unknown };\n}\n\n/**\n * Which content version the last paint showed. `markPainted` records the current one, read\n * from `contentVersion`'s latest committed value.\n */\nexport function usePaintedVersion(contentVersion: (() => number) | undefined): {\n getPaintedVersion: () => number;\n markPainted: () => void;\n} {\n const contentVersionRef = useLatest(contentVersion);\n const paintedVersionRef = useRef(0);\n const getPaintedVersion = useCallback(() => paintedVersionRef.current, []);\n const markPainted = useCallback(() => {\n paintedVersionRef.current = contentVersionRef.current?.() ?? 0;\n }, [contentVersionRef]);\n return { getPaintedVersion, markPainted };\n}\n\nfunction retainPolicy(retain: PaintInputs['retain']): LayerRetainPolicy | false {\n if (retain === false) return false;\n const minCommands = retain?.minCommands;\n return minCommands === undefined ? DEFAULT_LAYER_RETAIN : { minCommands };\n}\n\n/**\n * Publish the canvas's paint into `paintRef`, which the frame loop calls. The paint binds the\n * leased renderer to whichever canvas pixels go to and draws the layer stack through the\n * current view. The layer command cache lives as long as the canvas.\n */\nexport function useCanvasPaint(\n paintRef: { current: () => boolean },\n lease: CanvasRendererLease,\n target: Pick<CanvasElements, 'paintTargetRef' | 'ownCanvasRef' | 'paintRectRef'>,\n frame: PaintFrame,\n markPainted: () => void,\n requestRedraw: () => void,\n): void {\n const { paintTargetRef, ownCanvasRef, paintRectRef } = target;\n const { inputsRef, viewRef, gateRef, helpersRef } = frame;\n const layerCacheRef = useRef<LayerCommandCache>(new Map());\n const snapRef = useRef(new ViewSnap());\n\n const paint = useCallback((): boolean => {\n const c = paintTargetRef.current ?? ownCanvasRef.current;\n if (!c) return false;\n const rect = paintRectRef.current;\n const {\n layers: paintLayers, width: w, height: h, debugSink: sink, frameStats: timed,\n dpr: dprIn,\n flattenTolerance: paintFlattenTolerance,\n layerGroups: groups, shaders: paintShaders, retain, imageMinification,\n } = inputsRef.current;\n if (w === 0 || h === 0) return false;\n\n const debugFrame = beginDebugFrame(sink, timed, paintLayers);\n\n const renderer = lease.bind(c, { width: w, height: h, dpr: dprIn }, {\n tile: rect,\n flattenTolerance: paintFlattenTolerance,\n retainBudget: retain ? retain.budget : undefined,\n imageMinification,\n shaders: paintShaders,\n });\n // No WebGL2 (jsdom), or a context that is lost for now: nothing to paint.\n if (!renderer) return false;\n\n const dpr = resolveDpr(dprIn);\n // The renderer's own device pixels to the CSS pixel, from its rounded buffer width.\n const view = snapRef.current.draw(viewRef.current, Math.round(w * dpr) / w, retain !== false && retain?.whole !== false);\n const { layerVisibility: vis, layerOrder: order } = gateRef.current;\n const spans = debugFrame?.time();\n const commands = drawLayers(\n paintLayers,\n helpersRef.current,\n vis ?? NO_LAYER_VISIBILITY,\n order,\n view,\n { width: w, height: h, dpr },\n layerCacheRef.current,\n undefined,\n groups,\n spans,\n retainPolicy(retain),\n );\n renderer.render(commands, viewToMat3(view), spans ? { spans } : undefined);\n const stats = renderer.lastFrameStats();\n debugFrame?.end(stats);\n markPainted();\n if (snapRef.current.owesFrame(stats.retained.declined)) requestRedraw();\n return true;\n }, [gateRef, helpersRef, inputsRef, lease, markPainted, ownCanvasRef, paintRectRef, paintTargetRef, requestRedraw, viewRef]);\n useInsertionEffect(() => {\n paintRef.current = paint;\n });\n\n useEffect(() => {\n const layerCache = layerCacheRef.current;\n return () => { layerCache.clear(); };\n }, []);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoCA,SAAgB,kBAAkB,gBAGhC;CACA,MAAM,oBAAoB,UAAU,cAAc;CAClD,MAAM,oBAAoB,OAAO,CAAC;CAKlC,OAAO;EAAE,mBAJiB,kBAAkB,kBAAkB,SAAS,CAAC,CAI/D;EAAmB,aAHR,kBAAkB;GACpC,kBAAkB,UAAU,kBAAkB,UAAU,KAAK;EAC/D,GAAG,CAAC,iBAAiB,CACO;CAAY;AAC1C;AAEA,SAAS,aAAa,QAA0D;CAC9E,IAAI,WAAW,OAAO,OAAO;CAC7B,MAAM,cAAc,QAAQ;CAC5B,OAAO,gBAAgB,SAAY,uBAAuB,EAAE,YAAY;AAC1E;;;;;;AAOA,SAAgB,eACd,UACA,OACA,QACA,OACA,aACA,eACM;CACN,MAAM,EAAE,gBAAgB,cAAc,iBAAiB;CACvD,MAAM,EAAE,WAAW,SAAS,SAAS,eAAe;CACpD,MAAM,gBAAgB,uBAA0B,IAAI,IAAI,CAAC;CACzD,MAAM,UAAU,OAAO,IAAI,SAAS,CAAC;CAErC,MAAM,QAAQ,kBAA2B;EACvC,MAAM,IAAI,eAAe,WAAW,aAAa;EACjD,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,OAAO,aAAa;EAC1B,MAAM,EACJ,QAAQ,aAAa,OAAO,GAAG,QAAQ,GAAG,WAAW,MAAM,YAAY,OACvE,KAAK,OACL,kBAAkB,uBAClB,aAAa,QAAQ,SAAS,cAAc,QAAQ,sBAClD,UAAU;EACd,IAAI,MAAM,KAAK,MAAM,GAAG,OAAO;EAE/B,MAAM,aAAa,gBAAgB,MAAM,OAAO,WAAW;EAE3D,MAAM,WAAW,MAAM,KAAK,GAAG;GAAE,OAAO;GAAG,QAAQ;GAAG,KAAK;EAAM,GAAG;GAClE,MAAM;GACN,kBAAkB;GAClB,cAAc,SAAS,OAAO,SAAS;GACvC;GACA,SAAS;EACX,CAAC;EAED,IAAI,CAAC,UAAU,OAAO;EAEtB,MAAM,MAAM,WAAW,KAAK;EAE5B,MAAM,OAAO,QAAQ,QAAQ,KAAK,QAAQ,SAAS,KAAK,MAAM,IAAI,GAAG,IAAI,GAAG,WAAW,SAAS,QAAQ,UAAU,KAAK;EACvH,MAAM,EAAE,iBAAiB,KAAK,YAAY,UAAU,QAAQ;EAC5D,MAAM,QAAQ,YAAY,KAAK;EAC/B,MAAM,WAAW,WACf,aACA,WAAW,SACX,OAAO,qBACP,OACA,MACA;GAAE,OAAO;GAAG,QAAQ;GAAG;EAAI,GAC3B,cAAc,SACd,QACA,QACA,OACA,aAAa,MAAM,CACrB;EACA,SAAS,OAAO,UAAU,WAAW,IAAI,GAAG,QAAQ,EAAE,MAAM,IAAI,MAAS;EACzE,MAAM,QAAQ,SAAS,eAAe;EACtC,YAAY,IAAI,KAAK;EACrB,YAAY;EACZ,IAAI,QAAQ,QAAQ,UAAU,MAAM,SAAS,QAAQ,GAAG,cAAc;EACtE,OAAO;CACT,GAAG;EAAC;EAAS;EAAY;EAAW;EAAO;EAAa;EAAc;EAAc;EAAgB;EAAe;CAAO,CAAC;CAC3H,yBAAyB;EACvB,SAAS,UAAU;CACrB,CAAC;CAED,gBAAgB;EACd,MAAM,aAAa,cAAc;EACjC,aAAa;GAAE,WAAW,MAAM;EAAG;CACrC,GAAG,CAAC,CAAC;AACP"}
|
|
@@ -12,7 +12,7 @@ function useHitObserver(cb, pickerRef) {
|
|
|
12
12
|
return useMemo(() => {
|
|
13
13
|
if (!wired) return void 0;
|
|
14
14
|
return (world) => {
|
|
15
|
-
const result = pickerRef.current?.(world
|
|
15
|
+
const result = pickerRef.current?.(world);
|
|
16
16
|
cbRef.current?.(result ? {
|
|
17
17
|
id: result.id,
|
|
18
18
|
kind: result.kind
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHitObserver.js","names":[],"sources":["../../src/canvas/useHitObserver.ts"],"sourcesContent":["import { useMemo, type MutableRefObject } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { SceneCanvasHit } from './SceneCanvas';\n\ntype Picker = ((
|
|
1
|
+
{"version":3,"file":"useHitObserver.js","names":[],"sources":["../../src/canvas/useHitObserver.ts"],"sourcesContent":["import { useMemo, type MutableRefObject } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { SceneCanvasHit } from './SceneCanvas';\nimport type { WorldPoint } from '@weasel-js/routing';\n\ntype Picker = ((world: WorldPoint) => { id: string; kind: string } | null | undefined) | undefined;\n\n/** Turns a consumer's hit callback into the world-point observer the gesture\n * dispatcher takes, resolving the hit with the canvas's own picker. Identity\n * changes only between wired and not, so the dispatcher isn't rebuilt per\n * render. */\nexport function useHitObserver(\n cb: ((hit: SceneCanvasHit | null) => void) | undefined,\n pickerRef: MutableRefObject<Picker>,\n): ((world: WorldPoint) => void) | undefined {\n const cbRef = useLatest(cb);\n const wired = Boolean(cb);\n return useMemo(() => {\n if (!wired) return undefined;\n return (world) => {\n const result = pickerRef.current?.(world);\n cbRef.current?.(result ? { id: result.id, kind: result.kind } : null);\n };\n }, [wired, cbRef, pickerRef]);\n}\n"],"mappings":";;;;;;;;AAWA,SAAgB,eACd,IACA,WAC2C;CAC3C,MAAM,QAAQ,UAAU,EAAE;CAC1B,MAAM,QAAQ,QAAQ,EAAE;CACxB,OAAO,cAAc;EACnB,IAAI,CAAC,OAAO,OAAO;EACnB,QAAQ,UAAU;GAChB,MAAM,SAAS,UAAU,UAAU,KAAK;GACxC,MAAM,UAAU,SAAS;IAAE,IAAI,OAAO;IAAI,MAAM,OAAO;GAAK,IAAI,IAAI;EACtE;CACF,GAAG;EAAC;EAAO;EAAO;CAAS,CAAC;AAC9B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizeView } from "../core/viewport/view.js";
|
|
2
|
-
import { isRectAim, rectAim, viewForAim } from "../
|
|
2
|
+
import { isRectAim, rectAim, viewForAim } from "../domains/viewports/aim.js";
|
|
3
3
|
import { viewportWorldRect } from "../core/viewport/viewportWorldRect.js";
|
|
4
4
|
import { useCanvasView } from "./useCanvasView.js";
|
|
5
5
|
import { sizeOf } from "./useViewNesting.js";
|