@weasel-js/core 1.13.0-rc.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 +408 -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 +17 -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/buildSceneLayer.js +1 -1
- package/dist/canvas/buildSceneLayer.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +5 -3
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/canvasRenderer.js +6 -2
- package/dist/canvas/canvasRenderer.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 +7 -5
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/surfaceContribution.js.map +1 -1
- package/dist/canvas/useCanvasPaint.js +12 -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-B1RS-IJx.d.ts → depSchema-C3BwxitB.d.ts} +130 -104
- 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 +3 -2
- 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 +1445 -1053
- 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":"viewRegistry.js","names":[],"sources":["../../src/canvas/viewRegistry.tsx"],"sourcesContent":["/**\n * The seam between a canvas surface and the views drawn on it.\n *\n * Traffic runs both ways: the surface attaches a handle onto its frame and its\n * layer stack, and each view registers what it contributes back — the viewport\n * node it paints into and the record the gesture dispatcher routes its events\n * to. Both sides are thunks, read at draw and dispatch time, so neither can\n * serve the other a frame that has already moved.\n */\nimport { createContext, useContext, useMemo, useRef, type ReactNode } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { LayerHit } from 'affordances/types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport type { ViewportLayer } from 'features/viewports/viewportLayer';\nimport type { DispatcherViewTarget } from '@weasel-js/routing/internal';\nimport { createViewResolver, viewContains, type ViewResolver } from 'features/viewports/viewResolver';\nimport { createEmitter } from '@weasel-js/registry';\nimport type { ViewAt } from './canvasExtension';\nimport { isRectAim, rectAim, viewForAim, type ViewAim } from 'features/viewports/aim';\nimport { viewportWorldRect } from 'core/viewport/viewportWorldRect';\nimport type { ViewMarkerProps } from 'features/viewports/viewMarker';\nimport { createViewMarkerRegistry, type ViewMarkerRegistry } from './viewMarkers';\n\n/**\n * What a view needs from the surface hosting it.\n *\n * @internal Attached by the surface, read by `<CanvasView>`.\n */\nexport interface SurfaceHandle {\n /** A client point in the surface's own world — the conversion the surface\n * itself makes, its `clientToWorld` override included. */\n clientToWorld(clientX: number, clientY: number): [number, number];\n /** The outer camera. */\n view(): View;\n /** The canvas's CSS-pixel size. */\n dims(): Dims;\n /** The stack a view paints through its own camera, unless it narrows it. */\n layers(): readonly RenderLayer<unknown>[];\n requestRedraw(): void;\n /** The canvas's own view chrome — its selection, its overlay-aware bounds.\n * What a `<CanvasView>` answers for itself, answered for view zero. */\n chromeState(): ChromeState;\n /** Hit-test the externally registered layers at a world point, against the\n * frame and draw envelope of whichever view is asking. The public\n * `hitTestExtras` is this with the canvas's own frame and envelope. */\n hitTestExtras(\n worldX: number,\n worldY: number,\n view: View,\n dims: Dims,\n data: unknown,\n /** Consider only these layers — the ones the asking view paints. A layer\n * asked on a frame it was not drawn in answers for the wrong pixels. */\n only?: readonly RenderLayer<unknown>[],\n ): { layerId: string; hit: LayerHit } | null;\n /** Whether a registered layer claims a canvas-local point on the surface's\n * own frame. Registered layers paint over every view, so such a point is\n * theirs even inside a view's rect. */\n claimsAbove(x: number, y: number): boolean;\n /** The root camera's input write: clamped, forwarded when controlled, and ending a glide. */\n setView?(next: View): void;\n}\n\n/**\n * A client point in the surface's CSS pixels, from its top-left: the surface's\n * own world point put back through its camera, so a `clientToWorld` override\n * reaches every view declared on it.\n *\n * @internal\n */\nexport function surfaceLocal(\n surface: Pick<SurfaceHandle, 'clientToWorld' | 'view'> | null,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n if (!surface) return { x: clientX, y: clientY };\n const [worldX, worldY] = surface.clientToWorld(clientX, clientY);\n const view = surface.view();\n return { x: (worldX - view.x) * view.scale.x, y: (worldY - view.y) * view.scale.y };\n}\n\n/**\n * What one view contributes to the surface hosting it.\n *\n * @internal Written by `<CanvasView>`, read by the surface. Consumers declare\n * a view, not a registration.\n */\nexport interface ViewRegistration {\n id: string;\n /** The view this one is nested in: listed, painted and hit after it, and\n * its `layer` measures itself from that view's rect and camera. */\n parent?: string;\n /**\n * Paint and hit order, low to high. Views declared as props take their array\n * index; a view mounted as a child takes `Infinity`, so it lands after them\n * however React happens to order the mount effects.\n */\n order: number;\n /** The viewport node this view paints into. Its `resolvable` is what routes\n * input here. */\n layer: ViewportLayer<unknown>;\n /** `false` keeps input over the view on the canvas beneath it. */\n interactive: boolean;\n /** `false` leaves `layer` for a host to draw — a HUD window's interior — so\n * the surface does not paint it too. */\n paint: boolean;\n /** Everything about dispatching to this view except its id. */\n target: Omit<DispatcherViewTarget, 'id'>;\n /** Whether a scene layer reaches the screen in this view. For input that\n * reaches a view without a dispatch — hover — so it judges this view's\n * paint rather than the surface's. */\n layerIsPainted(layerId: string): boolean;\n /** The layers this view draws, in order — what a view nested in it paints\n * from. */\n drawnLayers(): readonly RenderLayer<unknown>[];\n /** Whether the view keeps its camera or its aim; the other is derived. */\n held: 'camera' | 'aim';\n /** What the view shows: the aim it holds, or its camera's. */\n aim(): ViewAim;\n /** `false` for an aim the view cannot read, or one with nowhere to go. */\n setAim(aim: ViewAim): boolean;\n}\n\n\n/** @internal */\nexport interface ViewRegistry extends ViewMarkerRegistry {\n /** Returns an unregister. */\n register(reg: ViewRegistration): () => void;\n /** Live registrations in paint order, each nested view after the view it\n * is nested in. */\n list(): readonly ViewRegistration[];\n /** The live registration with `id`, if any. */\n get(id: string): ViewRegistration | undefined;\n /** The innermost view painted under a canvas-local point, whether or not it\n * takes input, passing over `except` and the views nested in it. */\n viewAt(at: { x: number; y: number }, opts?: { except?: string }): ViewAt;\n /** What view `id` shows (`null`: the surface's root), or `null` while there is no such view. */\n aimOf(id: string | null): ViewAim | null;\n /** Point view `id` at `aim`; `false` when it cannot. */\n setAim(id: string | null, aim: ViewAim): boolean;\n /** Bumped whenever the list changes. Pair with {@link ViewRegistry.subscribe}\n * for `useSyncExternalStore`. */\n getVersion(): number;\n subscribe(fn: () => void): () => void;\n /** Which view a client point belongs to, sticky for a captured pointer.\n * One per surface: every caller that routes input — the dispatcher, pinch,\n * hover — must agree about where a point landed. */\n readonly resolver: ViewResolver;\n /** The hosting surface, or `null` before one attaches. */\n surface(): SurfaceHandle | null;\n /** Returns a detach, which leaves a handle attached since alone. */\n attachSurface(handle: SurfaceHandle): () => void;\n}\n\nconst ViewRegistryContext = createContext<ViewRegistry | null>(null);\n\n/** Mounts the registry for one surface. `<SceneCanvas>` mounts one; a view\n * declaring itself as a child must be inside it. */\nexport function ViewRegistryProvider({ children }: { children: ReactNode }) {\n const entriesRef = useRef(new Map<ViewRegistration, number>());\n const seqRef = useRef(0);\n const versionRef = useRef(0);\n const surfaceRef = useRef<SurfaceHandle | null>(null);\n const markersRef = useRef(new Map<() => ViewMarkerProps, RenderLayer<unknown>>());\n\n const registry = useMemo<ViewRegistry>(() => {\n const listeners = createEmitter();\n const changed = (): void => {\n versionRef.current++;\n listeners.emit();\n };\n const ordered = (): ViewRegistration[] => nestedOrder([...entriesRef.current.keys()].sort((a, b) => (\n a.order - b.order || entriesRef.current.get(a)! - entriesRef.current.get(b)!\n )));\n const get = (id: string): ViewRegistration | undefined => {\n for (const r of entriesRef.current.keys()) if (r.id === id) return r;\n return undefined;\n };\n /** Whether `r` is `id` or nested in it, however deep. */\n const within = (r: ViewRegistration, id: string): boolean => {\n const seen = new Set<ViewRegistration>();\n for (let at: ViewRegistration | undefined = r; at && !seen.has(at); at = at.parent === undefined ? undefined : get(at.parent)) {\n if (at.id === id) return true;\n seen.add(at);\n }\n return false;\n };\n const resolver = createViewResolver({\n views: () => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n return ordered().filter((r) => r.interactive).map((r) => r.layer.resolvable(view, dims));\n },\n root: () => surfaceRef.current?.view() ?? IDENTITY_VIEW,\n toLocal: (clientX, clientY) => surfaceLocal(surfaceRef.current, clientX, clientY),\n occluded: (x, y) => surfaceRef.current?.claimsAbove(x, y) ?? false,\n });\n const registry: ViewRegistry = {\n resolver,\n register: (reg) => {\n entriesRef.current.set(reg, seqRef.current++);\n changed();\n return () => { entriesRef.current.delete(reg); changed(); };\n },\n list: ordered,\n get,\n viewAt: ({ x, y }, opts) => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n const except = opts?.except;\n const list = ordered();\n for (let i = list.length - 1; i >= 0; i--) {\n const r = list[i]!;\n if (except !== undefined && within(r, except)) continue;\n const v = r.layer.resolvable(view, dims);\n if (viewContains(v, x, y)) return { id: v.id, view: v.view, rect: v.rect };\n }\n return { id: null, view, rect: { x: 0, y: 0, w: dims.width, h: dims.height } };\n },\n aimOf: (id) => {\n if (id !== null) return get(id)?.aim() ?? null;\n const surface = surfaceRef.current;\n return surface ? rectAim(null, viewportWorldRect(surface.view(), surface.dims())) : null;\n },\n setAim: (id, aim) => {\n if (id !== null) return get(id)?.setAim(aim) ?? false;\n const surface = surfaceRef.current;\n if (!isRectAim(aim) || !surface?.setView) return false;\n surface.setView(viewForAim(aim.extent, surface.dims(), 'exact'));\n return true;\n },\n ...createViewMarkerRegistry(markersRef.current, {\n host: { aimOf: (id) => registry.aimOf(id), setAim: (id, aim) => registry.setAim(id, aim) },\n surface: () => surfaceRef.current,\n resolve: (id, outer, dims) => get(id)?.layer.resolvable(outer, dims),\n changed,\n }),\n getVersion: () => versionRef.current,\n subscribe: listeners.subscribe,\n surface: () => surfaceRef.current,\n attachSurface: (handle) => {\n surfaceRef.current = handle;\n return () => { if (surfaceRef.current === handle) surfaceRef.current = null; };\n },\n };\n return registry;\n }, []);\n\n return <ViewRegistryContext.Provider value={registry}>{children}</ViewRegistryContext.Provider>;\n}\n\n/** The surface's view registry, or `null` outside one. */\nexport function useOptionalViewRegistry(): ViewRegistry | null {\n return useContext(ViewRegistryContext);\n}\n\n/** Each view followed by the views nested in it, in the order given. A view\n * whose parent is not registered keeps its place; one caught in a cycle\n * goes last. */\nfunction nestedOrder(sorted: readonly ViewRegistration[]): ViewRegistration[] {\n const ids = new Set(sorted.map((r) => r.id));\n const out: ViewRegistration[] = [];\n const placed = new Set<ViewRegistration>();\n const place = (r: ViewRegistration): void => {\n if (placed.has(r)) return;\n placed.add(r);\n out.push(r);\n for (const child of sorted) if (child.parent === r.id) place(child);\n };\n for (const r of sorted) if (r.parent === undefined || !ids.has(r.parent)) place(r);\n for (const r of sorted) place(r);\n return out;\n}\n\n/** The `Dims` a surface reports before it has measured itself. */\nexport const UNMEASURED_DIMS: Dims = { width: 0, height: 0 };\n\n/** The camera a view resolves against before its surface attaches. */\nexport const IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuEA,SAAgB,aACd,SACA,SACA,SAC0B;CAC1B,IAAI,CAAC,SAAS,OAAO;EAAE,GAAG;EAAS,GAAG;CAAQ;CAC9C,MAAM,CAAC,QAAQ,UAAU,QAAQ,cAAc,SAAS,OAAO;CAC/D,MAAM,OAAO,QAAQ,KAAK;CAC1B,OAAO;EAAE,IAAI,SAAS,KAAK,KAAK,KAAK,MAAM;EAAG,IAAI,SAAS,KAAK,KAAK,KAAK,MAAM;CAAE;AACpF;AA2EA,IAAM,sBAAsB,cAAmC,IAAI;;;AAInE,SAAgB,qBAAqB,EAAE,YAAqC;CAC1E,MAAM,aAAa,uBAAO,IAAI,IAA8B,CAAC;CAC7D,MAAM,SAAS,OAAO,CAAC;CACvB,MAAM,aAAa,OAAO,CAAC;CAC3B,MAAM,aAAa,OAA6B,IAAI;CACpD,MAAM,aAAa,uBAAO,IAAI,IAAiD,CAAC;CAEhF,MAAM,WAAW,cAA4B;EAC3C,MAAM,YAAY,cAAc;EAChC,MAAM,gBAAsB;GAC1B,WAAW;GACX,UAAU,KAAK;EACjB;EACA,MAAM,gBAAoC,YAAY,CAAC,GAAG,WAAW,QAAQ,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MAC5F,EAAE,QAAQ,EAAE,SAAS,WAAW,QAAQ,IAAI,CAAC,IAAK,WAAW,QAAQ,IAAI,CAAC,CAC3E,CAAC;EACF,MAAM,OAAO,OAA6C;GACxD,KAAK,MAAM,KAAK,WAAW,QAAQ,KAAK,GAAG,IAAI,EAAE,OAAO,IAAI,OAAO;EAErE;;EAEA,MAAM,UAAU,GAAqB,OAAwB;GAC3D,MAAM,uBAAO,IAAI,IAAsB;GACvC,KAAK,IAAI,KAAmC,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,GAAG,KAAK,GAAG,WAAW,SAAY,SAAY,IAAI,GAAG,MAAM,GAAG;IAC7H,IAAI,GAAG,OAAO,IAAI,OAAO;IACzB,KAAK,IAAI,EAAE;GACb;GACA,OAAO;EACT;EAYA,MAAM,WAAyB;GAC7B,UAZe,mBAAmB;IAClC,aAAa;KACX,MAAM,UAAU,WAAW;KAC3B,MAAM,OAAO,SAAS,KAAK,KAAK;KAChC,MAAM,OAAO,SAAS,KAAK,KAAK;KAChC,OAAO,QAAQ,CAAC,CAAC,QAAQ,MAAM,EAAE,WAAW,CAAC,CAAC,KAAK,MAAM,EAAE,MAAM,WAAW,MAAM,IAAI,CAAC;IACzF;IACA,YAAY,WAAW,SAAS,KAAK,KAAK;IAC1C,UAAU,SAAS,YAAY,aAAa,WAAW,SAAS,SAAS,OAAO;IAChF,WAAW,GAAG,MAAM,WAAW,SAAS,YAAY,GAAG,CAAC,KAAK;GAC/D,CAEE;GACA,WAAW,QAAQ;IACjB,WAAW,QAAQ,IAAI,KAAK,OAAO,SAAS;IAC5C,QAAQ;IACR,aAAa;KAAE,WAAW,QAAQ,OAAO,GAAG;KAAG,QAAQ;IAAG;GAC5D;GACA,MAAM;GACN;GACA,SAAS,EAAE,GAAG,KAAK,SAAS;IAC1B,MAAM,UAAU,WAAW;IAC3B,MAAM,OAAO,SAAS,KAAK,KAAK;IAChC,MAAM,OAAO,SAAS,KAAK,KAAK;IAChC,MAAM,SAAS,MAAM;IACrB,MAAM,OAAO,QAAQ;IACrB,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;KACzC,MAAM,IAAI,KAAK;KACf,IAAI,WAAW,UAAa,OAAO,GAAG,MAAM,GAAG;KAC/C,MAAM,IAAI,EAAE,MAAM,WAAW,MAAM,IAAI;KACvC,IAAI,aAAa,GAAG,GAAG,CAAC,GAAG,OAAO;MAAE,IAAI,EAAE;MAAI,MAAM,EAAE;MAAM,MAAM,EAAE;KAAK;IAC3E;IACA,OAAO;KAAE,IAAI;KAAM;KAAM,MAAM;MAAE,GAAG;MAAG,GAAG;MAAG,GAAG,KAAK;MAAO,GAAG,KAAK;KAAO;IAAE;GAC/E;GACA,QAAQ,OAAO;IACb,IAAI,OAAO,MAAM,OAAO,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK;IAC1C,MAAM,UAAU,WAAW;IAC3B,OAAO,UAAU,QAAQ,MAAM,kBAAkB,QAAQ,KAAK,GAAG,QAAQ,KAAK,CAAC,CAAC,IAAI;GACtF;GACA,SAAS,IAAI,QAAQ;IACnB,IAAI,OAAO,MAAM,OAAO,IAAI,EAAE,CAAC,EAAE,OAAO,GAAG,KAAK;IAChD,MAAM,UAAU,WAAW;IAC3B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,SAAS,OAAO;IACjD,QAAQ,QAAQ,WAAW,IAAI,QAAQ,QAAQ,KAAK,GAAG,OAAO,CAAC;IAC/D,OAAO;GACT;GACA,GAAG,yBAAyB,WAAW,SAAS;IAC9C,MAAM;KAAE,QAAQ,OAAO,SAAS,MAAM,EAAE;KAAG,SAAS,IAAI,QAAQ,SAAS,OAAO,IAAI,GAAG;IAAE;IACzF,eAAe,WAAW;IAC1B,UAAU,IAAI,OAAO,SAAS,IAAI,EAAE,CAAC,EAAE,MAAM,WAAW,OAAO,IAAI;IACnE;GACF,CAAC;GACD,kBAAkB,WAAW;GAC7B,WAAW,UAAU;GACrB,eAAe,WAAW;GAC1B,gBAAgB,WAAW;IACzB,WAAW,UAAU;IACrB,aAAa;KAAE,IAAI,WAAW,YAAY,QAAQ,WAAW,UAAU;IAAM;GAC/E;EACF;EACA,OAAO;CACT,GAAG,CAAC,CAAC;CAEL,OAAO,oBAAC,oBAAoB,UAArB;EAA8B,OAAO;EAAW;CAAuC,CAAA;AAChG;;AAGA,SAAgB,0BAA+C;CAC7D,OAAO,WAAW,mBAAmB;AACvC;;;;AAKA,SAAS,YAAY,QAAyD;CAC5E,MAAM,MAAM,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,EAAE,CAAC;CAC3C,MAAM,MAA0B,CAAC;CACjC,MAAM,yBAAS,IAAI,IAAsB;CACzC,MAAM,SAAS,MAA8B;EAC3C,IAAI,OAAO,IAAI,CAAC,GAAG;EACnB,OAAO,IAAI,CAAC;EACZ,IAAI,KAAK,CAAC;EACV,KAAK,MAAM,SAAS,QAAQ,IAAI,MAAM,WAAW,EAAE,IAAI,MAAM,KAAK;CACpE;CACA,KAAK,MAAM,KAAK,QAAQ,IAAI,EAAE,WAAW,UAAa,CAAC,IAAI,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACjF,KAAK,MAAM,KAAK,QAAQ,MAAM,CAAC;CAC/B,OAAO;AACT;;AAGA,IAAa,kBAAwB;CAAE,OAAO;CAAG,QAAQ;AAAE;;AAG3D,IAAa,gBAAsB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE"}
|
|
1
|
+
{"version":3,"file":"viewRegistry.js","names":[],"sources":["../../src/canvas/viewRegistry.tsx"],"sourcesContent":["/**\n * The seam between a canvas surface and the views drawn on it.\n *\n * Traffic runs both ways: the surface attaches a handle onto its frame and its\n * layer stack, and each view registers what it contributes back — the viewport\n * node it paints into and the record the gesture dispatcher routes its events\n * to. Both sides are thunks, read at draw and dispatch time, so neither can\n * serve the other a frame that has already moved.\n */\nimport { createContext, useContext, useMemo, useRef, type ReactNode } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { LayerHit } from 'affordances/types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { View } from 'core/viewport/view';\nimport type { ViewportLayer } from 'domains/viewports/viewportLayer';\nimport type { DispatcherViewTarget } from '@weasel-js/routing/internal';\nimport { createViewResolver, viewContains, type ViewResolver } from 'domains/viewports/viewResolver';\nimport { createEmitter } from '@weasel-js/registry';\nimport type { ViewAt } from './canvasExtension';\nimport { isRectAim, rectAim, viewForAim, type ViewAim } from 'domains/viewports/aim';\nimport { viewportWorldRect } from 'core/viewport/viewportWorldRect';\nimport type { ViewMarkerProps } from 'domains/viewports/viewMarker';\nimport { createViewMarkerRegistry, type ViewMarkerRegistry } from './viewMarkers';\n\n/**\n * What a view needs from the surface hosting it.\n *\n * @internal Attached by the surface, read by `<CanvasView>`.\n */\nexport interface SurfaceHandle {\n /** A client point in the surface's own world — the conversion the surface\n * itself makes, its `clientToWorld` override included. */\n clientToWorld(clientX: number, clientY: number): [number, number];\n /** The outer camera. */\n view(): View;\n /** The canvas's CSS-pixel size. */\n dims(): Dims;\n /** The stack a view paints through its own camera, unless it narrows it. */\n layers(): readonly RenderLayer<unknown>[];\n requestRedraw(): void;\n /** The canvas's own view chrome — its selection, its overlay-aware bounds.\n * What a `<CanvasView>` answers for itself, answered for view zero. */\n chromeState(): ChromeState;\n /** Hit-test the externally registered layers at a world point, against the\n * frame and draw envelope of whichever view is asking. The public\n * `hitTestExtras` is this with the canvas's own frame and envelope. */\n hitTestExtras(\n worldX: number,\n worldY: number,\n view: View,\n dims: Dims,\n data: unknown,\n /** Consider only these layers — the ones the asking view paints. A layer\n * asked on a frame it was not drawn in answers for the wrong pixels. */\n only?: readonly RenderLayer<unknown>[],\n ): { layerId: string; hit: LayerHit } | null;\n /** Whether a registered layer claims a canvas-local point on the surface's\n * own frame. Registered layers paint over every view, so such a point is\n * theirs even inside a view's rect. */\n claimsAbove(x: number, y: number): boolean;\n /** The root camera's input write: clamped, forwarded when controlled, and ending a glide. */\n setView?(next: View): void;\n}\n\n/**\n * A client point in the surface's CSS pixels, from its top-left: the surface's\n * own world point put back through its camera, so a `clientToWorld` override\n * reaches every view declared on it.\n *\n * @internal\n */\nexport function surfaceLocal(\n surface: Pick<SurfaceHandle, 'clientToWorld' | 'view'> | null,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n if (!surface) return { x: clientX, y: clientY };\n const [worldX, worldY] = surface.clientToWorld(clientX, clientY);\n const view = surface.view();\n return { x: (worldX - view.x) * view.scale.x, y: (worldY - view.y) * view.scale.y };\n}\n\n/**\n * What one view contributes to the surface hosting it.\n *\n * @internal Written by `<CanvasView>`, read by the surface. Consumers declare\n * a view, not a registration.\n */\nexport interface ViewRegistration {\n id: string;\n /** The view this one is nested in: listed, painted and hit after it, and\n * its `layer` measures itself from that view's rect and camera. */\n parent?: string;\n /**\n * Paint and hit order, low to high. Views declared as props take their array\n * index; a view mounted as a child takes `Infinity`, so it lands after them\n * however React happens to order the mount effects.\n */\n order: number;\n /** The viewport node this view paints into. Its `resolvable` is what routes\n * input here. */\n layer: ViewportLayer<unknown>;\n /** `false` keeps input over the view on the canvas beneath it. */\n interactive: boolean;\n /** `false` leaves `layer` for a host to draw — a HUD window's interior — so\n * the surface does not paint it too. */\n paint: boolean;\n /** Everything about dispatching to this view except its id. */\n target: Omit<DispatcherViewTarget, 'id'>;\n /** Whether a scene layer reaches the screen in this view. For input that\n * reaches a view without a dispatch — hover — so it judges this view's\n * paint rather than the surface's. */\n layerIsPainted(layerId: string): boolean;\n /** The layers this view draws, in order — what a view nested in it paints\n * from. */\n drawnLayers(): readonly RenderLayer<unknown>[];\n /** The selection the view owns, or the one the view it follows owns;\n * `undefined` while it shares the surface's. */\n selection(): SelectionApi | undefined;\n /** Whether the view keeps its camera or its aim; the other is derived. */\n held: 'camera' | 'aim';\n /** What the view shows: the aim it holds, or its camera's. */\n aim(): ViewAim;\n /** `false` for an aim the view cannot read, or one with nowhere to go. */\n setAim(aim: ViewAim): boolean;\n}\n\n\n/** @internal */\nexport interface ViewRegistry extends ViewMarkerRegistry {\n /** Returns an unregister. */\n register(reg: ViewRegistration): () => void;\n /** Live registrations in paint order, each nested view after the view it\n * is nested in. */\n list(): readonly ViewRegistration[];\n /** The live registration with `id`, if any. */\n get(id: string): ViewRegistration | undefined;\n /** The innermost view painted under a canvas-local point, whether or not it\n * takes input, passing over `except` and the views nested in it. */\n viewAt(at: { x: number; y: number }, opts?: { except?: string }): ViewAt;\n /** What view `id` shows (`null`: the surface's root), or `null` while there is no such view. */\n aimOf(id: string | null): ViewAim | null;\n /** Point view `id` at `aim`; `false` when it cannot. */\n setAim(id: string | null, aim: ViewAim): boolean;\n /** Bumped whenever the list changes. Pair with {@link ViewRegistry.subscribe}\n * for `useSyncExternalStore`. */\n getVersion(): number;\n subscribe(fn: () => void): () => void;\n /** Which view a client point belongs to, sticky for a captured pointer.\n * One per surface: every caller that routes input — the dispatcher, pinch,\n * hover — must agree about where a point landed. */\n readonly resolver: ViewResolver;\n /** The hosting surface, or `null` before one attaches. */\n surface(): SurfaceHandle | null;\n /** Returns a detach, which leaves a handle attached since alone. */\n attachSurface(handle: SurfaceHandle): () => void;\n}\n\nconst ViewRegistryContext = createContext<ViewRegistry | null>(null);\n\n/** Mounts the registry for one surface. `<SceneCanvas>` mounts one; a view\n * declaring itself as a child must be inside it. */\nexport function ViewRegistryProvider({ children }: { children: ReactNode }) {\n const entriesRef = useRef(new Map<ViewRegistration, number>());\n const seqRef = useRef(0);\n const versionRef = useRef(0);\n const surfaceRef = useRef<SurfaceHandle | null>(null);\n const markersRef = useRef(new Map<() => ViewMarkerProps, RenderLayer<unknown>>());\n\n const registry = useMemo<ViewRegistry>(() => {\n const listeners = createEmitter();\n const changed = (): void => {\n versionRef.current++;\n listeners.emit();\n };\n const ordered = (): ViewRegistration[] => nestedOrder([...entriesRef.current.keys()].sort((a, b) => (\n a.order - b.order || entriesRef.current.get(a)! - entriesRef.current.get(b)!\n )));\n const get = (id: string): ViewRegistration | undefined => {\n for (const r of entriesRef.current.keys()) if (r.id === id) return r;\n return undefined;\n };\n /** Whether `r` is `id` or nested in it, however deep. */\n const within = (r: ViewRegistration, id: string): boolean => {\n const seen = new Set<ViewRegistration>();\n for (let at: ViewRegistration | undefined = r; at && !seen.has(at); at = at.parent === undefined ? undefined : get(at.parent)) {\n if (at.id === id) return true;\n seen.add(at);\n }\n return false;\n };\n const resolver = createViewResolver({\n views: () => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n return ordered().filter((r) => r.interactive).map((r) => r.layer.resolvable(view, dims));\n },\n root: () => surfaceRef.current?.view() ?? IDENTITY_VIEW,\n toLocal: (clientX, clientY) => surfaceLocal(surfaceRef.current, clientX, clientY),\n occluded: (x, y) => surfaceRef.current?.claimsAbove(x, y) ?? false,\n });\n const registry: ViewRegistry = {\n resolver,\n register: (reg) => {\n entriesRef.current.set(reg, seqRef.current++);\n changed();\n return () => { entriesRef.current.delete(reg); changed(); };\n },\n list: ordered,\n get,\n viewAt: ({ x, y }, opts) => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n const except = opts?.except;\n const list = ordered();\n for (let i = list.length - 1; i >= 0; i--) {\n const r = list[i]!;\n if (except !== undefined && within(r, except)) continue;\n const v = r.layer.resolvable(view, dims);\n if (viewContains(v, x, y)) return { id: v.id, view: v.view, rect: v.rect };\n }\n return { id: null, view, rect: { x: 0, y: 0, w: dims.width, h: dims.height } };\n },\n aimOf: (id) => {\n if (id !== null) return get(id)?.aim() ?? null;\n const surface = surfaceRef.current;\n return surface ? rectAim(null, viewportWorldRect(surface.view(), surface.dims())) : null;\n },\n setAim: (id, aim) => {\n if (id !== null) return get(id)?.setAim(aim) ?? false;\n const surface = surfaceRef.current;\n if (!isRectAim(aim) || !surface?.setView) return false;\n surface.setView(viewForAim(aim.extent, surface.dims(), 'exact'));\n return true;\n },\n ...createViewMarkerRegistry(markersRef.current, {\n host: { aimOf: (id) => registry.aimOf(id), setAim: (id, aim) => registry.setAim(id, aim) },\n surface: () => surfaceRef.current,\n resolve: (id, outer, dims) => get(id)?.layer.resolvable(outer, dims),\n changed,\n }),\n getVersion: () => versionRef.current,\n subscribe: listeners.subscribe,\n surface: () => surfaceRef.current,\n attachSurface: (handle) => {\n surfaceRef.current = handle;\n return () => { if (surfaceRef.current === handle) surfaceRef.current = null; };\n },\n };\n return registry;\n }, []);\n\n return <ViewRegistryContext.Provider value={registry}>{children}</ViewRegistryContext.Provider>;\n}\n\n/** The surface's view registry, or `null` outside one. */\nexport function useOptionalViewRegistry(): ViewRegistry | null {\n return useContext(ViewRegistryContext);\n}\n\n/** Each view followed by the views nested in it, in the order given. A view\n * whose parent is not registered keeps its place; one caught in a cycle\n * goes last. */\nfunction nestedOrder(sorted: readonly ViewRegistration[]): ViewRegistration[] {\n const ids = new Set(sorted.map((r) => r.id));\n const out: ViewRegistration[] = [];\n const placed = new Set<ViewRegistration>();\n const place = (r: ViewRegistration): void => {\n if (placed.has(r)) return;\n placed.add(r);\n out.push(r);\n for (const child of sorted) if (child.parent === r.id) place(child);\n };\n for (const r of sorted) if (r.parent === undefined || !ids.has(r.parent)) place(r);\n for (const r of sorted) place(r);\n return out;\n}\n\n/** The `Dims` a surface reports before it has measured itself. */\nexport const UNMEASURED_DIMS: Dims = { width: 0, height: 0 };\n\n/** The camera a view resolves against before its surface attaches. */\nexport const IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAwEA,SAAgB,aACd,SACA,SACA,SAC0B;CAC1B,IAAI,CAAC,SAAS,OAAO;EAAE,GAAG;EAAS,GAAG;CAAQ;CAC9C,MAAM,CAAC,QAAQ,UAAU,QAAQ,cAAc,SAAS,OAAO;CAC/D,MAAM,OAAO,QAAQ,KAAK;CAC1B,OAAO;EAAE,IAAI,SAAS,KAAK,KAAK,KAAK,MAAM;EAAG,IAAI,SAAS,KAAK,KAAK,KAAK,MAAM;CAAE;AACpF;AA8EA,IAAM,sBAAsB,cAAmC,IAAI;;;AAInE,SAAgB,qBAAqB,EAAE,YAAqC;CAC1E,MAAM,aAAa,uBAAO,IAAI,IAA8B,CAAC;CAC7D,MAAM,SAAS,OAAO,CAAC;CACvB,MAAM,aAAa,OAAO,CAAC;CAC3B,MAAM,aAAa,OAA6B,IAAI;CACpD,MAAM,aAAa,uBAAO,IAAI,IAAiD,CAAC;CAEhF,MAAM,WAAW,cAA4B;EAC3C,MAAM,YAAY,cAAc;EAChC,MAAM,gBAAsB;GAC1B,WAAW;GACX,UAAU,KAAK;EACjB;EACA,MAAM,gBAAoC,YAAY,CAAC,GAAG,WAAW,QAAQ,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MAC5F,EAAE,QAAQ,EAAE,SAAS,WAAW,QAAQ,IAAI,CAAC,IAAK,WAAW,QAAQ,IAAI,CAAC,CAC3E,CAAC;EACF,MAAM,OAAO,OAA6C;GACxD,KAAK,MAAM,KAAK,WAAW,QAAQ,KAAK,GAAG,IAAI,EAAE,OAAO,IAAI,OAAO;EAErE;;EAEA,MAAM,UAAU,GAAqB,OAAwB;GAC3D,MAAM,uBAAO,IAAI,IAAsB;GACvC,KAAK,IAAI,KAAmC,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,GAAG,KAAK,GAAG,WAAW,SAAY,SAAY,IAAI,GAAG,MAAM,GAAG;IAC7H,IAAI,GAAG,OAAO,IAAI,OAAO;IACzB,KAAK,IAAI,EAAE;GACb;GACA,OAAO;EACT;EAYA,MAAM,WAAyB;GAC7B,UAZe,mBAAmB;IAClC,aAAa;KACX,MAAM,UAAU,WAAW;KAC3B,MAAM,OAAO,SAAS,KAAK,KAAK;KAChC,MAAM,OAAO,SAAS,KAAK,KAAK;KAChC,OAAO,QAAQ,CAAC,CAAC,QAAQ,MAAM,EAAE,WAAW,CAAC,CAAC,KAAK,MAAM,EAAE,MAAM,WAAW,MAAM,IAAI,CAAC;IACzF;IACA,YAAY,WAAW,SAAS,KAAK,KAAK;IAC1C,UAAU,SAAS,YAAY,aAAa,WAAW,SAAS,SAAS,OAAO;IAChF,WAAW,GAAG,MAAM,WAAW,SAAS,YAAY,GAAG,CAAC,KAAK;GAC/D,CAEE;GACA,WAAW,QAAQ;IACjB,WAAW,QAAQ,IAAI,KAAK,OAAO,SAAS;IAC5C,QAAQ;IACR,aAAa;KAAE,WAAW,QAAQ,OAAO,GAAG;KAAG,QAAQ;IAAG;GAC5D;GACA,MAAM;GACN;GACA,SAAS,EAAE,GAAG,KAAK,SAAS;IAC1B,MAAM,UAAU,WAAW;IAC3B,MAAM,OAAO,SAAS,KAAK,KAAK;IAChC,MAAM,OAAO,SAAS,KAAK,KAAK;IAChC,MAAM,SAAS,MAAM;IACrB,MAAM,OAAO,QAAQ;IACrB,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;KACzC,MAAM,IAAI,KAAK;KACf,IAAI,WAAW,UAAa,OAAO,GAAG,MAAM,GAAG;KAC/C,MAAM,IAAI,EAAE,MAAM,WAAW,MAAM,IAAI;KACvC,IAAI,aAAa,GAAG,GAAG,CAAC,GAAG,OAAO;MAAE,IAAI,EAAE;MAAI,MAAM,EAAE;MAAM,MAAM,EAAE;KAAK;IAC3E;IACA,OAAO;KAAE,IAAI;KAAM;KAAM,MAAM;MAAE,GAAG;MAAG,GAAG;MAAG,GAAG,KAAK;MAAO,GAAG,KAAK;KAAO;IAAE;GAC/E;GACA,QAAQ,OAAO;IACb,IAAI,OAAO,MAAM,OAAO,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK;IAC1C,MAAM,UAAU,WAAW;IAC3B,OAAO,UAAU,QAAQ,MAAM,kBAAkB,QAAQ,KAAK,GAAG,QAAQ,KAAK,CAAC,CAAC,IAAI;GACtF;GACA,SAAS,IAAI,QAAQ;IACnB,IAAI,OAAO,MAAM,OAAO,IAAI,EAAE,CAAC,EAAE,OAAO,GAAG,KAAK;IAChD,MAAM,UAAU,WAAW;IAC3B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,SAAS,OAAO;IACjD,QAAQ,QAAQ,WAAW,IAAI,QAAQ,QAAQ,KAAK,GAAG,OAAO,CAAC;IAC/D,OAAO;GACT;GACA,GAAG,yBAAyB,WAAW,SAAS;IAC9C,MAAM;KAAE,QAAQ,OAAO,SAAS,MAAM,EAAE;KAAG,SAAS,IAAI,QAAQ,SAAS,OAAO,IAAI,GAAG;IAAE;IACzF,eAAe,WAAW;IAC1B,UAAU,IAAI,OAAO,SAAS,IAAI,EAAE,CAAC,EAAE,MAAM,WAAW,OAAO,IAAI;IACnE;GACF,CAAC;GACD,kBAAkB,WAAW;GAC7B,WAAW,UAAU;GACrB,eAAe,WAAW;GAC1B,gBAAgB,WAAW;IACzB,WAAW,UAAU;IACrB,aAAa;KAAE,IAAI,WAAW,YAAY,QAAQ,WAAW,UAAU;IAAM;GAC/E;EACF;EACA,OAAO;CACT,GAAG,CAAC,CAAC;CAEL,OAAO,oBAAC,oBAAoB,UAArB;EAA8B,OAAO;EAAW;CAAuC,CAAA;AAChG;;AAGA,SAAgB,0BAA+C;CAC7D,OAAO,WAAW,mBAAmB;AACvC;;;;AAKA,SAAS,YAAY,QAAyD;CAC5E,MAAM,MAAM,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,EAAE,CAAC;CAC3C,MAAM,MAA0B,CAAC;CACjC,MAAM,yBAAS,IAAI,IAAsB;CACzC,MAAM,SAAS,MAA8B;EAC3C,IAAI,OAAO,IAAI,CAAC,GAAG;EACnB,OAAO,IAAI,CAAC;EACZ,IAAI,KAAK,CAAC;EACV,KAAK,MAAM,SAAS,QAAQ,IAAI,MAAM,WAAW,EAAE,IAAI,MAAM,KAAK;CACpE;CACA,KAAK,MAAM,KAAK,QAAQ,IAAI,EAAE,WAAW,UAAa,CAAC,IAAI,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACjF,KAAK,MAAM,KAAK,QAAQ,MAAM,CAAC;CAC/B,OAAO;AACT;;AAGA,IAAa,kBAAwB;CAAE,OAAO;CAAG,QAAQ;AAAE;;AAG3D,IAAa,gBAAsB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { sameView } from "../core/viewport/view.js";
|
|
2
|
+
|
|
3
|
+
//#region src/canvas/viewSnap.ts
|
|
4
|
+
/** A move within this of a whole device pixel is one: the renderer's own slack. */
|
|
5
|
+
var SLACK = 1e-4;
|
|
6
|
+
/**
|
|
7
|
+
* The view a canvas draws each frame under. While the view moves, its
|
|
8
|
+
* translation is rounded to whole device pixels from where the view last
|
|
9
|
+
* held, so a retained layer's surface moves whole pixels and is composited
|
|
10
|
+
* rather than drawn; once the view holds for a frame, it draws exactly.
|
|
11
|
+
* Hit-testing and every other reader of the view keep the exact one.
|
|
12
|
+
*
|
|
13
|
+
* The rounding is from where the view last held rather than from zero
|
|
14
|
+
* because that is where retained surfaces are drawn.
|
|
15
|
+
*/
|
|
16
|
+
var ViewSnap = class {
|
|
17
|
+
last;
|
|
18
|
+
/** The translation, CSS pixels, of the frame last drawn holding. */
|
|
19
|
+
grid;
|
|
20
|
+
snapped = false;
|
|
21
|
+
moved = false;
|
|
22
|
+
/** The view to draw `view` under, at `scale` device pixels to the CSS
|
|
23
|
+
* pixel. `snap` false draws every view exactly. */
|
|
24
|
+
draw(view, scale, snap) {
|
|
25
|
+
const moving = this.last !== void 0 && !sameView(this.last, view);
|
|
26
|
+
this.last = view;
|
|
27
|
+
const t = translationOf(view);
|
|
28
|
+
const grid = this.grid;
|
|
29
|
+
if (!snap || !moving || grid === void 0) {
|
|
30
|
+
this.moved = snap && grid !== void 0 && !(whole((t[0] - grid[0]) * scale) && whole((t[1] - grid[1]) * scale));
|
|
31
|
+
this.grid = t;
|
|
32
|
+
this.snapped = false;
|
|
33
|
+
return view;
|
|
34
|
+
}
|
|
35
|
+
const x = grid[0] + Math.round((t[0] - grid[0]) * scale) / scale;
|
|
36
|
+
const y = grid[1] + Math.round((t[1] - grid[1]) * scale) / scale;
|
|
37
|
+
this.moved = false;
|
|
38
|
+
this.snapped = x !== t[0] || y !== t[1];
|
|
39
|
+
if (!this.snapped) return view;
|
|
40
|
+
return {
|
|
41
|
+
x: -x / view.scale.x,
|
|
42
|
+
y: -y / view.scale.y,
|
|
43
|
+
scale: view.scale
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether the frame just drawn owes the canvas another: a snapped one, so
|
|
48
|
+
* the view is drawn exactly once it holds; and the frame it holds on, when
|
|
49
|
+
* that drew a retained layer straight through for being off the pixel grid
|
|
50
|
+
* its surface was drawn on, so the next frame draws the surface again on
|
|
51
|
+
* the grid the next move rounds to. `declined` is the renderer's count of
|
|
52
|
+
* retained layers it drew straight through.
|
|
53
|
+
*/
|
|
54
|
+
owesFrame(declined) {
|
|
55
|
+
return this.snapped || this.moved && declined > 0;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function translationOf(view) {
|
|
59
|
+
return [-view.x * view.scale.x, -view.y * view.scale.y];
|
|
60
|
+
}
|
|
61
|
+
function whole(n) {
|
|
62
|
+
return Math.abs(n - Math.round(n)) <= SLACK;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { ViewSnap };
|
|
67
|
+
//# sourceMappingURL=viewSnap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewSnap.js","names":[],"sources":["../../src/canvas/viewSnap.ts"],"sourcesContent":["import { sameView, type View } from 'core/viewport/view';\n\n/** A move within this of a whole device pixel is one: the renderer's own slack. */\nconst SLACK = 1e-4;\n\n/**\n * The view a canvas draws each frame under. While the view moves, its\n * translation is rounded to whole device pixels from where the view last\n * held, so a retained layer's surface moves whole pixels and is composited\n * rather than drawn; once the view holds for a frame, it draws exactly.\n * Hit-testing and every other reader of the view keep the exact one.\n *\n * The rounding is from where the view last held rather than from zero\n * because that is where retained surfaces are drawn.\n */\nexport class ViewSnap {\n private last: View | undefined;\n /** The translation, CSS pixels, of the frame last drawn holding. */\n private grid: readonly [number, number] | undefined;\n private snapped = false;\n private moved = false;\n\n /** The view to draw `view` under, at `scale` device pixels to the CSS\n * pixel. `snap` false draws every view exactly. */\n draw(view: View, scale: number, snap: boolean): View {\n const moving = this.last !== undefined && !sameView(this.last, view);\n this.last = view;\n const t = translationOf(view);\n const grid = this.grid;\n if (!snap || !moving || grid === undefined) {\n this.moved = snap && grid !== undefined && !(whole((t[0] - grid[0]) * scale) && whole((t[1] - grid[1]) * scale));\n this.grid = t;\n this.snapped = false;\n return view;\n }\n const x = grid[0] + Math.round((t[0] - grid[0]) * scale) / scale;\n const y = grid[1] + Math.round((t[1] - grid[1]) * scale) / scale;\n this.moved = false;\n this.snapped = x !== t[0] || y !== t[1];\n if (!this.snapped) return view;\n return { x: -x / view.scale.x, y: -y / view.scale.y, scale: view.scale };\n }\n\n /**\n * Whether the frame just drawn owes the canvas another: a snapped one, so\n * the view is drawn exactly once it holds; and the frame it holds on, when\n * that drew a retained layer straight through for being off the pixel grid\n * its surface was drawn on, so the next frame draws the surface again on\n * the grid the next move rounds to. `declined` is the renderer's count of\n * retained layers it drew straight through.\n */\n owesFrame(declined: number): boolean {\n return this.snapped || (this.moved && declined > 0);\n }\n}\n\nfunction translationOf(view: View): readonly [number, number] {\n return [-view.x * view.scale.x, -view.y * view.scale.y];\n}\n\nfunction whole(n: number): boolean {\n return Math.abs(n - Math.round(n)) <= SLACK;\n}\n"],"mappings":";;;;AAGA,IAAM,QAAQ;;;;;;;;;;;AAYd,IAAa,WAAb,MAAsB;CACpB;;CAEA;CACA,UAAkB;CAClB,QAAgB;;;CAIhB,KAAK,MAAY,OAAe,MAAqB;EACnD,MAAM,SAAS,KAAK,SAAS,UAAa,CAAC,SAAS,KAAK,MAAM,IAAI;EACnE,KAAK,OAAO;EACZ,MAAM,IAAI,cAAc,IAAI;EAC5B,MAAM,OAAO,KAAK;EAClB,IAAI,CAAC,QAAQ,CAAC,UAAU,SAAS,QAAW;GAC1C,KAAK,QAAQ,QAAQ,SAAS,UAAa,EAAE,OAAO,EAAE,KAAK,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,KAAK,KAAK,MAAM,KAAK;GAC9G,KAAK,OAAO;GACZ,KAAK,UAAU;GACf,OAAO;EACT;EACA,MAAM,IAAI,KAAK,KAAK,KAAK,OAAO,EAAE,KAAK,KAAK,MAAM,KAAK,IAAI;EAC3D,MAAM,IAAI,KAAK,KAAK,KAAK,OAAO,EAAE,KAAK,KAAK,MAAM,KAAK,IAAI;EAC3D,KAAK,QAAQ;EACb,KAAK,UAAU,MAAM,EAAE,MAAM,MAAM,EAAE;EACrC,IAAI,CAAC,KAAK,SAAS,OAAO;EAC1B,OAAO;GAAE,GAAG,CAAC,IAAI,KAAK,MAAM;GAAG,GAAG,CAAC,IAAI,KAAK,MAAM;GAAG,OAAO,KAAK;EAAM;CACzE;;;;;;;;;CAUA,UAAU,UAA2B;EACnC,OAAO,KAAK,WAAY,KAAK,SAAS,WAAW;CACnD;AACF;AAEA,SAAS,cAAc,MAAuC;CAC5D,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,MAAM,GAAG,CAAC,KAAK,IAAI,KAAK,MAAM,CAAC;AACxD;AAEA,SAAS,MAAM,GAAoB;CACjC,OAAO,KAAK,IAAI,IAAI,KAAK,MAAM,CAAC,CAAC,KAAK;AACxC"}
|
package/dist/clipboard.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { NodeId } from '@weasel-js/routing';
|
|
2
|
-
import { P as PasteOmission, C as ClipboardSnapshot, I as InsertAdapter } from './types-
|
|
3
|
-
import './types-
|
|
1
|
+
import { NodeId, WorldPoint } from '@weasel-js/routing';
|
|
2
|
+
import { P as PasteOmission, C as ClipboardSnapshot, I as InsertAdapter } from './types-VCtfchp-.js';
|
|
3
|
+
import './types-A_sNJYRd.js';
|
|
4
4
|
import '@weasel-js/geom';
|
|
5
5
|
import '@weasel-js/history';
|
|
6
|
-
import './poseDescriptor-
|
|
6
|
+
import './poseDescriptor-CvSxVhCd.js';
|
|
7
7
|
import '@weasel-js/animator';
|
|
8
8
|
|
|
9
9
|
type Replacer$1 = (key: string, value: unknown) => unknown;
|
|
@@ -24,10 +24,7 @@ interface UseClipboardOpsOptions {
|
|
|
24
24
|
* as `ctx.dropPoint`; adapters typically use it as the cluster origin
|
|
25
25
|
* (ignoring `offset`). When null/undefined, the hook falls back to the
|
|
26
26
|
* existing cascade-offset behavior. */
|
|
27
|
-
getDropPoint?: () =>
|
|
28
|
-
worldX: number;
|
|
29
|
-
worldY: number;
|
|
30
|
-
} | null;
|
|
27
|
+
getDropPoint?: () => WorldPoint | null;
|
|
31
28
|
/** Produce the OS-clipboard flavor map for a copied snapshot. Keys are MIME
|
|
32
29
|
* types, values the serialized payload. The kit default emits
|
|
33
30
|
* `application/x-weasel-clipboard+json` plus `text/plain` carrying the
|
|
@@ -48,10 +45,7 @@ type ClipboardFlavors = Record<string, string | Promise<string>>;
|
|
|
48
45
|
interface UseClipboardOpsReturn {
|
|
49
46
|
copy(): void;
|
|
50
47
|
/** Paste at `at` when given; otherwise at the drop point, else the cascade offset. */
|
|
51
|
-
paste(at?:
|
|
52
|
-
worldX: number;
|
|
53
|
-
worldY: number;
|
|
54
|
-
}): void;
|
|
48
|
+
paste(at?: WorldPoint): void;
|
|
55
49
|
isEmpty(): boolean;
|
|
56
50
|
}
|
|
57
51
|
/** In-memory copy/paste of selections via `InsertAdapter.snapshotSelection` / `commitPaste`. */
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { a as PlaneMap } from './types-
|
|
1
|
+
import { a as PlaneMap } from './types-A_sNJYRd.js';
|
|
2
2
|
import { Op } from '@weasel-js/history';
|
|
3
|
-
import { S as SnapTarget, M as MoveAdapter } from './types-
|
|
3
|
+
import { S as SnapTarget, M as MoveAdapter } from './types-VCtfchp-.js';
|
|
4
4
|
import { FramedBox, View } from '@weasel-js/geom';
|
|
5
5
|
import { KitSettingsStore } from '@weasel-js/react';
|
|
6
|
-
import { ModifierState, ResizeAnchor } from '@weasel-js/routing';
|
|
6
|
+
import { ModifierState, ResizeAnchor, OngoingOverlay } from '@weasel-js/routing';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* What a behavior needs to take a pose between the frame a node stores it in
|
|
10
10
|
* and world. A node under a container whose pose is a frame stores its pose
|
|
11
11
|
* in that frame, so a stored pose and a world pose are different values
|
|
12
|
-
* there; under
|
|
13
|
-
* an identity.
|
|
12
|
+
* there; under `IDENTITY_POSE_COMPOSITION` they are the same and every
|
|
13
|
+
* method is an identity.
|
|
14
14
|
*/
|
|
15
15
|
interface GestureFrames<TPose> {
|
|
16
16
|
/** `id`'s pose in world, as the scene shows it now: the gesture's preview
|
|
@@ -20,6 +20,9 @@ interface GestureFrames<TPose> {
|
|
|
20
20
|
toWorld(id: string, pose: TPose): TPose;
|
|
21
21
|
/** `worldPose` as `id` stores it: what a pose op or a preview takes. */
|
|
22
22
|
toLocal(id: string, worldPose: TPose): TPose;
|
|
23
|
+
/** `worldPose` as a child of `parentId` stores it, `null` for the top of
|
|
24
|
+
* the tree: what a node reparented there is posed with. */
|
|
25
|
+
toLocalUnder(parentId: string | null, worldPose: TPose): TPose;
|
|
23
26
|
/** How the world the gesture edits in maps into the world `layer`'s nodes
|
|
24
27
|
* are stored in, read now. Null when they are the same world. */
|
|
25
28
|
planeOf(layer: string): PlaneMap | null;
|
|
@@ -172,6 +175,8 @@ interface BehaviorResult<TPose> {
|
|
|
172
175
|
* ghost, added to those of the other behaviors. */
|
|
173
176
|
opaque?: Iterable<string>;
|
|
174
177
|
snap?: SnapTarget<TPose> | null;
|
|
178
|
+
/** Chrome to paint beside the drag this frame, added to the other behaviors'. */
|
|
179
|
+
overlays?: readonly OngoingOverlay[];
|
|
175
180
|
}
|
|
176
181
|
/** What a move behavior's `onEnd` is told. */
|
|
177
182
|
interface MoveEnd extends BehaviorEnd {
|
package/dist/clone.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { C as CloneBehavior, d as CloneBehaviorResult } from './clone-
|
|
2
|
-
import './types-
|
|
1
|
+
export { C as CloneBehavior, d as CloneBehaviorResult } from './clone-Bz4iqGfE.js';
|
|
2
|
+
import './types-A_sNJYRd.js';
|
|
3
3
|
import '@weasel-js/geom';
|
|
4
4
|
import '@weasel-js/history';
|
|
5
|
-
import './types-
|
|
6
|
-
import './poseDescriptor-
|
|
5
|
+
import './types-VCtfchp-.js';
|
|
6
|
+
import './poseDescriptor-CvSxVhCd.js';
|
|
7
7
|
import '@weasel-js/animator';
|
|
8
|
-
import '@weasel-js/react';
|
|
9
8
|
import '@weasel-js/routing';
|
|
9
|
+
import '@weasel-js/react';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { pathExclude, pathIntersect, pathSubtract, pathUnion } from "@weasel-js/geom/booleans";
|
|
2
|
+
|
|
3
|
+
//#region src/core/geometry/booleanOfPaths.ts
|
|
4
|
+
/**
|
|
5
|
+
* `op` over `paths`, given back to front, as one path. Subtract is
|
|
6
|
+
* Illustrator's "Minus Front": the backmost less everything in front of it.
|
|
7
|
+
* Curves are kept where an operand has them.
|
|
8
|
+
*/
|
|
9
|
+
function booleanOfPaths(op, paths) {
|
|
10
|
+
switch (op) {
|
|
11
|
+
case "union": return pathUnion(...paths);
|
|
12
|
+
case "intersect": return pathIntersect(...paths);
|
|
13
|
+
case "exclude": return pathExclude(...paths);
|
|
14
|
+
case "subtract": {
|
|
15
|
+
const front = paths.length === 2 ? paths[1] : pathUnion(...paths.slice(1));
|
|
16
|
+
return pathSubtract(paths[0], front);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { booleanOfPaths };
|
|
23
|
+
//# sourceMappingURL=booleanOfPaths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booleanOfPaths.js","names":[],"sources":["../../../src/core/geometry/booleanOfPaths.ts"],"sourcesContent":["import { pathUnion, pathIntersect, pathSubtract, pathExclude } from '@weasel-js/geom/booleans';\nimport type { Path, PolygonPath } from '@weasel-js/geom';\n\n/** The booleans that make one shape of their operands: the Pathfinder's four\n * shape modes, and what a boolean group can be set to. */\nexport type BooleanShapeOp = 'union' | 'intersect' | 'subtract' | 'exclude';\n\n/**\n * `op` over `paths`, given back to front, as one path. Subtract is\n * Illustrator's \"Minus Front\": the backmost less everything in front of it.\n * Curves are kept where an operand has them.\n */\nexport function booleanOfPaths(op: BooleanShapeOp, paths: readonly Path[]): PolygonPath {\n switch (op) {\n case 'union': return pathUnion(...paths);\n case 'intersect': return pathIntersect(...paths);\n case 'exclude': return pathExclude(...paths);\n case 'subtract': {\n const front = paths.length === 2 ? paths[1]! : pathUnion(...paths.slice(1));\n return pathSubtract(paths[0]!, front);\n }\n }\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,eAAe,IAAoB,OAAqC;CACtF,QAAQ,IAAR;EACE,KAAK,SAAS,OAAO,UAAU,GAAG,KAAK;EACvC,KAAK,aAAa,OAAO,cAAc,GAAG,KAAK;EAC/C,KAAK,WAAW,OAAO,YAAY,GAAG,KAAK;EAC3C,KAAK,YAAY;GACf,MAAM,QAAQ,MAAM,WAAW,IAAI,MAAM,KAAM,UAAU,GAAG,MAAM,MAAM,CAAC,CAAC;GAC1E,OAAO,aAAa,MAAM,IAAK,KAAK;EACtC;CACF;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { boundsOfPath, boxToBox, lerpCorners, transformPath, translatePath } from "@weasel-js/geom";
|
|
1
|
+
import { boundsOfPath, boxToBox, coordsFrom, lerpCorners, transformPath, translatePath, worldCoords } from "@weasel-js/geom";
|
|
2
2
|
|
|
3
3
|
//#region src/core/geometry/pathPoseDescriptor.ts
|
|
4
4
|
/**
|
|
@@ -31,8 +31,10 @@ var pathPoseDescriptor = {
|
|
|
31
31
|
height: a.height + (b.height - a.height) * t
|
|
32
32
|
};
|
|
33
33
|
if (a.kind === "polygon" && b.kind === "polygon" && a.coords.length === b.coords.length) {
|
|
34
|
-
const
|
|
35
|
-
|
|
34
|
+
const aw = worldCoords(a), bw = worldCoords(b);
|
|
35
|
+
const mid = new Float64Array(aw.length);
|
|
36
|
+
for (let i = 0; i < aw.length; i++) mid[i] = aw[i] + (bw[i] - aw[i]) * t;
|
|
37
|
+
const next = coordsFrom(a.origin, mid);
|
|
36
38
|
const { corners: _a, ...bare } = a;
|
|
37
39
|
const corners = lerpCorners(a.corners, b.corners, t);
|
|
38
40
|
return corners ? {
|
|
@@ -85,23 +87,24 @@ var pathPoseDescriptor = {
|
|
|
85
87
|
};
|
|
86
88
|
return {
|
|
87
89
|
count: like.coords.length,
|
|
88
|
-
to: (p) => p.kind === "polygon" ? Array.from(p
|
|
90
|
+
to: (p) => p.kind === "polygon" ? Array.from(worldCoords(p)) : [],
|
|
89
91
|
from: (a) => ({
|
|
90
92
|
...like,
|
|
91
|
-
coords:
|
|
93
|
+
coords: coordsFrom(like.origin, a)
|
|
92
94
|
}),
|
|
93
95
|
add: (p, d) => {
|
|
94
96
|
if (p.kind !== "polygon") return p;
|
|
95
|
-
const
|
|
96
|
-
for (let i = 0; i <
|
|
97
|
+
const moved = Float64Array.from(worldCoords(p));
|
|
98
|
+
for (let i = 0; i < moved.length; i++) moved[i] += d[i];
|
|
97
99
|
return {
|
|
98
100
|
...like,
|
|
99
|
-
coords
|
|
101
|
+
coords: coordsFrom(like.origin, moved)
|
|
100
102
|
};
|
|
101
103
|
},
|
|
102
104
|
write: (p, out, at) => {
|
|
103
105
|
if (p.kind !== "polygon") return;
|
|
104
|
-
|
|
106
|
+
const world = worldCoords(p);
|
|
107
|
+
for (let i = 0; i < world.length; i++) out[at + i] = world[i];
|
|
105
108
|
}
|
|
106
109
|
};
|
|
107
110
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathPoseDescriptor.js","names":[],"sources":["../../../src/core/geometry/pathPoseDescriptor.ts"],"sourcesContent":["import {\n boxToBox,\n boundsOfPath,\n lerpCorners,\n translatePath,\n transformPath,\n type Path,\n} from '@weasel-js/geom';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\n\n/**\n * `PoseDescriptor` for `Path` poses — wires `useResize` to operate\n * on `Path` directly. `getBounds` defers to the same `boundsOfPath` kernel\n * the rest of the kit uses.\n *\n * `remapBounds` mirrors `scalePathToBounds` but takes `src` explicitly: the\n * resize hook knows the group's origin AABB and uses it for every leaf,\n * instead of each leaf scaling against its own AABB (which would ignore\n * group context).\n */\nexport const pathPoseDescriptor: PoseDescriptor<Path> = {\n getBounds: (path) => boundsOfPath(path),\n remapBounds: (path, src, dst) => transformPath(\n path,\n boxToBox(src.x, src.y, src.width, src.height, dst.x, dst.y, dst.width, dst.height),\n ),\n fromBounds: (b) => ({ kind: 'rect', x: b.x, y: b.y, width: b.width, height: b.height }),\n translate: (path, dx, dy) => translatePath(path, dx, dy),\n lerp: (a, b, t) => {\n if (a.kind === 'rect' && b.kind === 'rect') {\n return {\n kind: 'rect',\n x: a.x + (b.x - a.x) * t,\n y: a.y + (b.y - a.y) * t,\n width: a.width + (b.width - a.width) * t,\n height: a.height + (b.height - a.height) * t,\n };\n }\n if (a.kind === 'polygon' && b.kind === 'polygon' && a.coords.length === b.coords.length) {\n const
|
|
1
|
+
{"version":3,"file":"pathPoseDescriptor.js","names":[],"sources":["../../../src/core/geometry/pathPoseDescriptor.ts"],"sourcesContent":["import {\n boxToBox,\n boundsOfPath,\n coordsFrom,\n lerpCorners,\n translatePath,\n transformPath,\n worldCoords,\n type Path,\n} from '@weasel-js/geom';\nimport type { PoseDescriptor } from 'core/geometry/poseDescriptor';\n\n/**\n * `PoseDescriptor` for `Path` poses — wires `useResize` to operate\n * on `Path` directly. `getBounds` defers to the same `boundsOfPath` kernel\n * the rest of the kit uses.\n *\n * `remapBounds` mirrors `scalePathToBounds` but takes `src` explicitly: the\n * resize hook knows the group's origin AABB and uses it for every leaf,\n * instead of each leaf scaling against its own AABB (which would ignore\n * group context).\n */\nexport const pathPoseDescriptor: PoseDescriptor<Path> = {\n getBounds: (path) => boundsOfPath(path),\n remapBounds: (path, src, dst) => transformPath(\n path,\n boxToBox(src.x, src.y, src.width, src.height, dst.x, dst.y, dst.width, dst.height),\n ),\n fromBounds: (b) => ({ kind: 'rect', x: b.x, y: b.y, width: b.width, height: b.height }),\n translate: (path, dx, dy) => translatePath(path, dx, dy),\n lerp: (a, b, t) => {\n if (a.kind === 'rect' && b.kind === 'rect') {\n return {\n kind: 'rect',\n x: a.x + (b.x - a.x) * t,\n y: a.y + (b.y - a.y) * t,\n width: a.width + (b.width - a.width) * t,\n height: a.height + (b.height - a.height) * t,\n };\n }\n if (a.kind === 'polygon' && b.kind === 'polygon' && a.coords.length === b.coords.length) {\n const aw = worldCoords(a), bw = worldCoords(b);\n const mid = new Float64Array(aw.length);\n for (let i = 0; i < aw.length; i++) mid[i] = aw[i] + (bw[i] - aw[i]) * t;\n const next = coordsFrom(a.origin, mid);\n const { corners: _a, ...bare } = a;\n const corners = lerpCorners(a.corners, b.corners, t);\n return corners ? { ...bare, coords: next, corners } : { ...bare, coords: next };\n }\n throw new Error('pathPoseDescriptor.lerp: incompatible path shapes');\n },\n // A polygon's axes are its coordinates, so a move needs both ends to have as many points.\n axes: (like) => {\n if (like.kind === 'rect') {\n return {\n count: 4,\n names: ['x', 'y', 'width', 'height'],\n to: (p) => (p.kind === 'rect' ? [p.x, p.y, p.width, p.height] : []),\n from: (a) => ({ kind: 'rect', x: a[0]!, y: a[1]!, width: a[2]!, height: a[3]! }),\n add: (p, d) => (p.kind !== 'rect' ? p : {\n kind: 'rect', x: p.x + d[0]!, y: p.y + d[1]!, width: p.width + d[2]!, height: p.height + d[3]!,\n }),\n write: (p, out, at) => {\n if (p.kind !== 'rect') return;\n out[at] = p.x;\n out[at + 1] = p.y;\n out[at + 2] = p.width;\n out[at + 3] = p.height;\n },\n };\n }\n return {\n count: like.coords.length,\n to: (p) => (p.kind === 'polygon' ? Array.from(worldCoords(p)) : []),\n from: (a) => ({ ...like, coords: coordsFrom(like.origin, a as ArrayLike<number>) }),\n add: (p, d) => {\n if (p.kind !== 'polygon') return p;\n const moved = Float64Array.from(worldCoords(p));\n for (let i = 0; i < moved.length; i++) moved[i]! += d[i]!;\n return { ...like, coords: coordsFrom(like.origin, moved) };\n },\n write: (p, out, at) => {\n if (p.kind !== 'polygon') return;\n const world = worldCoords(p);\n for (let i = 0; i < world.length; i++) out[at + i] = world[i]!;\n },\n };\n },\n // Path poses carry no x/y/width/height/rotation fields — the kit's\n // `wrapWithPoseRotation` helper has nothing to bind a rotation to, and\n // `remapBounds` would drop a synthetic rotation field on every commit.\n // Returning false here hides the rotation affordance for Path consumers\n // so the rotate cursor doesn't appear without a working interaction.\n supportsRotation: () => false,\n};\n"],"mappings":";;;;;;;;;;;;;AAsBA,IAAa,qBAA2C;CACtD,YAAY,SAAS,aAAa,IAAI;CACtC,cAAc,MAAM,KAAK,QAAQ,cAC/B,MACA,SAAS,IAAI,GAAG,IAAI,GAAG,IAAI,OAAO,IAAI,QAAQ,IAAI,GAAG,IAAI,GAAG,IAAI,OAAO,IAAI,MAAM,CACnF;CACA,aAAa,OAAO;EAAE,MAAM;EAAQ,GAAG,EAAE;EAAG,GAAG,EAAE;EAAG,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;CACrF,YAAY,MAAM,IAAI,OAAO,cAAc,MAAM,IAAI,EAAE;CACvD,OAAO,GAAG,GAAG,MAAM;EACjB,IAAI,EAAE,SAAS,UAAU,EAAE,SAAS,QAClC,OAAO;GACL,MAAM;GACN,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;GACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;GACvB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS;GACvC,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU;EAC7C;EAEF,IAAI,EAAE,SAAS,aAAa,EAAE,SAAS,aAAa,EAAE,OAAO,WAAW,EAAE,OAAO,QAAQ;GACvF,MAAM,KAAK,YAAY,CAAC,GAAG,KAAK,YAAY,CAAC;GAC7C,MAAM,MAAM,IAAI,aAAa,GAAG,MAAM;GACtC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM;GACvE,MAAM,OAAO,WAAW,EAAE,QAAQ,GAAG;GACrC,MAAM,EAAE,SAAS,IAAI,GAAG,SAAS;GACjC,MAAM,UAAU,YAAY,EAAE,SAAS,EAAE,SAAS,CAAC;GACnD,OAAO,UAAU;IAAE,GAAG;IAAM,QAAQ;IAAM;GAAQ,IAAI;IAAE,GAAG;IAAM,QAAQ;GAAK;EAChF;EACA,MAAM,IAAI,MAAM,mDAAmD;CACrE;CAEA,OAAO,SAAS;EACd,IAAI,KAAK,SAAS,QAChB,OAAO;GACL,OAAO;GACP,OAAO;IAAC;IAAK;IAAK;IAAS;GAAQ;GACnC,KAAK,MAAO,EAAE,SAAS,SAAS;IAAC,EAAE;IAAG,EAAE;IAAG,EAAE;IAAO,EAAE;GAAM,IAAI,CAAC;GACjE,OAAO,OAAO;IAAE,MAAM;IAAQ,GAAG,EAAE;IAAK,GAAG,EAAE;IAAK,OAAO,EAAE;IAAK,QAAQ,EAAE;GAAI;GAC9E,MAAM,GAAG,MAAO,EAAE,SAAS,SAAS,IAAI;IACtC,MAAM;IAAQ,GAAG,EAAE,IAAI,EAAE;IAAK,GAAG,EAAE,IAAI,EAAE;IAAK,OAAO,EAAE,QAAQ,EAAE;IAAK,QAAQ,EAAE,SAAS,EAAE;GAC7F;GACA,QAAQ,GAAG,KAAK,OAAO;IACrB,IAAI,EAAE,SAAS,QAAQ;IACvB,IAAI,MAAM,EAAE;IACZ,IAAI,KAAK,KAAK,EAAE;IAChB,IAAI,KAAK,KAAK,EAAE;IAChB,IAAI,KAAK,KAAK,EAAE;GAClB;EACF;EAEF,OAAO;GACL,OAAO,KAAK,OAAO;GACnB,KAAK,MAAO,EAAE,SAAS,YAAY,MAAM,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC;GACjE,OAAO,OAAO;IAAE,GAAG;IAAM,QAAQ,WAAW,KAAK,QAAQ,CAAsB;GAAE;GACjF,MAAM,GAAG,MAAM;IACb,IAAI,EAAE,SAAS,WAAW,OAAO;IACjC,MAAM,QAAQ,aAAa,KAAK,YAAY,CAAC,CAAC;IAC9C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,MAAM,MAAO,EAAE;IACtD,OAAO;KAAE,GAAG;KAAM,QAAQ,WAAW,KAAK,QAAQ,KAAK;IAAE;GAC3D;GACA,QAAQ,GAAG,KAAK,OAAO;IACrB,IAAI,EAAE,SAAS,WAAW;IAC1B,MAAM,QAAQ,YAAY,CAAC;IAC3B,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,MAAM;GAC7D;EACF;CACF;CAMA,wBAAwB;AAC1B"}
|
|
@@ -2,6 +2,15 @@ import { axisAlignedBounds } from "./unionBounds.js";
|
|
|
2
2
|
import { multiply, poseLinearOf, rotate } from "@weasel-js/geom";
|
|
3
3
|
|
|
4
4
|
//#region src/core/geometry/poseDescriptor.ts
|
|
5
|
+
/** A world `lead` expressed in the frame whose world pose is `frameWorld`, for a
|
|
6
|
+
* pose stored there; `null` is the root frame, where it is unchanged. */
|
|
7
|
+
function leadUnder(pc, frameWorld, lead) {
|
|
8
|
+
if (frameWorld === null) return lead;
|
|
9
|
+
return {
|
|
10
|
+
from: pc.decompose(frameWorld, lead.from),
|
|
11
|
+
to: pc.decompose(frameWorld, lead.to)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
5
14
|
/** The descriptor `node` should be read through: its own specialization when
|
|
6
15
|
* it declares one, and otherwise the descriptor unchanged. */
|
|
7
16
|
function poseDescriptorForNode(descriptor, node) {
|
|
@@ -214,8 +223,8 @@ function framedBounds(pose, geometry) {
|
|
|
214
223
|
}
|
|
215
224
|
/** Translate through `geometry.translate`, or via `remapBounds` onto a shifted
|
|
216
225
|
* copy of the pose's own bounds when the descriptor has none. */
|
|
217
|
-
function translatePoseViaDescriptor(pose, dx, dy, geometry) {
|
|
218
|
-
if (geometry.translate) return geometry.translate(pose, dx, dy);
|
|
226
|
+
function translatePoseViaDescriptor(pose, dx, dy, geometry, lead) {
|
|
227
|
+
if (geometry.translate) return lead ? geometry.translate(pose, dx, dy, lead) : geometry.translate(pose, dx, dy);
|
|
219
228
|
const src = geometry.getBounds(pose);
|
|
220
229
|
const dst = {
|
|
221
230
|
x: src.x + dx,
|
|
@@ -227,5 +236,5 @@ function translatePoseViaDescriptor(pose, dx, dy, geometry) {
|
|
|
227
236
|
}
|
|
228
237
|
|
|
229
238
|
//#endregion
|
|
230
|
-
export { RECT_POSE_DESCRIPTOR, ROTATED_POSE_DESCRIPTOR, framedBounds, poseDescriptorForNode, remapRotatedLeaf, translatePoseViaDescriptor, visualBoundsViaDescriptor };
|
|
239
|
+
export { RECT_POSE_DESCRIPTOR, ROTATED_POSE_DESCRIPTOR, framedBounds, leadUnder, poseDescriptorForNode, remapRotatedLeaf, translatePoseViaDescriptor, visualBoundsViaDescriptor };
|
|
231
240
|
//# sourceMappingURL=poseDescriptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poseDescriptor.js","names":[],"sources":["../../../src/core/geometry/poseDescriptor.ts"],"sourcesContent":["import type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { axisAlignedBounds } from 'core/geometry/unionBounds';\nimport { multiply, poseLinearOf, rotate } from '@weasel-js/geom';\nimport type { TargetAxes } from '@weasel-js/animator';\n\n/** `Bounds` with a required rotation angle (radians). Pivot is the AABB\n * center of the unrotated `{x, y, width, height}`. */\nexport interface RotatedPose extends Bounds {\n rotation: number;\n}\n\n/**\n * Bridges arbitrary `TPose` shapes into the kit's bounds-driven math: bounds\n * are read via `getBounds`, and `remapBounds` projects a result back into\n * TPose.\n *\n * `remapBounds(pose, src, dst)` subsumes both \"set my own AABB to dst\"\n * (single-leaf resize) and \"scale me as a leaf inside parent's src→dst rect\"\n * (group resize) — they're the same affine map.\n */\nexport interface PoseDescriptor<TPose, TNode = unknown> {\n getBounds(pose: TPose): Bounds;\n remapBounds(pose: TPose, src: Bounds, dst: Bounds): TPose;\n /** A pose occupying `bounds`, of the same kind as `template`. Unlike\n * `remapBounds`, the result carries none of the template's shape. */\n fromBounds(bounds: Bounds, template: TPose): TPose;\n /** Translate the pose by (dx, dy). Optional — when omitted, callers fall\n * back to a translation derived from `remapBounds` (origin shifted, no\n * scale). Path-shaped poses should provide this for performance. */\n translate?(pose: TPose, dx: number, dy: number): TPose;\n /** Interpolate between two poses. Optional — animation helpers fall back to\n * rect-shape lerp when omitted (which fails for non-rect poses). */\n lerp?(a: TPose, b: TPose, t: number): TPose;\n /** How a pose of `like`'s kind maps to the flat numbers an animation moves, and which of them\n * are angles. `from` keeps the fields of `like` that are not axes. Null for a pose this\n * descriptor cannot map; absent, a pose animates by its flat numeric fields. */\n axes?(like: TPose): TargetAxes<TPose> | null;\n /** Read the pose's rotation in radians. Pivot is the AABB center\n * (`getBounds(pose)` center). Default 0 when omitted. */\n getRotation?(pose: TPose): number;\n /** True iff this pose shape can carry a rotation; the rotation affordance\n * hides when it is `false`. When omitted, the kit assumes `true`, so a\n * descriptor whose poses cannot rotate (e.g. polygon Paths) should\n * return `false`. */\n supportsRotation?(pose: TPose): boolean;\n /** Write a rotation (radians) into the pose, bounds unchanged. Absent means\n * the pose cannot carry one, and the rotate action leaves it alone. */\n withRotation?(pose: TPose, rotation: number): TPose;\n /**\n * A descriptor specialized to one node. Every other method here sees only\n * the pose, so shapes sharing a pose type are indistinguishable to them.\n * Implement this to read `node.data` once and hand back a descriptor that\n * knows which shape it is describing. Callers reach it through\n * {@link poseDescriptorForNode}; sites with only an id keep using the\n * unspecialized descriptor.\n */\n forNode?(node: TNode): PoseDescriptor<TPose, TNode>;\n}\n\n/** The descriptor `node` should be read through: its own specialization when\n * it declares one, and otherwise the descriptor unchanged. */\nexport function poseDescriptorForNode<TPose, TNode>(\n descriptor: PoseDescriptor<TPose, TNode>,\n node: TNode,\n): PoseDescriptor<TPose, TNode> {\n return descriptor.forNode?.(node) ?? descriptor;\n}\n\n/** Identity geometry for `TPose extends Bounds`. Treats the pose as its\n * own bounds and remaps via affine scale against `src`/`dst`. */\nexport const RECT_POSE_DESCRIPTOR: PoseDescriptor<Bounds> = {\n getBounds: (p) => p,\n remapBounds: (p, src, dst) => {\n const sx = src.width === 0 ? 1 : dst.width / src.width;\n const sy = src.height === 0 ? 1 : dst.height / src.height;\n return {\n ...p,\n x: dst.x + (p.x - src.x) * sx,\n y: dst.y + (p.y - src.y) * sy,\n width: p.width * sx,\n height: p.height * sy,\n };\n },\n fromBounds: (b) => ({ x: b.x, y: b.y, width: b.width, height: b.height }),\n translate: (p, dx, dy) => ({ ...p, x: p.x + dx, y: p.y + dy }),\n lerp: (a, b, t) => ({\n ...a,\n x: a.x + (b.x - a.x) * t,\n y: a.y + (b.y - a.y) * t,\n width: a.width + (b.width - a.width) * t,\n height: a.height + (b.height - a.height) * t,\n }),\n axes: (like) => {\n const flat = [like.x, like.y, like.width, like.height];\n if (!flat.every((n) => typeof n === 'number')) return null;\n return {\n count: 5,\n names: ['x', 'y', 'width', 'height', 'rotation'],\n angles: [4],\n to: (p) => [p.x, p.y, p.width, p.height, p.rotation ?? 0],\n from: (a) => {\n const pose = { ...like, x: a[0]!, y: a[1]!, width: a[2]!, height: a[3]! };\n if (like.rotation !== undefined || a[4] !== 0) pose.rotation = a[4]!;\n return pose;\n },\n add: (p, d) => {\n const pose = { ...p, x: p.x + d[0]!, y: p.y + d[1]!, width: p.width + d[2]!, height: p.height + d[3]! };\n const rotation = (p.rotation ?? 0) + d[4]!;\n if (p.rotation !== undefined || rotation !== 0) pose.rotation = rotation;\n return pose;\n },\n write: (p, out, at) => {\n out[at] = p.x;\n out[at + 1] = p.y;\n out[at + 2] = p.width;\n out[at + 3] = p.height;\n out[at + 4] = p.rotation ?? 0;\n },\n };\n },\n // `withRotation` writes into `Bounds`' optional `rotation`, so this reads\n // the same channel, or a pose reports itself unrotated while painting rotated.\n getRotation: (p) => p.rotation ?? 0,\n withRotation: (p, rotation) => ({ ...p, rotation }),\n};\n\n/** Identity geometry for `RotatedPose`: `RECT_POSE_DESCRIPTOR`'s projection,\n * with a required `rotation`. */\nexport const ROTATED_POSE_DESCRIPTOR: PoseDescriptor<RotatedPose> = {\n getBounds: RECT_POSE_DESCRIPTOR.getBounds as PoseDescriptor<RotatedPose>['getBounds'],\n remapBounds: RECT_POSE_DESCRIPTOR.remapBounds as PoseDescriptor<RotatedPose>['remapBounds'],\n translate: RECT_POSE_DESCRIPTOR.translate as PoseDescriptor<RotatedPose>['translate'],\n lerp: RECT_POSE_DESCRIPTOR.lerp as PoseDescriptor<RotatedPose>['lerp'],\n axes: RECT_POSE_DESCRIPTOR.axes as PoseDescriptor<RotatedPose>['axes'],\n fromBounds: (b) => ({ x: b.x, y: b.y, width: b.width, height: b.height, rotation: 0 }),\n getRotation: (p) => p.rotation,\n withRotation: (p, rotation) => ({ ...p, rotation }),\n};\n\n/**\n * Remap a leaf drawn in a frame of its own, turned or skewed, inside a group\n * resize.\n *\n * The naive `remapBounds` scales a leaf's axis-aligned `width`/`height` by the\n * group's per-axis factors and carries `rotation` through untouched. That is\n * right when `src`/`dst` are expressed in the leaf's own frame. In a group\n * resize they are world-frame: at 90° a horizontal stretch should grow the\n * leaf's `height`, and the naive map grows its `width`.\n *\n * What this computes is the group affine applied to the leaf's frame. The\n * center moves exactly; each box axis takes the length of its own image under\n * the turn; the rotation follows the image of the box's x-axis; and the\n * parallelogram a non-uniform scale makes of a turned box, which those cannot\n * hold, is written to the leaf's `transform`.\n */\nexport function remapRotatedLeaf<TPose extends RotatedPose>(\n pose: TPose,\n src: Bounds,\n dst: Bounds,\n): TPose {\n const sx = src.width === 0 ? 1 : dst.width / src.width;\n const sy = src.height === 0 ? 1 : dst.height / src.height;\n const theta = pose.rotation ?? 0;\n const cos = Math.cos(theta);\n const sin = Math.sin(theta);\n\n // Images of the leaf's local unit axes under the group's diagonal scale.\n const ux = sx * cos, uy = sy * sin;\n const vx = -sx * sin, vy = sy * cos;\n const kx = Math.hypot(ux, uy);\n const ky = Math.hypot(vx, vy);\n\n const width = pose.width * kx;\n const height = pose.height * ky;\n const rotation = Math.atan2(uy, ux);\n\n // The center is a point, so it takes the group affine directly.\n const cxNew = dst.x + (pose.x + pose.width / 2 - src.x) * sx;\n const cyNew = dst.y + (pose.y + pose.height / 2 - src.y) * sy;\n\n const next: RotatedPose = { ...pose, x: cxNew - width / 2, y: cyNew - height / 2, width, height, rotation };\n // A collapsed axis leaves no frame to solve for; the leaf keeps the transform it had.\n if (kx === 0 || ky === 0) return next as TPose;\n const [ta, tb, tc, td] = poseLinearOf(pose) ?? [1, 0, 0, 1];\n // The linear part the leaf must draw with, less what the new box and rotation already hold.\n const [a, b, c, d] = multiply(\n multiply(rotate(-rotation), [ux, uy, vx, vy, 0, 0]),\n [ta / kx, tb / kx, tc / ky, td / ky, 0, 0],\n );\n if (Math.abs(a - 1) < 1e-12 && Math.abs(b) < 1e-12 && Math.abs(c) < 1e-12 && Math.abs(d - 1) < 1e-12) {\n delete next.transform;\n } else {\n next.transform = [a, b, c, d];\n }\n return next as TPose;\n}\n\n/** The pose's visual bounds: its box expanded to cover the box as drawn\n * under its rotation and `transform`, so a turned or skewed shape reports\n * the extent of its ink. */\nexport function visualBoundsViaDescriptor<TPose>(\n pose: TPose,\n geometry: PoseDescriptor<TPose>,\n): Bounds {\n return axisAlignedBounds(framedBounds(pose, geometry));\n}\n\n/** The pose's box with the rotation and `transform` it is drawn in: what\n * `poseFrameOf` reads, for a pose only its descriptor can read. */\nexport function framedBounds<TPose>(pose: TPose, geometry: PoseDescriptor<TPose>): Bounds {\n const b = geometry.getBounds(pose);\n const rotation = geometry.getRotation?.(pose) ?? b.rotation ?? 0;\n const transform = poseLinearOf(pose) ?? poseLinearOf(b);\n if (!rotation && !transform) return b;\n return { ...b, ...(rotation ? { rotation } : {}), ...(transform ? { transform } : {}) };\n}\n\n/** Translate through `geometry.translate`, or via `remapBounds` onto a shifted\n * copy of the pose's own bounds when the descriptor has none. */\nexport function translatePoseViaDescriptor<TPose>(\n pose: TPose,\n dx: number,\n dy: number,\n geometry: PoseDescriptor<TPose>,\n): TPose {\n if (geometry.translate) return geometry.translate(pose, dx, dy);\n const src = geometry.getBounds(pose);\n const dst = { x: src.x + dx, y: src.y + dy, width: src.width, height: src.height };\n return geometry.remapBounds(pose, src, dst);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"poseDescriptor.js","names":[],"sources":["../../../src/core/geometry/poseDescriptor.ts"],"sourcesContent":["import type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { axisAlignedBounds } from 'core/geometry/unionBounds';\nimport { multiply, poseLinearOf, rotate } from '@weasel-js/geom';\nimport type { TargetAxes } from '@weasel-js/animator';\nimport type { PoseComposition } from 'core/scene/composeFrame';\n\n/** `Bounds` with a required rotation angle (radians). Pivot is the AABB\n * center of the unrotated `{x, y, width, height}`. */\nexport interface RotatedPose extends Bounds {\n rotation: number;\n}\n\n/** One pose before and after a translate, handed to the translate of another\n * pose in the same move. Both are in the frame the translated pose is stored\n * in: world for a root, its frame's coordinates for a pose under a container. */\nexport interface TranslateLead<TPose> {\n readonly from: TPose;\n readonly to: TPose;\n}\n\n/** A world `lead` expressed in the frame whose world pose is `frameWorld`, for a\n * pose stored there; `null` is the root frame, where it is unchanged. */\nexport function leadUnder<TPose>(\n pc: Pick<PoseComposition<TPose>, 'decompose'>,\n frameWorld: TPose | null,\n lead: TranslateLead<TPose>,\n): TranslateLead<TPose> {\n if (frameWorld === null) return lead;\n return { from: pc.decompose(frameWorld, lead.from), to: pc.decompose(frameWorld, lead.to) };\n}\n\n/**\n * Bridges arbitrary `TPose` shapes into the kit's bounds-driven math: bounds\n * are read via `getBounds`, and `remapBounds` projects a result back into\n * TPose.\n *\n * `remapBounds(pose, src, dst)` subsumes both \"set my own AABB to dst\"\n * (single-leaf resize) and \"scale me as a leaf inside parent's src→dst rect\"\n * (group resize) — they're the same affine map.\n */\nexport interface PoseDescriptor<TPose, TNode = unknown> {\n getBounds(pose: TPose): Bounds;\n remapBounds(pose: TPose, src: Bounds, dst: Bounds): TPose;\n /** A pose occupying `bounds`, of the same kind as `template`. Unlike\n * `remapBounds`, the result carries none of the template's shape. */\n fromBounds(bounds: Bounds, template: TPose): TPose;\n /** Translate the pose by (dx, dy). Optional — when omitted, callers fall\n * back to a translation derived from `remapBounds` (origin shifted, no\n * scale). Path-shaped poses should provide this for performance.\n *\n * `lead`, when given, is how another pose in the same move went under this\n * same (dx, dy). A descriptor for which (dx, dy) is one translation for\n * every pose ignores it. One that resolves it from where the pose sits, as\n * under a perspective camera, moves `pose` by the lead's own change, so a\n * move of several lands each of them by the amount it landed the lead. */\n translate?(pose: TPose, dx: number, dy: number, lead?: TranslateLead<TPose>): TPose;\n /** Interpolate between two poses. Optional — animation helpers fall back to\n * rect-shape lerp when omitted (which fails for non-rect poses). */\n lerp?(a: TPose, b: TPose, t: number): TPose;\n /** How a pose of `like`'s kind maps to the flat numbers an animation moves, and which of them\n * are angles. `from` keeps the fields of `like` that are not axes. Null for a pose this\n * descriptor cannot map; absent, a pose animates by its flat numeric fields. */\n axes?(like: TPose): TargetAxes<TPose> | null;\n /** Read the pose's rotation in radians. Pivot is the AABB center\n * (`getBounds(pose)` center). Default 0 when omitted. */\n getRotation?(pose: TPose): number;\n /** True iff this pose shape can carry a rotation; the rotation affordance\n * hides when it is `false`. When omitted, the kit assumes `true`, so a\n * descriptor whose poses cannot rotate (e.g. polygon Paths) should\n * return `false`. */\n supportsRotation?(pose: TPose): boolean;\n /** Write a rotation (radians) into the pose, bounds unchanged. Absent means\n * the pose cannot carry one, and the rotate action leaves it alone. */\n withRotation?(pose: TPose, rotation: number): TPose;\n /**\n * A descriptor specialized to one node. Every other method here sees only\n * the pose, so shapes sharing a pose type are indistinguishable to them.\n * Implement this to read `node.data` once and hand back a descriptor that\n * knows which shape it is describing. Callers reach it through\n * {@link poseDescriptorForNode}; sites with only an id keep using the\n * unspecialized descriptor.\n */\n forNode?(node: TNode): PoseDescriptor<TPose, TNode>;\n}\n\n/** The descriptor `node` should be read through: its own specialization when\n * it declares one, and otherwise the descriptor unchanged. */\nexport function poseDescriptorForNode<TPose, TNode>(\n descriptor: PoseDescriptor<TPose, TNode>,\n node: TNode,\n): PoseDescriptor<TPose, TNode> {\n return descriptor.forNode?.(node) ?? descriptor;\n}\n\n/** Identity geometry for `TPose extends Bounds`. Treats the pose as its\n * own bounds and remaps via affine scale against `src`/`dst`. */\nexport const RECT_POSE_DESCRIPTOR: PoseDescriptor<Bounds> = {\n getBounds: (p) => p,\n remapBounds: (p, src, dst) => {\n const sx = src.width === 0 ? 1 : dst.width / src.width;\n const sy = src.height === 0 ? 1 : dst.height / src.height;\n return {\n ...p,\n x: dst.x + (p.x - src.x) * sx,\n y: dst.y + (p.y - src.y) * sy,\n width: p.width * sx,\n height: p.height * sy,\n };\n },\n fromBounds: (b) => ({ x: b.x, y: b.y, width: b.width, height: b.height }),\n translate: (p, dx, dy) => ({ ...p, x: p.x + dx, y: p.y + dy }),\n lerp: (a, b, t) => ({\n ...a,\n x: a.x + (b.x - a.x) * t,\n y: a.y + (b.y - a.y) * t,\n width: a.width + (b.width - a.width) * t,\n height: a.height + (b.height - a.height) * t,\n }),\n axes: (like) => {\n const flat = [like.x, like.y, like.width, like.height];\n if (!flat.every((n) => typeof n === 'number')) return null;\n return {\n count: 5,\n names: ['x', 'y', 'width', 'height', 'rotation'],\n angles: [4],\n to: (p) => [p.x, p.y, p.width, p.height, p.rotation ?? 0],\n from: (a) => {\n const pose = { ...like, x: a[0]!, y: a[1]!, width: a[2]!, height: a[3]! };\n if (like.rotation !== undefined || a[4] !== 0) pose.rotation = a[4]!;\n return pose;\n },\n add: (p, d) => {\n const pose = { ...p, x: p.x + d[0]!, y: p.y + d[1]!, width: p.width + d[2]!, height: p.height + d[3]! };\n const rotation = (p.rotation ?? 0) + d[4]!;\n if (p.rotation !== undefined || rotation !== 0) pose.rotation = rotation;\n return pose;\n },\n write: (p, out, at) => {\n out[at] = p.x;\n out[at + 1] = p.y;\n out[at + 2] = p.width;\n out[at + 3] = p.height;\n out[at + 4] = p.rotation ?? 0;\n },\n };\n },\n // `withRotation` writes into `Bounds`' optional `rotation`, so this reads\n // the same channel, or a pose reports itself unrotated while painting rotated.\n getRotation: (p) => p.rotation ?? 0,\n withRotation: (p, rotation) => ({ ...p, rotation }),\n};\n\n/** Identity geometry for `RotatedPose`: `RECT_POSE_DESCRIPTOR`'s projection,\n * with a required `rotation`. */\nexport const ROTATED_POSE_DESCRIPTOR: PoseDescriptor<RotatedPose> = {\n getBounds: RECT_POSE_DESCRIPTOR.getBounds as PoseDescriptor<RotatedPose>['getBounds'],\n remapBounds: RECT_POSE_DESCRIPTOR.remapBounds as PoseDescriptor<RotatedPose>['remapBounds'],\n translate: RECT_POSE_DESCRIPTOR.translate as PoseDescriptor<RotatedPose>['translate'],\n lerp: RECT_POSE_DESCRIPTOR.lerp as PoseDescriptor<RotatedPose>['lerp'],\n axes: RECT_POSE_DESCRIPTOR.axes as PoseDescriptor<RotatedPose>['axes'],\n fromBounds: (b) => ({ x: b.x, y: b.y, width: b.width, height: b.height, rotation: 0 }),\n getRotation: (p) => p.rotation,\n withRotation: (p, rotation) => ({ ...p, rotation }),\n};\n\n/**\n * Remap a leaf drawn in a frame of its own, turned or skewed, inside a group\n * resize.\n *\n * The naive `remapBounds` scales a leaf's axis-aligned `width`/`height` by the\n * group's per-axis factors and carries `rotation` through untouched. That is\n * right when `src`/`dst` are expressed in the leaf's own frame. In a group\n * resize they are world-frame: at 90° a horizontal stretch should grow the\n * leaf's `height`, and the naive map grows its `width`.\n *\n * What this computes is the group affine applied to the leaf's frame. The\n * center moves exactly; each box axis takes the length of its own image under\n * the turn; the rotation follows the image of the box's x-axis; and the\n * parallelogram a non-uniform scale makes of a turned box, which those cannot\n * hold, is written to the leaf's `transform`.\n */\nexport function remapRotatedLeaf<TPose extends RotatedPose>(\n pose: TPose,\n src: Bounds,\n dst: Bounds,\n): TPose {\n const sx = src.width === 0 ? 1 : dst.width / src.width;\n const sy = src.height === 0 ? 1 : dst.height / src.height;\n const theta = pose.rotation ?? 0;\n const cos = Math.cos(theta);\n const sin = Math.sin(theta);\n\n // Images of the leaf's local unit axes under the group's diagonal scale.\n const ux = sx * cos, uy = sy * sin;\n const vx = -sx * sin, vy = sy * cos;\n const kx = Math.hypot(ux, uy);\n const ky = Math.hypot(vx, vy);\n\n const width = pose.width * kx;\n const height = pose.height * ky;\n const rotation = Math.atan2(uy, ux);\n\n // The center is a point, so it takes the group affine directly.\n const cxNew = dst.x + (pose.x + pose.width / 2 - src.x) * sx;\n const cyNew = dst.y + (pose.y + pose.height / 2 - src.y) * sy;\n\n const next: RotatedPose = { ...pose, x: cxNew - width / 2, y: cyNew - height / 2, width, height, rotation };\n // A collapsed axis leaves no frame to solve for; the leaf keeps the transform it had.\n if (kx === 0 || ky === 0) return next as TPose;\n const [ta, tb, tc, td] = poseLinearOf(pose) ?? [1, 0, 0, 1];\n // The linear part the leaf must draw with, less what the new box and rotation already hold.\n const [a, b, c, d] = multiply(\n multiply(rotate(-rotation), [ux, uy, vx, vy, 0, 0]),\n [ta / kx, tb / kx, tc / ky, td / ky, 0, 0],\n );\n if (Math.abs(a - 1) < 1e-12 && Math.abs(b) < 1e-12 && Math.abs(c) < 1e-12 && Math.abs(d - 1) < 1e-12) {\n delete next.transform;\n } else {\n next.transform = [a, b, c, d];\n }\n return next as TPose;\n}\n\n/** The pose's visual bounds: its box expanded to cover the box as drawn\n * under its rotation and `transform`, so a turned or skewed shape reports\n * the extent of its ink. */\nexport function visualBoundsViaDescriptor<TPose>(\n pose: TPose,\n geometry: PoseDescriptor<TPose>,\n): Bounds {\n return axisAlignedBounds(framedBounds(pose, geometry));\n}\n\n/** The pose's box with the rotation and `transform` it is drawn in: what\n * `poseFrameOf` reads, for a pose only its descriptor can read. */\nexport function framedBounds<TPose>(pose: TPose, geometry: PoseDescriptor<TPose>): Bounds {\n const b = geometry.getBounds(pose);\n const rotation = geometry.getRotation?.(pose) ?? b.rotation ?? 0;\n const transform = poseLinearOf(pose) ?? poseLinearOf(b);\n if (!rotation && !transform) return b;\n return { ...b, ...(rotation ? { rotation } : {}), ...(transform ? { transform } : {}) };\n}\n\n/** Translate through `geometry.translate`, or via `remapBounds` onto a shifted\n * copy of the pose's own bounds when the descriptor has none. */\nexport function translatePoseViaDescriptor<TPose>(\n pose: TPose,\n dx: number,\n dy: number,\n geometry: PoseDescriptor<TPose>,\n lead?: TranslateLead<TPose>,\n): TPose {\n if (geometry.translate) return lead ? geometry.translate(pose, dx, dy, lead) : geometry.translate(pose, dx, dy);\n const src = geometry.getBounds(pose);\n const dst = { x: src.x + dx, y: src.y + dy, width: src.width, height: src.height };\n return geometry.remapBounds(pose, src, dst);\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyToPoint, invert, poseFrameOf } from "@weasel-js/geom";
|
|
1
|
+
import { applyToPoint, invert, poseFrameOf, pxExtentInFrame } from "@weasel-js/geom";
|
|
2
2
|
|
|
3
3
|
//#region src/core/geometry/targetFrame.ts
|
|
4
4
|
/**
|
|
@@ -90,18 +90,7 @@ function normalToWorld(xf, n) {
|
|
|
90
90
|
/** `pxExtent` along the target's own axes: how far along each an edge across
|
|
91
91
|
* it must move to move `px` on screen. */
|
|
92
92
|
function pxExtentIn(px, scale, xf) {
|
|
93
|
-
|
|
94
|
-
const ux = a * scale.x, uy = b * scale.y;
|
|
95
|
-
const vx = c * scale.x, vy = d * scale.y;
|
|
96
|
-
const det = Math.abs(ux * vy - uy * vx);
|
|
97
|
-
if (det === 0) return {
|
|
98
|
-
x: 0,
|
|
99
|
-
y: 0
|
|
100
|
-
};
|
|
101
|
-
return {
|
|
102
|
-
x: px * Math.hypot(vx, vy) / det,
|
|
103
|
-
y: px * Math.hypot(ux, uy) / det
|
|
104
|
-
};
|
|
93
|
+
return pxExtentInFrame(px, scale, xf.toWorld);
|
|
105
94
|
}
|
|
106
95
|
/** The target's box corners in world, TL/TR/BR/BL. */
|
|
107
96
|
function targetCorners(b, xf) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"targetFrame.js","names":[],"sources":["../../../src/core/geometry/targetFrame.ts"],"sourcesContent":["/**\n * The frame selection chrome reads a target in: `poseFrameOf` over the\n * target's bounds, with the helpers the outline, the handles, their hit\n * regions, and the resize and rotate actions all place themselves by.\n */\nimport { applyToPoint, invert, poseFrameOf, type Affine, type Scale2 } from '@weasel-js/geom';\n\n/** Local↔world maps for a chrome target. Local is the target's unrotated box\n * frame; world is where the node is drawn. */\nexport interface TargetTransform {\n /** No rotation and no `transform`: local is world. */\n identity: boolean;\n /** Target box frame → world. */\n toWorld: Affine;\n /** World → target box frame. `null` when the target's `transform` is\n * singular: it draws nothing, and no world point maps back. */\n toLocal: Affine | null;\n}\n\nconst IDENTITY: Affine = [1, 0, 0, 1, 0, 0];\n\nexport const IDENTITY_TARGET: TargetTransform = { identity: true, toWorld: IDENTITY, toLocal: IDENTITY };\n\n/** The frame `bounds` is drawn in: its `rotation`, its `transform`, or both. */\nexport function targetTransformOf(bounds: unknown): TargetTransform {\n const toWorld = poseFrameOf(bounds);\n if (!toWorld) return IDENTITY_TARGET;\n return { identity: false, toWorld, toLocal: invert(toWorld) };\n}\n\n/** How far the target's x axis is turned from world's, radians. */\nexport function angleOf(xf: TargetTransform): number {\n return xf.identity ? 0 : Math.atan2(xf.toWorld[1], xf.toWorld[0]);\n}\n\n/** Whether the frame only turns the target: no skew, no stretch, no flip. */\nexport function isRigid(xf: TargetTransform): boolean {\n if (xf.identity) return true;\n const [a, b, c, d] = xf.toWorld;\n return Math.abs(a - d) < 1e-9 && Math.abs(b + c) < 1e-9 && Math.abs(a * a + b * b - 1) < 1e-9;\n}\n\n/** local point → world point. */\nexport function localToWorld(xf: TargetTransform, lx: number, ly: number): { x: number; y: number } {\n if (xf.identity) return { x: lx, y: ly };\n const [x, y] = applyToPoint(xf.toWorld, lx, ly);\n return { x, y };\n}\n\n/** world point → local point; `null` where nothing maps back. */\nexport function worldToLocal(xf: TargetTransform, wx: number, wy: number): { x: number; y: number } | null {\n if (xf.identity) return { x: wx, y: wy };\n if (!xf.toLocal) return null;\n const [x, y] = applyToPoint(xf.toLocal, wx, wy);\n return { x, y };\n}\n\n/** A world delta in the target's own axes; `null` where nothing maps back. */\nexport function worldDeltaToLocal(xf: TargetTransform, dx: number, dy: number): { x: number; y: number } | null {\n if (xf.identity) return { x: dx, y: dy };\n const m = xf.toLocal;\n return m && { x: m[0] * dx + m[2] * dy, y: m[1] * dx + m[3] * dy };\n}\n\n/** A local edge's outward normal as a world direction, unnormalized. A normal\n * goes through the inverse transpose, so it stays square to a skewed edge. */\nexport function normalToWorld(xf: TargetTransform, n: { x: number; y: number }): { x: number; y: number } {\n if (xf.identity) return n;\n const [a, b, c, d] = xf.toWorld;\n const k = Math.sign(a * d - b * c) || 1;\n return { x: (d * n.x - b * n.y) * k, y: (a * n.y - c * n.x) * k };\n}\n\n/** `pxExtent` along the target's own axes: how far along each an edge across\n * it must move to move `px` on screen. */\nexport function pxExtentIn(px: number, scale: Scale2, xf: TargetTransform): { x: number; y: number } {\n
|
|
1
|
+
{"version":3,"file":"targetFrame.js","names":[],"sources":["../../../src/core/geometry/targetFrame.ts"],"sourcesContent":["/**\n * The frame selection chrome reads a target in: `poseFrameOf` over the\n * target's bounds, with the helpers the outline, the handles, their hit\n * regions, and the resize and rotate actions all place themselves by.\n */\nimport { applyToPoint, invert, poseFrameOf, pxExtentInFrame, type Affine, type Scale2 } from '@weasel-js/geom';\n\n/** Local↔world maps for a chrome target. Local is the target's unrotated box\n * frame; world is where the node is drawn. */\nexport interface TargetTransform {\n /** No rotation and no `transform`: local is world. */\n identity: boolean;\n /** Target box frame → world. */\n toWorld: Affine;\n /** World → target box frame. `null` when the target's `transform` is\n * singular: it draws nothing, and no world point maps back. */\n toLocal: Affine | null;\n}\n\nconst IDENTITY: Affine = [1, 0, 0, 1, 0, 0];\n\nexport const IDENTITY_TARGET: TargetTransform = { identity: true, toWorld: IDENTITY, toLocal: IDENTITY };\n\n/** The frame `bounds` is drawn in: its `rotation`, its `transform`, or both. */\nexport function targetTransformOf(bounds: unknown): TargetTransform {\n const toWorld = poseFrameOf(bounds);\n if (!toWorld) return IDENTITY_TARGET;\n return { identity: false, toWorld, toLocal: invert(toWorld) };\n}\n\n/** How far the target's x axis is turned from world's, radians. */\nexport function angleOf(xf: TargetTransform): number {\n return xf.identity ? 0 : Math.atan2(xf.toWorld[1], xf.toWorld[0]);\n}\n\n/** Whether the frame only turns the target: no skew, no stretch, no flip. */\nexport function isRigid(xf: TargetTransform): boolean {\n if (xf.identity) return true;\n const [a, b, c, d] = xf.toWorld;\n return Math.abs(a - d) < 1e-9 && Math.abs(b + c) < 1e-9 && Math.abs(a * a + b * b - 1) < 1e-9;\n}\n\n/** local point → world point. */\nexport function localToWorld(xf: TargetTransform, lx: number, ly: number): { x: number; y: number } {\n if (xf.identity) return { x: lx, y: ly };\n const [x, y] = applyToPoint(xf.toWorld, lx, ly);\n return { x, y };\n}\n\n/** world point → local point; `null` where nothing maps back. */\nexport function worldToLocal(xf: TargetTransform, wx: number, wy: number): { x: number; y: number } | null {\n if (xf.identity) return { x: wx, y: wy };\n if (!xf.toLocal) return null;\n const [x, y] = applyToPoint(xf.toLocal, wx, wy);\n return { x, y };\n}\n\n/** A world delta in the target's own axes; `null` where nothing maps back. */\nexport function worldDeltaToLocal(xf: TargetTransform, dx: number, dy: number): { x: number; y: number } | null {\n if (xf.identity) return { x: dx, y: dy };\n const m = xf.toLocal;\n return m && { x: m[0] * dx + m[2] * dy, y: m[1] * dx + m[3] * dy };\n}\n\n/** A local edge's outward normal as a world direction, unnormalized. A normal\n * goes through the inverse transpose, so it stays square to a skewed edge. */\nexport function normalToWorld(xf: TargetTransform, n: { x: number; y: number }): { x: number; y: number } {\n if (xf.identity) return n;\n const [a, b, c, d] = xf.toWorld;\n const k = Math.sign(a * d - b * c) || 1;\n return { x: (d * n.x - b * n.y) * k, y: (a * n.y - c * n.x) * k };\n}\n\n/** `pxExtent` along the target's own axes: how far along each an edge across\n * it must move to move `px` on screen. */\nexport function pxExtentIn(px: number, scale: Scale2, xf: TargetTransform): { x: number; y: number } {\n return pxExtentInFrame(px, scale, xf.toWorld);\n}\n\n/** The target's box corners in world, TL/TR/BR/BL. */\nexport function targetCorners(\n b: { x: number; y: number; width: number; height: number },\n xf: TargetTransform,\n): { x: number; y: number }[] {\n return [\n localToWorld(xf, b.x, b.y),\n localToWorld(xf, b.x + b.width, b.y),\n localToWorld(xf, b.x + b.width, b.y + b.height),\n localToWorld(xf, b.x, b.y + b.height),\n ];\n}\n"],"mappings":";;;;;;;;AAmBA,IAAM,WAAmB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;AAAC;AAE1C,IAAa,kBAAmC;CAAE,UAAU;CAAM,SAAS;CAAU,SAAS;AAAS;;AAGvG,SAAgB,kBAAkB,QAAkC;CAClE,MAAM,UAAU,YAAY,MAAM;CAClC,IAAI,CAAC,SAAS,OAAO;CACrB,OAAO;EAAE,UAAU;EAAO;EAAS,SAAS,OAAO,OAAO;CAAE;AAC9D;;AAGA,SAAgB,QAAQ,IAA6B;CACnD,OAAO,GAAG,WAAW,IAAI,KAAK,MAAM,GAAG,QAAQ,IAAI,GAAG,QAAQ,EAAE;AAClE;;AAGA,SAAgB,QAAQ,IAA8B;CACpD,IAAI,GAAG,UAAU,OAAO;CACxB,MAAM,CAAC,GAAG,GAAG,GAAG,KAAK,GAAG;CACxB,OAAO,KAAK,IAAI,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI;AAC3F;;AAGA,SAAgB,aAAa,IAAqB,IAAY,IAAsC;CAClG,IAAI,GAAG,UAAU,OAAO;EAAE,GAAG;EAAI,GAAG;CAAG;CACvC,MAAM,CAAC,GAAG,KAAK,aAAa,GAAG,SAAS,IAAI,EAAE;CAC9C,OAAO;EAAE;EAAG;CAAE;AAChB;;AAGA,SAAgB,aAAa,IAAqB,IAAY,IAA6C;CACzG,IAAI,GAAG,UAAU,OAAO;EAAE,GAAG;EAAI,GAAG;CAAG;CACvC,IAAI,CAAC,GAAG,SAAS,OAAO;CACxB,MAAM,CAAC,GAAG,KAAK,aAAa,GAAG,SAAS,IAAI,EAAE;CAC9C,OAAO;EAAE;EAAG;CAAE;AAChB;;AAGA,SAAgB,kBAAkB,IAAqB,IAAY,IAA6C;CAC9G,IAAI,GAAG,UAAU,OAAO;EAAE,GAAG;EAAI,GAAG;CAAG;CACvC,MAAM,IAAI,GAAG;CACb,OAAO,KAAK;EAAE,GAAG,EAAE,KAAK,KAAK,EAAE,KAAK;EAAI,GAAG,EAAE,KAAK,KAAK,EAAE,KAAK;CAAG;AACnE;;;AAIA,SAAgB,cAAc,IAAqB,GAAuD;CACxG,IAAI,GAAG,UAAU,OAAO;CACxB,MAAM,CAAC,GAAG,GAAG,GAAG,KAAK,GAAG;CACxB,MAAM,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK;CACtC,OAAO;EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK;EAAG,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK;CAAE;AAClE;;;AAIA,SAAgB,WAAW,IAAY,OAAe,IAA+C;CACnG,OAAO,gBAAgB,IAAI,OAAO,GAAG,OAAO;AAC9C;;AAGA,SAAgB,cACd,GACA,IAC4B;CAC5B,OAAO;EACL,aAAa,IAAI,EAAE,GAAG,EAAE,CAAC;EACzB,aAAa,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;EACnC,aAAa,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM;EAC9C,aAAa,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM;CACtC;AACF"}
|
|
@@ -154,12 +154,13 @@ function drawLayerOnce(layer, data, view, dims, cache, onLayerError, retain) {
|
|
|
154
154
|
}
|
|
155
155
|
if (cmds.length === 0) return [];
|
|
156
156
|
const effects = layer.effects && layer.effects.length > 0 ? layer.effects : void 0;
|
|
157
|
-
const kept = retain &&
|
|
157
|
+
const kept = retain && cache ? retainedId(layer, cache, view, retain) : void 0;
|
|
158
158
|
const marked = kept ? { retain: kept } : {};
|
|
159
159
|
if ((layer.space ?? "world") === "screen") {
|
|
160
160
|
if (effects) return [{
|
|
161
161
|
kind: "group",
|
|
162
162
|
effects,
|
|
163
|
+
...marked,
|
|
163
164
|
children: cmds
|
|
164
165
|
}];
|
|
165
166
|
return kept ? [{
|