@weasel-js/core 1.12.0 → 1.13.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 +199 -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/barrel/text.js +2 -2
- 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/useContributionRoles.js +2 -0
- package/dist/canvas/SceneCanvas/useContributionRoles.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/buildSceneLayer.js +1 -1
- package/dist/canvas/buildSceneLayer.js.map +1 -1
- package/dist/canvas/buildSceneTree.js +14 -20
- 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/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 +13 -8
- 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/useCanvasPaint.js +3 -1
- package/dist/canvas/useCanvasPaint.js.map +1 -1
- 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/layers/render.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-CpIQzKWQ.d.ts} +38 -59
- 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/features/viewports/viewportLayer.js +2 -1
- package/dist/features/viewports/viewportLayer.js.map +1 -1
- package/dist/index.d.ts +230 -77
- package/dist/index.js +16 -6
- 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
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inheritedPaintIn, inheritedPaintOf, sceneAncestors } from "../core/scene/inheritedPaint.js";
|
|
2
2
|
import { INHERIT } from "@weasel-js/paint";
|
|
3
3
|
|
|
4
4
|
//#region src/canvas/inheritedPaints.ts
|
|
5
|
-
/** What `node`'s `'inherit'` fields take from its containers in `scene`, or
|
|
6
|
-
* `undefined` for a node that inherits nothing. */
|
|
7
|
-
function inheritedPaintIn(scene, node) {
|
|
8
|
-
if (!inheritsPaint(node)) return void 0;
|
|
9
|
-
return inheritedPaintOf(node, sceneAncestors((id) => scene.get(id), node));
|
|
10
|
-
}
|
|
11
5
|
var INHERITS_BOTH = { data: {
|
|
12
6
|
fill: INHERIT,
|
|
13
7
|
stroke: INHERIT
|
|
@@ -25,20 +19,37 @@ function sceneInheritedPaintOf(scene) {
|
|
|
25
19
|
/**
|
|
26
20
|
* Wrap a `drawOne` so a node whose fill or stroke is `'inherit'` arrives at
|
|
27
21
|
* the painter with its containers' paint on `ctx.inherited`, as
|
|
28
|
-
* `withDerivedPaths` does for a derived path. Every other node
|
|
29
|
-
* caller
|
|
22
|
+
* `withDerivedPaths` does for a derived path. Every other node, and one whose
|
|
23
|
+
* caller already says what it inherits — a ghost whose containers are mid-edit —
|
|
24
|
+
* passes the caller's `ctx` through untouched.
|
|
30
25
|
*/
|
|
31
26
|
function withInheritedPaints(scene, drawOne) {
|
|
32
27
|
const inheritedOf = sceneInheritedPaintOf(scene);
|
|
33
28
|
return (node, pose, view, ctx) => {
|
|
34
|
-
const inherited = inheritedOf(node);
|
|
29
|
+
const inherited = ctx?.inherited ?? inheritedOf(node);
|
|
35
30
|
return inherited === void 0 ? drawOne(node, pose, view, ctx) : drawOne(node, pose, view, {
|
|
36
31
|
...ctx,
|
|
37
32
|
inherited
|
|
38
33
|
});
|
|
39
34
|
};
|
|
40
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* {@link withInheritedPaints} for a cull's `paintBounds`: a node whose stroke
|
|
38
|
+
* is `'inherit'` is measured with its containers' stroke on `ctx.inherited`,
|
|
39
|
+
* unless its caller already says what it inherits. For a consumer culling its
|
|
40
|
+
* own scene walk with `defaultPaintBounds`, which cannot bound such a node alone.
|
|
41
|
+
*/
|
|
42
|
+
function withInheritedPaintBounds(scene, paintBounds) {
|
|
43
|
+
const inheritedOf = sceneInheritedPaintOf(scene);
|
|
44
|
+
return (node, pose, view, ctx) => {
|
|
45
|
+
const inherited = ctx?.inherited ?? inheritedOf(node);
|
|
46
|
+
return paintBounds(node, pose, view, inherited === void 0 ? ctx : {
|
|
47
|
+
...ctx,
|
|
48
|
+
inherited
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
|
41
52
|
|
|
42
53
|
//#endregion
|
|
43
|
-
export { inheritedPaintIn, paintAbove, sceneInheritedPaintOf, withInheritedPaints };
|
|
54
|
+
export { inheritedPaintIn, paintAbove, sceneInheritedPaintOf, withInheritedPaintBounds, withInheritedPaints };
|
|
44
55
|
//# sourceMappingURL=inheritedPaints.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inheritedPaints.js","names":[],"sources":["../../src/canvas/inheritedPaints.ts"],"sourcesContent":["import { INHERIT } from '@weasel-js/paint';\nimport type { Node, NodeId } from 'core/scene/types';\nimport {
|
|
1
|
+
{"version":3,"file":"inheritedPaints.js","names":[],"sources":["../../src/canvas/inheritedPaints.ts"],"sourcesContent":["import { INHERIT } from '@weasel-js/paint';\nimport type { Node, NodeId } from 'core/scene/types';\nimport { inheritedPaintIn, inheritedPaintOf, sceneAncestors, type InheritedPaint } from 'core/scene/inheritedPaint';\nimport type { SceneViewDrawOne } from './nodeShapeTypes';\nimport type { PaintBoundsFn } from './paintCull';\n\n/** The part of a scene the lookup reads. */\ntype ParentLookup<TNode> = { get(id: NodeId): TNode | undefined };\n\ntype PaintedNode = { data?: unknown; parent?: NodeId | null };\n\nexport { inheritedPaintIn };\n\nconst INHERITS_BOTH = { data: { fill: INHERIT, stroke: INHERIT } };\n\n/** What an `'inherit'` fill and stroke on `node` would take from its\n * containers in `scene`, whatever `node` holds now — what an editor offering\n * the choice shows it would paint. */\nexport function paintAbove<TNode extends PaintedNode>(scene: ParentLookup<TNode>, node: TNode): InheritedPaint {\n return inheritedPaintOf(INHERITS_BOTH, sceneAncestors((id: never) => scene.get(id), node));\n}\n\n/** {@link inheritedPaintIn} bound to `scene`. */\nexport function sceneInheritedPaintOf<TNode extends PaintedNode>(\n scene: ParentLookup<TNode>,\n): (node: TNode) => InheritedPaint | undefined {\n return (node) => inheritedPaintIn(scene, node);\n}\n\n/**\n * Wrap a `drawOne` so a node whose fill or stroke is `'inherit'` arrives at\n * the painter with its containers' paint on `ctx.inherited`, as\n * `withDerivedPaths` does for a derived path. Every other node, and one whose\n * caller already says what it inherits — a ghost whose containers are mid-edit —\n * passes the caller's `ctx` through untouched.\n */\nexport function withInheritedPaints<TData, TLayer extends string, TPose>(\n scene: ParentLookup<Node<TData, TLayer, TPose>>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const inheritedOf = sceneInheritedPaintOf(scene);\n return (node, pose, view, ctx) => {\n const inherited = ctx?.inherited ?? inheritedOf(node);\n return inherited === undefined ? drawOne(node, pose, view, ctx) : drawOne(node, pose, view, { ...ctx, inherited });\n };\n}\n\n/**\n * {@link withInheritedPaints} for a cull's `paintBounds`: a node whose stroke\n * is `'inherit'` is measured with its containers' stroke on `ctx.inherited`,\n * unless its caller already says what it inherits. For a consumer culling its\n * own scene walk with `defaultPaintBounds`, which cannot bound such a node alone.\n */\nexport function withInheritedPaintBounds<TData, TLayer extends string, TPose>(\n scene: ParentLookup<Node<TData, TLayer, TPose>>,\n paintBounds: PaintBoundsFn<Node<TData, TLayer, TPose>, TPose>,\n): PaintBoundsFn<Node<TData, TLayer, TPose>, TPose> {\n const inheritedOf = sceneInheritedPaintOf(scene);\n return (node, pose, view, ctx) => {\n const inherited = ctx?.inherited ?? inheritedOf(node);\n return paintBounds(node, pose, view, inherited === undefined ? ctx : { ...ctx, inherited });\n };\n}\n"],"mappings":";;;;AAaA,IAAM,gBAAgB,EAAE,MAAM;CAAE,MAAM;CAAS,QAAQ;AAAQ,EAAE;;;;AAKjE,SAAgB,WAAsC,OAA4B,MAA6B;CAC7G,OAAO,iBAAiB,eAAe,gBAAgB,OAAc,MAAM,IAAI,EAAE,GAAG,IAAI,CAAC;AAC3F;;AAGA,SAAgB,sBACd,OAC6C;CAC7C,QAAQ,SAAS,iBAAiB,OAAO,IAAI;AAC/C;;;;;;;;AASA,SAAgB,oBACd,OACA,SACwC;CACxC,MAAM,cAAc,sBAAsB,KAAK;CAC/C,QAAQ,MAAM,MAAM,MAAM,QAAQ;EAChC,MAAM,YAAY,KAAK,aAAa,YAAY,IAAI;EACpD,OAAO,cAAc,SAAY,QAAQ,MAAM,MAAM,MAAM,GAAG,IAAI,QAAQ,MAAM,MAAM,MAAM;GAAE,GAAG;GAAK;EAAU,CAAC;CACnH;AACF;;;;;;;AAQA,SAAgB,yBACd,OACA,aACkD;CAClD,MAAM,cAAc,sBAAsB,KAAK;CAC/C,QAAQ,MAAM,MAAM,MAAM,QAAQ;EAChC,MAAM,YAAY,KAAK,aAAa,YAAY,IAAI;EACpD,OAAO,YAAY,MAAM,MAAM,MAAM,cAAc,SAAY,MAAM;GAAE,GAAG;GAAK;EAAU,CAAC;CAC5F;AACF"}
|
|
@@ -6,12 +6,6 @@ import { markerReach, resolveStrokeWidth, widestStrokeWidth } from "@weasel-js/s
|
|
|
6
6
|
|
|
7
7
|
//#region src/canvas/nodeShapes/nodePaint.ts
|
|
8
8
|
paintDrawKindRegistry.subscribe(bumpNodeMemoGeneration);
|
|
9
|
-
/** How a container's silhouette is inked: not at all, since it paints nothing. */
|
|
10
|
-
var CONTAINER_INK = Object.freeze({
|
|
11
|
-
filled: false,
|
|
12
|
-
outset: 0,
|
|
13
|
-
inset: 0
|
|
14
|
-
});
|
|
15
9
|
/** Per-side grab reach for a resolved stroke: world units for what the
|
|
16
10
|
* renderer builds in world, screen pixels for what it builds on screen — a
|
|
17
11
|
* `{ px }` width, and each head by its own size's units. */
|
|
@@ -122,5 +116,5 @@ function pathPaintOf(data, inherited) {
|
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
//#endregion
|
|
125
|
-
export {
|
|
119
|
+
export { inkReach, pathPaintOf, strokeOutset, strokeOutsetPerAxis, withAnimatedVertexColors };
|
|
126
120
|
//# sourceMappingURL=nodePaint.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodePaint.js","names":[],"sources":["../../../src/canvas/nodeShapes/nodePaint.ts"],"sourcesContent":["import type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport { resolveFill, resolveStroke } from '@weasel-js/paint';\nimport type { NodeInk, NodeInkCtx, NodePaintCtx } from 'canvas/NodeShape';\nimport { countPathAnchors } from '@weasel-js/geom';\nimport { markerReach } from '@weasel-js/stroke';\nimport { resolveStrokeWidth, widestStrokeWidth } from '@weasel-js/stroke';\nimport type { DrawCommand, PathDrawCommand } from '@weasel-js/renderer';\nimport { paintDrawKindRegistry } from '@weasel-js/renderer';\nimport { bumpNodeMemoGeneration } from 'core/scene/nodeMemo';\nimport type { InheritedPaint } from 'core/scene/inheritedPaint';\n\n// The painters memoize a node's paint and resolve a fill's frame inside the\n// memo, so the kind set is ambient state that memo cannot see change.\npaintDrawKindRegistry.subscribe(bumpNodeMemoGeneration);\n\n/**
|
|
1
|
+
{"version":3,"file":"nodePaint.js","names":[],"sources":["../../../src/canvas/nodeShapes/nodePaint.ts"],"sourcesContent":["import type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport { resolveFill, resolveStroke } from '@weasel-js/paint';\nimport type { NodeInk, NodeInkCtx, NodePaintCtx } from 'canvas/NodeShape';\nimport { countPathAnchors } from '@weasel-js/geom';\nimport { markerReach } from '@weasel-js/stroke';\nimport { resolveStrokeWidth, widestStrokeWidth } from '@weasel-js/stroke';\nimport type { DrawCommand, PathDrawCommand } from '@weasel-js/renderer';\nimport { paintDrawKindRegistry } from '@weasel-js/renderer';\nimport { bumpNodeMemoGeneration } from 'core/scene/nodeMemo';\nimport type { InheritedPaint } from 'core/scene/inheritedPaint';\n\n// The painters memoize a node's paint and resolve a fill's frame inside the\n// memo, so the kind set is ambient state that memo cannot see change.\npaintDrawKindRegistry.subscribe(bumpNodeMemoGeneration);\n\n/** Per-side grab reach for a resolved stroke: world units for what the\n * renderer builds in world, screen pixels for what it builds on screen — a\n * `{ px }` width, and each head by its own size's units. */\nexport function inkReach(\n stroke: Stroke | null,\n scale: number | undefined,\n): Pick<NodeInk, 'outset' | 'inset' | 'outsetPx' | 'insetPx'> {\n if (stroke === null) return { outset: 0, inset: 0 };\n // A marker paints past the path's own end, and the kit's rule is that\n // visible chrome is hittable — so the reach has to cover it.\n const heads = markerReach(stroke);\n const sides = (width: number, headReach: number) => {\n switch (stroke.align ?? 'center') {\n case 'inner': return { outset: headReach, inset: width };\n case 'outer': return { outset: width + headReach, inset: 0 };\n default: return { outset: width / 2 + headReach, inset: width / 2 };\n }\n };\n const width = stroke.width ?? 1;\n // Per-vertex widths are world units even on a `{ px }` stroke.\n const worldWidth = widestStrokeWidth(typeof width === 'object' ? 0 : resolveStrokeWidth(width, scale ?? 1), stroke.vertexWidths);\n const world = sides(worldWidth, heads.world);\n if (typeof width !== 'object' && !(heads.px > 0)) return world;\n const px = sides(typeof width === 'object' ? width.px : 0, heads.px);\n return { outset: world.outset, inset: world.inset, outsetPx: px.outset, insetPx: px.inset };\n}\n\n/** How far `stroke` reaches outside its outline on every side, world units at\n * `scale`: {@link inkReach}'s outset with its screen part resolved. */\nexport function strokeOutset(stroke: Stroke | null, scale: number): number {\n const r = inkReach(stroke, scale);\n return r.outset + (r.outsetPx ?? 0) / (scale > 0 ? scale : 1);\n}\n\n/** {@link strokeOutset} per axis of the frame `ctx.screen` maps from: `x` past\n * the left and right sides, `y` past the top and bottom. A `{ px }` reach\n * is a disk on screen, and its extent along each axis of that frame is the\n * support of the disk pulled back through the map. */\nexport function strokeOutsetPerAxis(stroke: Stroke | null, ctx: NodeInkCtx | undefined): { x: number; y: number } {\n const scale = ctx?.scale ?? 1;\n const screen = ctx?.screen;\n if (!screen) {\n const r = strokeOutset(stroke, scale);\n return { x: r, y: r };\n }\n const r = inkReach(stroke, scale);\n const px = r.outsetPx ?? 0;\n if (px === 0) return { x: r.outset, y: r.outset };\n const [a, b, c, d] = screen;\n const det = Math.abs(a * d - b * c);\n if (!(det > 0)) return { x: r.outset, y: r.outset };\n return { x: r.outset + (px * Math.hypot(c, d)) / det, y: r.outset + (px * Math.hypot(a, b)) / det };\n}\n\n/** Apply {@link NodePaintCtx.vertexColors} to a path painter's body — its first\n * command. Applied outside the paint memo, whose key\n * cannot see an override: `cmds` comes back as-is when nothing changes, and\n * otherwise as a copy, since a memoized list is not ours to edit. Colors whose\n * length does not match the path's anchors are dropped. */\nexport function withAnimatedVertexColors(cmds: DrawCommand[], ctx: NodePaintCtx | undefined): DrawCommand[] {\n const resolve = ctx?.vertexColors;\n const body = cmds[0];\n if (resolve === undefined || body?.kind !== 'path') return cmds;\n const expected = 4 * countPathAnchors(body.path);\n const fill = body.fill ? resolve('fill', body.vertexColors) : undefined;\n const stroke = body.stroke ? resolve('stroke', body.stroke.vertexColors) : undefined;\n const fillChanged = fill?.length === expected && fill !== body.vertexColors;\n const strokeChanged = stroke?.length === expected && stroke !== body.stroke?.vertexColors;\n if (!fillChanged && !strokeChanged) return cmds;\n const next: PathDrawCommand = {\n ...body,\n ...(fillChanged ? { vertexColors: fill as number[] } : {}),\n ...(strokeChanged ? { stroke: { ...body.stroke!, vertexColors: stroke as number[] } } : {}),\n };\n return [next, ...cmds.slice(1)];\n}\n\n/** The paint a path node wears, resolved: what an action that turns ink into\n * geometry reads from a node and tells a consumer to put on a result. */\nexport interface PathPaint {\n /** `null` is no fill. */\n fill: Fill | null;\n /** Absent for no stroke. */\n stroke?: Stroke;\n}\n\n/** {@link PathPaint} of a node that keeps its paint in `data.fill` and\n * `data.stroke`, as the built-in path painter draws it: a stroke or a fill\n * with no `paint` is none, and an `'inherit'` one is `inherited`'s. */\nexport function pathPaintOf(\n data: { fill?: Fill | Inherit | null; stroke?: Stroke | Inherit | null },\n inherited?: InheritedPaint,\n): PathPaint {\n const stroke = resolveStroke(data.stroke, inherited?.stroke);\n const fill = resolveFill(data.fill, inherited?.fill);\n return stroke !== null ? { fill, stroke } : { fill };\n}\n"],"mappings":";;;;;;;AAaA,sBAAsB,UAAU,sBAAsB;;;;AAKtD,SAAgB,SACd,QACA,OAC4D;CAC5D,IAAI,WAAW,MAAM,OAAO;EAAE,QAAQ;EAAG,OAAO;CAAE;CAGlD,MAAM,QAAQ,YAAY,MAAM;CAChC,MAAM,SAAS,OAAe,cAAsB;EAClD,QAAQ,OAAO,SAAS,UAAxB;GACE,KAAK,SAAS,OAAO;IAAE,QAAQ;IAAW,OAAO;GAAM;GACvD,KAAK,SAAS,OAAO;IAAE,QAAQ,QAAQ;IAAW,OAAO;GAAE;GAC3D,SAAS,OAAO;IAAE,QAAQ,QAAQ,IAAI;IAAW,OAAO,QAAQ;GAAE;EACpE;CACF;CACA,MAAM,QAAQ,OAAO,SAAS;CAG9B,MAAM,QAAQ,MADK,kBAAkB,OAAO,UAAU,WAAW,IAAI,mBAAmB,OAAO,SAAS,CAAC,GAAG,OAAO,YAC/F,GAAY,MAAM,KAAK;CAC3C,IAAI,OAAO,UAAU,YAAY,EAAE,MAAM,KAAK,IAAI,OAAO;CACzD,MAAM,KAAK,MAAM,OAAO,UAAU,WAAW,MAAM,KAAK,GAAG,MAAM,EAAE;CACnE,OAAO;EAAE,QAAQ,MAAM;EAAQ,OAAO,MAAM;EAAO,UAAU,GAAG;EAAQ,SAAS,GAAG;CAAM;AAC5F;;;AAIA,SAAgB,aAAa,QAAuB,OAAuB;CACzE,MAAM,IAAI,SAAS,QAAQ,KAAK;CAChC,OAAO,EAAE,UAAU,EAAE,YAAY,MAAM,QAAQ,IAAI,QAAQ;AAC7D;;;;;AAMA,SAAgB,oBAAoB,QAAuB,KAAuD;CAChH,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,SAAS,KAAK;CACpB,IAAI,CAAC,QAAQ;EACX,MAAM,IAAI,aAAa,QAAQ,KAAK;EACpC,OAAO;GAAE,GAAG;GAAG,GAAG;EAAE;CACtB;CACA,MAAM,IAAI,SAAS,QAAQ,KAAK;CAChC,MAAM,KAAK,EAAE,YAAY;CACzB,IAAI,OAAO,GAAG,OAAO;EAAE,GAAG,EAAE;EAAQ,GAAG,EAAE;CAAO;CAChD,MAAM,CAAC,GAAG,GAAG,GAAG,KAAK;CACrB,MAAM,MAAM,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC;CAClC,IAAI,EAAE,MAAM,IAAI,OAAO;EAAE,GAAG,EAAE;EAAQ,GAAG,EAAE;CAAO;CAClD,OAAO;EAAE,GAAG,EAAE,SAAU,KAAK,KAAK,MAAM,GAAG,CAAC,IAAK;EAAK,GAAG,EAAE,SAAU,KAAK,KAAK,MAAM,GAAG,CAAC,IAAK;CAAI;AACpG;;;;;;AAOA,SAAgB,yBAAyB,MAAqB,KAA8C;CAC1G,MAAM,UAAU,KAAK;CACrB,MAAM,OAAO,KAAK;CAClB,IAAI,YAAY,UAAa,MAAM,SAAS,QAAQ,OAAO;CAC3D,MAAM,WAAW,IAAI,iBAAiB,KAAK,IAAI;CAC/C,MAAM,OAAO,KAAK,OAAO,QAAQ,QAAQ,KAAK,YAAY,IAAI;CAC9D,MAAM,SAAS,KAAK,SAAS,QAAQ,UAAU,KAAK,OAAO,YAAY,IAAI;CAC3E,MAAM,cAAc,MAAM,WAAW,YAAY,SAAS,KAAK;CAC/D,MAAM,gBAAgB,QAAQ,WAAW,YAAY,WAAW,KAAK,QAAQ;CAC7E,IAAI,CAAC,eAAe,CAAC,eAAe,OAAO;CAM3C,OAAO,CAAC;EAJN,GAAG;EACH,GAAI,cAAc,EAAE,cAAc,KAAiB,IAAI,CAAC;EACxD,GAAI,gBAAgB,EAAE,QAAQ;GAAE,GAAG,KAAK;GAAS,cAAc;EAAmB,EAAE,IAAI,CAAC;CAEnF,GAAM,GAAG,KAAK,MAAM,CAAC,CAAC;AAChC;;;;AAcA,SAAgB,YACd,MACA,WACW;CACX,MAAM,SAAS,cAAc,KAAK,QAAQ,WAAW,MAAM;CAC3D,MAAM,OAAO,YAAY,KAAK,MAAM,WAAW,IAAI;CACnD,OAAO,WAAW,OAAO;EAAE;EAAM;CAAO,IAAI,EAAE,KAAK;AACrD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nodeMemo } from "../../core/scene/nodeMemo.js";
|
|
2
2
|
import { PAINT_SLOT } from "./memo.js";
|
|
3
|
-
import {
|
|
3
|
+
import { inkReach, pathPaintOf, withAnimatedVertexColors } from "./nodePaint.js";
|
|
4
4
|
import { strokedPoseBox } from "./strokedBounds.js";
|
|
5
5
|
import { DERIVED_PAINTER, derivedDrawnBounds } from "./derivedPainter.js";
|
|
6
6
|
import { paintsPose, poseBoxBounds } from "./poseBox.js";
|
|
@@ -15,7 +15,7 @@ var PATH_PAINTER = {
|
|
|
15
15
|
if (!paintsPose(node.pose)) return false;
|
|
16
16
|
return node.data?.path != null;
|
|
17
17
|
},
|
|
18
|
-
paint: (node, pose, ctx) =>
|
|
18
|
+
paint: (node, pose, ctx) => ctx?.derivedPath != null ? DERIVED_PAINTER.paint(node, pose, ctx) : withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {
|
|
19
19
|
const d = node.data;
|
|
20
20
|
const projected = pathInPoseFrame(d.path, pose);
|
|
21
21
|
const { fill: declared, stroke: strokeSpec } = pathPaintOf(d, ctx?.inherited);
|
|
@@ -35,7 +35,6 @@ var PATH_PAINTER = {
|
|
|
35
35
|
return pathInPoseFrame(d.path, pose);
|
|
36
36
|
},
|
|
37
37
|
ink: (node, _pose, ctx) => {
|
|
38
|
-
if (node.kind === "container") return CONTAINER_INK;
|
|
39
38
|
const { fill, stroke } = pathPaintOf(node.data, ctx?.inherited);
|
|
40
39
|
return {
|
|
41
40
|
filled: fill !== null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pathPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/pathPainter.ts"],"sourcesContent":["import type {\n Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { pathInPoseFrame } from '@weasel-js/geom';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { DERIVED_PAINTER, derivedDrawnBounds } from './derivedPainter';\nimport { PAINT_SLOT } from './memo';\nimport {
|
|
1
|
+
{"version":3,"file":"pathPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/pathPainter.ts"],"sourcesContent":["import type {\n Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { pathInPoseFrame } from '@weasel-js/geom';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { DERIVED_PAINTER, derivedDrawnBounds } from './derivedPainter';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, pathPaintOf, withAnimatedVertexColors } from './nodePaint';\nimport { resolveStroke } from '@weasel-js/paint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\nimport { strokedPoseBox } from './strokedBounds';\n\ntype PathData = { fill?: Fill | Inherit | null; stroke?: Stroke | Inherit | null; vertexColors?: number[] };\n\n// ─── Built-in painters ─────────────────────────────────────────────────\nexport const PATH_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:path',\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const d = node.data as { path?: Path } | null;\n return d?.path != null;\n },\n // Memoized: a pure function of `(data, pose)`, and `pathInPoseFrame`\n // allocates on every call but the identity (`kind: 'rect'`) branch. See\n // PAINT_SLOT.\n paint: (node, pose, ctx) => ctx?.derivedPath != null\n // A pose override's path stands in for `data.path`, already where it is drawn.\n ? DERIVED_PAINTER.paint(node, pose, ctx)\n : withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {\n const d = node.data as PathData & { path: Path };\n const projected = pathInPoseFrame(d.path, pose);\n const { fill: declared, stroke: strokeSpec } = pathPaintOf(d, ctx?.inherited);\n // The pose is baked into `projected` rather than emitted as a transform,\n // so a box-relative gradient has to be baked onto the same box here or\n // it would arrive in the renderer referring to a frame that never exists.\n const fill = declared && commandFill(declared, pose);\n const stroke = strokeSpec && commandStroke(strokeSpec, pose);\n const cmd: DrawCommand = {\n kind: 'path',\n path: projected,\n ...(fill ? { fill } : {}),\n ...(fill && d.vertexColors ? { vertexColors: d.vertexColors } : {}),\n ...(stroke ? { stroke } : {}),\n };\n return [cmd];\n }, ctx?.inherited), ctx),\n silhouette: (node, pose, ctx) => {\n if (ctx?.derivedPath != null) return ctx.derivedPath;\n const d = node.data as { path: Path };\n return pathInPoseFrame(d.path, pose);\n },\n ink: (node, _pose, ctx) => {\n const { fill, stroke } = pathPaintOf(node.data as PathData, ctx?.inherited);\n return { filled: fill !== null, ...inkReach(stroke ?? null, ctx?.scale) };\n },\n // `pathInPoseFrame` fits the path's own bounds to the pose box.\n bounds: (node, pose, ctx) =>\n poseBoxBounds(pose, resolveStroke((node.data as PathData).stroke, ctx?.inherited?.stroke), ctx),\n drawnBounds: (node, pose, ctx) => {\n if (ctx?.derivedPath != null) return derivedDrawnBounds(node, ctx);\n const d = node.data as PathData & { path: Path };\n return strokedPoseBox(node, pose, resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx, (at) => pathInPoseFrame(d.path, at));\n },\n};\n"],"mappings":";;;;;;;;;;;AAoBA,IAAa,eAAkD;CAC7D,IAAI;CACJ,UAAU,SAAS;EACjB,IAAI,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;EAEnC,OADU,KAAK,MACL,QAAQ;CACpB;CAIA,QAAQ,MAAM,MAAM,QAAQ,KAAK,eAAe,OAE5C,gBAAgB,MAAM,MAAM,MAAM,GAAG,IACrC,yBAAyB,SAAS,MAAM,YAAY,YAAY;EAClE,MAAM,IAAI,KAAK;EACf,MAAM,YAAY,gBAAgB,EAAE,MAAM,IAAI;EAC9C,MAAM,EAAE,MAAM,UAAU,QAAQ,eAAe,YAAY,GAAG,KAAK,SAAS;EAI5E,MAAM,OAAO,YAAY,YAAY,UAAU,IAAI;EACnD,MAAM,SAAS,cAAc,cAAc,YAAY,IAAI;EAQ3D,OAAO,CAAC;GANN,MAAM;GACN,MAAM;GACN,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;GACvB,GAAI,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,IAAI,CAAC;GACjE,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAErB,CAAG;CACb,GAAG,KAAK,SAAS,GAAG,GAAG;CACvB,aAAa,MAAM,MAAM,QAAQ;EAC/B,IAAI,KAAK,eAAe,MAAM,OAAO,IAAI;EACzC,MAAM,IAAI,KAAK;EACf,OAAO,gBAAgB,EAAE,MAAM,IAAI;CACrC;CACA,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,EAAE,MAAM,WAAW,YAAY,KAAK,MAAkB,KAAK,SAAS;EAC1E,OAAO;GAAE,QAAQ,SAAS;GAAM,GAAG,SAAS,UAAU,MAAM,KAAK,KAAK;EAAE;CAC1E;CAEA,SAAS,MAAM,MAAM,QACnB,cAAc,MAAM,cAAe,KAAK,KAAkB,QAAQ,KAAK,WAAW,MAAM,GAAG,GAAG;CAChG,cAAc,MAAM,MAAM,QAAQ;EAChC,IAAI,KAAK,eAAe,MAAM,OAAO,mBAAmB,MAAM,GAAG;EACjE,MAAM,IAAI,KAAK;EACf,OAAO,eAAe,MAAM,MAAM,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM,GAAG,MAAM,OAAO,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC7H;AACF"}
|
|
@@ -5,7 +5,7 @@ import { resolveFill } from "@weasel-js/paint";
|
|
|
5
5
|
//#region src/canvas/nodeShapes/rectFallbackPainter.ts
|
|
6
6
|
var RECT_FALLBACK_PAINTER = {
|
|
7
7
|
id: "kit:rect-fallback",
|
|
8
|
-
matches: (node) => node.kind !== "
|
|
8
|
+
matches: (node) => node.kind !== "group" && paintsPose(node.pose),
|
|
9
9
|
paint: (node, pose, ctx) => {
|
|
10
10
|
const d = node.data;
|
|
11
11
|
const p = pose;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rectFallbackPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/rectFallbackPainter.ts"],"sourcesContent":["import type { Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill } from '@weasel-js/renderer';\nimport { resolveFill } from '@weasel-js/paint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\n\n// ─── Built-in painters ─────────────────────────────────────────────────\nexport const RECT_FALLBACK_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n // Last-resort painter — matches any rect-posed
|
|
1
|
+
{"version":3,"file":"rectFallbackPainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/rectFallbackPainter.ts"],"sourcesContent":["import type { Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill } from '@weasel-js/renderer';\nimport { resolveFill } from '@weasel-js/paint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\n\n// ─── Built-in painters ─────────────────────────────────────────────────\nexport const RECT_FALLBACK_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n // Last-resort painter — matches any rect-posed mark, so it must be\n // registered last within `'normal'`.\n // Consumers who want a different\n // fallback should register their own painter at `'high'` priority and let\n // this one never fire (or unregister it explicitly).\n id: 'kit:rect-fallback',\n matches: (node) => node.kind !== 'group' && paintsPose(node.pose),\n paint: (node, pose, ctx) => {\n const d = node.data as { fill?: Fill | Inherit | null } | null;\n const p = pose;\n const declared = resolveFill(d?.fill, ctx?.inherited?.fill);\n const fill = declared && commandFill(declared, p);\n return [{\n kind: 'path',\n path: { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height },\n ...(fill ? { fill } : {}),\n }];\n },\n silhouette: (_node, pose) => {\n const p = pose;\n return { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height };\n },\n bounds: (_node, pose) => poseBoxBounds(pose, null, undefined),\n};\n"],"mappings":";;;;;AAQA,IAAa,wBAA2D;CAMtE,IAAI;CACJ,UAAU,SAAS,KAAK,SAAS,WAAW,WAAW,KAAK,IAAI;CAChE,QAAQ,MAAM,MAAM,QAAQ;EAC1B,MAAM,IAAI,KAAK;EACf,MAAM,IAAI;EACV,MAAM,WAAW,YAAY,GAAG,MAAM,KAAK,WAAW,IAAI;EAC1D,MAAM,OAAO,YAAY,YAAY,UAAU,CAAC;EAChD,OAAO,CAAC;GACN,MAAM;GACN,MAAM;IAAE,MAAM;IAAQ,GAAG,EAAE;IAAG,GAAG,EAAE;IAAG,OAAO,EAAE;IAAO,QAAQ,EAAE;GAAO;GACvE,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EACzB,CAAC;CACH;CACA,aAAa,OAAO,SAAS;EAC3B,MAAM,IAAI;EACV,OAAO;GAAE,MAAM;GAAQ,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;EAAO;CAC1E;CACA,SAAS,OAAO,SAAS,cAAc,MAAM,MAAM,MAAS;AAC9D"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nodeMemo } from "../../core/scene/nodeMemo.js";
|
|
2
2
|
import { PAINT_SLOT } from "./memo.js";
|
|
3
|
-
import {
|
|
3
|
+
import { inkReach, withAnimatedVertexColors } from "./nodePaint.js";
|
|
4
4
|
import { outsetPoseBox, strokedPoseBox } from "./strokedBounds.js";
|
|
5
5
|
import { paintsPose, poseBoxBounds } from "./poseBox.js";
|
|
6
6
|
import { commandFill, commandStroke } from "@weasel-js/renderer";
|
|
@@ -19,7 +19,7 @@ var SHAPE_PAINTER = {
|
|
|
19
19
|
const s = node.data?.shape;
|
|
20
20
|
return s != null && SHAPE_KINDS.has(s);
|
|
21
21
|
},
|
|
22
|
-
paint: (node, pose, ctx) =>
|
|
22
|
+
paint: (node, pose, ctx) => withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {
|
|
23
23
|
const d = node.data;
|
|
24
24
|
const path = pathForShape(d, pose);
|
|
25
25
|
const declaredFill = resolveFill(d.fill, ctx?.inherited?.fill);
|
|
@@ -38,7 +38,6 @@ var SHAPE_PAINTER = {
|
|
|
38
38
|
return pathForShape(d, pose);
|
|
39
39
|
},
|
|
40
40
|
ink: (node, _pose, ctx) => {
|
|
41
|
-
if (node.kind === "container") return CONTAINER_INK;
|
|
42
41
|
const d = node.data;
|
|
43
42
|
return {
|
|
44
43
|
filled: resolveFill(d.fill, ctx?.inherited?.fill) !== null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shapePainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/shapePainter.ts"],"sourcesContent":["import {\n ellipsePath,\n regularPolygonPath,\n starPath,\n type Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport { PAINT_SLOT } from './memo';\nimport {
|
|
1
|
+
{"version":3,"file":"shapePainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/shapePainter.ts"],"sourcesContent":["import {\n ellipsePath,\n regularPolygonPath,\n starPath,\n type Path\n} from '@weasel-js/geom';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { RectPose } from 'core/scene/types';\nimport { commandFill, commandStroke } from '@weasel-js/renderer';\nimport { PAINT_SLOT } from './memo';\nimport { inkReach, withAnimatedVertexColors } from './nodePaint';\nimport { resolveFill, resolveStroke } from '@weasel-js/paint';\nimport { paintsPose, poseBoxBounds } from './poseBox';\nimport { outsetPoseBox, strokedPoseBox } from './strokedBounds';\n\ntype ShapeData = {\n shape: string;\n fill?: Fill | Inherit | null;\n stroke?: Stroke | Inherit | null;\n sides?: number;\n points?: number;\n};\n\n// ─── Built-in painters ─────────────────────────────────────────────────\n/** Built-in shape dispatcher — matches when `data.shape` names a kit-known\n * shape kind. Computes both paint and silhouette from the pose so consumer\n * scenes can declare clipping/rendering directly in JSON without\n * registering a custom painter. */\nexport const SHAPE_PAINTER: NodeShapeEntry<unknown, RectPose> = {\n id: 'kit:shape',\n matches: (node) => {\n if (!paintsPose(node.pose)) return false;\n const s = (node.data as { shape?: string } | null)?.shape;\n return s != null && SHAPE_KINDS.has(s);\n },\n // Memoized: a pure function of `(data, pose)`, and `pathForShape` builds a\n // fresh `Uint8Array` + `Float32Array` for every ellipse, polygon and star.\n // See PAINT_SLOT.\n paint: (node, pose, ctx) => withAnimatedVertexColors(nodeMemo(node, PAINT_SLOT, pose, () => {\n const d = node.data as ShapeData;\n const path = pathForShape(d, pose);\n const declaredFill = resolveFill(d.fill, ctx?.inherited?.fill);\n const shapeFill = declaredFill && commandFill(declaredFill, pose);\n const strokeSpec = resolveStroke(d.stroke, ctx?.inherited?.stroke);\n const stroke = strokeSpec && commandStroke(strokeSpec, pose);\n return [{\n kind: 'path',\n path,\n ...(shapeFill ? { fill: shapeFill } : {}),\n ...(stroke ? { stroke } : {}),\n }];\n }, ctx?.inherited), ctx),\n silhouette: (node, pose) => {\n const d = node.data as { shape: string; sides?: number; points?: number };\n return pathForShape(d, pose);\n },\n ink: (node, _pose, ctx) => {\n const d = node.data as ShapeData;\n return {\n filled: resolveFill(d.fill, ctx?.inherited?.fill) !== null,\n ...inkReach(resolveStroke(d.stroke, ctx?.inherited?.stroke), ctx?.scale),\n };\n },\n // Every shape kind is inscribed in the pose box.\n bounds: (node, pose, ctx) =>\n poseBoxBounds(pose, resolveStroke((node.data as ShapeData).stroke, ctx?.inherited?.stroke), ctx),\n // A rect's or an ellipse's stroke reaches its box evenly; a polygon's or a\n // star's corners are sharper than a right angle, so their miters are measured.\n drawnBounds: (node, pose, ctx) => {\n const d = node.data as ShapeData;\n const stroke = resolveStroke(d.stroke, ctx?.inherited?.stroke);\n return d.shape === 'polygon' || d.shape === 'star'\n ? strokedPoseBox(node, pose, stroke, ctx, (at) => pathForShape(d, at))\n : outsetPoseBox(pose, stroke, ctx);\n },\n};\n\nexport const SHAPE_KINDS = new Set(['rect', 'ellipse', 'polygon', 'star']);\n\nexport function pathForShape(\n d: { shape: string; sides?: number; points?: number },\n p: RectPose,\n): Path {\n switch (d.shape) {\n case 'ellipse':\n return ellipsePath(p);\n case 'polygon': {\n const cx = p.x + p.width / 2;\n const cy = p.y + p.height / 2;\n const r = Math.min(p.width, p.height) / 2;\n return regularPolygonPath({ x: cx, y: cy }, r, d.sides ?? 6);\n }\n case 'star': {\n const cx = p.x + p.width / 2;\n const cy = p.y + p.height / 2;\n const r = Math.min(p.width, p.height) / 2;\n return starPath({ x: cx, y: cy }, r, d.points ?? 5);\n }\n case 'rect':\n default:\n return { kind: 'rect', x: p.x, y: p.y, width: p.width, height: p.height };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA8BA,IAAa,gBAAmD;CAC9D,IAAI;CACJ,UAAU,SAAS;EACjB,IAAI,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;EACnC,MAAM,IAAK,KAAK,MAAoC;EACpD,OAAO,KAAK,QAAQ,YAAY,IAAI,CAAC;CACvC;CAIA,QAAQ,MAAM,MAAM,QAAQ,yBAAyB,SAAS,MAAM,YAAY,YAAY;EAC1F,MAAM,IAAI,KAAK;EACf,MAAM,OAAO,aAAa,GAAG,IAAI;EACjC,MAAM,eAAe,YAAY,EAAE,MAAM,KAAK,WAAW,IAAI;EAC7D,MAAM,YAAY,gBAAgB,YAAY,cAAc,IAAI;EAChE,MAAM,aAAa,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM;EACjE,MAAM,SAAS,cAAc,cAAc,YAAY,IAAI;EAC3D,OAAO,CAAC;GACN,MAAM;GACN;GACA,GAAI,YAAY,EAAE,MAAM,UAAU,IAAI,CAAC;GACvC,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC7B,CAAC;CACH,GAAG,KAAK,SAAS,GAAG,GAAG;CACvB,aAAa,MAAM,SAAS;EAC1B,MAAM,IAAI,KAAK;EACf,OAAO,aAAa,GAAG,IAAI;CAC7B;CACA,MAAM,MAAM,OAAO,QAAQ;EACzB,MAAM,IAAI,KAAK;EACf,OAAO;GACL,QAAQ,YAAY,EAAE,MAAM,KAAK,WAAW,IAAI,MAAM;GACtD,GAAG,SAAS,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM,GAAG,KAAK,KAAK;EACzE;CACF;CAEA,SAAS,MAAM,MAAM,QACnB,cAAc,MAAM,cAAe,KAAK,KAAmB,QAAQ,KAAK,WAAW,MAAM,GAAG,GAAG;CAGjG,cAAc,MAAM,MAAM,QAAQ;EAChC,MAAM,IAAI,KAAK;EACf,MAAM,SAAS,cAAc,EAAE,QAAQ,KAAK,WAAW,MAAM;EAC7D,OAAO,EAAE,UAAU,aAAa,EAAE,UAAU,SACxC,eAAe,MAAM,MAAM,QAAQ,MAAM,OAAO,aAAa,GAAG,EAAE,CAAC,IACnE,cAAc,MAAM,QAAQ,GAAG;CACrC;AACF;AAEA,IAAa,8BAAc,IAAI,IAAI;CAAC;CAAQ;CAAW;CAAW;AAAM,CAAC;AAEzE,SAAgB,aACd,GACA,GACM;CACN,QAAQ,EAAE,OAAV;EACE,KAAK,WACH,OAAO,YAAY,CAAC;EACtB,KAAK,WAAW;GACd,MAAM,KAAK,EAAE,IAAI,EAAE,QAAQ;GAC3B,MAAM,KAAK,EAAE,IAAI,EAAE,SAAS;GAC5B,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI;GACxC,OAAO,mBAAmB;IAAE,GAAG;IAAI,GAAG;GAAG,GAAG,GAAG,EAAE,SAAS,CAAC;EAC7D;EACA,KAAK,QAAQ;GACX,MAAM,KAAK,EAAE,IAAI,EAAE,QAAQ;GAC3B,MAAM,KAAK,EAAE,IAAI,EAAE,SAAS;GAC5B,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI;GACxC,OAAO,SAAS;IAAE,GAAG;IAAI,GAAG;GAAG,GAAG,GAAG,EAAE,UAAU,CAAC;EACpD;EAEA,SACE,OAAO;GAAE,MAAM;GAAQ,GAAG,EAAE;GAAG,GAAG,EAAE;GAAG,OAAO,EAAE;GAAO,QAAQ,EAAE;EAAO;CAC5E;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { findShapeSilhouette } from "./NodeShape.js";
|
|
2
|
+
|
|
3
|
+
//#region src/canvas/ownClip.ts
|
|
4
|
+
/**
|
|
5
|
+
* What a node declaring `clip: 'outline'` clips its children to, in world
|
|
6
|
+
* coordinates: the silhouette of whichever painter claims it, the rect
|
|
7
|
+
* fallback's box included, or `null` when none does. `derived` supplies a
|
|
8
|
+
* derived path when there is one.
|
|
9
|
+
*/
|
|
10
|
+
function clipOutline(node, pose, derived) {
|
|
11
|
+
return findShapeSilhouette(node, pose, { derivedPath: derived?.() ?? null });
|
|
12
|
+
}
|
|
13
|
+
/** The clip a node imposes on its children, or null when it imposes none:
|
|
14
|
+
* its `clipFromPose` when it has one, else its {@link clipOutline} when it
|
|
15
|
+
* declares `clip: 'outline'`. */
|
|
16
|
+
function ownClipOf(node, pose, derived) {
|
|
17
|
+
if (typeof node.clipFromPose === "function") return node.clipFromPose(pose);
|
|
18
|
+
if (node.clip !== "outline") return null;
|
|
19
|
+
return clipOutline(node, pose, derived);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { clipOutline, ownClipOf };
|
|
24
|
+
//# sourceMappingURL=ownClip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ownClip.js","names":[],"sources":["../../src/canvas/ownClip.ts"],"sourcesContent":["import type { Path } from '@weasel-js/geom';\nimport { findShapeSilhouette } from 'canvas/NodeShape';\nimport type { ClipFromPoseFn, Node, NodeClip } from 'core/scene/types';\n\n/** What {@link ownClipOf} reads of a node. */\nexport interface ClipBearing<TPose> {\n clipFromPose?: ClipFromPoseFn<TPose>;\n clip?: NodeClip;\n}\n\n/**\n * What a node declaring `clip: 'outline'` clips its children to, in world\n * coordinates: the silhouette of whichever painter claims it, the rect\n * fallback's box included, or `null` when none does. `derived` supplies a\n * derived path when there is one.\n */\nexport function clipOutline<TPose>(\n node: Node<unknown, string, TPose>,\n pose: TPose,\n derived?: () => Path | null | undefined,\n): Path | null {\n return findShapeSilhouette(node, pose, { derivedPath: derived?.() ?? null });\n}\n\n/** The clip a node imposes on its children, or null when it imposes none:\n * its `clipFromPose` when it has one, else its {@link clipOutline} when it\n * declares `clip: 'outline'`. */\nexport function ownClipOf<TPose>(\n node: ClipBearing<TPose>,\n pose: TPose,\n derived?: () => Path | null | undefined,\n): Path | null {\n if (typeof node.clipFromPose === 'function') return node.clipFromPose(pose);\n if (node.clip !== 'outline') return null;\n return clipOutline(node as unknown as Node<unknown, string, TPose>, pose, derived);\n}\n"],"mappings":";;;;;;;;;AAgBA,SAAgB,YACd,MACA,MACA,SACa;CACb,OAAO,oBAAoB,MAAM,MAAM,EAAE,aAAa,UAAU,KAAK,KAAK,CAAC;AAC7E;;;;AAKA,SAAgB,UACd,MACA,MACA,SACa;CACb,IAAI,OAAO,KAAK,iBAAiB,YAAY,OAAO,KAAK,aAAa,IAAI;CAC1E,IAAI,KAAK,SAAS,WAAW,OAAO;CACpC,OAAO,YAAY,MAAiD,MAAM,OAAO;AACnF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paintCull.js","names":["viewToMat3","mat3"],"sources":["../../src/canvas/paintCull.ts"],"sourcesContent":["import type { View } from 'core/viewport/view';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseFrameOf } from '@weasel-js/geom';\nimport { boundsMissRect, type CullRect } from '@weasel-js/renderer';\nimport { mat3, viewToMat3 } from '@weasel-js/geom';\n\n/** Where a node's paint can reach, before its pose frame — the scene\n * slot's `paintBounds`. `null` means unknown, and the node is painted. */\nexport type PaintBoundsFn<TNode, TPose> = (node: TNode, pose: TPose, view: View) => Bounds | null;\n\n/**\n * A predicate true for the nodes whose paint cannot reach `rect` (screen\n * pixels) under `view`, by `paintBounds` taken through the pose frame\n * (`poseFrameOf`) `wrapNodeOutput` applies and then the view. A node `paintBounds` cannot\n * bound is never culled.\n */\nexport function paintMissesView<TNode, TPose>(\n paintBounds: PaintBoundsFn<TNode, TPose>,\n view: View,\n rect: CullRect,\n): (node: TNode, pose: TPose, layerView?: View) => boolean {\n // `layerView` is a parallax plane's own view; planes are few, so one slot\n // of memo covers a walk that meets them layer by layer.\n let last = view;\n let toScreen = viewToMat3(view);\n return (node, pose, layerView = view) => {\n if (layerView !== last) { last = layerView; toScreen = viewToMat3(layerView); }\n const b = paintBounds(node, pose, layerView);\n if (!b) return false;\n const frame = poseFrameOf(pose);\n const m = frame ? mat3.multiply(toScreen, mat3.fromAffine(frame)) : toScreen;\n return boundsMissRect(b, m, rect);\n };\n}\n"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"paintCull.js","names":["viewToMat3","mat3"],"sources":["../../src/canvas/paintCull.ts"],"sourcesContent":["import type { View } from 'core/viewport/view';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseFrameOf } from '@weasel-js/geom';\nimport { boundsMissRect, type CullRect } from '@weasel-js/renderer';\nimport { mat3, viewToMat3 } from '@weasel-js/geom';\nimport type { NodePaintCtx } from './nodeShapeTypes';\n\n/** Where a node's paint can reach, before its pose frame — the scene\n * slot's `paintBounds`. `null` means unknown, and the node is painted.\n * `ctx.inherited` is what its `'inherit'` fill and stroke take, as `drawOne` gets it. */\nexport type PaintBoundsFn<TNode, TPose> = (\n node: TNode, pose: TPose, view: View, ctx?: Pick<NodePaintCtx, 'inherited'>,\n) => Bounds | null;\n\n/**\n * A predicate true for the nodes whose paint cannot reach `rect` (screen\n * pixels) under `view`, by `paintBounds` taken through the pose frame\n * (`poseFrameOf`) `wrapNodeOutput` applies and then the view. A node `paintBounds` cannot\n * bound is never culled.\n */\nexport function paintMissesView<TNode, TPose>(\n paintBounds: PaintBoundsFn<TNode, TPose>,\n view: View,\n rect: CullRect,\n): (node: TNode, pose: TPose, layerView?: View) => boolean {\n // `layerView` is a parallax plane's own view; planes are few, so one slot\n // of memo covers a walk that meets them layer by layer.\n let last = view;\n let toScreen = viewToMat3(view);\n return (node, pose, layerView = view) => {\n if (layerView !== last) { last = layerView; toScreen = viewToMat3(layerView); }\n const b = paintBounds(node, pose, layerView);\n if (!b) return false;\n const frame = poseFrameOf(pose);\n const m = frame ? mat3.multiply(toScreen, mat3.fromAffine(frame)) : toScreen;\n return boundsMissRect(b, m, rect);\n };\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAgB,gBACd,aACA,MACA,MACyD;CAGzD,IAAI,OAAO;CACX,IAAI,WAAWA,aAAW,IAAI;CAC9B,QAAQ,MAAM,MAAM,YAAY,SAAS;EACvC,IAAI,cAAc,MAAM;GAAE,OAAO;GAAW,WAAWA,aAAW,SAAS;EAAG;EAC9E,MAAM,IAAI,YAAY,MAAM,MAAM,SAAS;EAC3C,IAAI,CAAC,GAAG,OAAO;EACf,MAAM,QAAQ,YAAY,IAAI;EAC9B,MAAM,IAAI,QAAQC,OAAK,SAAS,UAAUA,OAAK,WAAW,KAAK,CAAC,IAAI;EACpE,OAAO,eAAe,GAAG,GAAG,IAAI;CAClC;AACF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { framedBounds, poseDescriptorForNode } from "../../core/geometry/poseDescriptor.js";
|
|
2
|
+
import { AUTO_POSE_DESCRIPTOR, isPathLike, isRectPose } from "../../core/geometry/autoPoseDescriptor.js";
|
|
3
|
+
import { findShapeInk } from "../NodeShape.js";
|
|
4
|
+
import { regionBoundsOf } from "../../core/geometry/regionHit.js";
|
|
5
|
+
import { applyToPoint, boundsOfPath, poseFrameOf } from "@weasel-js/geom";
|
|
6
|
+
|
|
7
|
+
//#region src/canvas/pickIndex/nodeBroadBox.ts
|
|
8
|
+
/**
|
|
9
|
+
* One node's box in the pick index: a world box that every query's `hits`
|
|
10
|
+
* accepts only inside of, before that query's own screen-pixel slop.
|
|
11
|
+
*
|
|
12
|
+
* The point query and the region query read a node differently, so the box is
|
|
13
|
+
* the union of both: the region's `regionBoundsOf` through the node's own
|
|
14
|
+
* descriptor, and the frame box the point query's `poseContainsRotated` tests,
|
|
15
|
+
* grown in the pose's own frame by the ink's `outset`. A derived node is read
|
|
16
|
+
* by its path alone, as both queries read it.
|
|
17
|
+
*/
|
|
18
|
+
function grow(out, b, by) {
|
|
19
|
+
const x1 = b.x + b.width, y1 = b.y + b.height;
|
|
20
|
+
out[0] = Math.min(out[0], Math.min(b.x, x1) - by);
|
|
21
|
+
out[1] = Math.min(out[1], Math.min(b.y, y1) - by);
|
|
22
|
+
out[2] = Math.max(out[2], Math.max(b.x, x1) + by);
|
|
23
|
+
out[3] = Math.max(out[3], Math.max(b.y, y1) + by);
|
|
24
|
+
}
|
|
25
|
+
/** The local box `poseContainsRotated` tests, grown by `by` and carried through
|
|
26
|
+
* the pose's frame, as that function reads it. */
|
|
27
|
+
function framedBox(out, pose, d, by) {
|
|
28
|
+
const local = isPathLike(pose) ? boundsOfPath(pose) : d.getBounds(pose);
|
|
29
|
+
const framed = d === AUTO_POSE_DESCRIPTOR ? pose : framedBounds(pose, d);
|
|
30
|
+
const frame = poseFrameOf(framed);
|
|
31
|
+
if (!frame) {
|
|
32
|
+
grow(out, local, by);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const x0 = local.x - by, y0 = local.y - by;
|
|
36
|
+
const x1 = local.x + local.width + by, y1 = local.y + local.height + by;
|
|
37
|
+
for (const [x, y] of [
|
|
38
|
+
[x0, y0],
|
|
39
|
+
[x1, y0],
|
|
40
|
+
[x1, y1],
|
|
41
|
+
[x0, y1]
|
|
42
|
+
]) {
|
|
43
|
+
const [wx, wy] = applyToPoint(frame, x, y);
|
|
44
|
+
grow(out, {
|
|
45
|
+
x: wx,
|
|
46
|
+
y: wy,
|
|
47
|
+
width: 0,
|
|
48
|
+
height: 0
|
|
49
|
+
}, 0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Float error in a frame's round trip must not put an accepted point just outside. */
|
|
53
|
+
function pad(b) {
|
|
54
|
+
const e = 1e-9 * (1 + Math.max(Math.abs(b[0]), Math.abs(b[1]), Math.abs(b[2]), Math.abs(b[3])));
|
|
55
|
+
b[0] -= e;
|
|
56
|
+
b[1] -= e;
|
|
57
|
+
b[2] += e;
|
|
58
|
+
b[3] += e;
|
|
59
|
+
}
|
|
60
|
+
var UNINHERITED = Object.freeze({ scale: 1 });
|
|
61
|
+
/** Writes the box into `out` and returns the ink's reach measured on screen,
|
|
62
|
+
* which no world box can hold. */
|
|
63
|
+
function nodeBroadBox(node, pose, derived, inherited, descriptor, out) {
|
|
64
|
+
const ink = findShapeInk(node, pose, inherited === void 0 ? UNINHERITED : {
|
|
65
|
+
scale: 1,
|
|
66
|
+
inherited
|
|
67
|
+
});
|
|
68
|
+
const outset = Math.max(0, ink?.outset ?? 0);
|
|
69
|
+
out[0] = out[1] = Infinity;
|
|
70
|
+
out[2] = out[3] = -Infinity;
|
|
71
|
+
if (derived) grow(out, boundsOfPath(derived), outset);
|
|
72
|
+
else {
|
|
73
|
+
const own = poseDescriptorForNode(descriptor, node);
|
|
74
|
+
if (descriptor === AUTO_POSE_DESCRIPTOR && own === descriptor && isRectPose(pose) && poseFrameOf(pose) === null) grow(out, pose, outset);
|
|
75
|
+
else {
|
|
76
|
+
grow(out, regionBoundsOf(node, pose, own), outset);
|
|
77
|
+
framedBox(out, pose, descriptor, outset);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
pad(out);
|
|
81
|
+
return Math.max(0, ink?.outsetPx ?? 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
export { nodeBroadBox };
|
|
86
|
+
//# sourceMappingURL=nodeBroadBox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeBroadBox.js","names":[],"sources":["../../../src/canvas/pickIndex/nodeBroadBox.ts"],"sourcesContent":["/**\n * One node's box in the pick index: a world box that every query's `hits`\n * accepts only inside of, before that query's own screen-pixel slop.\n *\n * The point query and the region query read a node differently, so the box is\n * the union of both: the region's `regionBoundsOf` through the node's own\n * descriptor, and the frame box the point query's `poseContainsRotated` tests,\n * grown in the pose's own frame by the ink's `outset`. A derived node is read\n * by its path alone, as both queries read it.\n */\nimport { applyToPoint, boundsOfPath, poseFrameOf, type Path } from '@weasel-js/geom';\nimport { findShapeInk } from 'canvas/NodeShape';\nimport { AUTO_POSE_DESCRIPTOR, isPathLike, isRectPose } from 'core/geometry/autoPoseDescriptor';\nimport { framedBounds } from 'core/geometry/poseDescriptor';\nimport { regionBoundsOf } from 'core/geometry/regionHit';\nimport type { InheritedPaint } from 'core/scene/inheritedPaint';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'interactions/actions/resize/geometry';\n\n/** `[minX, minY, maxX, maxY]`, the layout `createBvh(2)` takes. */\nexport type Box2 = [number, number, number, number];\n\ntype Rect = { x: number; y: number; width: number; height: number };\n\nfunction grow(out: Box2, b: Rect, by: number): void {\n const x1 = b.x + b.width, y1 = b.y + b.height;\n out[0] = Math.min(out[0], Math.min(b.x, x1) - by);\n out[1] = Math.min(out[1], Math.min(b.y, y1) - by);\n out[2] = Math.max(out[2], Math.max(b.x, x1) + by);\n out[3] = Math.max(out[3], Math.max(b.y, y1) + by);\n}\n\n/** The local box `poseContainsRotated` tests, grown by `by` and carried through\n * the pose's frame, as that function reads it. */\nfunction framedBox(out: Box2, pose: unknown, d: PoseDescriptor<unknown>, by: number): void {\n const local: Rect = isPathLike(pose) ? boundsOfPath(pose) : d.getBounds(pose);\n const framed = d === AUTO_POSE_DESCRIPTOR ? pose : framedBounds(pose, d);\n const frame = poseFrameOf(framed);\n if (!frame) {\n grow(out, local, by);\n return;\n }\n const x0 = local.x - by, y0 = local.y - by;\n const x1 = local.x + local.width + by, y1 = local.y + local.height + by;\n for (const [x, y] of [[x0, y0], [x1, y0], [x1, y1], [x0, y1]] as const) {\n const [wx, wy] = applyToPoint(frame, x, y);\n grow(out, { x: wx, y: wy, width: 0, height: 0 }, 0);\n }\n}\n\n/** Float error in a frame's round trip must not put an accepted point just outside. */\nfunction pad(b: Box2): void {\n const e = 1e-9 * (1 + Math.max(Math.abs(b[0]), Math.abs(b[1]), Math.abs(b[2]), Math.abs(b[3])));\n b[0] -= e; b[1] -= e; b[2] += e; b[3] += e;\n}\n\nconst UNINHERITED = Object.freeze({ scale: 1 });\n\n/** Writes the box into `out` and returns the ink's reach measured on screen,\n * which no world box can hold. */\nexport function nodeBroadBox(\n node: object,\n pose: unknown,\n derived: Path | null | undefined,\n inherited: InheritedPaint | undefined,\n descriptor: PoseDescriptor<unknown>,\n out: Box2,\n): number {\n const ink = findShapeInk(node as never, pose, inherited === undefined ? UNINHERITED : { scale: 1, inherited });\n const outset = Math.max(0, ink?.outset ?? 0);\n out[0] = out[1] = Infinity;\n out[2] = out[3] = -Infinity;\n if (derived) {\n grow(out, boundsOfPath(derived), outset);\n } else {\n const own = poseDescriptorForNode(descriptor, node);\n if (descriptor === AUTO_POSE_DESCRIPTOR && own === descriptor && isRectPose(pose) && poseFrameOf(pose) === null) {\n // Both reads come to the pose rect itself.\n grow(out, pose, outset);\n } else {\n grow(out, regionBoundsOf(node, pose, own), outset);\n framedBox(out, pose, descriptor, outset);\n }\n }\n pad(out);\n return Math.max(0, ink?.outsetPx ?? 0);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuBA,SAAS,KAAK,KAAW,GAAS,IAAkB;CAClD,MAAM,KAAK,EAAE,IAAI,EAAE,OAAO,KAAK,EAAE,IAAI,EAAE;CACvC,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;CAChD,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;CAChD,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;CAChD,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;AAClD;;;AAIA,SAAS,UAAU,KAAW,MAAe,GAA4B,IAAkB;CACzF,MAAM,QAAc,WAAW,IAAI,IAAI,aAAa,IAAI,IAAI,EAAE,UAAU,IAAI;CAC5E,MAAM,SAAS,MAAM,uBAAuB,OAAO,aAAa,MAAM,CAAC;CACvE,MAAM,QAAQ,YAAY,MAAM;CAChC,IAAI,CAAC,OAAO;EACV,KAAK,KAAK,OAAO,EAAE;EACnB;CACF;CACA,MAAM,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,IAAI;CACxC,MAAM,KAAK,MAAM,IAAI,MAAM,QAAQ,IAAI,KAAK,MAAM,IAAI,MAAM,SAAS;CACrE,KAAK,MAAM,CAAC,GAAG,MAAM;EAAC,CAAC,IAAI,EAAE;EAAG,CAAC,IAAI,EAAE;EAAG,CAAC,IAAI,EAAE;EAAG,CAAC,IAAI,EAAE;CAAC,GAAY;EACtE,MAAM,CAAC,IAAI,MAAM,aAAa,OAAO,GAAG,CAAC;EACzC,KAAK,KAAK;GAAE,GAAG;GAAI,GAAG;GAAI,OAAO;GAAG,QAAQ;EAAE,GAAG,CAAC;CACpD;AACF;;AAGA,SAAS,IAAI,GAAe;CAC1B,MAAM,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,EAAE,GAAG,KAAK,IAAI,EAAE,EAAE,GAAG,KAAK,IAAI,EAAE,EAAE,GAAG,KAAK,IAAI,EAAE,EAAE,CAAC;CAC7F,EAAE,MAAM;CAAG,EAAE,MAAM;CAAG,EAAE,MAAM;CAAG,EAAE,MAAM;AAC3C;AAEA,IAAM,cAAc,OAAO,OAAO,EAAE,OAAO,EAAE,CAAC;;;AAI9C,SAAgB,aACd,MACA,MACA,SACA,WACA,YACA,KACQ;CACR,MAAM,MAAM,aAAa,MAAe,MAAM,cAAc,SAAY,cAAc;EAAE,OAAO;EAAG;CAAU,CAAC;CAC7G,MAAM,SAAS,KAAK,IAAI,GAAG,KAAK,UAAU,CAAC;CAC3C,IAAI,KAAK,IAAI,KAAK;CAClB,IAAI,KAAK,IAAI,KAAK;CAClB,IAAI,SACF,KAAK,KAAK,aAAa,OAAO,GAAG,MAAM;MAClC;EACL,MAAM,MAAM,sBAAsB,YAAY,IAAI;EAClD,IAAI,eAAe,wBAAwB,QAAQ,cAAc,WAAW,IAAI,KAAK,YAAY,IAAI,MAAM,MAEzG,KAAK,KAAK,MAAM,MAAM;OACjB;GACL,KAAK,KAAK,eAAe,MAAM,MAAM,GAAG,GAAG,MAAM;GACjD,UAAU,KAAK,MAAM,YAAY,MAAM;EACzC;CACF;CACA,IAAI,GAAG;CACP,OAAO,KAAK,IAAI,GAAG,KAAK,YAAY,CAAC;AACvC"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { nodeMemoGeneration } from "../../core/scene/nodeMemo.js";
|
|
2
|
+
import { inheritedPaintOf, inheritsPaint } from "../../core/scene/inheritedPaint.js";
|
|
3
|
+
import { nodesAsShown } from "../../core/scene/nodeAsShown.js";
|
|
4
|
+
import { getNodeShapes } from "../NodeShape.js";
|
|
5
|
+
import { scenePaintedPoseOf } from "../derivedPath.js";
|
|
6
|
+
import { sceneParentsOf, sceneShownPathOf } from "../scenePickReads.js";
|
|
7
|
+
import { nodeBroadBox } from "./nodeBroadBox.js";
|
|
8
|
+
import { markerGeneration } from "@weasel-js/stroke";
|
|
9
|
+
import { createBvh } from "@weasel-js/geom/nd";
|
|
10
|
+
|
|
11
|
+
//#region src/canvas/pickIndex/scenePickIndex.ts
|
|
12
|
+
/**
|
|
13
|
+
* The per-scene pick index: one BVH per scene layer over every node's broad
|
|
14
|
+
* box (`nodeBroadBox`), keyed by the node's place in the render order it was
|
|
15
|
+
* built from. A pick after anything it read can have changed walks the scene
|
|
16
|
+
* and only records that state; the next pick to find the scene unchanged
|
|
17
|
+
* builds, so a scene edited before every pick never pays for a build. Nodes it
|
|
18
|
+
* cannot box conservatively are on `always` and are tested by every walk.
|
|
19
|
+
*/
|
|
20
|
+
var builds = 0;
|
|
21
|
+
var CACHE = /* @__PURE__ */ new WeakMap();
|
|
22
|
+
/** Past this share of the scene the index costs more than the walk it narrows. */
|
|
23
|
+
var MAX_SHARE = .5;
|
|
24
|
+
var BuiltIndex = class {
|
|
25
|
+
built;
|
|
26
|
+
trees;
|
|
27
|
+
always;
|
|
28
|
+
maxOutsetPx;
|
|
29
|
+
layers;
|
|
30
|
+
/** The render order the candidates are read out of. */
|
|
31
|
+
order;
|
|
32
|
+
/** Build position to position in `order`, or null while they are the same array. */
|
|
33
|
+
remap = null;
|
|
34
|
+
marks;
|
|
35
|
+
found;
|
|
36
|
+
constructor(built, trees, always, maxOutsetPx) {
|
|
37
|
+
this.built = built;
|
|
38
|
+
this.trees = trees;
|
|
39
|
+
this.always = always;
|
|
40
|
+
this.maxOutsetPx = maxOutsetPx;
|
|
41
|
+
this.layers = [...trees.keys()];
|
|
42
|
+
this.order = built;
|
|
43
|
+
this.marks = new Uint8Array(built.length);
|
|
44
|
+
this.found = new Int32Array(Math.max(16, always.length));
|
|
45
|
+
}
|
|
46
|
+
/** Follow a new render order array the scene hands back for the same state. */
|
|
47
|
+
rankBy(order) {
|
|
48
|
+
if (order === this.order) return;
|
|
49
|
+
this.order = order;
|
|
50
|
+
if (order === this.built) {
|
|
51
|
+
this.remap = null;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const rank = /* @__PURE__ */ new Map();
|
|
55
|
+
for (let i = 0; i < order.length; i++) rank.set(order[i].id, i);
|
|
56
|
+
const remap = new Int32Array(this.built.length);
|
|
57
|
+
for (let i = 0; i < remap.length; i++) remap[i] = rank.get(this.built[i].id) ?? -1;
|
|
58
|
+
this.remap = remap;
|
|
59
|
+
}
|
|
60
|
+
/** The nodes the boxes reach, in render order, or null when that is most of the scene. */
|
|
61
|
+
candidates(broads) {
|
|
62
|
+
const limit = Math.floor(this.order.length * MAX_SHARE);
|
|
63
|
+
let count = 0;
|
|
64
|
+
const add = (built) => {
|
|
65
|
+
const at = this.remap === null ? built : this.remap[built];
|
|
66
|
+
if (at < 0) return true;
|
|
67
|
+
if (count === this.found.length) {
|
|
68
|
+
const next = new Int32Array(count * 2);
|
|
69
|
+
next.set(this.found);
|
|
70
|
+
this.found = next;
|
|
71
|
+
}
|
|
72
|
+
this.found[count++] = at;
|
|
73
|
+
return count <= limit;
|
|
74
|
+
};
|
|
75
|
+
for (const i of this.always) if (!add(i)) return null;
|
|
76
|
+
for (let l = 0; l < this.layers.length; l++) {
|
|
77
|
+
const broad = broads[l];
|
|
78
|
+
const tree = this.trees.get(this.layers[l]);
|
|
79
|
+
if (broad === null || tree === void 0) continue;
|
|
80
|
+
const { x, y, width, height } = broad.box;
|
|
81
|
+
const k = broad.pxPerWorld;
|
|
82
|
+
const by = this.maxOutsetPx > 0 && k !== void 0 ? k > 0 ? this.maxOutsetPx / k : Infinity : 0;
|
|
83
|
+
let over = false;
|
|
84
|
+
tree.queryBox([
|
|
85
|
+
x - by,
|
|
86
|
+
y - by,
|
|
87
|
+
x + width + by,
|
|
88
|
+
y + height + by
|
|
89
|
+
], (i) => {
|
|
90
|
+
if (add(i)) return true;
|
|
91
|
+
over = true;
|
|
92
|
+
return false;
|
|
93
|
+
});
|
|
94
|
+
if (over) return null;
|
|
95
|
+
}
|
|
96
|
+
return this.readOut(count);
|
|
97
|
+
}
|
|
98
|
+
readOut(count) {
|
|
99
|
+
const { order, found } = this;
|
|
100
|
+
const out = new Array(count);
|
|
101
|
+
if (count * 32 < order.length) {
|
|
102
|
+
const ranks = found.subarray(0, count).sort();
|
|
103
|
+
for (let i = 0; i < count; i++) out[i] = order[ranks[i]];
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
const marks = this.marks.length >= order.length ? this.marks : new Uint8Array(order.length);
|
|
107
|
+
for (let i = 0; i < count; i++) marks[found[i]] = 1;
|
|
108
|
+
let n = 0;
|
|
109
|
+
for (let i = 0; n < count && i < order.length; i++) {
|
|
110
|
+
if (marks[i] === 0) continue;
|
|
111
|
+
marks[i] = 0;
|
|
112
|
+
out[n++] = order[i];
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
function hasCopyingAncestor(parents) {
|
|
118
|
+
return parents.some((p) => typeof p.copiesFromPose === "function");
|
|
119
|
+
}
|
|
120
|
+
function build(scene, descriptor, order) {
|
|
121
|
+
builds++;
|
|
122
|
+
const poseOf = scenePaintedPoseOf(scene);
|
|
123
|
+
const pathOf = sceneShownPathOf(scene);
|
|
124
|
+
const parentsOf = sceneParentsOf(scene);
|
|
125
|
+
const entries = /* @__PURE__ */ new Map();
|
|
126
|
+
const always = [];
|
|
127
|
+
let maxOutsetPx = 0;
|
|
128
|
+
for (let i = 0; i < order.length; i++) {
|
|
129
|
+
const node = order[i];
|
|
130
|
+
const parents = parentsOf(node);
|
|
131
|
+
if (hasCopyingAncestor(parents)) {
|
|
132
|
+
always.push(i);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const box = [
|
|
136
|
+
0,
|
|
137
|
+
0,
|
|
138
|
+
0,
|
|
139
|
+
0
|
|
140
|
+
];
|
|
141
|
+
let outsetPx;
|
|
142
|
+
try {
|
|
143
|
+
const inherited = inheritsPaint(node) ? inheritedPaintOf(node, parents) : void 0;
|
|
144
|
+
outsetPx = nodeBroadBox(node, poseOf(node), pathOf(node), inherited, descriptor, box);
|
|
145
|
+
} catch {
|
|
146
|
+
always.push(i);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (!Number.isFinite(outsetPx) || !box.every(Number.isFinite)) {
|
|
150
|
+
always.push(i);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (outsetPx > maxOutsetPx) maxOutsetPx = outsetPx;
|
|
154
|
+
let list = entries.get(node.layer);
|
|
155
|
+
if (list === void 0) entries.set(node.layer, list = []);
|
|
156
|
+
list.push([i, box]);
|
|
157
|
+
}
|
|
158
|
+
const trees = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const [layer, list] of entries) {
|
|
160
|
+
const tree = createBvh(2, list);
|
|
161
|
+
if (tree.size !== list.length) {
|
|
162
|
+
for (const [i, box] of list) if (!(box[0] <= box[2] && box[1] <= box[3])) always.push(i);
|
|
163
|
+
}
|
|
164
|
+
trees.set(layer, tree);
|
|
165
|
+
}
|
|
166
|
+
return new BuiltIndex(order, trees, Int32Array.from(always), maxOutsetPx);
|
|
167
|
+
}
|
|
168
|
+
function current(scene, descriptor) {
|
|
169
|
+
getNodeShapes();
|
|
170
|
+
const version = scene.getVersion();
|
|
171
|
+
const overrides = scene.overrides.getGeneration();
|
|
172
|
+
const memo = nodeMemoGeneration();
|
|
173
|
+
const markers = markerGeneration();
|
|
174
|
+
let perScene = CACHE.get(scene);
|
|
175
|
+
if (perScene === void 0) CACHE.set(scene, perScene = /* @__PURE__ */ new WeakMap());
|
|
176
|
+
const hit = perScene.get(descriptor);
|
|
177
|
+
const order = nodesAsShown(scene.overrides, scene.renderOrderNodes());
|
|
178
|
+
if (hit && hit.version === version && hit.overrides === overrides && hit.memo === memo && hit.markers === markers) {
|
|
179
|
+
if (hit.index === null) hit.index = build(scene, descriptor, order);
|
|
180
|
+
else hit.index.rankBy(order);
|
|
181
|
+
return hit.index;
|
|
182
|
+
}
|
|
183
|
+
perScene.set(descriptor, {
|
|
184
|
+
version,
|
|
185
|
+
overrides,
|
|
186
|
+
memo,
|
|
187
|
+
markers,
|
|
188
|
+
index: null
|
|
189
|
+
});
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
/** What `PickSource.candidates` answers for a scene source with an index. */
|
|
193
|
+
function indexedCandidates(scene, descriptor, broadFor) {
|
|
194
|
+
const index = current(scene, descriptor);
|
|
195
|
+
if (index === null) return null;
|
|
196
|
+
const broads = [];
|
|
197
|
+
for (const layer of index.layers) {
|
|
198
|
+
const broad = broadFor(layer);
|
|
199
|
+
if (broad === void 0) return null;
|
|
200
|
+
if (broad !== null && Number.isNaN(broad.box.x + broad.box.y + broad.box.width + broad.box.height)) return null;
|
|
201
|
+
broads.push(broad);
|
|
202
|
+
}
|
|
203
|
+
return index.candidates(broads);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
//#endregion
|
|
207
|
+
export { indexedCandidates };
|
|
208
|
+
//# sourceMappingURL=scenePickIndex.js.map
|