@weasel-js/core 1.12.0 → 1.13.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/dist/animation/poseTarget.js +46 -15
- package/dist/animation/poseTarget.js.map +1 -1
- package/dist/animation/poseTransition.js +3 -3
- package/dist/animation/poseTransition.js.map +1 -1
- package/dist/animation/reflow.js +3 -6
- package/dist/animation/reflow.js.map +1 -1
- package/dist/animation/rig/bindRig.js +2 -2
- package/dist/animation/rig/bindRig.js.map +1 -1
- package/dist/animation/rig/skinBinding.js +2 -2
- package/dist/animation/rig/skinBinding.js.map +1 -1
- package/dist/barrel/canvas.js +5 -3
- package/dist/barrel/chrome.js +3 -1
- package/dist/barrel/paths.js +3 -2
- package/dist/barrel/routing.js +2 -2
- package/dist/barrel/scene.js +6 -1
- package/dist/canvas/NodeShape.js +1 -0
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -1
- package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
- package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
- package/dist/canvas/SceneCanvas/scenePointQuery.js +20 -2
- package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +3 -0
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
- package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +7 -0
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -0
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
- package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +5 -3
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +19 -2
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/accentColor.js +26 -3
- package/dist/canvas/accentColor.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +14 -20
- package/dist/canvas/buildSceneTree.js.map +1 -1
- package/dist/canvas/copiesAbove.js +1 -1
- package/dist/canvas/copiesAbove.js.map +1 -1
- package/dist/canvas/defaultDrawOne.js +10 -7
- package/dist/canvas/defaultDrawOne.js.map +1 -1
- package/dist/canvas/deps/areaSelect.js +5 -2
- package/dist/canvas/deps/areaSelect.js.map +1 -1
- package/dist/canvas/deps/booleans.js +1 -1
- package/dist/canvas/deps/booleans.js.map +1 -1
- package/dist/canvas/deps/enteredGroup.js +81 -0
- package/dist/canvas/deps/enteredGroup.js.map +1 -0
- package/dist/canvas/deps/hitTestArea.js +27 -15
- package/dist/canvas/deps/hitTestArea.js.map +1 -1
- package/dist/canvas/deps/insert.js +4 -36
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/insertSite.js +39 -0
- package/dist/canvas/deps/insertSite.js.map +1 -0
- package/dist/canvas/deps/lassoSelect.js +6 -3
- package/dist/canvas/deps/lassoSelect.js.map +1 -1
- package/dist/canvas/deps/pathLeaf.js +1 -1
- package/dist/canvas/deps/pathLeaf.js.map +1 -1
- package/dist/canvas/deps/slice.js +2 -1
- package/dist/canvas/deps/slice.js.map +1 -1
- package/dist/canvas/derivedPath.js +14 -6
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/doubleClickTarget.js +9 -11
- package/dist/canvas/doubleClickTarget.js.map +1 -1
- package/dist/canvas/drawnBounds.js +16 -7
- package/dist/canvas/drawnBounds.js.map +1 -1
- package/dist/canvas/inheritedPaints.js +22 -11
- package/dist/canvas/inheritedPaints.js.map +1 -1
- package/dist/canvas/nodeShapes/nodePaint.js +1 -7
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
- package/dist/canvas/nodeShapes/pathPainter.js +2 -3
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +1 -1
- package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/shapePainter.js +2 -3
- package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
- package/dist/canvas/ownClip.js +24 -0
- package/dist/canvas/ownClip.js.map +1 -0
- package/dist/canvas/paintCull.js.map +1 -1
- package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
- package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
- package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
- package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
- package/dist/canvas/pickWalk.js +14 -90
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/poseCascade.js +1 -1
- package/dist/canvas/poseCascade.js.map +1 -1
- package/dist/canvas/publishPreviews.js +97 -0
- package/dist/canvas/publishPreviews.js.map +1 -0
- package/dist/canvas/sceneAdapter.js +7 -5
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/sceneClipboard.js +17 -10
- package/dist/canvas/sceneClipboard.js.map +1 -1
- package/dist/canvas/scenePickReads.js +37 -0
- package/dist/canvas/scenePickReads.js.map +1 -0
- package/dist/canvas/scenePickSource.js +74 -0
- package/dist/canvas/scenePickSource.js.map +1 -0
- package/dist/canvas/sceneSlotWiring.js +9 -4
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +9 -5
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/shownNodes.js +15 -0
- package/dist/canvas/shownNodes.js.map +1 -0
- package/dist/canvas/useSceneAdapter.js +5 -3
- package/dist/canvas/useSceneAdapter.js.map +1 -1
- package/dist/clipboard.d.ts +2 -2
- package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
- package/dist/clone.d.ts +3 -3
- package/dist/core/isPlainObject.js +2 -2
- package/dist/core/layers/layerGroup.js.map +1 -1
- package/dist/core/scene/NodeRouting.js.map +1 -1
- package/dist/core/scene/arrivalWindow.js +1 -1
- package/dist/core/scene/arrivalWindow.js.map +1 -1
- package/dist/core/scene/clipNothing.js +2 -5
- package/dist/core/scene/clipNothing.js.map +1 -1
- package/dist/core/scene/declaredFieldSetters.js +44 -0
- package/dist/core/scene/declaredFieldSetters.js.map +1 -0
- package/dist/core/scene/depMemo.js +3 -0
- package/dist/core/scene/depMemo.js.map +1 -1
- package/dist/core/scene/dependents.js +3 -1
- package/dist/core/scene/dependents.js.map +1 -1
- package/dist/core/scene/documentOf.js +43 -0
- package/dist/core/scene/documentOf.js.map +1 -0
- package/dist/core/scene/effectivePose.js +37 -13
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/fieldEdits.js +17 -14
- package/dist/core/scene/fieldEdits.js.map +1 -1
- package/dist/core/scene/historyForm.js +4 -4
- package/dist/core/scene/historyForm.js.map +1 -1
- package/dist/core/scene/inheritedPaint.js +27 -5
- package/dist/core/scene/inheritedPaint.js.map +1 -1
- package/dist/core/scene/kitOps.js +3 -23
- package/dist/core/scene/kitOps.js.map +1 -1
- package/dist/core/scene/layoutPass.js +1 -2
- package/dist/core/scene/layoutPass.js.map +1 -1
- package/dist/core/scene/nodeAsShown.js +72 -0
- package/dist/core/scene/nodeAsShown.js.map +1 -0
- package/dist/core/scene/nodeFnFields.js +2 -10
- package/dist/core/scene/nodeFnFields.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +6 -1
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/nodeStore.js +2 -4
- package/dist/core/scene/nodeStore.js.map +1 -1
- package/dist/core/scene/pathEffects/derive.js +3 -2
- package/dist/core/scene/pathEffects/derive.js.map +1 -1
- package/dist/core/scene/pathEffects/foundHidden.js +35 -0
- package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
- package/dist/core/scene/plyStacks.js +89 -0
- package/dist/core/scene/plyStacks.js.map +1 -0
- package/dist/core/scene/poseHold.js +42 -24
- package/dist/core/scene/poseHold.js.map +1 -1
- package/dist/core/scene/poseOverrides.js +35 -38
- package/dist/core/scene/poseOverrides.js.map +1 -1
- package/dist/core/scene/posePlies.js +59 -0
- package/dist/core/scene/posePlies.js.map +1 -0
- package/dist/core/scene/reconcileSpecs.js +6 -15
- package/dist/core/scene/reconcileSpecs.js.map +1 -1
- package/dist/core/scene/renderOrder.js +10 -4
- package/dist/core/scene/renderOrder.js.map +1 -1
- package/dist/core/scene/sameValue.js +17 -0
- package/dist/core/scene/sameValue.js.map +1 -0
- package/dist/core/scene/scene.js +3 -3
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneEdits.js +8 -0
- package/dist/core/scene/sceneEdits.js.map +1 -1
- package/dist/core/scene/sceneState.js +1 -2
- package/dist/core/scene/sceneState.js.map +1 -1
- package/dist/core/scene/serialization.js +45 -9
- package/dist/core/scene/serialization.js.map +1 -1
- package/dist/core/scene/staleReplay.js +5 -2
- package/dist/core/scene/staleReplay.js.map +1 -1
- package/dist/core/scene/statedFields.js +4 -2
- package/dist/core/scene/statedFields.js.map +1 -1
- package/dist/core/scene/treeEdits.js +2 -4
- package/dist/core/scene/treeEdits.js.map +1 -1
- package/dist/core/scene/treeOrder.js +3 -1
- package/dist/core/scene/treeOrder.js.map +1 -1
- package/dist/core/scene/treeReads.js +1 -2
- package/dist/core/scene/treeReads.js.map +1 -1
- package/dist/core/scene/treeShape.js +13 -0
- package/dist/core/scene/treeShape.js.map +1 -0
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/useScene.js +1 -1
- package/dist/core/scene/useScene.js.map +1 -1
- package/dist/{cornerHandles-p8fDIAKt.d.ts → cornerHandles-CNCb7aTY.d.ts} +1 -1
- package/dist/{depSchema-mG19aEdD.d.ts → depSchema-B1RS-IJx.d.ts} +27 -54
- package/dist/devtools/handlers.js +1 -1
- package/dist/devtools/handlers.js.map +1 -1
- package/dist/devtools/protocol.js.map +1 -1
- package/dist/devtools-protocol.d.ts +4 -3
- package/dist/devtools.d.ts +3 -2
- package/dist/features/measure/measureHit.js +5 -3
- package/dist/features/measure/measureHit.js.map +1 -1
- package/dist/features/measure/measurement.js +1 -1
- package/dist/features/measure/measurement.js.map +1 -1
- package/dist/features/modes/modeLayers.js +14 -7
- package/dist/features/modes/modeLayers.js.map +1 -1
- package/dist/features/modes/modeTriggers.js +3 -3
- package/dist/features/modes/modeTriggers.js.map +1 -1
- package/dist/features/pathEffects/effectSources.js +25 -16
- package/dist/features/pathEffects/effectSources.js.map +1 -1
- package/dist/features/pathEffects/readSource.js +1 -1
- package/dist/features/pathEffects/readSource.js.map +1 -1
- package/dist/features/pathEffects/useEffectSourcesLayer.js +15 -19
- package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -1
- package/dist/features/paths/editablePath.js +1 -1
- package/dist/features/paths/editablePath.js.map +1 -1
- package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
- package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
- package/dist/features/selection/enteredGroupFrame.js +65 -0
- package/dist/features/selection/enteredGroupFrame.js.map +1 -0
- package/dist/features/selection/enteredGroupScope.js +44 -0
- package/dist/features/selection/enteredGroupScope.js.map +1 -0
- package/dist/features/text/sceneTextDrafts.js +26 -27
- package/dist/features/text/sceneTextDrafts.js.map +1 -1
- package/dist/features/text/textHitAt.js +4 -2
- package/dist/features/text/textHitAt.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +3 -3
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/index.d.ts +225 -73
- package/dist/index.js +15 -5
- package/dist/interactions/actions/admitsChild.js +11 -0
- package/dist/interactions/actions/admitsChild.js.map +1 -0
- package/dist/interactions/actions/defaults/contextMenu.js +16 -3
- package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
- package/dist/interactions/actions/defaults/createPaintAction.js +4 -1
- package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
- package/dist/interactions/actions/defaults/enterGroup.js +88 -0
- package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
- package/dist/interactions/actions/defaults/enterPathEdit.js +1 -1
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
- package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +7 -7
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/selectAll.js +7 -3
- package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
- package/dist/interactions/actions/defaults/selectDeep.js +35 -0
- package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
- package/dist/interactions/actions/drawnBoxes.js +2 -1
- package/dist/interactions/actions/drawnBoxes.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +1 -0
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/parent.js +7 -5
- package/dist/interactions/actions/insert/parent.js.map +1 -1
- package/dist/interactions/actions/insert/placeAction.js +3 -2
- package/dist/interactions/actions/insert/placeAction.js.map +1 -1
- package/dist/interactions/actions/insert/session.js +2 -2
- package/dist/interactions/actions/insert/session.js.map +1 -1
- package/dist/interactions/actions/move/commit/reparent.js +1 -1
- package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
- package/dist/interactions/actions/move/gestureAdapter.js +6 -7
- package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
- package/dist/interactions/actions/move/layout/exits.js +1 -1
- package/dist/interactions/actions/move/layout/exits.js.map +1 -1
- package/dist/interactions/actions/move/session.js +4 -4
- package/dist/interactions/actions/move/session.js.map +1 -1
- package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
- package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
- package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
- package/dist/{math-B56zxVs5.d.ts → math-C_elShP_.d.ts} +2 -5
- package/dist/math.d.ts +1 -1
- package/dist/move.d.ts +4 -4
- package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
- package/dist/resize.d.ts +4 -4
- package/dist/test-seams.d.ts +6 -6
- package/dist/tools/builtin/pressTarget.js +22 -0
- package/dist/tools/builtin/pressTarget.js.map +1 -0
- package/dist/tools/builtin/select/adapterPointQuery.js +3 -3
- package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
- package/dist/tools/builtin/select/collapseDeferred.js +40 -0
- package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
- package/dist/tools/builtin/select/groupEntry.js +82 -0
- package/dist/tools/builtin/select/groupEntry.js.map +1 -0
- package/dist/tools/builtin/select/pressDecision.js +1 -1
- package/dist/tools/builtin/select/pressDecision.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +61 -45
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
- package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
- package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -25
|
@@ -7,18 +7,20 @@ import { useMemo } from "react";
|
|
|
7
7
|
* the adapter keeps its identity across renders unless one of them changes.
|
|
8
8
|
*/
|
|
9
9
|
function useSceneAdapter(scene, options = {}) {
|
|
10
|
-
const { selection, commitInsert, insertLayer, poseDescriptor } = options;
|
|
10
|
+
const { selection, commitInsert, insertLayer, poseDescriptor, pasteInto } = options;
|
|
11
11
|
return useMemo(() => sceneToAdapter(scene, {
|
|
12
12
|
selection,
|
|
13
13
|
commitInsert,
|
|
14
14
|
insertLayer,
|
|
15
|
-
poseDescriptor
|
|
15
|
+
poseDescriptor,
|
|
16
|
+
...pasteInto ? { pasteInto } : {}
|
|
16
17
|
}), [
|
|
17
18
|
scene,
|
|
18
19
|
selection,
|
|
19
20
|
commitInsert,
|
|
20
21
|
insertLayer,
|
|
21
|
-
poseDescriptor
|
|
22
|
+
poseDescriptor,
|
|
23
|
+
pasteInto
|
|
22
24
|
]);
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneAdapter.js","names":[],"sources":["../../src/canvas/useSceneAdapter.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport type { Scene } from 'core/scene/types';\nimport {\n sceneToAdapter,\n type SceneCanvasAdapter,\n type SceneToAdapterOptions,\n} from './sceneAdapter';\n\n/**\n * React-hook wrapper around `sceneToAdapter`. Memoizes on every option, so\n * the adapter keeps its identity across renders unless one of them changes.\n */\nexport function useSceneAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const {\n selection, commitInsert, insertLayer, poseDescriptor,\n } = options;\n return useMemo(\n () => sceneToAdapter(scene, {\n selection, commitInsert, insertLayer, poseDescriptor,\n }),\n [scene, selection, commitInsert, insertLayer, poseDescriptor],\n );\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,gBACd,OACA,UAAuD,CAAC,GACd;CAC1C,MAAM,EACJ,WAAW,cAAc,aAAa,
|
|
1
|
+
{"version":3,"file":"useSceneAdapter.js","names":[],"sources":["../../src/canvas/useSceneAdapter.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport type { Scene } from 'core/scene/types';\nimport {\n sceneToAdapter,\n type SceneCanvasAdapter,\n type SceneToAdapterOptions,\n} from './sceneAdapter';\n\n/**\n * React-hook wrapper around `sceneToAdapter`. Memoizes on every option, so\n * the adapter keeps its identity across renders unless one of them changes.\n */\nexport function useSceneAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const {\n selection, commitInsert, insertLayer, poseDescriptor, pasteInto,\n } = options;\n return useMemo(\n () => sceneToAdapter(scene, {\n selection, commitInsert, insertLayer, poseDescriptor, ...(pasteInto ? { pasteInto } : {}),\n }),\n [scene, selection, commitInsert, insertLayer, poseDescriptor, pasteInto],\n );\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,gBACd,OACA,UAAuD,CAAC,GACd;CAC1C,MAAM,EACJ,WAAW,cAAc,aAAa,gBAAgB,cACpD;CACJ,OAAO,cACC,eAAe,OAAO;EAC1B;EAAW;EAAc;EAAa;EAAgB,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CACzF,CAAC,GACD;EAAC;EAAO;EAAW;EAAc;EAAa;EAAgB;CAAS,CACzE;AACF"}
|
package/dist/clipboard.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NodeId } from '@weasel-js/routing';
|
|
2
|
-
import { P as PasteOmission, C as ClipboardSnapshot, I as InsertAdapter } from './types-
|
|
3
|
-
import './types-
|
|
2
|
+
import { P as PasteOmission, C as ClipboardSnapshot, I as InsertAdapter } from './types-p2alu-R1.js';
|
|
3
|
+
import './types-49uYJN3j.js';
|
|
4
4
|
import '@weasel-js/geom';
|
|
5
5
|
import '@weasel-js/history';
|
|
6
6
|
import './poseDescriptor-DN2yfnq_.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as PlaneMap } from './types-
|
|
1
|
+
import { a as PlaneMap } from './types-49uYJN3j.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-p2alu-R1.js';
|
|
4
4
|
import { FramedBox, View } from '@weasel-js/geom';
|
|
5
5
|
import { KitSettingsStore } from '@weasel-js/react';
|
|
6
6
|
import { ModifierState, ResizeAnchor } from '@weasel-js/routing';
|
package/dist/clone.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { C as CloneBehavior, d as CloneBehaviorResult } from './clone-
|
|
2
|
-
import './types-
|
|
1
|
+
export { C as CloneBehavior, d as CloneBehaviorResult } from './clone-qb2e1F5p.js';
|
|
2
|
+
import './types-49uYJN3j.js';
|
|
3
3
|
import '@weasel-js/geom';
|
|
4
4
|
import '@weasel-js/history';
|
|
5
|
-
import './types-
|
|
5
|
+
import './types-p2alu-R1.js';
|
|
6
6
|
import './poseDescriptor-DN2yfnq_.js';
|
|
7
7
|
import '@weasel-js/animator';
|
|
8
8
|
import '@weasel-js/react';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { isPlainObject, isPlainPrototype } from "@weasel-js/registry";
|
|
1
|
+
import { isPlainObject as isPlainObject$1, isPlainPrototype } from "@weasel-js/registry";
|
|
2
2
|
|
|
3
|
-
export { isPlainObject, isPlainPrototype };
|
|
3
|
+
export { isPlainObject$1 as isPlainObject, isPlainPrototype };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layerGroup.js","names":[],"sources":["../../../src/core/layers/layerGroup.ts"],"sourcesContent":["import type { BlendFactors, CompositeOp, DrawCommand, Effect } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport type { Dims } from './render';\n\n/**\n * Several consecutive layers composited as one.\n *\n * A layer's own `effects` run over that layer alone, which is the wrong\n * picture whenever a pass reads neighboring pixels: `blur(A over B)` is not\n * `blur(A) over blur(B)`. A group draws its members into one buffer, runs one\n * chain over it, and composites the result back once.\n *\n * Membership is by `RenderLayer.id` — the same names `layerOrder` and\n * `layerVisibility` use, not the `layers` map's slot keys.\n *\n * Only *consecutive* members share a buffer. A group whose members are\n * separated in the render order is drawn as one bracket per run, with a\n * warning.\n *\n * This is a render-stack bracket, not a scene `
|
|
1
|
+
{"version":3,"file":"layerGroup.js","names":[],"sources":["../../../src/core/layers/layerGroup.ts"],"sourcesContent":["import type { BlendFactors, CompositeOp, DrawCommand, Effect } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport type { Dims } from './render';\n\n/**\n * Several consecutive layers composited as one.\n *\n * A layer's own `effects` run over that layer alone, which is the wrong\n * picture whenever a pass reads neighboring pixels: `blur(A over B)` is not\n * `blur(A) over blur(B)`. A group draws its members into one buffer, runs one\n * chain over it, and composites the result back once.\n *\n * Membership is by `RenderLayer.id` — the same names `layerOrder` and\n * `layerVisibility` use, not the `layers` map's slot keys.\n *\n * Only *consecutive* members share a buffer. A group whose members are\n * separated in the render order is drawn as one bracket per run, with a\n * warning.\n *\n * This is a render-stack bracket, not a scene `GroupNode`.\n */\nexport interface LayerGroup {\n /** Names the group in warnings; not a layer id and never drawn. */\n id: string;\n /**\n * Member layer ids. Order here is ignored — the render order decides.\n *\n * A name claims that layer and every layer under it in the `:` namespace, so\n * `'scene'` covers the `scene:<layerId>` layers a scene with declared layers\n * draws as. The separator is required: `'scene'` does not reach `scenery`.\n * A name matching no layer being drawn is warned about.\n */\n layers: readonly string[];\n /** Passes over the group's combined pixels, in order. A thunk is re-read on\n * every frame, so an animating radius costs no React render. */\n effects?: readonly Effect[] | ((view: View, dims: Dims) => readonly Effect[]);\n /** Opacity applied to the group's composited result, not to each member. */\n alpha?: number;\n /** 4×5 color matrix (row-major, 20 numbers) applied to the composited\n * result. See `GroupDrawCommand.colorMatrix`. */\n colorMatrix?: number[];\n /** How the composited result joins the layers under it. Default\n * `source-over`. See `GroupDrawCommand.composite`. */\n composite?: CompositeOp | BlendFactors;\n}\n\n/**\n * Layer id → the group that owns it. First claim wins across groups, with a\n * warning for the second; the same group naming a layer twice — `['scene',\n * 'scene:tiles']` — passes quietly.\n */\nexport function groupMembership(\n groups: readonly LayerGroup[] | undefined,\n layerIds: Iterable<string>,\n): Map<string, LayerGroup> | null {\n if (!groups || groups.length === 0) return null;\n const stack = [...layerIds];\n const byLayer = new Map<string, LayerGroup>();\n for (const group of groups) {\n for (const name of group.layers) {\n let matched = false;\n for (const id of stack) {\n if (id !== name && !id.startsWith(`${name}:`)) continue;\n matched = true;\n const claimed = byLayer.get(id);\n if (claimed === group) continue;\n if (claimed) {\n console.warn(\n `[weasel] layer \"${id}\" is claimed by layer groups \"${claimed.id}\" and ` +\n `\"${group.id}\"; it stays in \"${claimed.id}\".`,\n );\n continue;\n }\n byLayer.set(id, group);\n }\n if (!matched) {\n console.warn(\n `[weasel] layer group \"${group.id}\" names \"${name}\", which matches no layer ` +\n 'being drawn, so it groups nothing. Check the id against the layer stack — a ' +\n 'scene with declared layers draws as \"scene:<layerId>\", which \"scene\" covers.',\n );\n }\n }\n }\n return byLayer;\n}\n\n/**\n * Put one run of a group's commands under its shared compositing.\n *\n * No transform: every member already carries its own space wrap, so a second\n * one here would apply the view twice to world layers and once to screen ones.\n * A group that composites plainly gets no wrapper at all.\n */\nexport function wrapGroup(\n group: LayerGroup,\n children: DrawCommand[],\n view: View,\n dims: Dims,\n): DrawCommand[] {\n const declared = typeof group.effects === 'function'\n ? group.effects(view, dims)\n : group.effects;\n const effects = declared && declared.length > 0 ? declared : undefined;\n if (!effects && group.alpha === undefined && group.colorMatrix === undefined\n && group.composite === undefined) return children;\n const fades = group.alpha !== undefined && group.alpha < 1;\n return [{\n kind: 'group',\n ...(effects ? { effects } : {}),\n ...(!effects && (fades || group.colorMatrix !== undefined) ? { isolate: true } : {}),\n ...(group.alpha !== undefined ? { alpha: group.alpha } : {}),\n ...(group.colorMatrix !== undefined ? { colorMatrix: group.colorMatrix } : {}),\n ...(group.composite !== undefined ? { composite: group.composite } : {}),\n children,\n }];\n}\n"],"mappings":";;;;;;AAmDA,SAAgB,gBACd,QACA,UACgC;CAChC,IAAI,CAAC,UAAU,OAAO,WAAW,GAAG,OAAO;CAC3C,MAAM,QAAQ,CAAC,GAAG,QAAQ;CAC1B,MAAM,0BAAU,IAAI,IAAwB;CAC5C,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,QAAQ,MAAM,QAAQ;EAC/B,IAAI,UAAU;EACd,KAAK,MAAM,MAAM,OAAO;GACtB,IAAI,OAAO,QAAQ,CAAC,GAAG,WAAW,GAAG,KAAK,EAAE,GAAG;GAC/C,UAAU;GACV,MAAM,UAAU,QAAQ,IAAI,EAAE;GAC9B,IAAI,YAAY,OAAO;GACvB,IAAI,SAAS;IACX,QAAQ,KACN,mBAAmB,GAAG,gCAAgC,QAAQ,GAAG,SAC7D,MAAM,GAAG,kBAAkB,QAAQ,GAAG,GAC5C;IACA;GACF;GACA,QAAQ,IAAI,IAAI,KAAK;EACvB;EACA,IAAI,CAAC,SACH,QAAQ,KACN,yBAAyB,MAAM,GAAG,WAAW,KAAK,mLAGpD;CAEJ;CAEF,OAAO;AACT;;;;;;;;AASA,SAAgB,UACd,OACA,UACA,MACA,MACe;CACf,MAAM,WAAW,OAAO,MAAM,YAAY,aACtC,MAAM,QAAQ,MAAM,IAAI,IACxB,MAAM;CACV,MAAM,UAAU,YAAY,SAAS,SAAS,IAAI,WAAW;CAC7D,IAAI,CAAC,WAAW,MAAM,UAAU,UAAa,MAAM,gBAAgB,UAC5D,MAAM,cAAc,QAAW,OAAO;CAC7C,MAAM,QAAQ,MAAM,UAAU,UAAa,MAAM,QAAQ;CACzD,OAAO,CAAC;EACN,MAAM;EACN,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC7B,GAAI,CAAC,YAAY,SAAS,MAAM,gBAAgB,UAAa,EAAE,SAAS,KAAK,IAAI,CAAC;EAClF,GAAI,MAAM,UAAU,SAAY,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;EAC1D,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC5E,GAAI,MAAM,cAAc,SAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;EACtE;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeRouting.js","names":[],"sources":["../../../src/core/scene/NodeRouting.ts"],"sourcesContent":["/**\n * A routing-trait classifier entry. Other traits (shape, label, icon, …)\n * are independent registries — fields like `label` or `icon` do NOT belong\n * on this interface.\n */\nexport interface NodeRoutingEntry {\n /** Unique kind name. Routing tables key on this string. Consumer-defined;\n * the kit places no constraint beyond uniqueness within a registry. */\n name: string;\n /** Predicate over a node's `data` payload, and the node itself where the caller has one, for\n * a kind its data cannot show (`'
|
|
1
|
+
{"version":3,"file":"NodeRouting.js","names":[],"sources":["../../../src/core/scene/NodeRouting.ts"],"sourcesContent":["/**\n * A routing-trait classifier entry. Other traits (shape, label, icon, …)\n * are independent registries — fields like `label` or `icon` do NOT belong\n * on this interface.\n */\nexport interface NodeRoutingEntry {\n /** Unique kind name. Routing tables key on this string. Consumer-defined;\n * the kit places no constraint beyond uniqueness within a registry. */\n name: string;\n /** Predicate over a node's `data` payload, and the node itself where the caller has one, for\n * a kind its data cannot show (`'group'`). First registered kind whose `matches` returns\n * true claims the node. */\n matches: (data: unknown, node?: RoutedNode) => boolean;\n}\n\n/** What a routing entry may read of a node beside its data. */\nexport interface RoutedNode {\n /** The node's structural kind: `'mark'` or `'group'`. */\n readonly kind?: string;\n}\n\n/**\n * The **routing trait's** registry: answers \"what routing-kind string does\n * this node's data map to?\" The result flows into declarative tool-routing\n * tables (`{ target: 'rect', actionId: 'move' }`).\n */\nexport interface NodeRouting {\n /** Register a kind. Order matters: first match wins during `classify`.\n * Throws if a kind with this name is already registered. */\n register(kind: NodeRoutingEntry): void;\n /** Walk registered kinds in registration order; return the first kind\n * whose `matches(data, node)` returns true, or `'unknown'` if none match. */\n classify(data: unknown, node?: RoutedNode): string;\n /** Lookup a kind entry by name. */\n get(name: string): NodeRoutingEntry | undefined;\n /** Enumerate registered kinds in registration order. */\n list(): readonly NodeRoutingEntry[];\n}\n\n/** The routing kind of a node: the name of the first entry whose `matches(data, node)` returns\n * true, or `'unknown'`. What `NodeRouting.classify` answers, for a caller holding a plain list. */\nexport function routeKind(\n entries: readonly NodeRoutingEntry[],\n data: unknown,\n node?: RoutedNode,\n): string {\n for (const kind of entries) {\n if (kind.matches(data, node)) return kind.name;\n }\n return 'unknown';\n}\n\n/** Build a registry of per-node-kind routing entries. Registering a duplicate\n * kind name throws. */\nexport function createNodeRouting(): NodeRouting {\n const entries: NodeRoutingEntry[] = [];\n const byName = new Map<string, NodeRoutingEntry>();\n return {\n register(kind) {\n if (byName.has(kind.name)) {\n throw new Error(\n `createNodeRouting: duplicate kind name \"${kind.name}\"`,\n );\n }\n byName.set(kind.name, kind);\n entries.push(kind);\n },\n classify(data, node) {\n return routeKind(entries, data, node);\n },\n get(name) {\n return byName.get(name);\n },\n list() {\n return entries;\n },\n };\n}\n"],"mappings":";;;AAyCA,SAAgB,UACd,SACA,MACA,MACQ;CACR,KAAK,MAAM,QAAQ,SACjB,IAAI,KAAK,QAAQ,MAAM,IAAI,GAAG,OAAO,KAAK;CAE5C,OAAO;AACT;;;AAIA,SAAgB,oBAAiC;CAC/C,MAAM,UAA8B,CAAC;CACrC,MAAM,yBAAS,IAAI,IAA8B;CACjD,OAAO;EACL,SAAS,MAAM;GACb,IAAI,OAAO,IAAI,KAAK,IAAI,GACtB,MAAM,IAAI,MACR,2CAA2C,KAAK,KAAK,EACvD;GAEF,OAAO,IAAI,KAAK,MAAM,IAAI;GAC1B,QAAQ,KAAK,IAAI;EACnB;EACA,SAAS,MAAM,MAAM;GACnB,OAAO,UAAU,SAAS,MAAM,IAAI;EACtC;EACA,IAAI,MAAM;GACR,OAAO,OAAO,IAAI,IAAI;EACxB;EACA,OAAO;GACL,OAAO;EACT;CACF;AACF"}
|
|
@@ -57,7 +57,7 @@ function createArrivalWindow(env) {
|
|
|
57
57
|
const landed = heldMoves(nodes, pending, true);
|
|
58
58
|
const left = heldMoves(nodes, departures, false);
|
|
59
59
|
departures = /* @__PURE__ */ new Map();
|
|
60
|
-
const touched = new Set([...changed].filter((id) => nodes.
|
|
60
|
+
const touched = new Set([...changed].filter((id) => nodes.has(id)));
|
|
61
61
|
if (landed.size === 0 && touched.size === 0) return [];
|
|
62
62
|
const poses = handler(landed, touched, left);
|
|
63
63
|
if (poses === null) throw new SceneArrivalRefused(landed);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrivalWindow.js","names":[],"sources":["../../../src/core/scene/arrivalWindow.ts"],"sourcesContent":["/**\n * The scene's arrival window: what a live edit did to containers, collected\n * while it runs and handed to a layout handler before it closes.\n *\n * The window is open while a live edit is collecting the nodes that joined a\n * container, the ones that left one, and every container whose children or\n * size it changed; whoever opened the window settles it before the edit\n * closes. Undo, redo and journal replays never open it: they restore an\n * arrangement that was already decided.\n */\nimport { createEmitter } from '@weasel-js/registry';\nimport type { Op } from 'core/ops/types';\nimport { createArrangeOp, type ArrangedPose } from 'core/ops/arrange';\nimport { SceneArrivalRefused } from './arrivals';\nimport type { LayoutMove, Node, NodeId, SceneArrivalHandler } from './types';\n\n/** What the window needs from the scene it serves. */\nexport interface ArrivalWindowEnv<TData, TLayer extends string, TPose> {\n nodes: Map<NodeId, Node<TData, TLayer, TPose>>;\n /** The pass over the scene's declared layouts, which settles arrivals once\n * a container has declared one. An installed handler takes its place. */\n layoutPass: SceneArrivalHandler<TPose>;\n /** Whether a bare scene call would record an edit of its own right now:\n * no batch is open and no history engine is driving. */\n recordsBareEdits(): boolean;\n /** Write one arranged pose. Bound into the arrange op, since not every\n * adapter an edit is applied through can write poses. */\n writePose(id: string, pose: unknown): void;\n}\n\nexport interface ArrivalWindow<TPose> {\n /** A container was given a layout: from here on the scene has arrivals to\n * settle even with no handler installed. */\n layoutDeclared(): void;\n /** Install the handler that settles arrivals. Returns its remover. */\n setHandler(handler: SceneArrivalHandler<TPose>): () => void;\n /** Hear the poses each live edit's settle wrote, after the edit lands. */\n onReflow(listener: (moves: readonly LayoutMove<TPose>[]) => void): () => void;\n noteArrival(id: NodeId, parent: NodeId | null, from: NodeId | null): void;\n noteDeparture(id: NodeId, from: NodeId | null, to: NodeId | null): void;\n /** A container's child set or order changed, it changed size, or a child was posed. */\n noteChange(id: NodeId | null): void;\n /** Whether a bare scene call has to run as a batch of its own, so the\n * layout pass it triggers lands in its undo step. */\n bareLayoutEdit(): boolean;\n /** Open the window when a handler wants one — or `always`, for an edit that\n * may give the scene its first layout partway through — and no enclosing\n * edit holds it already; true when this caller now owns it. */\n open(always?: boolean): boolean;\n /** Close the window and ask the handler what the arrivals get. Throws\n * `SceneArrivalRefused` when it refuses, which reverts the edit. */\n settle(): ArrangedPose[];\n /** Report what the settled edit wrote, now that it has landed. */\n emitReflow(): void;\n /** Close the window with nothing settled or reported: its owner's edit is\n * over, however it ended. */\n abandon(): void;\n /** Run an op-driven edit that may carry arrivals: a refusal reverts it and\n * is swallowed, the way a drop onto a full container is — nothing lands.\n * Returns whether the edit landed. */\n runEdit(ops: Op[], run: (ops: Op[]) => void): boolean;\n}\n\n/** Of the ids noted per container, those the landed edit bears out: still\n * under it when `joined`, gone from it otherwise. A container that is itself\n * gone is left out. */\nfunction heldMoves(\n nodes: ReadonlyMap<NodeId, { parent: NodeId | null }>,\n noted: ReadonlyMap<NodeId, NodeId[]>,\n joined: boolean,\n): Map<NodeId, NodeId[]> {\n const out = new Map<NodeId, NodeId[]>();\n for (const [parent, ids] of noted) {\n const held = ids.filter((id) => (nodes.get(id)?.parent === parent) === joined);\n if (held.length > 0 && nodes.has(parent)) out.set(parent, held);\n }\n return out;\n}\n\nexport function createArrivalWindow<TData, TLayer extends string, TPose>(\n env: ArrivalWindowEnv<TData, TLayer, TPose>,\n): ArrivalWindow<TPose> {\n const { nodes } = env;\n let installed: SceneArrivalHandler<TPose> | null = null;\n let hasLayouts = false;\n let arrivals: Map<NodeId, NodeId[]> | null = null;\n let departures = new Map<NodeId, NodeId[]>();\n let changed = new Set<NodeId>();\n /** What the last settle wrote, reported to `onReflow` once its edit lands. */\n let reflowDue: LayoutMove<TPose>[] | null = null;\n const reflowListeners = createEmitter<[moves: readonly LayoutMove<TPose>[]]>();\n\n const activeHandler = (): SceneArrivalHandler<TPose> | null => installed ?? (hasLayouts ? env.layoutPass : null);\n\n function open(always = false): boolean {\n if (arrivals !== null || (!always && activeHandler() === null)) return false;\n arrivals = new Map();\n departures = new Map();\n changed = new Set();\n reflowDue = null;\n return true;\n }\n\n function emitReflow(): void {\n const moves = reflowDue;\n reflowDue = null;\n if (moves === null || moves.length === 0) return;\n reflowListeners.emit(moves);\n }\n\n function settle(): ArrangedPose[] {\n const pending = arrivals;\n arrivals = null;\n const handler = activeHandler();\n if (pending === null || handler === null) return [];\n // Only what is still there once the edit's other mutations have landed.\n const landed = heldMoves(nodes, pending, true);\n // Only what is gone from the container once the edit has landed: a child\n // that left and came back did not depart.\n const left = heldMoves(nodes, departures, false);\n departures = new Map();\n const touched = new Set([...changed].filter((id) => nodes.get(id)?.kind === 'container'));\n if (landed.size === 0 && touched.size === 0) return [];\n const poses = handler(landed, touched, left);\n if (poses === null) throw new SceneArrivalRefused(landed);\n const out: ArrangedPose[] = [];\n for (const [id, to] of poses) {\n const node = nodes.get(id);\n if (node) out.push({ id, from: node.pose, to });\n }\n reflowDue = out as LayoutMove<TPose>[];\n return out;\n }\n\n function abandon(): void {\n arrivals = null;\n reflowDue = null;\n }\n\n /** `ops` plus the op that settles their arrivals, when this call owns the\n * window; `ops` unchanged otherwise. Opened even on a scene with no layout\n * yet: an edit's op list is fixed before it runs, and any op in it may give\n * the scene its first. The arrange op that settles nothing elides itself. */\n function withArrivalOps(ops: Op[]): { ops: Op[]; owned: boolean } {\n if (!open(true)) return { ops, owned: false };\n return { ops: [...ops, createArrangeOp({}, settle, env.writePose)], owned: true };\n }\n\n return {\n layoutDeclared: () => { hasLayouts = true; },\n setHandler(handler) {\n installed = handler;\n return () => {\n if (installed === handler) installed = null;\n };\n },\n onReflow: reflowListeners.subscribe,\n noteArrival(id, parent, from) {\n if (arrivals === null || parent === null || parent === from) return;\n changed.add(parent);\n const list = arrivals.get(parent);\n if (list === undefined) arrivals.set(parent, [id]);\n else if (!list.includes(id)) list.push(id);\n },\n noteDeparture(id, from, to) {\n if (arrivals === null || from === null || from === to) return;\n changed.add(from);\n // One that joined earlier in this edit was never there to leave.\n if (arrivals.get(from)?.includes(id)) return;\n const list = departures.get(from);\n if (list === undefined) departures.set(from, [id]);\n else if (!list.includes(id)) list.push(id);\n },\n noteChange(id) {\n if (arrivals !== null && id !== null) changed.add(id);\n },\n bareLayoutEdit: () => activeHandler() !== null && arrivals === null && env.recordsBareEdits(),\n open,\n settle,\n emitReflow,\n abandon,\n runEdit(ops, run) {\n const { ops: all, owned } = withArrivalOps(ops);\n // An edit inside another's window leaves a refusal to the one that owns\n // it, which reverts the whole edit and answers for it.\n if (!owned) return run(all), true;\n try {\n run(all);\n emitReflow();\n return true;\n } catch (err) {\n if (!(err instanceof SceneArrivalRefused)) throw err;\n return false;\n } finally {\n abandon();\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"arrivalWindow.js","names":[],"sources":["../../../src/core/scene/arrivalWindow.ts"],"sourcesContent":["/**\n * The scene's arrival window: what a live edit did to containers, collected\n * while it runs and handed to a layout handler before it closes.\n *\n * The window is open while a live edit is collecting the nodes that joined a\n * container, the ones that left one, and every container whose children or\n * size it changed; whoever opened the window settles it before the edit\n * closes. Undo, redo and journal replays never open it: they restore an\n * arrangement that was already decided.\n */\nimport { createEmitter } from '@weasel-js/registry';\nimport type { Op } from 'core/ops/types';\nimport { createArrangeOp, type ArrangedPose } from 'core/ops/arrange';\nimport { SceneArrivalRefused } from './arrivals';\nimport type { LayoutMove, Node, NodeId, SceneArrivalHandler } from './types';\n\n/** What the window needs from the scene it serves. */\nexport interface ArrivalWindowEnv<TData, TLayer extends string, TPose> {\n nodes: Map<NodeId, Node<TData, TLayer, TPose>>;\n /** The pass over the scene's declared layouts, which settles arrivals once\n * a container has declared one. An installed handler takes its place. */\n layoutPass: SceneArrivalHandler<TPose>;\n /** Whether a bare scene call would record an edit of its own right now:\n * no batch is open and no history engine is driving. */\n recordsBareEdits(): boolean;\n /** Write one arranged pose. Bound into the arrange op, since not every\n * adapter an edit is applied through can write poses. */\n writePose(id: string, pose: unknown): void;\n}\n\nexport interface ArrivalWindow<TPose> {\n /** A container was given a layout: from here on the scene has arrivals to\n * settle even with no handler installed. */\n layoutDeclared(): void;\n /** Install the handler that settles arrivals. Returns its remover. */\n setHandler(handler: SceneArrivalHandler<TPose>): () => void;\n /** Hear the poses each live edit's settle wrote, after the edit lands. */\n onReflow(listener: (moves: readonly LayoutMove<TPose>[]) => void): () => void;\n noteArrival(id: NodeId, parent: NodeId | null, from: NodeId | null): void;\n noteDeparture(id: NodeId, from: NodeId | null, to: NodeId | null): void;\n /** A container's child set or order changed, it changed size, or a child was posed. */\n noteChange(id: NodeId | null): void;\n /** Whether a bare scene call has to run as a batch of its own, so the\n * layout pass it triggers lands in its undo step. */\n bareLayoutEdit(): boolean;\n /** Open the window when a handler wants one — or `always`, for an edit that\n * may give the scene its first layout partway through — and no enclosing\n * edit holds it already; true when this caller now owns it. */\n open(always?: boolean): boolean;\n /** Close the window and ask the handler what the arrivals get. Throws\n * `SceneArrivalRefused` when it refuses, which reverts the edit. */\n settle(): ArrangedPose[];\n /** Report what the settled edit wrote, now that it has landed. */\n emitReflow(): void;\n /** Close the window with nothing settled or reported: its owner's edit is\n * over, however it ended. */\n abandon(): void;\n /** Run an op-driven edit that may carry arrivals: a refusal reverts it and\n * is swallowed, the way a drop onto a full container is — nothing lands.\n * Returns whether the edit landed. */\n runEdit(ops: Op[], run: (ops: Op[]) => void): boolean;\n}\n\n/** Of the ids noted per container, those the landed edit bears out: still\n * under it when `joined`, gone from it otherwise. A container that is itself\n * gone is left out. */\nfunction heldMoves(\n nodes: ReadonlyMap<NodeId, { parent: NodeId | null }>,\n noted: ReadonlyMap<NodeId, NodeId[]>,\n joined: boolean,\n): Map<NodeId, NodeId[]> {\n const out = new Map<NodeId, NodeId[]>();\n for (const [parent, ids] of noted) {\n const held = ids.filter((id) => (nodes.get(id)?.parent === parent) === joined);\n if (held.length > 0 && nodes.has(parent)) out.set(parent, held);\n }\n return out;\n}\n\nexport function createArrivalWindow<TData, TLayer extends string, TPose>(\n env: ArrivalWindowEnv<TData, TLayer, TPose>,\n): ArrivalWindow<TPose> {\n const { nodes } = env;\n let installed: SceneArrivalHandler<TPose> | null = null;\n let hasLayouts = false;\n let arrivals: Map<NodeId, NodeId[]> | null = null;\n let departures = new Map<NodeId, NodeId[]>();\n let changed = new Set<NodeId>();\n /** What the last settle wrote, reported to `onReflow` once its edit lands. */\n let reflowDue: LayoutMove<TPose>[] | null = null;\n const reflowListeners = createEmitter<[moves: readonly LayoutMove<TPose>[]]>();\n\n const activeHandler = (): SceneArrivalHandler<TPose> | null => installed ?? (hasLayouts ? env.layoutPass : null);\n\n function open(always = false): boolean {\n if (arrivals !== null || (!always && activeHandler() === null)) return false;\n arrivals = new Map();\n departures = new Map();\n changed = new Set();\n reflowDue = null;\n return true;\n }\n\n function emitReflow(): void {\n const moves = reflowDue;\n reflowDue = null;\n if (moves === null || moves.length === 0) return;\n reflowListeners.emit(moves);\n }\n\n function settle(): ArrangedPose[] {\n const pending = arrivals;\n arrivals = null;\n const handler = activeHandler();\n if (pending === null || handler === null) return [];\n // Only what is still there once the edit's other mutations have landed.\n const landed = heldMoves(nodes, pending, true);\n // Only what is gone from the container once the edit has landed: a child\n // that left and came back did not depart.\n const left = heldMoves(nodes, departures, false);\n departures = new Map();\n const touched = new Set([...changed].filter((id) => nodes.has(id)));\n if (landed.size === 0 && touched.size === 0) return [];\n const poses = handler(landed, touched, left);\n if (poses === null) throw new SceneArrivalRefused(landed);\n const out: ArrangedPose[] = [];\n for (const [id, to] of poses) {\n const node = nodes.get(id);\n if (node) out.push({ id, from: node.pose, to });\n }\n reflowDue = out as LayoutMove<TPose>[];\n return out;\n }\n\n function abandon(): void {\n arrivals = null;\n reflowDue = null;\n }\n\n /** `ops` plus the op that settles their arrivals, when this call owns the\n * window; `ops` unchanged otherwise. Opened even on a scene with no layout\n * yet: an edit's op list is fixed before it runs, and any op in it may give\n * the scene its first. The arrange op that settles nothing elides itself. */\n function withArrivalOps(ops: Op[]): { ops: Op[]; owned: boolean } {\n if (!open(true)) return { ops, owned: false };\n return { ops: [...ops, createArrangeOp({}, settle, env.writePose)], owned: true };\n }\n\n return {\n layoutDeclared: () => { hasLayouts = true; },\n setHandler(handler) {\n installed = handler;\n return () => {\n if (installed === handler) installed = null;\n };\n },\n onReflow: reflowListeners.subscribe,\n noteArrival(id, parent, from) {\n if (arrivals === null || parent === null || parent === from) return;\n changed.add(parent);\n const list = arrivals.get(parent);\n if (list === undefined) arrivals.set(parent, [id]);\n else if (!list.includes(id)) list.push(id);\n },\n noteDeparture(id, from, to) {\n if (arrivals === null || from === null || from === to) return;\n changed.add(from);\n // One that joined earlier in this edit was never there to leave.\n if (arrivals.get(from)?.includes(id)) return;\n const list = departures.get(from);\n if (list === undefined) departures.set(from, [id]);\n else if (!list.includes(id)) list.push(id);\n },\n noteChange(id) {\n if (arrivals !== null && id !== null) changed.add(id);\n },\n bareLayoutEdit: () => activeHandler() !== null && arrivals === null && env.recordsBareEdits(),\n open,\n settle,\n emitReflow,\n abandon,\n runEdit(ops, run) {\n const { ops: all, owned } = withArrivalOps(ops);\n // An edit inside another's window leaves a refusal to the one that owns\n // it, which reverts the whole edit and answers for it.\n if (!owned) return run(all), true;\n try {\n run(all);\n emitReflow();\n return true;\n } catch (err) {\n if (!(err instanceof SceneArrivalRefused)) throw err;\n return false;\n } finally {\n abandon();\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
//#region src/core/scene/clipNothing.ts
|
|
2
2
|
/** Registry key for {@link clipNothing}; every scene's registry carries it. */
|
|
3
3
|
var CLIP_NOTHING = "kit:clipNothing";
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
6
|
-
* children may draw outside it. A container without one clips its children to
|
|
7
|
-
* its painter's silhouette.
|
|
8
|
-
*/
|
|
4
|
+
/** A `clipFromPose` that clips nothing, registered in every scene so a save
|
|
5
|
+
* naming {@link CLIP_NOTHING} loads. */
|
|
9
6
|
var clipNothing = () => null;
|
|
10
7
|
|
|
11
8
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipNothing.js","names":[],"sources":["../../../src/core/scene/clipNothing.ts"],"sourcesContent":["/** Registry key for {@link clipNothing}; every scene's registry carries it. */\nexport const CLIP_NOTHING = 'kit:clipNothing';\n\n
|
|
1
|
+
{"version":3,"file":"clipNothing.js","names":[],"sources":["../../../src/core/scene/clipNothing.ts"],"sourcesContent":["/** Registry key for {@link clipNothing}; every scene's registry carries it. */\nexport const CLIP_NOTHING = 'kit:clipNothing';\n\n/** A `clipFromPose` that clips nothing, registered in every scene so a save\n * naming {@link CLIP_NOTHING} loads. */\nexport const clipNothing = (): null => null;\n"],"mappings":";;AACA,IAAa,eAAe;;;AAI5B,IAAa,oBAA0B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { sameValue } from "./sameValue.js";
|
|
2
|
+
|
|
3
|
+
//#region src/core/scene/declaredFieldSetters.ts
|
|
4
|
+
/** Stored only while declared, so a snapshot of a node declaring none carries neither. */
|
|
5
|
+
function write(node, field, to) {
|
|
6
|
+
if (to === void 0) delete node[field];
|
|
7
|
+
else node[field] = to;
|
|
8
|
+
}
|
|
9
|
+
var KIND = {
|
|
10
|
+
units: "kit:setUnits",
|
|
11
|
+
clip: "kit:setClip"
|
|
12
|
+
};
|
|
13
|
+
/** Register `kit:setUnits` and `kit:setClip`, and answer with the scene
|
|
14
|
+
* methods that record them. */
|
|
15
|
+
function createDeclaredFieldSetters(env) {
|
|
16
|
+
for (const field of ["units", "clip"]) env.register(KIND[field], {
|
|
17
|
+
apply: (p) => {
|
|
18
|
+
const { id, to } = p;
|
|
19
|
+
write(env.requireNode(id), field, to);
|
|
20
|
+
},
|
|
21
|
+
revert: (p) => {
|
|
22
|
+
const { id, from } = p;
|
|
23
|
+
write(env.requireNode(id), field, from);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const set = (field, label, verb) => (id, to) => {
|
|
27
|
+
const from = env.requireNode(id)[field];
|
|
28
|
+
env.refuse(id, verb);
|
|
29
|
+
if (sameValue(from, to ?? void 0)) return;
|
|
30
|
+
env.record(KIND[field], {
|
|
31
|
+
id,
|
|
32
|
+
from,
|
|
33
|
+
to: to ?? void 0
|
|
34
|
+
}, label);
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
setUnits: set("units", "setUnits", "set the units of"),
|
|
38
|
+
setClip: set("clip", "setClip", "set the clip of")
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { createDeclaredFieldSetters };
|
|
44
|
+
//# sourceMappingURL=declaredFieldSetters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declaredFieldSetters.js","names":[],"sources":["../../../src/core/scene/declaredFieldSetters.ts"],"sourcesContent":["/**\n * What a node declares for its children — the unit scope they are shown in and\n * the clip they are drawn in: the kit ops that write each, and the scene\n * methods that record them.\n */\nimport type { UnitScope } from '@weasel-js/quantity';\nimport type { LockGuard } from './lockGuard';\nimport { sameValue } from './sameValue';\nimport type { Node, NodeClip, NodeId, RegisteredOp } from './types';\n\n/** The scene methods that write a node's declared `units` and `clip`, each one undo step. */\nexport interface SceneDeclaredFieldSetters {\n /** Declare the unit scope of container `id`, or clear it with `null`. */\n setUnits(id: NodeId, units: UnitScope | null): void;\n /** Declare the clip `id` draws its children in (see `SceneNode.clip`), or clear it with `null`. */\n setClip(id: NodeId, clip: NodeClip | null): void;\n}\n\n/** What `createDeclaredFieldSetters` needs from the scene it is built into. */\nexport interface DeclaredFieldSettersEnv<TData, TLayer extends string, TPose> {\n register(kind: string, handler: RegisteredOp<unknown>): void;\n requireNode(id: NodeId): Node<TData, TLayer, TPose>;\n refuse: LockGuard<TLayer>['refuse'];\n record(kind: string, payload: unknown, label: string): void;\n}\n\ntype Declared = { units?: UnitScope; clip?: NodeClip };\ntype Field = keyof Declared;\ninterface DeclaredPayload { id: NodeId; from?: unknown; to?: unknown }\n\n/** Stored only while declared, so a snapshot of a node declaring none carries neither. */\nfunction write(node: Declared, field: Field, to: unknown): void {\n if (to === undefined) delete node[field];\n else (node as Record<Field, unknown>)[field] = to;\n}\n\nconst KIND: Record<Field, string> = { units: 'kit:setUnits', clip: 'kit:setClip' };\n\n/** Register `kit:setUnits` and `kit:setClip`, and answer with the scene\n * methods that record them. */\nexport function createDeclaredFieldSetters<TData, TLayer extends string, TPose>(\n env: DeclaredFieldSettersEnv<TData, TLayer, TPose>,\n): SceneDeclaredFieldSetters {\n for (const field of ['units', 'clip'] as const) {\n env.register(KIND[field], {\n apply: (p) => { const { id, to } = p as DeclaredPayload; write(env.requireNode(id), field, to); },\n revert: (p) => { const { id, from } = p as DeclaredPayload; write(env.requireNode(id), field, from); },\n });\n }\n\n const set = (field: Field, label: string, verb: string) => (id: NodeId, to: unknown): void => {\n const from = (env.requireNode(id) as Declared)[field];\n env.refuse(id, verb);\n if (sameValue(from, to ?? undefined)) return;\n env.record(KIND[field], { id, from, to: to ?? undefined } satisfies DeclaredPayload, label);\n };\n\n return {\n setUnits: set('units', 'setUnits', 'set the units of'),\n setClip: set('clip', 'setClip', 'set the clip of'),\n };\n}\n"],"mappings":";;;;AA+BA,SAAS,MAAM,MAAgB,OAAc,IAAmB;CAC9D,IAAI,OAAO,QAAW,OAAO,KAAK;MAC7B,KAAiC,SAAS;AACjD;AAEA,IAAM,OAA8B;CAAE,OAAO;CAAgB,MAAM;AAAc;;;AAIjF,SAAgB,2BACd,KAC2B;CAC3B,KAAK,MAAM,SAAS,CAAC,SAAS,MAAM,GAClC,IAAI,SAAS,KAAK,QAAQ;EACxB,QAAQ,MAAM;GAAE,MAAM,EAAE,IAAI,OAAO;GAAsB,MAAM,IAAI,YAAY,EAAE,GAAG,OAAO,EAAE;EAAG;EAChG,SAAS,MAAM;GAAE,MAAM,EAAE,IAAI,SAAS;GAAsB,MAAM,IAAI,YAAY,EAAE,GAAG,OAAO,IAAI;EAAG;CACvG,CAAC;CAGH,MAAM,OAAO,OAAc,OAAe,UAAkB,IAAY,OAAsB;EAC5F,MAAM,OAAQ,IAAI,YAAY,EAAE,CAAC,CAAc;EAC/C,IAAI,OAAO,IAAI,IAAI;EACnB,IAAI,UAAU,MAAM,MAAM,MAAS,GAAG;EACtC,IAAI,OAAO,KAAK,QAAQ;GAAE;GAAI;GAAM,IAAI,MAAM;EAAU,GAA6B,KAAK;CAC5F;CAEA,OAAO;EACL,UAAU,IAAI,SAAS,YAAY,kBAAkB;EACrD,SAAS,IAAI,QAAQ,WAAW,iBAAiB;CACnD;AACF"}
|
|
@@ -30,6 +30,9 @@ var RecordedDep = class {
|
|
|
30
30
|
get path() {
|
|
31
31
|
return this.paths[this.index] = this.dep.path;
|
|
32
32
|
}
|
|
33
|
+
get inherited() {
|
|
34
|
+
return this.dep.inherited;
|
|
35
|
+
}
|
|
33
36
|
};
|
|
34
37
|
/** Run `derive` against `deps`, recording in place what it was run against.
|
|
35
38
|
* Mutating the record is what updates the memo slot, which holds this object. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"depMemo.js","names":[],"sources":["../../../src/core/scene/depMemo.ts"],"sourcesContent":["/**\n * What a memoized derivation was derived *from*, so a hit can re-check it.\n *\n * A derivation keyed on the deriving node's own authored pose only notices a\n * change someone pushed. A dependency that changed with nothing pushed behind\n * it — an ancestor's frame moving, a removal, a dependency appearing, a lookup\n * answering nodes no scene edits — is invisible to that key. Recording what\n * the derivation was handed and comparing it on the hit path is what notices\n * it without a trigger.\n *\n * What is recorded is what a derivation may read of a dependency: its node (an\n * identity), its pose (by value), its `data` (a reference, which every edit\n * replaces), its `layer`, and its path where the derivation read it. Anything\n * else on the node — its parent, its children — still needs a push. A\n * derivation that read glyph outlines also records the face generation, since\n * a face landing changes its result with nothing in the scene changed.\n */\nimport type { Path } from '@weasel-js/geom';\nimport { fontGeneration, noteFontRead, withFontReads } from './fontReads';\nimport { samePoseValue, snapshotPose } from './poseSnapshot';\nimport type { DerivedDep, Node, NodeId } from './types';\n\n/** Each dependency's node — an identity, so a restored clone is a different\n * one — with a copy of the pose it resolved to, its `data` and `layer`, and\n * the path it answered, `undefined` where the derivation never asked. */\nexport interface DepRecord<TPose> {\n nodes: (object | undefined)[];\n poses: (TPose | undefined)[];\n data: unknown[];\n layers: (string | undefined)[];\n paths: (Path | null | undefined)[];\n /** The face generation it was derived under, or `null` when it read no\n * glyph outlines. */\n faces: number | null;\n}\n\nexport function emptyDepRecord<TPose>(): DepRecord<TPose> {\n return { nodes: [], poses: [], data: [], layers: [], paths: [], faces: null };\n}\n\n/** A dependency as the derivation is handed it, noting a read of its path. A\n * class for the reason `SceneDep` is one: a prototype accessor is cheap. */\nclass RecordedDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(\n private readonly dep: DerivedDep<TPose>,\n private readonly paths: (Path | null | undefined)[],\n private readonly index: number,\n ) {\n this.node = dep.node;\n this.pose = dep.pose;\n }\n\n get path(): Path | null {\n return (this.paths[this.index] = this.dep.path);\n }\n}\n\n/** Run `derive` against `deps`, recording in place what it was run against.\n * Mutating the record is what updates the memo slot, which holds this object. */\nexport function deriveRecorded<TPose, T>(\n into: DepRecord<TPose>,\n deps: readonly (DerivedDep<TPose> | undefined)[],\n derive: (deps: readonly (DerivedDep<TPose> | undefined)[]) => T,\n): T {\n into.nodes.length = 0;\n into.poses.length = 0;\n into.data.length = 0;\n into.layers.length = 0;\n into.paths.length = 0;\n const handed = deps.map((dep, i) => {\n into.nodes.push(dep?.node);\n into.poses.push(dep === undefined ? undefined : snapshotPose(dep.pose));\n into.data.push(dep?.node.data);\n into.layers.push(dep?.node.layer);\n into.paths.push(undefined);\n return dep === undefined ? undefined : new RecordedDep(dep, into.paths, i);\n });\n const generation = fontGeneration();\n const { value, fonts } = withFontReads(() => derive(handed));\n into.faces = fonts ? generation : null;\n return value;\n}\n\n/** Whether the dependencies still resolve to what the record was taken from.\n * Resolves them one at a time rather than taking an array: this is the hit\n * path, and it walks every derived node's every dependency on every frame. */\nexport function sameDeps<TPose>(\n record: DepRecord<TPose>,\n ids: readonly NodeId[],\n depOf: (id: NodeId) => DerivedDep<TPose> | undefined,\n): boolean {\n if (record.nodes.length !== ids.length) return false;\n if (record.faces !== null && record.faces !== fontGeneration()) return false;\n for (let i = 0; i < ids.length; i++) {\n const dep = depOf(ids[i]);\n if (record.nodes[i] !== (dep === undefined ? undefined : dep.node)) return false;\n if (dep === undefined) continue;\n if (record.data[i] !== dep.node.data || record.layers[i] !== dep.node.layer) return false;\n if (!samePoseValue(record.poses[i], dep.pose)) return false;\n const path = record.paths[i];\n if (path !== undefined && path !== dep.path) return false;\n }\n // A hit is still a read, for any derivation enclosing this one.\n if (record.faces !== null) noteFontRead();\n return true;\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"depMemo.js","names":[],"sources":["../../../src/core/scene/depMemo.ts"],"sourcesContent":["/**\n * What a memoized derivation was derived *from*, so a hit can re-check it.\n *\n * A derivation keyed on the deriving node's own authored pose only notices a\n * change someone pushed. A dependency that changed with nothing pushed behind\n * it — an ancestor's frame moving, a removal, a dependency appearing, a lookup\n * answering nodes no scene edits — is invisible to that key. Recording what\n * the derivation was handed and comparing it on the hit path is what notices\n * it without a trigger.\n *\n * What is recorded is what a derivation may read of a dependency: its node (an\n * identity), its pose (by value), its `data` (a reference, which every edit\n * replaces), its `layer`, and its path where the derivation read it. Anything\n * else on the node — its parent, its children — still needs a push. A\n * derivation that read glyph outlines also records the face generation, since\n * a face landing changes its result with nothing in the scene changed.\n */\nimport type { Path } from '@weasel-js/geom';\nimport type { InheritedPaint } from './inheritedPaint';\nimport { fontGeneration, noteFontRead, withFontReads } from './fontReads';\nimport { samePoseValue, snapshotPose } from './poseSnapshot';\nimport type { DerivedDep, Node, NodeId } from './types';\n\n/** Each dependency's node — an identity, so a restored clone is a different\n * one — with a copy of the pose it resolved to, its `data` and `layer`, and\n * the path it answered, `undefined` where the derivation never asked. */\nexport interface DepRecord<TPose> {\n nodes: (object | undefined)[];\n poses: (TPose | undefined)[];\n data: unknown[];\n layers: (string | undefined)[];\n paths: (Path | null | undefined)[];\n /** The face generation it was derived under, or `null` when it read no\n * glyph outlines. */\n faces: number | null;\n}\n\nexport function emptyDepRecord<TPose>(): DepRecord<TPose> {\n return { nodes: [], poses: [], data: [], layers: [], paths: [], faces: null };\n}\n\n/** A dependency as the derivation is handed it, noting a read of its path. A\n * class for the reason `SceneDep` is one: a prototype accessor is cheap. */\nclass RecordedDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(\n private readonly dep: DerivedDep<TPose>,\n private readonly paths: (Path | null | undefined)[],\n private readonly index: number,\n ) {\n this.node = dep.node;\n this.pose = dep.pose;\n }\n\n get path(): Path | null {\n return (this.paths[this.index] = this.dep.path);\n }\n\n get inherited(): InheritedPaint | undefined {\n return this.dep.inherited;\n }\n}\n\n/** Run `derive` against `deps`, recording in place what it was run against.\n * Mutating the record is what updates the memo slot, which holds this object. */\nexport function deriveRecorded<TPose, T>(\n into: DepRecord<TPose>,\n deps: readonly (DerivedDep<TPose> | undefined)[],\n derive: (deps: readonly (DerivedDep<TPose> | undefined)[]) => T,\n): T {\n into.nodes.length = 0;\n into.poses.length = 0;\n into.data.length = 0;\n into.layers.length = 0;\n into.paths.length = 0;\n const handed = deps.map((dep, i) => {\n into.nodes.push(dep?.node);\n into.poses.push(dep === undefined ? undefined : snapshotPose(dep.pose));\n into.data.push(dep?.node.data);\n into.layers.push(dep?.node.layer);\n into.paths.push(undefined);\n return dep === undefined ? undefined : new RecordedDep(dep, into.paths, i);\n });\n const generation = fontGeneration();\n const { value, fonts } = withFontReads(() => derive(handed));\n into.faces = fonts ? generation : null;\n return value;\n}\n\n/** Whether the dependencies still resolve to what the record was taken from.\n * Resolves them one at a time rather than taking an array: this is the hit\n * path, and it walks every derived node's every dependency on every frame. */\nexport function sameDeps<TPose>(\n record: DepRecord<TPose>,\n ids: readonly NodeId[],\n depOf: (id: NodeId) => DerivedDep<TPose> | undefined,\n): boolean {\n if (record.nodes.length !== ids.length) return false;\n if (record.faces !== null && record.faces !== fontGeneration()) return false;\n for (let i = 0; i < ids.length; i++) {\n const dep = depOf(ids[i]);\n if (record.nodes[i] !== (dep === undefined ? undefined : dep.node)) return false;\n if (dep === undefined) continue;\n if (record.data[i] !== dep.node.data || record.layers[i] !== dep.node.layer) return false;\n if (!samePoseValue(record.poses[i], dep.pose)) return false;\n const path = record.paths[i];\n if (path !== undefined && path !== dep.path) return false;\n }\n // A hit is still a read, for any derivation enclosing this one.\n if (record.faces !== null) noteFontRead();\n return true;\n}\n"],"mappings":";;;;AAqCA,SAAgB,iBAA0C;CACxD,OAAO;EAAE,OAAO,CAAC;EAAG,OAAO,CAAC;EAAG,MAAM,CAAC;EAAG,QAAQ,CAAC;EAAG,OAAO,CAAC;EAAG,OAAO;CAAK;AAC9E;;;AAIA,IAAM,cAAN,MAAsD;CAKjC;CACA;CACA;CANnB;CACA;CAEA,YACE,KACA,OACA,OACA;EAHiB,KAAA,MAAA;EACA,KAAA,QAAA;EACA,KAAA,QAAA;EAEjB,KAAK,OAAO,IAAI;EAChB,KAAK,OAAO,IAAI;CAClB;CAEA,IAAI,OAAoB;EACtB,OAAQ,KAAK,MAAM,KAAK,SAAS,KAAK,IAAI;CAC5C;CAEA,IAAI,YAAwC;EAC1C,OAAO,KAAK,IAAI;CAClB;AACF;;;AAIA,SAAgB,eACd,MACA,MACA,QACG;CACH,KAAK,MAAM,SAAS;CACpB,KAAK,MAAM,SAAS;CACpB,KAAK,KAAK,SAAS;CACnB,KAAK,OAAO,SAAS;CACrB,KAAK,MAAM,SAAS;CACpB,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM;EAClC,KAAK,MAAM,KAAK,KAAK,IAAI;EACzB,KAAK,MAAM,KAAK,QAAQ,SAAY,SAAY,aAAa,IAAI,IAAI,CAAC;EACtE,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI;EAC7B,KAAK,OAAO,KAAK,KAAK,KAAK,KAAK;EAChC,KAAK,MAAM,KAAK,MAAS;EACzB,OAAO,QAAQ,SAAY,SAAY,IAAI,YAAY,KAAK,KAAK,OAAO,CAAC;CAC3E,CAAC;CACD,MAAM,aAAa,eAAe;CAClC,MAAM,EAAE,OAAO,UAAU,oBAAoB,OAAO,MAAM,CAAC;CAC3D,KAAK,QAAQ,QAAQ,aAAa;CAClC,OAAO;AACT;;;;AAKA,SAAgB,SACd,QACA,KACA,OACS;CACT,IAAI,OAAO,MAAM,WAAW,IAAI,QAAQ,OAAO;CAC/C,IAAI,OAAO,UAAU,QAAQ,OAAO,UAAU,eAAe,GAAG,OAAO;CACvE,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;EACnC,MAAM,MAAM,MAAM,IAAI,EAAE;EACxB,IAAI,OAAO,MAAM,QAAQ,QAAQ,SAAY,SAAY,IAAI,OAAO,OAAO;EAC3E,IAAI,QAAQ,QAAW;EACvB,IAAI,OAAO,KAAK,OAAO,IAAI,KAAK,QAAQ,OAAO,OAAO,OAAO,IAAI,KAAK,OAAO,OAAO;EACpF,IAAI,CAAC,cAAc,OAAO,MAAM,IAAI,IAAI,IAAI,GAAG,OAAO;EACtD,MAAM,OAAO,OAAO,MAAM;EAC1B,IAAI,SAAS,UAAa,SAAS,IAAI,MAAM,OAAO;CACtD;CAEA,IAAI,OAAO,UAAU,MAAM,aAAa;CACxC,OAAO;AACT"}
|
|
@@ -6,6 +6,8 @@ function sceneDependentsOf(scene, id) {
|
|
|
6
6
|
if (!scene.get(id)) return NONE;
|
|
7
7
|
return scene.removalClosure([id]).filter((d) => d !== id && !scene.ancestorsOf(d).includes(id));
|
|
8
8
|
}
|
|
9
|
+
/** The ids `node` names in its `dependsOn`, in order: none for a node naming `'children'` or nothing. */
|
|
10
|
+
var sourcesOf = (node) => Array.isArray(node?.dependsOn) ? node.dependsOn : NONE;
|
|
9
11
|
/**
|
|
10
12
|
* The nodes `node`'s geometry derives from: the ids it named, or its own
|
|
11
13
|
* children when it named `'children'`.
|
|
@@ -96,5 +98,5 @@ function sameDependsOn(a, b) {
|
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
//#endregion
|
|
99
|
-
export { createDependentsIndex, dependencyIdsOf, sameDependsOn, sceneDependentsOf };
|
|
101
|
+
export { createDependentsIndex, dependencyIdsOf, sameDependsOn, sceneDependentsOf, sourcesOf };
|
|
100
102
|
//# sourceMappingURL=dependents.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependents.js","names":[],"sources":["../../../src/core/scene/dependents.ts"],"sourcesContent":["/**\n * Which nodes must recompute when a given node's pose changes — the reverse\n * index, plus {@link dependencyIdsOf}, the forward read it is built from.\n */\nimport type { NodeId, Scene } from './types';\n\nconst NONE: readonly NodeId[] = Object.freeze([]);\n\n/** The nodes in `scene` that list `id` in their `dependsOn`, directly or through others, less any inside `id`'s own\n * subtree. */\nexport function sceneDependentsOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: NodeId,\n): readonly NodeId[] {\n if (!scene.get(id)) return NONE;\n return scene.removalClosure([id]).filter((d) => d !== id && !scene.ancestorsOf(d).includes(id));\n}\n\n/**\n * The nodes `node`'s geometry derives from: the ids it named, or its own\n * children when it named `'children'`.\n *\n * The `'children'` form is deliberately absent from the reverse index — a\n * container is not a dependent of the children it hugs, or deleting one child\n * would cascade the whole group away. Its invalidation is the ancestor walk in\n * `scene.ts` instead.\n */\nexport function dependencyIdsOf(\n node: { id: NodeId; dependsOn?: readonly NodeId[] | 'children' },\n childrenOf: (id: NodeId) => readonly NodeId[],\n): readonly NodeId[] {\n const deps = node.dependsOn;\n if (deps === undefined) return NONE;\n return deps === 'children' ? childrenOf(node.id) : deps;\n}\n\nexport interface DependentsIndex {\n add(id: NodeId, deps: readonly NodeId[]): void;\n /** Forget what `id` declared. Deliberately not symmetric: who depends on\n * `id` is those nodes' declaration, not `id`'s, and outlives its removal. */\n remove(id: NodeId): void;\n /** Forget every registration. */\n clear(): void;\n /** True when nothing derives from anything. */\n isEmpty(): boolean;\n dependentsOf(id: NodeId): Iterable<NodeId>;\n /** Nodes that derive from `id` directly or through a chain. Excludes `id`\n * itself unless a cycle leads back to it. */\n transitiveDependentsOf(id: NodeId): Iterable<NodeId>;\n}\n\nexport function createDependentsIndex(): DependentsIndex {\n /** dependency -> nodes deriving from it */\n const forward = new Map<NodeId, Set<NodeId>>();\n /** dependent -> the dependencies it registered against */\n const reverse = new Map<NodeId, readonly NodeId[]>();\n\n /** Drop `id`'s registrations against its current dependencies. Shared by\n * `add` (which re-registers after) and `remove` (which does not). */\n function detachOwnDeps(id: NodeId): void {\n const old = reverse.get(id);\n if (old === undefined) return;\n for (const dep of old) {\n const set = forward.get(dep);\n if (set === undefined) continue;\n set.delete(id);\n if (set.size === 0) forward.delete(dep);\n }\n reverse.delete(id);\n }\n\n return {\n add(id, deps) {\n detachOwnDeps(id);\n if (deps.length === 0) return;\n reverse.set(id, [...deps]);\n for (const dep of deps) {\n let set = forward.get(dep);\n if (set === undefined) { set = new Set(); forward.set(dep, set); }\n set.add(id);\n }\n },\n\n remove(id) {\n detachOwnDeps(id);\n },\n\n clear() {\n forward.clear();\n reverse.clear();\n },\n\n isEmpty() {\n return forward.size === 0;\n },\n\n dependentsOf(id) {\n const set = forward.get(id);\n return set === undefined ? NONE : [...set];\n },\n\n transitiveDependentsOf(id) {\n // Runs per node per frame; the miss must not allocate.\n const direct = forward.get(id);\n if (direct === undefined) return NONE;\n const out = new Set<NodeId>();\n const stack: NodeId[] = [...direct];\n while (stack.length > 0) {\n const next = stack.pop()!;\n if (out.has(next)) continue; // also what stops a cycle\n out.add(next);\n for (const d of forward.get(next) ?? []) stack.push(d);\n }\n return out;\n },\n };\n}\n\n/**\n * Whether two `dependsOn` values name the same thing, so a retarget to what a\n * node already declares can be elided. Order is part of it: a router reads its\n * ends positionally, and swapping them reverses the edge.\n */\nexport function sameDependsOn(\n a: readonly NodeId[] | 'children' | undefined,\n b: readonly NodeId[] | 'children' | undefined,\n): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n if (a === 'children' || b === 'children') return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n"],"mappings":";AAMA,IAAM,OAA0B,OAAO,OAAO,CAAC,CAAC;;;AAIhD,SAAgB,kBACd,OACA,IACmB;CACnB,IAAI,CAAC,MAAM,IAAI,EAAE,GAAG,OAAO;CAC3B,OAAO,MAAM,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,MAAM,CAAC,MAAM,YAAY,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAChG;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"dependents.js","names":[],"sources":["../../../src/core/scene/dependents.ts"],"sourcesContent":["/**\n * Which nodes must recompute when a given node's pose changes — the reverse\n * index, plus {@link dependencyIdsOf}, the forward read it is built from.\n */\nimport type { NodeId, Scene } from './types';\n\nconst NONE: readonly NodeId[] = Object.freeze([]);\n\n/** The nodes in `scene` that list `id` in their `dependsOn`, directly or through others, less any inside `id`'s own\n * subtree. */\nexport function sceneDependentsOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: NodeId,\n): readonly NodeId[] {\n if (!scene.get(id)) return NONE;\n return scene.removalClosure([id]).filter((d) => d !== id && !scene.ancestorsOf(d).includes(id));\n}\n\n/** The ids `node` names in its `dependsOn`, in order: none for a node naming `'children'` or nothing. */\nexport const sourcesOf = (node: { dependsOn?: unknown } | undefined): readonly NodeId[] =>\n (Array.isArray(node?.dependsOn) ? node.dependsOn as readonly NodeId[] : NONE);\n\n/**\n * The nodes `node`'s geometry derives from: the ids it named, or its own\n * children when it named `'children'`.\n *\n * The `'children'` form is deliberately absent from the reverse index — a\n * container is not a dependent of the children it hugs, or deleting one child\n * would cascade the whole group away. Its invalidation is the ancestor walk in\n * `scene.ts` instead.\n */\nexport function dependencyIdsOf(\n node: { id: NodeId; dependsOn?: readonly NodeId[] | 'children' },\n childrenOf: (id: NodeId) => readonly NodeId[],\n): readonly NodeId[] {\n const deps = node.dependsOn;\n if (deps === undefined) return NONE;\n return deps === 'children' ? childrenOf(node.id) : deps;\n}\n\nexport interface DependentsIndex {\n add(id: NodeId, deps: readonly NodeId[]): void;\n /** Forget what `id` declared. Deliberately not symmetric: who depends on\n * `id` is those nodes' declaration, not `id`'s, and outlives its removal. */\n remove(id: NodeId): void;\n /** Forget every registration. */\n clear(): void;\n /** True when nothing derives from anything. */\n isEmpty(): boolean;\n dependentsOf(id: NodeId): Iterable<NodeId>;\n /** Nodes that derive from `id` directly or through a chain. Excludes `id`\n * itself unless a cycle leads back to it. */\n transitiveDependentsOf(id: NodeId): Iterable<NodeId>;\n}\n\nexport function createDependentsIndex(): DependentsIndex {\n /** dependency -> nodes deriving from it */\n const forward = new Map<NodeId, Set<NodeId>>();\n /** dependent -> the dependencies it registered against */\n const reverse = new Map<NodeId, readonly NodeId[]>();\n\n /** Drop `id`'s registrations against its current dependencies. Shared by\n * `add` (which re-registers after) and `remove` (which does not). */\n function detachOwnDeps(id: NodeId): void {\n const old = reverse.get(id);\n if (old === undefined) return;\n for (const dep of old) {\n const set = forward.get(dep);\n if (set === undefined) continue;\n set.delete(id);\n if (set.size === 0) forward.delete(dep);\n }\n reverse.delete(id);\n }\n\n return {\n add(id, deps) {\n detachOwnDeps(id);\n if (deps.length === 0) return;\n reverse.set(id, [...deps]);\n for (const dep of deps) {\n let set = forward.get(dep);\n if (set === undefined) { set = new Set(); forward.set(dep, set); }\n set.add(id);\n }\n },\n\n remove(id) {\n detachOwnDeps(id);\n },\n\n clear() {\n forward.clear();\n reverse.clear();\n },\n\n isEmpty() {\n return forward.size === 0;\n },\n\n dependentsOf(id) {\n const set = forward.get(id);\n return set === undefined ? NONE : [...set];\n },\n\n transitiveDependentsOf(id) {\n // Runs per node per frame; the miss must not allocate.\n const direct = forward.get(id);\n if (direct === undefined) return NONE;\n const out = new Set<NodeId>();\n const stack: NodeId[] = [...direct];\n while (stack.length > 0) {\n const next = stack.pop()!;\n if (out.has(next)) continue; // also what stops a cycle\n out.add(next);\n for (const d of forward.get(next) ?? []) stack.push(d);\n }\n return out;\n },\n };\n}\n\n/**\n * Whether two `dependsOn` values name the same thing, so a retarget to what a\n * node already declares can be elided. Order is part of it: a router reads its\n * ends positionally, and swapping them reverses the edge.\n */\nexport function sameDependsOn(\n a: readonly NodeId[] | 'children' | undefined,\n b: readonly NodeId[] | 'children' | undefined,\n): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n if (a === 'children' || b === 'children') return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n"],"mappings":";AAMA,IAAM,OAA0B,OAAO,OAAO,CAAC,CAAC;;;AAIhD,SAAgB,kBACd,OACA,IACmB;CACnB,IAAI,CAAC,MAAM,IAAI,EAAE,GAAG,OAAO;CAC3B,OAAO,MAAM,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,MAAM,CAAC,MAAM,YAAY,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAChG;;AAGA,IAAa,aAAa,SACvB,MAAM,QAAQ,MAAM,SAAS,IAAI,KAAK,YAAiC;;;;;;;;;;AAW1E,SAAgB,gBACd,MACA,YACmB;CACnB,MAAM,OAAO,KAAK;CAClB,IAAI,SAAS,QAAW,OAAO;CAC/B,OAAO,SAAS,aAAa,WAAW,KAAK,EAAE,IAAI;AACrD;AAiBA,SAAgB,wBAAyC;;CAEvD,MAAM,0BAAU,IAAI,IAAyB;;CAE7C,MAAM,0BAAU,IAAI,IAA+B;;;CAInD,SAAS,cAAc,IAAkB;EACvC,MAAM,MAAM,QAAQ,IAAI,EAAE;EAC1B,IAAI,QAAQ,QAAW;EACvB,KAAK,MAAM,OAAO,KAAK;GACrB,MAAM,MAAM,QAAQ,IAAI,GAAG;GAC3B,IAAI,QAAQ,QAAW;GACvB,IAAI,OAAO,EAAE;GACb,IAAI,IAAI,SAAS,GAAG,QAAQ,OAAO,GAAG;EACxC;EACA,QAAQ,OAAO,EAAE;CACnB;CAEA,OAAO;EACL,IAAI,IAAI,MAAM;GACZ,cAAc,EAAE;GAChB,IAAI,KAAK,WAAW,GAAG;GACvB,QAAQ,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC;GACzB,KAAK,MAAM,OAAO,MAAM;IACtB,IAAI,MAAM,QAAQ,IAAI,GAAG;IACzB,IAAI,QAAQ,QAAW;KAAE,sBAAM,IAAI,IAAI;KAAG,QAAQ,IAAI,KAAK,GAAG;IAAG;IACjE,IAAI,IAAI,EAAE;GACZ;EACF;EAEA,OAAO,IAAI;GACT,cAAc,EAAE;EAClB;EAEA,QAAQ;GACN,QAAQ,MAAM;GACd,QAAQ,MAAM;EAChB;EAEA,UAAU;GACR,OAAO,QAAQ,SAAS;EAC1B;EAEA,aAAa,IAAI;GACf,MAAM,MAAM,QAAQ,IAAI,EAAE;GAC1B,OAAO,QAAQ,SAAY,OAAO,CAAC,GAAG,GAAG;EAC3C;EAEA,uBAAuB,IAAI;GAEzB,MAAM,SAAS,QAAQ,IAAI,EAAE;GAC7B,IAAI,WAAW,QAAW,OAAO;GACjC,MAAM,sBAAM,IAAI,IAAY;GAC5B,MAAM,QAAkB,CAAC,GAAG,MAAM;GAClC,OAAO,MAAM,SAAS,GAAG;IACvB,MAAM,OAAO,MAAM,IAAI;IACvB,IAAI,IAAI,IAAI,IAAI,GAAG;IACnB,IAAI,IAAI,IAAI;IACZ,KAAK,MAAM,KAAK,QAAQ,IAAI,IAAI,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC;GACvD;GACA,OAAO;EACT;CACF;AACF;;;;;;AAOA,SAAgB,cACd,GACA,GACS;CACT,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,MAAM,UAAa,MAAM,QAAW,OAAO;CAC/C,IAAI,MAAM,cAAc,MAAM,YAAY,OAAO;CACjD,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;CAClC,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,IAAI,EAAE,OAAO,EAAE,IAAI,OAAO;CAC7D,OAAO;AACT"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/core/scene/documentOf.ts
|
|
2
|
+
var refuse = () => {
|
|
3
|
+
throw new Error("documentOf: the document reading holds no overrides; write them to the scene");
|
|
4
|
+
};
|
|
5
|
+
/** The empty table a document reading answers with. */
|
|
6
|
+
var DOCUMENT_OVERRIDES = Object.freeze({
|
|
7
|
+
set: refuse,
|
|
8
|
+
get: () => void 0,
|
|
9
|
+
read: (_id) => void 0,
|
|
10
|
+
has: () => false,
|
|
11
|
+
ids: () => [],
|
|
12
|
+
clear: refuse,
|
|
13
|
+
clearAll: refuse,
|
|
14
|
+
commit: refuse,
|
|
15
|
+
subscribe: () => () => {},
|
|
16
|
+
getGeneration: () => 0,
|
|
17
|
+
hold: () => refuse()
|
|
18
|
+
});
|
|
19
|
+
var views = /* @__PURE__ */ new WeakMap();
|
|
20
|
+
/**
|
|
21
|
+
* `source` read as its document holds it: the same object in every respect but
|
|
22
|
+
* its `overrides`, which are empty, so no pose, path or data override reaches
|
|
23
|
+
* anything handed it. One view per source, so memos keyed on it hit. The
|
|
24
|
+
* readers it is handed to memoize the document reading apart from the shown
|
|
25
|
+
* one, as `documentPose` and `documentDepOf` do.
|
|
26
|
+
*/
|
|
27
|
+
function documentOf(source) {
|
|
28
|
+
if (isDocument(source)) return source;
|
|
29
|
+
let view = views.get(source);
|
|
30
|
+
if (view === void 0) {
|
|
31
|
+
view = Object.create(source, { overrides: { value: DOCUMENT_OVERRIDES } });
|
|
32
|
+
views.set(source, view);
|
|
33
|
+
}
|
|
34
|
+
return view;
|
|
35
|
+
}
|
|
36
|
+
/** Whether `source` is a {@link documentOf} view. */
|
|
37
|
+
function isDocument(source) {
|
|
38
|
+
return source.overrides === DOCUMENT_OVERRIDES;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { documentOf, isDocument };
|
|
43
|
+
//# sourceMappingURL=documentOf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documentOf.js","names":[],"sources":["../../../src/core/scene/documentOf.ts"],"sourcesContent":["/**\n * `documentOf` — a scene read as its document holds it.\n *\n * Commands and exports act on the document: Expand bakes what an effect's\n * sources store, an SVG save writes it, Align measures it. Handed the scene\n * itself, every reader they reach — `effectivePose`, `derivedDepOf`, the\n * canvas's derived paths and drawn bounds — would bake in whatever a gesture or\n * an open text edit is showing. Handed `documentOf(scene)`, each of them reads\n * the document, because the view it is handed holds no overrides.\n */\nimport type { NodeId, PoseHold, PoseOverride, PoseOverrides } from './types';\n\nconst refuse = (): never => {\n throw new Error('documentOf: the document reading holds no overrides; write them to the scene');\n};\n\n/** The empty table a document reading answers with. */\nconst DOCUMENT_OVERRIDES: PoseOverrides<never, never> = Object.freeze({\n set: refuse,\n get: (): undefined => undefined,\n read: (_id: NodeId): PoseOverride<never, never> | undefined => undefined,\n has: () => false,\n ids: () => [],\n clear: refuse,\n clearAll: refuse,\n commit: refuse,\n subscribe: () => () => {},\n getGeneration: () => 0,\n hold: (): PoseHold<never, never> => refuse(),\n});\n\nconst views = new WeakMap<object, object>();\n\n/**\n * `source` read as its document holds it: the same object in every respect but\n * its `overrides`, which are empty, so no pose, path or data override reaches\n * anything handed it. One view per source, so memos keyed on it hit. The\n * readers it is handed to memoize the document reading apart from the shown\n * one, as `documentPose` and `documentDepOf` do.\n */\nexport function documentOf<TSource extends { readonly overrides: unknown }>(source: TSource): TSource {\n if (isDocument(source)) return source;\n let view = views.get(source);\n if (view === undefined) {\n view = Object.create(source, { overrides: { value: DOCUMENT_OVERRIDES } }) as object;\n views.set(source, view);\n }\n return view as TSource;\n}\n\n/** Whether `source` is a {@link documentOf} view. */\nexport function isDocument(source: { readonly overrides: unknown }): boolean {\n return source.overrides === DOCUMENT_OVERRIDES;\n}\n"],"mappings":";AAYA,IAAM,eAAsB;CAC1B,MAAM,IAAI,MAAM,8EAA8E;AAChG;;AAGA,IAAM,qBAAkD,OAAO,OAAO;CACpE,KAAK;CACL,WAAsB;CACtB,OAAO,QAAwD;CAC/D,WAAW;CACX,WAAW,CAAC;CACZ,OAAO;CACP,UAAU;CACV,QAAQ;CACR,uBAAuB,CAAC;CACxB,qBAAqB;CACrB,YAAoC,OAAO;AAC7C,CAAC;AAED,IAAM,wBAAQ,IAAI,QAAwB;;;;;;;;AAS1C,SAAgB,WAA4D,QAA0B;CACpG,IAAI,WAAW,MAAM,GAAG,OAAO;CAC/B,IAAI,OAAO,MAAM,IAAI,MAAM;CAC3B,IAAI,SAAS,QAAW;EACtB,OAAO,OAAO,OAAO,QAAQ,EAAE,WAAW,EAAE,OAAO,mBAAmB,EAAE,CAAC;EACzE,MAAM,IAAI,QAAQ,IAAI;CACxB;CACA,OAAO;AACT;;AAGA,SAAgB,WAAW,QAAkD;CAC3E,OAAO,OAAO,cAAc;AAC9B"}
|
|
@@ -3,6 +3,9 @@ import { carryDepPath, depFrameRebase, pathAsPainted } from "./depFrame.js";
|
|
|
3
3
|
import { dependencyIdsOf } from "./dependents.js";
|
|
4
4
|
import { dropPoseKeyedMemoSlots, nodeMemo } from "./nodeMemo.js";
|
|
5
5
|
import { resolveDerivedPath } from "./derivedPath.js";
|
|
6
|
+
import { inheritedPaintIn } from "./inheritedPaint.js";
|
|
7
|
+
import { isDocument } from "./documentOf.js";
|
|
8
|
+
import { nodeAsShown, shownLookup } from "./nodeAsShown.js";
|
|
6
9
|
|
|
7
10
|
//#region src/core/scene/effectivePose.ts
|
|
8
11
|
/**
|
|
@@ -39,9 +42,14 @@ var cycleHits = 0;
|
|
|
39
42
|
* derivation the same frame it is published.
|
|
40
43
|
*/
|
|
41
44
|
function derivedPose(source, node) {
|
|
42
|
-
return derivedPoseIn(source, node,
|
|
45
|
+
return derivedPoseIn(source, node, readingOf(source));
|
|
43
46
|
}
|
|
44
|
-
|
|
47
|
+
/** How `source` is read: as its document holds it when it is a `documentOf` view. */
|
|
48
|
+
function readingOf(source) {
|
|
49
|
+
return isDocument(source) ? "document" : "effective";
|
|
50
|
+
}
|
|
51
|
+
function derivedPoseIn(source, stored, reading) {
|
|
52
|
+
const node = shownIn(source, stored, reading);
|
|
45
53
|
const derive = node.derivePose;
|
|
46
54
|
if (derive === void 0) return null;
|
|
47
55
|
const ids = dependencyIdsOf(node, source.childrenOf);
|
|
@@ -84,13 +92,15 @@ function run(node, derive, deps, into) {
|
|
|
84
92
|
* resolved before it is read.
|
|
85
93
|
*/
|
|
86
94
|
function derivedDepOf(source, id, reader) {
|
|
87
|
-
|
|
95
|
+
const reading = readingOf(source);
|
|
96
|
+
return reader === void 0 ? depIn(source, id, reading) : depsAsRead(source, reader, reading)(id);
|
|
88
97
|
}
|
|
89
|
-
/** `id` as {@link derivedDepOf} answers it, but at the pose and
|
|
90
|
-
* document holds: no override anywhere down its dependencies counts.
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
/** `id` as {@link derivedDepOf} answers it, but at the pose, path and data the
|
|
99
|
+
* document holds: no override anywhere down its dependencies counts. The same
|
|
100
|
+
* reading as `derivedDepOf(documentOf(source), …)`, for a reader that must not
|
|
101
|
+
* see an in-flight gesture or an open edit, such as an edit pass. */
|
|
102
|
+
function documentDepOf(source, id, reader) {
|
|
103
|
+
return reader === void 0 ? depIn(source, id, "document") : depsAsRead(source, reader, "document")(id);
|
|
94
104
|
}
|
|
95
105
|
/** `(id) => that dependency as `reader`'s derivation is handed it`: its pose
|
|
96
106
|
* in `reader`'s frame where the scene composes frames. A `'children'`
|
|
@@ -125,6 +135,9 @@ var RebasedDep = class {
|
|
|
125
135
|
const path = this.dep.path;
|
|
126
136
|
return path === null ? null : carryDepPath(path, this.dep.pose, this.pose);
|
|
127
137
|
}
|
|
138
|
+
get inherited() {
|
|
139
|
+
return this.dep.inherited;
|
|
140
|
+
}
|
|
128
141
|
};
|
|
129
142
|
function depIn(source, id, reading) {
|
|
130
143
|
const node = source.get(id);
|
|
@@ -138,15 +151,16 @@ function depIn(source, id, reading) {
|
|
|
138
151
|
*/
|
|
139
152
|
var SceneDep = class {
|
|
140
153
|
source;
|
|
141
|
-
posed;
|
|
142
154
|
reading;
|
|
143
155
|
node;
|
|
144
156
|
pose;
|
|
145
157
|
shown;
|
|
146
|
-
|
|
158
|
+
posed;
|
|
159
|
+
constructor(source, stored, reading) {
|
|
147
160
|
this.source = source;
|
|
148
|
-
this.posed = posed;
|
|
149
161
|
this.reading = reading;
|
|
162
|
+
const posed = shownIn(source, stored, reading);
|
|
163
|
+
this.posed = posed;
|
|
150
164
|
this.node = posed;
|
|
151
165
|
const override = reading === "effective" ? source.overrides.read(posed.id) : void 0;
|
|
152
166
|
this.shown = override?.path;
|
|
@@ -157,7 +171,16 @@ var SceneDep = class {
|
|
|
157
171
|
const { source, reading } = this;
|
|
158
172
|
return resolveDerivedPath(this.posed, depsAsRead(source, this.posed, reading), (depId) => source.childrenOf(depId), PATH_SLOT[reading]);
|
|
159
173
|
}
|
|
174
|
+
get inherited() {
|
|
175
|
+
const { source, reading } = this;
|
|
176
|
+
return inheritedPaintIn(reading === "effective" ? shownLookup(source) : source, this.posed);
|
|
177
|
+
}
|
|
160
178
|
};
|
|
179
|
+
/** `node` as `reading` reads it: with the data an override shows it with in the effective
|
|
180
|
+
* reading, as stored in the document one. */
|
|
181
|
+
function shownIn(source, node, reading) {
|
|
182
|
+
return reading === "effective" ? nodeAsShown(source.overrides, node) : node;
|
|
183
|
+
}
|
|
161
184
|
function poseIn(source, node, reading) {
|
|
162
185
|
if (reading === "effective") {
|
|
163
186
|
const override = source.overrides.read(node.id)?.pose;
|
|
@@ -172,7 +195,8 @@ function poseIn(source, node, reading) {
|
|
|
172
195
|
* something it derives from is being dragged.
|
|
173
196
|
*
|
|
174
197
|
* For a reader that must not see an in-flight gesture, such as an action
|
|
175
|
-
* capturing the `from` of a transform op.
|
|
198
|
+
* capturing the `from` of a transform op. The same reading as
|
|
199
|
+
* `effectivePose(documentOf(source), node)`.
|
|
176
200
|
*/
|
|
177
201
|
function documentPose(source, node) {
|
|
178
202
|
return poseIn(source, node, "document");
|
|
@@ -182,7 +206,7 @@ function documentPose(source, node) {
|
|
|
182
206
|
* comes from here.
|
|
183
207
|
*/
|
|
184
208
|
function effectivePose(source, node) {
|
|
185
|
-
return poseIn(source, node,
|
|
209
|
+
return poseIn(source, node, readingOf(source));
|
|
186
210
|
}
|
|
187
211
|
/**
|
|
188
212
|
* Whether `node`'s pose is a frame its children are expressed in, under a
|