@weasel-js/core 1.14.1 → 1.14.2
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 +51 -0
- package/README.md +1 -1
- package/dist/core/paint/meshKindLazy.js +9 -10
- package/dist/core/paint/meshKindLazy.js.map +1 -1
- package/dist/core/scene/lockGuard.js +1 -0
- package/dist/core/scene/lockGuard.js.map +1 -1
- package/dist/core/scene/nodeGates.js +42 -22
- package/dist/core/scene/nodeGates.js.map +1 -1
- package/dist/core/scene/poseRemovals.js +1 -0
- package/dist/core/scene/poseRemovals.js.map +1 -1
- package/dist/core/scene/statedFields.js +10 -8
- package/dist/core/scene/statedFields.js.map +1 -1
- package/dist/core/scene/treeEdits.js +4 -9
- package/dist/core/scene/treeEdits.js.map +1 -1
- package/dist/{depSchema-C3BwxitB.d.ts → depSchema-DD9H89gX.d.ts} +17 -13
- package/dist/domains/snap/probes2d.js.map +1 -1
- package/dist/domains/snap/resizeBox.js +43 -22
- package/dist/domains/snap/resizeBox.js.map +1 -1
- package/dist/domains/snap/session2d.js +14 -7
- package/dist/domains/snap/session2d.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/test-seams.d.ts +2 -2
- package/dist/version.js +1 -1
- package/package.json +25 -25
|
@@ -11,37 +11,57 @@ function hidesSubtree(node) {
|
|
|
11
11
|
* a fresh one after an edit.
|
|
12
12
|
*/
|
|
13
13
|
function nodeGates(scene) {
|
|
14
|
-
const hiddenLayers =
|
|
15
|
-
const lockedLayers =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* layer counting when `layers` holds it. */
|
|
22
|
-
const inherited = (flag, layers) => {
|
|
14
|
+
const hiddenLayers = flaggedLayers(scene, "hidden");
|
|
15
|
+
const lockedLayers = flaggedLayers(scene, "locked");
|
|
16
|
+
const ownLock = lockedLayers === null ? (node) => node.locked === true : (node) => node.locked === true || lockedLayers.has(node.layer);
|
|
17
|
+
/** Whether `test` holds on the node at `parent` or any of its ancestors.
|
|
18
|
+
* Only ancestors are remembered, so a walk over a flat scene allocates
|
|
19
|
+
* nothing; that walk is every pick made after an edit. */
|
|
20
|
+
const above = (test) => {
|
|
23
21
|
const memo = /* @__PURE__ */ new Map();
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
const path = [];
|
|
23
|
+
return (parent) => {
|
|
24
|
+
if (parent === null) return false;
|
|
25
|
+
let out = false;
|
|
26
|
+
let seen = null;
|
|
27
|
+
path.length = 0;
|
|
28
|
+
for (let at = parent; at !== null;) {
|
|
29
|
+
const known = memo.get(at);
|
|
30
|
+
if (known !== void 0) {
|
|
31
|
+
out = known;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
const node = scene.get(at);
|
|
35
|
+
if (node === void 0) break;
|
|
36
|
+
if (path.length > 64) {
|
|
37
|
+
seen ??= new Set(path);
|
|
38
|
+
if (seen.has(at)) break;
|
|
39
|
+
seen.add(at);
|
|
40
|
+
}
|
|
41
|
+
path.push(at);
|
|
42
|
+
if (test(node)) {
|
|
43
|
+
out = true;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
at = node.parent ?? null;
|
|
47
|
+
}
|
|
48
|
+
for (const id of path) memo.set(id, out);
|
|
33
49
|
return out;
|
|
34
50
|
};
|
|
35
|
-
return (id) => climb(id, /* @__PURE__ */ new Set());
|
|
36
51
|
};
|
|
37
|
-
const
|
|
52
|
+
const hiddenAbove = above(hidesSubtree);
|
|
53
|
+
const lockedAbove = above(ownLock);
|
|
38
54
|
return {
|
|
39
55
|
hidden(id) {
|
|
40
56
|
const node = scene.get(id);
|
|
41
57
|
if (node === void 0) return false;
|
|
42
|
-
return hiddenLayers
|
|
58
|
+
return hiddenLayers?.has(node.layer) === true || hidesSubtree(node) || hiddenAbove(node.parent ?? null);
|
|
43
59
|
},
|
|
44
|
-
locked
|
|
60
|
+
locked(id) {
|
|
61
|
+
const node = scene.get(id);
|
|
62
|
+
if (node === void 0) return false;
|
|
63
|
+
return ownLock(node) || lockedAbove(node.parent ?? null);
|
|
64
|
+
}
|
|
45
65
|
};
|
|
46
66
|
}
|
|
47
67
|
/** Whether `test` holds on the node at `id` or any ancestor, without the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeGates.js","names":[],"sources":["../../../src/core/scene/nodeGates.ts"],"sourcesContent":["/**\n * Whether a node is shown, and whether it is in reach — the one place either\n * question is answered. Painting, picking, area selection, select-all,\n * snapping, export, and the scene's own lock guard all ask here.\n *\n * - **Hidden**: its own layer is not visible, or it or any ancestor carries\n * `hidden`. A layer hides only the nodes tagged to it; a hidden node hides\n * its whole subtree, as SVG's `display=\"none\"` does.\n * - **Locked**: it or any ancestor carries `locked`, or sits on a locked\n * layer. A lock covers the whole subtree, whatever layers it spans.\n */\nimport type { NodeId } from './types';\n\n/** The node fields the gates read. A partial scene stand-in may leave `parent` out. */\nexport interface GatedNode {\n readonly layer: string;\n readonly parent?: string | null;\n readonly hidden?: boolean;\n readonly locked?: boolean;\n}\n\n/** The layer fields the gates read. Only an explicit `visible: false` hides. */\nexport interface GatedLayer {\n readonly id: string;\n readonly visible?: boolean;\n readonly locked?: boolean;\n}\n\n/** What the gates read of a scene. A `Scene` is one. */\nexport interface GateSource {\n readonly layers?: readonly GatedLayer[];\n get(id: NodeId): GatedNode | undefined;\n}\n\n/** Whether `node`'s own flag hides it and its subtree. A walk from the roots\n * down prunes where this holds instead of asking {@link NodeGates.hidden}\n * per node; the two agree because this is the flag that answer climbs for. */\nexport function hidesSubtree(node: { readonly hidden?: boolean }): boolean {\n return node.hidden === true;\n}\n\n/** Both answers, for one read of a scene. */\nexport interface NodeGates {\n /** Whether `id` is not drawn. An id not in the scene is not hidden. */\n hidden(id: string): boolean;\n /** Whether `id` is out of reach. An id not in the scene is not locked. */\n locked(id: string): boolean;\n}\n\n/**\n * The gates over `scene` as it stands now. Ancestor answers are remembered,\n * so a walk over every node climbs each chain once: build one per walk, and\n * a fresh one after an edit.\n */\nexport function nodeGates(scene: GateSource): NodeGates {\n const hiddenLayers =
|
|
1
|
+
{"version":3,"file":"nodeGates.js","names":[],"sources":["../../../src/core/scene/nodeGates.ts"],"sourcesContent":["/**\n * Whether a node is shown, and whether it is in reach — the one place either\n * question is answered. Painting, picking, area selection, select-all,\n * snapping, export, and the scene's own lock guard all ask here.\n *\n * - **Hidden**: its own layer is not visible, or it or any ancestor carries\n * `hidden`. A layer hides only the nodes tagged to it; a hidden node hides\n * its whole subtree, as SVG's `display=\"none\"` does.\n * - **Locked**: it or any ancestor carries `locked`, or sits on a locked\n * layer. A lock covers the whole subtree, whatever layers it spans.\n */\nimport type { NodeId } from './types';\n\n/** The node fields the gates read. A partial scene stand-in may leave `parent` out. */\nexport interface GatedNode {\n readonly layer: string;\n readonly parent?: string | null;\n readonly hidden?: boolean;\n readonly locked?: boolean;\n}\n\n/** The layer fields the gates read. Only an explicit `visible: false` hides. */\nexport interface GatedLayer {\n readonly id: string;\n readonly visible?: boolean;\n readonly locked?: boolean;\n}\n\n/** What the gates read of a scene. A `Scene` is one. */\nexport interface GateSource {\n readonly layers?: readonly GatedLayer[];\n get(id: NodeId): GatedNode | undefined;\n}\n\n/** Whether `node`'s own flag hides it and its subtree. A walk from the roots\n * down prunes where this holds instead of asking {@link NodeGates.hidden}\n * per node; the two agree because this is the flag that answer climbs for. */\nexport function hidesSubtree(node: { readonly hidden?: boolean }): boolean {\n return node.hidden === true;\n}\n\n/** Both answers, for one read of a scene. */\nexport interface NodeGates {\n /** Whether `id` is not drawn. An id not in the scene is not hidden. */\n hidden(id: string): boolean;\n /** Whether `id` is out of reach. An id not in the scene is not locked. */\n locked(id: string): boolean;\n}\n\n/**\n * The gates over `scene` as it stands now. Ancestor answers are remembered,\n * so a walk over every node climbs each chain once: build one per walk, and\n * a fresh one after an edit.\n */\nexport function nodeGates(scene: GateSource): NodeGates {\n const hiddenLayers = flaggedLayers(scene, 'hidden');\n const lockedLayers = flaggedLayers(scene, 'locked');\n const ownLock = lockedLayers === null\n ? (node: GatedNode) => node.locked === true\n : (node: GatedNode) => node.locked === true || lockedLayers.has(node.layer);\n\n /** Whether `test` holds on the node at `parent` or any of its ancestors.\n * Only ancestors are remembered, so a walk over a flat scene allocates\n * nothing; that walk is every pick made after an edit. */\n const above = (test: (node: GatedNode) => boolean) => {\n const memo = new Map<string, boolean>();\n const path: string[] = [];\n return (parent: string | null): boolean => {\n if (parent === null) return false;\n let out = false;\n let seen: Set<string> | null = null;\n path.length = 0;\n for (let at: string | null = parent; at !== null; ) {\n const known = memo.get(at);\n if (known !== undefined) { out = known; break; }\n const node = scene.get(at as NodeId);\n if (node === undefined) break;\n if (path.length > 64) {\n seen ??= new Set(path);\n if (seen.has(at)) break;\n seen.add(at);\n }\n path.push(at);\n if (test(node)) { out = true; break; }\n at = node.parent ?? null;\n }\n for (const id of path) memo.set(id, out);\n return out;\n };\n };\n\n const hiddenAbove = above(hidesSubtree);\n const lockedAbove = above(ownLock);\n return {\n hidden(id) {\n const node = scene.get(id as NodeId);\n if (node === undefined) return false;\n return hiddenLayers?.has(node.layer) === true || hidesSubtree(node) || hiddenAbove(node.parent ?? null);\n },\n locked(id) {\n const node = scene.get(id as NodeId);\n if (node === undefined) return false;\n return ownLock(node) || lockedAbove(node.parent ?? null);\n },\n };\n}\n\n/** Whether `test` holds on the node at `id` or any ancestor, without the\n * allocation {@link nodeGates} spends on a walk: the scene's lock guard asks\n * this on every mutation. A parent cycle answers false, as the walk's does. */\nfunction climbs(scene: GateSource, id: string, test: (node: GatedNode) => boolean): boolean {\n let seen: Set<string> | null = null;\n let hops = 0;\n for (let at: string | null = id; at !== null; ) {\n const node = scene.get(at as NodeId);\n if (node === undefined) return false;\n if (++hops > 64) {\n seen ??= new Set();\n if (seen.has(at)) return false;\n seen.add(at);\n }\n if (test(node)) return true;\n at = node.parent ?? null;\n }\n return false;\n}\n\n/** The layers flagged `flag`, or null when none is: the usual answer, which\n * lets a climb skip the layer test at every hop. */\nfunction flaggedLayers(scene: GateSource, flag: 'locked' | 'hidden'): Set<string> | null {\n let out: Set<string> | null = null;\n for (const l of scene.layers ?? []) {\n if (flag === 'locked' ? l.locked === true : l.visible === false) (out ??= new Set()).add(l.id);\n }\n return out;\n}\n\n/** {@link NodeGates.hidden} for one id. */\nexport function isNodeHidden(scene: GateSource, id: NodeId): boolean {\n const node = scene.get(id);\n if (node === undefined) return false;\n return flaggedLayers(scene, 'hidden')?.has(node.layer) === true || climbs(scene, id, hidesSubtree);\n}\n\n/** {@link NodeGates.locked} for one id. */\nexport function isNodeLocked(scene: GateSource, id: NodeId): boolean {\n const layers = flaggedLayers(scene, 'locked');\n return climbs(scene, id, layers === null\n ? (node) => node.locked === true\n : (node) => node.locked === true || layers.has(node.layer));\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poseRemovals.js","names":[],"sources":["../../../src/core/scene/poseRemovals.ts"],"sourcesContent":["/**\n * The removal half of a scene's pose-write report: each node a change takes\n * off screen, read as it was shown just before it went.\n */\nimport type { PoseRemoval, PoseWrite, RemovedNode } from './poseWrites';\nimport type { NodeId } from './types';\n\ninterface Parented { id: NodeId; parent: NodeId | null }\n\nexport interface RemovalLog<TPose, TNode> {\n /** Read what removing `roots` and their descendants among `nodes` takes off\n * screen, before the removal runs; the returned call records it once it\n * has. A node `pending` holds as added by this change is dropped from it\n * and goes unreported. */\n seeOff(\n roots: readonly Parented[],\n nodes: readonly Parented[],\n how: Pick<PoseRemoval<TPose, TNode>, 'origin' | 'label'>,\n pending: Map<NodeId, PoseWrite<TPose>>,\n ): () => void;\n /** Forget `id`'s removal — it is back — and answer where it was shown. */\n takeBack(id: NodeId): TPose | undefined;\n /** The removals since the last drain, a child removed with its parent\n * carried beneath it. */\n drain(): PoseRemoval<TPose, TNode>[];\n}\n\nexport function createRemovalLog<TPose, TNode>(\n shown: (id: NodeId) => { node: TNode; pose: TPose } | undefined,\n): RemovalLog<TPose, TNode> {\n const removals = new Map<NodeId, PoseRemoval<TPose, TNode>>();\n return {\n seeOff(roots, nodes, how, pending) {\n const under = new Map<NodeId | null, Parented[]>();\n for (const n of nodes) {\n const list = under.get(n.parent);\n if (list) list.push(n);\n else under.set(n.parent, [n]);\n }\n const removedNode = (n: Parented): RemovedNode<TPose, TNode> | null => {\n const s = shown(n.id);\n if (!s) return null;\n const children = (under.get(n.id) ?? []).map(removedNode).filter((c) => c !== null);\n return { node: s.node, pose: s.pose, children };\n };\n const seen = roots.map((r) => [r, removedNode(r)] as const);\n return () => {\n const added = new Set<NodeId>();\n for (const n of nodes) {\n const w = pending.get(n.id);\n if (w && w.from === undefined) added.add(n.id);\n pending.delete(n.id);\n }\n for (const [r, removed] of seen) {\n if (removed && !added.has(r.id)) removals.set(r.id, { id: r.id, parent: r.parent, ...removed, ...how });\n }\n };\n },\n takeBack(id) {\n const gone = removals.get(id);\n removals.delete(id);\n return gone?.pose;\n },\n drain() {\n // Undoing a group's add takes its children first, each its own root.\n const out: PoseRemoval<TPose, TNode>[] = [];\n for (const r of removals.values()) {\n const up = r.parent !== null ? removals.get(r.parent) : undefined;\n if (up) (up.children as RemovedNode<TPose, TNode>[]).push(r);\n else out.push(r);\n }\n removals.clear();\n return out;\n },\n };\n}\n"],"mappings":";AA2BA,SAAgB,iBACd,OAC0B;CAC1B,MAAM,2BAAW,IAAI,IAAuC;CAC5D,OAAO;EACL,OAAO,OAAO,OAAO,KAAK,SAAS;GACjC,MAAM,wBAAQ,IAAI,IAA+B;GACjD,KAAK,MAAM,KAAK,OAAO;IACrB,MAAM,OAAO,MAAM,IAAI,EAAE,MAAM;IAC/B,IAAI,MAAM,KAAK,KAAK,CAAC;SAChB,MAAM,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;GAC9B;GACA,MAAM,eAAe,MAAkD;IACrE,MAAM,IAAI,MAAM,EAAE,EAAE;IACpB,IAAI,CAAC,GAAG,OAAO;IACf,MAAM,YAAY,MAAM,IAAI,EAAE,EAAE,KAAK,CAAC,EAAA,CAAG,IAAI,WAAW,CAAC,CAAC,QAAQ,MAAM,MAAM,IAAI;IAClF,OAAO;KAAE,MAAM,EAAE;KAAM,MAAM,EAAE;KAAM;IAAS;GAChD;GACA,MAAM,OAAO,MAAM,KAAK,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAU;GAC1D,aAAa;IACX,MAAM,wBAAQ,IAAI,IAAY;IAC9B,KAAK,MAAM,KAAK,OAAO;KACrB,MAAM,IAAI,QAAQ,IAAI,EAAE,EAAE;KAC1B,IAAI,KAAK,EAAE,SAAS,QAAW,MAAM,IAAI,EAAE,EAAE;KAC7C,QAAQ,OAAO,EAAE,EAAE;IACrB;IACA,KAAK,MAAM,CAAC,GAAG,YAAY,MACzB,IAAI,WAAW,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,SAAS,IAAI,EAAE,IAAI;KAAE,IAAI,EAAE;KAAI,QAAQ,EAAE;KAAQ,GAAG;KAAS,GAAG;IAAI,CAAC;GAE1G;EACF;EACA,SAAS,IAAI;GACX,MAAM,OAAO,SAAS,IAAI,EAAE;GAC5B,SAAS,OAAO,EAAE;GAClB,OAAO,MAAM;EACf;EACA,QAAQ;
|
|
1
|
+
{"version":3,"file":"poseRemovals.js","names":[],"sources":["../../../src/core/scene/poseRemovals.ts"],"sourcesContent":["/**\n * The removal half of a scene's pose-write report: each node a change takes\n * off screen, read as it was shown just before it went.\n */\nimport type { PoseRemoval, PoseWrite, RemovedNode } from './poseWrites';\nimport type { NodeId } from './types';\n\ninterface Parented { id: NodeId; parent: NodeId | null }\n\nexport interface RemovalLog<TPose, TNode> {\n /** Read what removing `roots` and their descendants among `nodes` takes off\n * screen, before the removal runs; the returned call records it once it\n * has. A node `pending` holds as added by this change is dropped from it\n * and goes unreported. */\n seeOff(\n roots: readonly Parented[],\n nodes: readonly Parented[],\n how: Pick<PoseRemoval<TPose, TNode>, 'origin' | 'label'>,\n pending: Map<NodeId, PoseWrite<TPose>>,\n ): () => void;\n /** Forget `id`'s removal — it is back — and answer where it was shown. */\n takeBack(id: NodeId): TPose | undefined;\n /** The removals since the last drain, a child removed with its parent\n * carried beneath it. */\n drain(): PoseRemoval<TPose, TNode>[];\n}\n\nexport function createRemovalLog<TPose, TNode>(\n shown: (id: NodeId) => { node: TNode; pose: TPose } | undefined,\n): RemovalLog<TPose, TNode> {\n const removals = new Map<NodeId, PoseRemoval<TPose, TNode>>();\n return {\n seeOff(roots, nodes, how, pending) {\n const under = new Map<NodeId | null, Parented[]>();\n for (const n of nodes) {\n const list = under.get(n.parent);\n if (list) list.push(n);\n else under.set(n.parent, [n]);\n }\n const removedNode = (n: Parented): RemovedNode<TPose, TNode> | null => {\n const s = shown(n.id);\n if (!s) return null;\n const children = (under.get(n.id) ?? []).map(removedNode).filter((c) => c !== null);\n return { node: s.node, pose: s.pose, children };\n };\n const seen = roots.map((r) => [r, removedNode(r)] as const);\n return () => {\n const added = new Set<NodeId>();\n for (const n of nodes) {\n const w = pending.get(n.id);\n if (w && w.from === undefined) added.add(n.id);\n pending.delete(n.id);\n }\n for (const [r, removed] of seen) {\n if (removed && !added.has(r.id)) removals.set(r.id, { id: r.id, parent: r.parent, ...removed, ...how });\n }\n };\n },\n takeBack(id) {\n const gone = removals.get(id);\n removals.delete(id);\n return gone?.pose;\n },\n drain() {\n if (removals.size === 0) return [];\n // Undoing a group's add takes its children first, each its own root.\n const out: PoseRemoval<TPose, TNode>[] = [];\n for (const r of removals.values()) {\n const up = r.parent !== null ? removals.get(r.parent) : undefined;\n if (up) (up.children as RemovedNode<TPose, TNode>[]).push(r);\n else out.push(r);\n }\n removals.clear();\n return out;\n },\n };\n}\n"],"mappings":";AA2BA,SAAgB,iBACd,OAC0B;CAC1B,MAAM,2BAAW,IAAI,IAAuC;CAC5D,OAAO;EACL,OAAO,OAAO,OAAO,KAAK,SAAS;GACjC,MAAM,wBAAQ,IAAI,IAA+B;GACjD,KAAK,MAAM,KAAK,OAAO;IACrB,MAAM,OAAO,MAAM,IAAI,EAAE,MAAM;IAC/B,IAAI,MAAM,KAAK,KAAK,CAAC;SAChB,MAAM,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;GAC9B;GACA,MAAM,eAAe,MAAkD;IACrE,MAAM,IAAI,MAAM,EAAE,EAAE;IACpB,IAAI,CAAC,GAAG,OAAO;IACf,MAAM,YAAY,MAAM,IAAI,EAAE,EAAE,KAAK,CAAC,EAAA,CAAG,IAAI,WAAW,CAAC,CAAC,QAAQ,MAAM,MAAM,IAAI;IAClF,OAAO;KAAE,MAAM,EAAE;KAAM,MAAM,EAAE;KAAM;IAAS;GAChD;GACA,MAAM,OAAO,MAAM,KAAK,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAU;GAC1D,aAAa;IACX,MAAM,wBAAQ,IAAI,IAAY;IAC9B,KAAK,MAAM,KAAK,OAAO;KACrB,MAAM,IAAI,QAAQ,IAAI,EAAE,EAAE;KAC1B,IAAI,KAAK,EAAE,SAAS,QAAW,MAAM,IAAI,EAAE,EAAE;KAC7C,QAAQ,OAAO,EAAE,EAAE;IACrB;IACA,KAAK,MAAM,CAAC,GAAG,YAAY,MACzB,IAAI,WAAW,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,SAAS,IAAI,EAAE,IAAI;KAAE,IAAI,EAAE;KAAI,QAAQ,EAAE;KAAQ,GAAG;KAAS,GAAG;IAAI,CAAC;GAE1G;EACF;EACA,SAAS,IAAI;GACX,MAAM,OAAO,SAAS,IAAI,EAAE;GAC5B,SAAS,OAAO,EAAE;GAClB,OAAO,MAAM;EACf;EACA,QAAQ;GACN,IAAI,SAAS,SAAS,GAAG,OAAO,CAAC;GAEjC,MAAM,MAAmC,CAAC;GAC1C,KAAK,MAAM,KAAK,SAAS,OAAO,GAAG;IACjC,MAAM,KAAK,EAAE,WAAW,OAAO,SAAS,IAAI,EAAE,MAAM,IAAI;IACxD,IAAI,IAAI,GAAI,SAAyC,KAAK,CAAC;SACtD,IAAI,KAAK,CAAC;GACjB;GACA,SAAS,MAAM;GACf,OAAO;EACT;CACF;AACF"}
|
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
* each present only when stated, so an unstated one keeps the scene's
|
|
4
4
|
* default, and a flag states only `true`. What a copy of a node carries over
|
|
5
5
|
* besides its kind, layer, pose, data and functions. */
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
function statedFields(n) {
|
|
7
|
+
const out = {};
|
|
8
|
+
if (n.pickable !== void 0) out.pickable = n.pickable;
|
|
9
|
+
if (n.hidden === true) out.hidden = true;
|
|
10
|
+
if (n.locked === true) out.locked = true;
|
|
11
|
+
if (n.units !== void 0) out.units = n.units;
|
|
12
|
+
if (n.clip !== void 0) out.clip = n.clip;
|
|
13
|
+
if (n.consumesChildren === true) out.consumesChildren = true;
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
14
16
|
|
|
15
17
|
//#endregion
|
|
16
18
|
export { statedFields };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statedFields.js","names":[],"sources":["../../../src/core/scene/statedFields.ts"],"sourcesContent":["import type { UnitScope } from '@weasel-js/quantity';\nimport type { NodeClip } from './types';\n\n/** The plain optional fields a node or an `AddNodeSpec` may state. */\nexport interface StatedNodeFields {\n pickable?: boolean;\n hidden?: boolean;\n locked?: boolean;\n units?: UnitScope;\n clip?: NodeClip;\n consumesChildren?: boolean;\n}\n\n/** The plain optional fields of a node or spec as a spread for `scene.add`:\n * each present only when stated, so an unstated one keeps the scene's\n * default, and a flag states only `true`. What a copy of a node carries over\n * besides its kind, layer, pose, data and functions. */\nexport
|
|
1
|
+
{"version":3,"file":"statedFields.js","names":[],"sources":["../../../src/core/scene/statedFields.ts"],"sourcesContent":["import type { UnitScope } from '@weasel-js/quantity';\nimport type { NodeClip } from './types';\n\n/** The plain optional fields a node or an `AddNodeSpec` may state. */\nexport interface StatedNodeFields {\n pickable?: boolean;\n hidden?: boolean;\n locked?: boolean;\n units?: UnitScope;\n clip?: NodeClip;\n consumesChildren?: boolean;\n}\n\n/** The plain optional fields of a node or spec as a spread for `scene.add`:\n * each present only when stated, so an unstated one keeps the scene's\n * default, and a flag states only `true`. What a copy of a node carries over\n * besides its kind, layer, pose, data and functions. */\nexport function statedFields(n: StatedNodeFields & { kind?: string }): StatedNodeFields {\n const out: StatedNodeFields = {};\n if (n.pickable !== undefined) out.pickable = n.pickable;\n if (n.hidden === true) out.hidden = true;\n if (n.locked === true) out.locked = true;\n if (n.units !== undefined) out.units = n.units;\n if (n.clip !== undefined) out.clip = n.clip;\n if (n.consumesChildren === true) out.consumesChildren = true;\n return out;\n}\n"],"mappings":";;;;;AAiBA,SAAgB,aAAa,GAA2D;CACtF,MAAM,MAAwB,CAAC;CAC/B,IAAI,EAAE,aAAa,QAAW,IAAI,WAAW,EAAE;CAC/C,IAAI,EAAE,WAAW,MAAM,IAAI,SAAS;CACpC,IAAI,EAAE,WAAW,MAAM,IAAI,SAAS;CACpC,IAAI,EAAE,UAAU,QAAW,IAAI,QAAQ,EAAE;CACzC,IAAI,EAAE,SAAS,QAAW,IAAI,OAAO,EAAE;CACvC,IAAI,EAAE,qBAAqB,MAAM,IAAI,mBAAmB;CACxD,OAAO;AACT"}
|
|
@@ -53,22 +53,17 @@ function createTreeEdits(env) {
|
|
|
53
53
|
assertSubtreeLayer(spec.id, spec.layer, parent, p.layer);
|
|
54
54
|
refuse(parent, "add under");
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
const payload = {
|
|
57
57
|
id,
|
|
58
58
|
kind: spec.kind,
|
|
59
59
|
layer: spec.layer,
|
|
60
60
|
pose: spec.pose,
|
|
61
61
|
data: spec.data,
|
|
62
62
|
parent,
|
|
63
|
-
index: spec.index ?? siblingsOf(parent).length
|
|
64
|
-
...spec.dependsOn !== void 0 ? { dependsOn: spec.dependsOn } : {},
|
|
65
|
-
...statedFields(spec),
|
|
66
|
-
...fnKeysOf(fnFields, {
|
|
67
|
-
...spec,
|
|
68
|
-
id,
|
|
69
|
-
kind: spec.kind
|
|
70
|
-
}, "opKey")
|
|
63
|
+
index: spec.index ?? siblingsOf(parent).length
|
|
71
64
|
};
|
|
65
|
+
if (spec.dependsOn !== void 0) payload.dependsOn = spec.dependsOn;
|
|
66
|
+
return Object.assign(payload, statedFields(spec), fnKeysOf(fnFields, spec, "opKey"));
|
|
72
67
|
}
|
|
73
68
|
function removeMany(rootIds) {
|
|
74
69
|
if (rootIds.length === 0) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"treeEdits.js","names":[],"sources":["../../../src/core/scene/treeEdits.ts"],"sourcesContent":["/**\n * The scene's edits to its tree: adding and removing nodes, reparenting, and\n * reordering siblings. Each checks its arguments, then records one kit op.\n */\nimport type { ArrivalWindow } from './arrivalWindow';\nimport type { RemoveSnapshot } from './kitOps';\nimport type { createLayerStack } from './layerStack';\nimport type { LockGuard } from './lockGuard';\nimport { fnKeysOf, type FnBearingNode, type NodeFnField } from './nodeFnFields';\nimport type { createNodeStore } from './nodeStore';\nimport type { Recorder } from './recorder';\nimport { SceneMembershipError } from './staleReplay';\nimport { statedFields } from './statedFields';\nimport type { AddNodeSpec, Node, NodeId, Scene } from './types';\n\nexport interface TreeEditsEnv<TData, TLayer extends string, TPose> {\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n generateId(): NodeId;\n store: Pick<\n ReturnType<typeof createNodeStore<TData, TLayer, TPose>>,\n 'requireNode' | 'siblingsOf' | 'descendants' | 'removalClosure' | 'patchNodeFunctions'\n >;\n locks: Pick<LockGuard<TLayer>, 'refuse' | 'refuseLayer' | 'lifted'>;\n layers: Pick<ReturnType<typeof createLayerStack<TLayer>>, 'requireLayerIndex' | 'assertSubtreeLayer'>;\n fnFields: readonly NodeFnField[];\n /** Run a registered op without recording it. */\n runOp(kind: string, payload: unknown): void;\n recorder: Pick<Recorder, 'record' | 'edit'>;\n arrivals: Pick<ArrivalWindow<TPose>, 'noteArrival' | 'noteDeparture' | 'noteChange'>;\n}\n\nexport interface TreeEdits<TData, TLayer extends string, TPose>\n extends Pick<Scene<TData, TLayer, TPose>, 'add' | 'remove' | 'removalClosure' | 'removeMany' | 'move' | 'reorder'> {\n /** Place nodes as the document a scene starts from, or is loaded with:\n * nothing is recorded, and no lock refuses. Specs list parents before\n * children, the order `toJSON()` emits. */\n construct(specs: readonly AddNodeSpec<TData, TLayer, TPose>[]): void;\n}\n\n/** What `kit:remove` carries for the nodes `ids`, read before any of them\n * goes. Throws on an id that is not in the scene. */\nfunction removeSnapshot<TData, TLayer extends string, TPose>(\n ids: readonly NodeId[],\n removing: ReadonlySet<NodeId>,\n store: Pick<TreeEditsEnv<TData, TLayer, TPose>['store'], 'requireNode' | 'siblingsOf'>,\n fnFields: readonly NodeFnField[],\n): RemoveSnapshot<TData, TLayer, TPose> {\n const nodes: Node<TData, TLayer, TPose>[] = ids.map((nid) => {\n const n = store.requireNode(nid);\n return n.children ? { ...n, children: [...n.children] } : { ...n };\n });\n // The rest is reachable through a snapshotted parent's cloned `children`.\n const detached = nodes\n .filter((n) => n.parent === null || !removing.has(n.parent))\n .map((n) => ({ id: n.id, parent: n.parent, index: store.siblingsOf(n.parent).indexOf(n.id) }))\n .sort((l, r) => l.index - r.index);\n const fnKeys = nodes.flatMap((n) => {\n const keys = fnKeysOf(fnFields, n as unknown as FnBearingNode, 'opKey');\n return Object.keys(keys).length === 0 ? [] : [{ id: n.id, ...keys }];\n });\n return { nodes, detached, ...(fnKeys.length > 0 ? { fnKeys } : {}) };\n}\n\nexport function createTreeEdits<TData, TLayer extends string, TPose>(\n env: TreeEditsEnv<TData, TLayer, TPose>,\n): TreeEdits<TData, TLayer, TPose> {\n const { nodes, fnFields } = env;\n const { requireNode, siblingsOf, descendants, removalClosure, patchNodeFunctions } = env.store;\n const { refuse, refuseLayer } = env.locks;\n const { requireLayerIndex, assertSubtreeLayer } = env.layers;\n const { record, edit } = env.recorder;\n const { noteArrival, noteDeparture, noteChange } = env.arrivals;\n\n /** Check that `spec` can be placed, and answer with the `kit:add` payload\n * that places it. Throws on an id collision, an unknown or locked layer,\n * a locked parent, and a layer below the\n * parent's. */\n function addPayload(spec: AddNodeSpec<TData, TLayer, TPose>) {\n const id = spec.id ?? env.generateId();\n if (nodes.has(id)) throw new SceneMembershipError(`Scene: id collision on \"${id}\"`);\n requireLayerIndex(spec.layer);\n refuseLayer(spec.layer, 'add to');\n const parent = spec.parent ?? null;\n if (parent !== null) {\n const p = requireNode(parent);\n assertSubtreeLayer(spec.id, spec.layer, parent, p.layer);\n refuse(parent, 'add under');\n }\n return {\n id, kind: spec.kind, layer: spec.layer, pose: spec.pose, data: spec.data,\n parent, index: spec.index ?? siblingsOf(parent).length,\n ...(spec.dependsOn !== undefined ? { dependsOn: spec.dependsOn } : {}),\n ...statedFields(spec),\n ...fnKeysOf(fnFields, { ...spec, id, kind: spec.kind } as unknown as FnBearingNode, 'opKey'),\n };\n }\n\n function removeMany(rootIds: readonly NodeId[]): void {\n if (rootIds.length === 0) return;\n // Every id resolves against the pre-removal tree, so an id another one\n // cascades away is absorbed rather than being a second, failing removal.\n const { ids, removing } = removalClosure(rootIds);\n for (const nid of ids) refuse(nid, 'remove');\n const payload = removeSnapshot(ids, removing, env.store, fnFields);\n const { detached } = payload;\n edit('remove', detached.some((d) => d.parent !== null), () => {\n for (const d of detached) noteDeparture(d.id, d.parent, null);\n record('kit:remove', payload, 'remove');\n });\n }\n\n return {\n construct(specs) {\n env.locks.lifted(() => {\n for (const spec of specs) {\n const payload = addPayload(spec);\n env.runOp('kit:add', payload);\n patchNodeFunctions(spec, payload.id, null);\n }\n });\n },\n\n add(spec) {\n const payload = addPayload(spec);\n const label = `add ${spec.kind}`;\n return edit(label, payload.parent !== null, () => {\n noteArrival(payload.id, payload.parent, null);\n record('kit:add', payload, label);\n patchNodeFunctions(spec, payload.id, payload);\n return payload.id;\n });\n },\n\n remove: (id) => removeMany([id]),\n\n removalClosure: (rootIds) => removalClosure(rootIds).ids,\n\n removeMany,\n\n move(id, parent, index) {\n const node = requireNode(id);\n refuse(id, 'move');\n if (parent !== null) {\n refuse(parent, 'move nodes into');\n const p = requireNode(parent);\n assertSubtreeLayer(id, node.layer, parent, p.layer);\n if (parent === id) throw new Error('Scene: cannot parent a node to itself');\n const desc: NodeId[] = [];\n descendants(id, desc);\n if (desc.includes(parent)) {\n throw new Error('Scene: cannot parent a node to its own descendant');\n }\n }\n const fromParent = node.parent;\n edit('move', parent !== null || fromParent !== null, () => {\n const fromIndex = siblingsOf(fromParent).indexOf(id);\n const toIndex = index ?? siblingsOf(parent).length;\n noteArrival(id, parent, fromParent);\n noteDeparture(id, fromParent, parent);\n noteChange(fromParent);\n record('kit:move', { id, fromParent, fromIndex, toParent: parent, toIndex }, 'move');\n });\n },\n\n reorder(id, index) {\n const node = requireNode(id);\n refuse(id, 'reorder');\n const parent = node.parent;\n edit('reorder', parent !== null, () => {\n noteChange(parent);\n const fromIndex = siblingsOf(parent).indexOf(id);\n record('kit:move', { id, fromParent: parent, fromIndex, toParent: parent, toIndex: index }, 'reorder');\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"treeEdits.js","names":[],"sources":["../../../src/core/scene/treeEdits.ts"],"sourcesContent":["/**\n * The scene's edits to its tree: adding and removing nodes, reparenting, and\n * reordering siblings. Each checks its arguments, then records one kit op.\n */\nimport type { ArrivalWindow } from './arrivalWindow';\nimport type { RemoveSnapshot } from './kitOps';\nimport type { createLayerStack } from './layerStack';\nimport type { LockGuard } from './lockGuard';\nimport { fnKeysOf, type FnBearingNode, type NodeFnField } from './nodeFnFields';\nimport type { createNodeStore } from './nodeStore';\nimport type { Recorder } from './recorder';\nimport { SceneMembershipError } from './staleReplay';\nimport { statedFields } from './statedFields';\nimport type { AddNodeSpec, Node, NodeId, Scene } from './types';\n\nexport interface TreeEditsEnv<TData, TLayer extends string, TPose> {\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n generateId(): NodeId;\n store: Pick<\n ReturnType<typeof createNodeStore<TData, TLayer, TPose>>,\n 'requireNode' | 'siblingsOf' | 'descendants' | 'removalClosure' | 'patchNodeFunctions'\n >;\n locks: Pick<LockGuard<TLayer>, 'refuse' | 'refuseLayer' | 'lifted'>;\n layers: Pick<ReturnType<typeof createLayerStack<TLayer>>, 'requireLayerIndex' | 'assertSubtreeLayer'>;\n fnFields: readonly NodeFnField[];\n /** Run a registered op without recording it. */\n runOp(kind: string, payload: unknown): void;\n recorder: Pick<Recorder, 'record' | 'edit'>;\n arrivals: Pick<ArrivalWindow<TPose>, 'noteArrival' | 'noteDeparture' | 'noteChange'>;\n}\n\nexport interface TreeEdits<TData, TLayer extends string, TPose>\n extends Pick<Scene<TData, TLayer, TPose>, 'add' | 'remove' | 'removalClosure' | 'removeMany' | 'move' | 'reorder'> {\n /** Place nodes as the document a scene starts from, or is loaded with:\n * nothing is recorded, and no lock refuses. Specs list parents before\n * children, the order `toJSON()` emits. */\n construct(specs: readonly AddNodeSpec<TData, TLayer, TPose>[]): void;\n}\n\n/** What `kit:remove` carries for the nodes `ids`, read before any of them\n * goes. Throws on an id that is not in the scene. */\nfunction removeSnapshot<TData, TLayer extends string, TPose>(\n ids: readonly NodeId[],\n removing: ReadonlySet<NodeId>,\n store: Pick<TreeEditsEnv<TData, TLayer, TPose>['store'], 'requireNode' | 'siblingsOf'>,\n fnFields: readonly NodeFnField[],\n): RemoveSnapshot<TData, TLayer, TPose> {\n const nodes: Node<TData, TLayer, TPose>[] = ids.map((nid) => {\n const n = store.requireNode(nid);\n return n.children ? { ...n, children: [...n.children] } : { ...n };\n });\n // The rest is reachable through a snapshotted parent's cloned `children`.\n const detached = nodes\n .filter((n) => n.parent === null || !removing.has(n.parent))\n .map((n) => ({ id: n.id, parent: n.parent, index: store.siblingsOf(n.parent).indexOf(n.id) }))\n .sort((l, r) => l.index - r.index);\n const fnKeys = nodes.flatMap((n) => {\n const keys = fnKeysOf(fnFields, n as unknown as FnBearingNode, 'opKey');\n return Object.keys(keys).length === 0 ? [] : [{ id: n.id, ...keys }];\n });\n return { nodes, detached, ...(fnKeys.length > 0 ? { fnKeys } : {}) };\n}\n\nexport function createTreeEdits<TData, TLayer extends string, TPose>(\n env: TreeEditsEnv<TData, TLayer, TPose>,\n): TreeEdits<TData, TLayer, TPose> {\n const { nodes, fnFields } = env;\n const { requireNode, siblingsOf, descendants, removalClosure, patchNodeFunctions } = env.store;\n const { refuse, refuseLayer } = env.locks;\n const { requireLayerIndex, assertSubtreeLayer } = env.layers;\n const { record, edit } = env.recorder;\n const { noteArrival, noteDeparture, noteChange } = env.arrivals;\n\n /** Check that `spec` can be placed, and answer with the `kit:add` payload\n * that places it. Throws on an id collision, an unknown or locked layer,\n * a locked parent, and a layer below the\n * parent's. */\n function addPayload(spec: AddNodeSpec<TData, TLayer, TPose>) {\n const id = spec.id ?? env.generateId();\n if (nodes.has(id)) throw new SceneMembershipError(`Scene: id collision on \"${id}\"`);\n requireLayerIndex(spec.layer);\n refuseLayer(spec.layer, 'add to');\n const parent = spec.parent ?? null;\n if (parent !== null) {\n const p = requireNode(parent);\n assertSubtreeLayer(spec.id, spec.layer, parent, p.layer);\n refuse(parent, 'add under');\n }\n const payload: {\n id: NodeId; kind: 'mark' | 'group'; layer: TLayer; pose: TPose; data: TData;\n parent: NodeId | null; index: number; dependsOn?: AddNodeSpec<TData, TLayer, TPose>['dependsOn'];\n } = {\n id, kind: spec.kind, layer: spec.layer, pose: spec.pose, data: spec.data,\n parent, index: spec.index ?? siblingsOf(parent).length,\n };\n if (spec.dependsOn !== undefined) payload.dependsOn = spec.dependsOn;\n // Assigned rather than spread into the literal: a spread after named fields is V8's slow clone.\n return Object.assign(payload, statedFields(spec), fnKeysOf(fnFields, spec as unknown as FnBearingNode, 'opKey'));\n }\n\n function removeMany(rootIds: readonly NodeId[]): void {\n if (rootIds.length === 0) return;\n // Every id resolves against the pre-removal tree, so an id another one\n // cascades away is absorbed rather than being a second, failing removal.\n const { ids, removing } = removalClosure(rootIds);\n for (const nid of ids) refuse(nid, 'remove');\n const payload = removeSnapshot(ids, removing, env.store, fnFields);\n const { detached } = payload;\n edit('remove', detached.some((d) => d.parent !== null), () => {\n for (const d of detached) noteDeparture(d.id, d.parent, null);\n record('kit:remove', payload, 'remove');\n });\n }\n\n return {\n construct(specs) {\n env.locks.lifted(() => {\n for (const spec of specs) {\n const payload = addPayload(spec);\n env.runOp('kit:add', payload);\n patchNodeFunctions(spec, payload.id, null);\n }\n });\n },\n\n add(spec) {\n const payload = addPayload(spec);\n const label = `add ${spec.kind}`;\n return edit(label, payload.parent !== null, () => {\n noteArrival(payload.id, payload.parent, null);\n record('kit:add', payload, label);\n patchNodeFunctions(spec, payload.id, payload);\n return payload.id;\n });\n },\n\n remove: (id) => removeMany([id]),\n\n removalClosure: (rootIds) => removalClosure(rootIds).ids,\n\n removeMany,\n\n move(id, parent, index) {\n const node = requireNode(id);\n refuse(id, 'move');\n if (parent !== null) {\n refuse(parent, 'move nodes into');\n const p = requireNode(parent);\n assertSubtreeLayer(id, node.layer, parent, p.layer);\n if (parent === id) throw new Error('Scene: cannot parent a node to itself');\n const desc: NodeId[] = [];\n descendants(id, desc);\n if (desc.includes(parent)) {\n throw new Error('Scene: cannot parent a node to its own descendant');\n }\n }\n const fromParent = node.parent;\n edit('move', parent !== null || fromParent !== null, () => {\n const fromIndex = siblingsOf(fromParent).indexOf(id);\n const toIndex = index ?? siblingsOf(parent).length;\n noteArrival(id, parent, fromParent);\n noteDeparture(id, fromParent, parent);\n noteChange(fromParent);\n record('kit:move', { id, fromParent, fromIndex, toParent: parent, toIndex }, 'move');\n });\n },\n\n reorder(id, index) {\n const node = requireNode(id);\n refuse(id, 'reorder');\n const parent = node.parent;\n edit('reorder', parent !== null, () => {\n noteChange(parent);\n const fromIndex = siblingsOf(parent).indexOf(id);\n record('kit:move', { id, fromParent: parent, fromIndex, toParent: parent, toIndex: index }, 'reorder');\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1249,12 +1249,13 @@ interface SourceCandidate<P = Point> {
|
|
|
1249
1249
|
/** `'handle'` is an angle about an anchor, as `'rotate'` is about a pivot,
|
|
1250
1250
|
* but asks only sources that name it. */
|
|
1251
1251
|
type SnapGesture = 'point' | 'move' | 'resize' | 'rotate' | 'handle';
|
|
1252
|
-
/**
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
interface
|
|
1256
|
-
|
|
1257
|
-
|
|
1252
|
+
/** One side of the subject's box a resize moves: on `axis`, its `side` moves
|
|
1253
|
+
* by `rate · (delta − delta0)`. A turned box's axis-aligned extent moves at
|
|
1254
|
+
* a rate off its axis, and both its sides on one axis may move at once. */
|
|
1255
|
+
interface SnapSide<P = Point> {
|
|
1256
|
+
axis: 'x' | 'y';
|
|
1257
|
+
side: 'min' | 'max';
|
|
1258
|
+
rate: P;
|
|
1258
1259
|
}
|
|
1259
1260
|
/** A point on the subject that should land on a locus. */
|
|
1260
1261
|
interface SnapProbeAt<P = Point> {
|
|
@@ -1274,9 +1275,12 @@ interface SnapCollect<P = Point> {
|
|
|
1274
1275
|
* puts it: a move's union of boxes, or the box a resize drags. Absent for
|
|
1275
1276
|
* other gestures. */
|
|
1276
1277
|
box?: FramedBox;
|
|
1277
|
-
/**
|
|
1278
|
-
* a move
|
|
1279
|
-
|
|
1278
|
+
/** The sides of `box` a resize moves; a side not listed stands still.
|
|
1279
|
+
* Absent for a move, which carries the whole box with it. */
|
|
1280
|
+
sides?: readonly SnapSide<P>[];
|
|
1281
|
+
/** The subject's drawn box where the resolved move `delta` (a function
|
|
1282
|
+
* mark's argument) puts it. Absent where `box` is. */
|
|
1283
|
+
boxAt?(delta: P): FramedBox;
|
|
1280
1284
|
/** `id`'s drawn box, measured as `box` is. Absent in a host with no 2D
|
|
1281
1285
|
* scene. */
|
|
1282
1286
|
drawnBox?(id: string): FramedBox | undefined;
|
|
@@ -1402,9 +1406,9 @@ interface SceneCanvasSnap {
|
|
|
1402
1406
|
/** The reach of a source that sets none, in screen px. Default: the kit
|
|
1403
1407
|
* setting `targets.snapPx`. */
|
|
1404
1408
|
reachPx?: number;
|
|
1405
|
-
/** How a pose's origin is read, for the `origin` probe a grid
|
|
1406
|
-
*
|
|
1407
|
-
* `AUTO_ORIGIN_PROJECTION`. */
|
|
1409
|
+
/** How a pose's origin is read, for the `origin` probe a grid snaps on a
|
|
1410
|
+
* turned container's lattice: a pose neither a rect nor a `Path` needs
|
|
1411
|
+
* its own. Default `AUTO_ORIGIN_PROJECTION`. */
|
|
1408
1412
|
originProjection?: OriginProjection<unknown>;
|
|
1409
1413
|
/** Whether the canvas draws the snap marks. Default `true`. With `false`
|
|
1410
1414
|
* the marks are still written to the `snapReadout` dep's store, for a
|
|
@@ -2847,4 +2851,4 @@ declare module '@weasel-js/routing' {
|
|
|
2847
2851
|
}
|
|
2848
2852
|
}
|
|
2849
2853
|
|
|
2850
|
-
export { type SourceCandidate as $, type AnchorMarkerShape as A, type CreateOutlinesAdapter as B, type ContentHandlerEntry as C, type Dims as D, type EditAnchorsDep as E, type FitViewToBoundsOptions as F, type GestureLifecycleOptions as G, type HandleLocks as H, type InertiaConfig as I, type PaintKindEntry as J, type PathEditTarget as K, type LayerHit as L, type FinishedEditFrame as M, type PathSymmetryDep as N, type BooleanOpOptions as O, type PathEditMark as P, type BooleansAdapter as Q, type RenderLayer as R, type SnapSource as S, type TextOverflow as T, type BooleanOp as U, type ViewAnimationOptions as V, type PathPaint as W, type BooleanOpResult as X, type BooleanStrokes as Y, type SnapMods as Z, _resetPaintKindsForTests as _, type PathEditExtension as a, type PreviewPathsDep as a$, type SnapProbeAt as a0, type SnapCollect as a1, type SnapMark as a2, type SnapDep as a3, type SnapOutcome as a4, type SnapReadoutStyle as a5, type AngleCandidate as a6, type AngleCollect as a7, type AnimateToBoundsOptions as a8, type AreaSelectDep as a9, type LayerRetainPolicy as aA, type LayoutDep as aB, type Locus as aC, type MinSize as aD, type NodeAtPointDep as aE, type OutlinePathSpec as aF, PATH_OVERLAY_ROLES as aG, type PaintKind as aH, type PaintKindEditorProps as aI, type PaintKindFamily as aJ, type PaintKindLoader as aK, type PanBounds as aL, type PathEditDrag as aM, type PathEditDragStart as aN, type PathEditFinishCtx as aO, type PathEditHandleMove as aP, type PathEditMarksCtx as aQ, type PathEditOther as aR, type PathEditPart as aS, type PathEditStore as aT, type PathForm as aU, type PathOverlay as aV, type PathOverlayOptions as aW, type PathOverlayStyle as aX, type PathRepresentation as aY, type PathTransformFinishCtx as aZ, type PoseChange as a_, type BooleanOpPreview as aa, type BooleanPreviewPiece as ab, type CanDropIntoDep as ac, type ClipboardDep as ad, type ClipboardIngestCtx as ae, type CreateOutlinesResult as af, type CustomPaintContext as ag, DEFAULT_LAYER_RETAIN as ah, type DecayLoopConfig as ai, type DropRefusal as aj, type DropVerdict as ak, type FinishedPathEdit as al, GEOMETRY_BOUND_DATA as am, type HandleLock as an, type HitTestView as ao, type IngestCtx as ap, type IngestionDep as aq, type InsertDep as ar, type InsertEnds as as, type InsertExtras as at, type InsertIntoDep as au, type InsertLive as av, type LassoSelectDep as aw, type LayerCommandCache as ax, type LayerDrawFailure as ay, type LayerDrawSpan as az, type SnapGesture as b, type ResizePolicy as b0, STICKY_DEFAULTS as b1, type SnapBegin as b2, type
|
|
2854
|
+
export { type SourceCandidate as $, type AnchorMarkerShape as A, type CreateOutlinesAdapter as B, type ContentHandlerEntry as C, type Dims as D, type EditAnchorsDep as E, type FitViewToBoundsOptions as F, type GestureLifecycleOptions as G, type HandleLocks as H, type InertiaConfig as I, type PaintKindEntry as J, type PathEditTarget as K, type LayerHit as L, type FinishedEditFrame as M, type PathSymmetryDep as N, type BooleanOpOptions as O, type PathEditMark as P, type BooleansAdapter as Q, type RenderLayer as R, type SnapSource as S, type TextOverflow as T, type BooleanOp as U, type ViewAnimationOptions as V, type PathPaint as W, type BooleanOpResult as X, type BooleanStrokes as Y, type SnapMods as Z, _resetPaintKindsForTests as _, type PathEditExtension as a, type PreviewPathsDep as a$, type SnapProbeAt as a0, type SnapCollect as a1, type SnapMark as a2, type SnapDep as a3, type SnapOutcome as a4, type SnapReadoutStyle as a5, type AngleCandidate as a6, type AngleCollect as a7, type AnimateToBoundsOptions as a8, type AreaSelectDep as a9, type LayerRetainPolicy as aA, type LayoutDep as aB, type Locus as aC, type MinSize as aD, type NodeAtPointDep as aE, type OutlinePathSpec as aF, PATH_OVERLAY_ROLES as aG, type PaintKind as aH, type PaintKindEditorProps as aI, type PaintKindFamily as aJ, type PaintKindLoader as aK, type PanBounds as aL, type PathEditDrag as aM, type PathEditDragStart as aN, type PathEditFinishCtx as aO, type PathEditHandleMove as aP, type PathEditMarksCtx as aQ, type PathEditOther as aR, type PathEditPart as aS, type PathEditStore as aT, type PathForm as aU, type PathOverlay as aV, type PathOverlayOptions as aW, type PathOverlayStyle as aX, type PathRepresentation as aY, type PathTransformFinishCtx as aZ, type PoseChange as a_, type BooleanOpPreview as aa, type BooleanPreviewPiece as ab, type CanDropIntoDep as ac, type ClipboardDep as ad, type ClipboardIngestCtx as ae, type CreateOutlinesResult as af, type CustomPaintContext as ag, DEFAULT_LAYER_RETAIN as ah, type DecayLoopConfig as ai, type DropRefusal as aj, type DropVerdict as ak, type FinishedPathEdit as al, GEOMETRY_BOUND_DATA as am, type HandleLock as an, type HitTestView as ao, type IngestCtx as ap, type IngestionDep as aq, type InsertDep as ar, type InsertEnds as as, type InsertExtras as at, type InsertIntoDep as au, type InsertLive as av, type LassoSelectDep as aw, type LayerCommandCache as ax, type LayerDrawFailure as ay, type LayerDrawSpan as az, type SnapGesture as b, type ResizePolicy as b0, STICKY_DEFAULTS as b1, type SnapBegin as b2, type SnapFrame as b3, type SnapMarks as b4, type SnapReadoutDep as b5, type SnapSide as b6, type SvgUnpacker as b7, type TextEditDep as b8, type TextNodeData as b9, paletteInsertPaint as bA, pathPaintOf as bB, patternFamily as bC, previewBooleanOp as bD, refusalOf as bE, registerContentHandler as bF, registerPaintKind as bG, registerPaintKindLoader as bH, seedTile as bI, stickyData as bJ, stickyNodeFactory as bK, subscribeToSources as bL, switchGradientKind as bM, textBoxOf as bN, useDecayLoop as bO, useEnteredGroup as bP, useViewAnimation as bQ, useViewAnimationOn as bR, viewAxes as bS, withoutGeometryBoundData as bT, type TransformPreviewNode as ba, type TransientPath as bb, VIEW_ANIMATION_KEY as bc, type ViewAnimationApi as bd, type ViewApi as be, type ViewChannel as bf, applyBooleanOp as bg, applyCreateOutlines as bh, asPaint as bi, createEnteredGroup as bj, createPathOverlay as bk, createSnapOutcomeStore as bl, depsUnchanged as bm, drawLayers as bn, drawOneLayer as bo, fitViewToBounds as bp, getPaintKind as bq, isLayerPainted as br, isLayerVisible as bs, listGradientKinds as bt, listPaintKinds as bu, minSizeOf as bv, paintKindOf as bw, paintKindRegistry as bx, paintedLayers as by, paintsLayer as bz, type SnapConstrain as c, type SnapResizeBox as d, type SnapGeometry as e, type SnapOutcomeStore as f, type SnapSession as g, type LayerDims as h, type ViewportDims as i, type RedrawSource as j, type EditAnchorsParams as k, type SliceDep as l, type StickyStyle as m, type EnteredGroupApi as n, type ChromeState as o, type LayerGroup as p, type TransformFollower as q, type GeometryProjection as r, type InsertNodeFactory as s, type InsertPaint as t, type SvgIngestOptions as u, type SceneCanvasSnap as v, type Affordance as w, type AffordanceRegion as x, type AffordanceBinding as y, type CommonAffordanceScratch as z };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probes2d.js","names":[],"sources":["../../../src/domains/snap/probes2d.ts"],"sourcesContent":["import { unionBounds } from 'core/geometry/unionBounds';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport type { SnapBegin } from 'interactions/actions/snapDep';\nimport type { SnapProbeAt } from './types';\nimport { resizeProbes, resizeSubject, type ResizeSubject } from './resizeBox';\nimport type { Point } from '@weasel-js/geom';\n\n/** How a 2D session reads its subject at press. */\nexport interface ProbeReads {\n /** `id`'s world pose now, or undefined when it is gone. */\n pose(id: string): unknown;\n /** The axis-aligned box `id` is drawn with at `pose`, stroke included. */\n drawnBox(id: string, pose: unknown): Bounds;\n /** The point of `pose`
|
|
1
|
+
{"version":3,"file":"probes2d.js","names":[],"sources":["../../../src/domains/snap/probes2d.ts"],"sourcesContent":["import { unionBounds } from 'core/geometry/unionBounds';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport type { SnapBegin } from 'interactions/actions/snapDep';\nimport type { SnapProbeAt } from './types';\nimport { resizeProbes, resizeSubject, type ResizeSubject } from './resizeBox';\nimport type { Point } from '@weasel-js/geom';\n\n/** How a 2D session reads its subject at press. */\nexport interface ProbeReads {\n /** `id`'s world pose now, or undefined when it is gone. */\n pose(id: string): unknown;\n /** The axis-aligned box `id` is drawn with at `pose`, stroke included. */\n drawnBox(id: string, pose: unknown): Bounds;\n /** The point of `pose` the `origin` probe sits at: its `OriginProjection` origin. */\n origin(pose: unknown): Point;\n /** The point of the subject's own geometry nearest `press`, or null. */\n geometryAt?(press: Point, ids: readonly string[]): Point | null;\n}\n\n/** The union drawn box of the subjects, or null when none is left. */\nexport function subjectBox(ids: readonly string[], reads: ProbeReads): Bounds | null {\n const boxes: Bounds[] = [];\n for (const id of ids) {\n const pose = reads.pose(id);\n if (pose !== undefined) boxes.push(reads.drawnBox(id, pose));\n }\n return unionBounds(boxes);\n}\n\n/** What a gesture drags, read once at press. */\nexport interface Subject2d {\n /** A move's subjects' union drawn box. */\n box: Bounds | null;\n /** A resize's box and frame: the one it names, else the subjects' drawn\n * box, axis-aligned. */\n resize: ResizeSubject | null;\n}\n\n/** The subject of `req`, null where the gesture has none or nothing is left. */\nexport function subjectOf(req: SnapBegin, reads: ProbeReads): Subject2d {\n if (req.gesture === 'move') return { box: subjectBox(req.ids ?? [], reads), resize: null };\n if (req.gesture !== 'resize' || !req.handle) return { box: null, resize: null };\n if (req.box) return { box: null, resize: resizeSubject(req.box, req.handle) };\n const bounds = subjectBox(req.ids ?? [], reads);\n return { box: null, resize: bounds ? resizeSubject({ bounds }, req.handle) : null };\n}\n\n/** The probes a gesture snaps, where they are at press. */\nexport function buildProbes(req: SnapBegin, reads: ProbeReads, subject = subjectOf(req, reads)): SnapProbeAt[] {\n const ids = req.ids ?? [];\n switch (req.gesture) {\n case 'point':\n return [{ id: 'pointer', at: req.press, role: 'pointer' }];\n case 'resize':\n return subject.resize ? resizeProbes(subject.resize) : [{ id: 'pointer', at: req.press, role: 'pointer' }];\n case 'move':\n return moveProbes(req.press, ids, reads, subject.box);\n case 'rotate':\n case 'handle':\n return [];\n }\n}\n\nfunction moveProbes(press: Point, ids: readonly string[], reads: ProbeReads, box: Bounds | null): SnapProbeAt[] {\n const out: SnapProbeAt[] = [];\n for (const id of ids) {\n const pose = reads.pose(id);\n if (pose !== undefined) out.push({ id: `origin:${id}`, at: reads.origin(pose), role: 'origin' });\n }\n if (box) {\n out.push(\n { id: 'box.min', at: { x: box.x, y: box.y }, role: 'box' },\n { id: 'box.center', at: { x: box.x + box.width / 2, y: box.y + box.height / 2 }, role: 'box' },\n { id: 'box.max', at: { x: box.x + box.width, y: box.y + box.height }, role: 'box' },\n );\n }\n const geometry = reads.geometryAt?.(press, ids) ?? press;\n out.push({ id: 'geometry', at: { x: geometry.x, y: geometry.y }, role: 'geometry' });\n return out;\n}\n"],"mappings":";;;;;AAoBA,SAAgB,WAAW,KAAwB,OAAkC;CACnF,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,MAAM,KAAK,EAAE;EAC1B,IAAI,SAAS,QAAW,MAAM,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC;CAC7D;CACA,OAAO,YAAY,KAAK;AAC1B;;AAYA,SAAgB,UAAU,KAAgB,OAA8B;CACtE,IAAI,IAAI,YAAY,QAAQ,OAAO;EAAE,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,KAAK;EAAG,QAAQ;CAAK;CACzF,IAAI,IAAI,YAAY,YAAY,CAAC,IAAI,QAAQ,OAAO;EAAE,KAAK;EAAM,QAAQ;CAAK;CAC9E,IAAI,IAAI,KAAK,OAAO;EAAE,KAAK;EAAM,QAAQ,cAAc,IAAI,KAAK,IAAI,MAAM;CAAE;CAC5E,MAAM,SAAS,WAAW,IAAI,OAAO,CAAC,GAAG,KAAK;CAC9C,OAAO;EAAE,KAAK;EAAM,QAAQ,SAAS,cAAc,EAAE,OAAO,GAAG,IAAI,MAAM,IAAI;CAAK;AACpF;;AAGA,SAAgB,YAAY,KAAgB,OAAmB,UAAU,UAAU,KAAK,KAAK,GAAkB;CAC7G,MAAM,MAAM,IAAI,OAAO,CAAC;CACxB,QAAQ,IAAI,SAAZ;EACE,KAAK,SACH,OAAO,CAAC;GAAE,IAAI;GAAW,IAAI,IAAI;GAAO,MAAM;EAAU,CAAC;EAC3D,KAAK,UACH,OAAO,QAAQ,SAAS,aAAa,QAAQ,MAAM,IAAI,CAAC;GAAE,IAAI;GAAW,IAAI,IAAI;GAAO,MAAM;EAAU,CAAC;EAC3G,KAAK,QACH,OAAO,WAAW,IAAI,OAAO,KAAK,OAAO,QAAQ,GAAG;EACtD,KAAK;EACL,KAAK,UACH,OAAO,CAAC;CACZ;AACF;AAEA,SAAS,WAAW,OAAc,KAAwB,OAAmB,KAAmC;CAC9G,MAAM,MAAqB,CAAC;CAC5B,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,MAAM,KAAK,EAAE;EAC1B,IAAI,SAAS,QAAW,IAAI,KAAK;GAAE,IAAI,UAAU;GAAM,IAAI,MAAM,OAAO,IAAI;GAAG,MAAM;EAAS,CAAC;CACjG;CACA,IAAI,KACF,IAAI,KACF;EAAE,IAAI;EAAW,IAAI;GAAE,GAAG,IAAI;GAAG,GAAG,IAAI;EAAE;EAAG,MAAM;CAAM,GACzD;EAAE,IAAI;EAAc,IAAI;GAAE,GAAG,IAAI,IAAI,IAAI,QAAQ;GAAG,GAAG,IAAI,IAAI,IAAI,SAAS;EAAE;EAAG,MAAM;CAAM,GAC7F;EAAE,IAAI;EAAW,IAAI;GAAE,GAAG,IAAI,IAAI,IAAI;GAAO,GAAG,IAAI,IAAI,IAAI;EAAO;EAAG,MAAM;CAAM,CACpF;CAEF,MAAM,WAAW,MAAM,aAAa,OAAO,GAAG,KAAK;CACnD,IAAI,KAAK;EAAE,IAAI;EAAY,IAAI;GAAE,GAAG,SAAS;GAAG,GAAG,SAAS;EAAE;EAAG,MAAM;CAAW,CAAC;CACnF,OAAO;AACT"}
|
|
@@ -80,10 +80,10 @@ function movedBox(s, delta) {
|
|
|
80
80
|
};
|
|
81
81
|
return movedEdges(s.bounds, s.handle, local.x, local.y);
|
|
82
82
|
}
|
|
83
|
-
/** The box's
|
|
84
|
-
function
|
|
83
|
+
/** The box's corners in ctx coordinates once the handle has moved by `delta`. */
|
|
84
|
+
function cornersAt(s, delta) {
|
|
85
85
|
const b = movedBox(s, delta);
|
|
86
|
-
|
|
86
|
+
return [
|
|
87
87
|
s.at({
|
|
88
88
|
x: b.x,
|
|
89
89
|
y: b.y
|
|
@@ -101,6 +101,10 @@ function resizedBox(s, delta) {
|
|
|
101
101
|
y: b.y + b.height
|
|
102
102
|
})
|
|
103
103
|
];
|
|
104
|
+
}
|
|
105
|
+
/** The box's axis-aligned extent once the handle has moved by `delta`. */
|
|
106
|
+
function resizedBox(s, delta) {
|
|
107
|
+
const corners = cornersAt(s, delta);
|
|
104
108
|
const xs = corners.map((c) => c.x);
|
|
105
109
|
const ys = corners.map((c) => c.y);
|
|
106
110
|
const x = Math.min(...xs);
|
|
@@ -112,26 +116,43 @@ function resizedBox(s, delta) {
|
|
|
112
116
|
height: Math.max(...ys) - y
|
|
113
117
|
};
|
|
114
118
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
*
|
|
118
|
-
* extent
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
var near = (a, b, scale) => Math.abs(a - b) <= 1e-9 * Math.max(1, scale);
|
|
120
|
+
/** The sides of {@link resizedBox} a drag at `delta` moves, and how fast. A
|
|
121
|
+
* side moves with the corners at its extreme; where two of them tie and move
|
|
122
|
+
* apart, as a turned box's do at a kink of its extent, it is left out. */
|
|
123
|
+
function resizeSides(s, delta) {
|
|
124
|
+
const now = cornersAt(s, delta);
|
|
125
|
+
const stepX = cornersAt(s, {
|
|
126
|
+
x: delta.x + 1,
|
|
127
|
+
y: delta.y
|
|
128
|
+
});
|
|
129
|
+
const stepY = cornersAt(s, {
|
|
130
|
+
x: delta.x,
|
|
131
|
+
y: delta.y + 1
|
|
132
|
+
});
|
|
133
|
+
const out = [];
|
|
134
|
+
for (const axis of ["x", "y"]) {
|
|
135
|
+
const vs = now.map((c) => c[axis]);
|
|
136
|
+
const span = Math.max(...vs) - Math.min(...vs);
|
|
137
|
+
for (const side of ["min", "max"]) {
|
|
138
|
+
const edge = side === "min" ? Math.min(...vs) : Math.max(...vs);
|
|
139
|
+
const rates = now.flatMap((c, i) => near(c[axis], edge, span) ? [{
|
|
140
|
+
x: stepX[i][axis] - c[axis],
|
|
141
|
+
y: stepY[i][axis] - c[axis]
|
|
142
|
+
}] : []);
|
|
143
|
+
const rate = rates[0];
|
|
144
|
+
const size = Math.hypot(rate.x, rate.y);
|
|
145
|
+
if (size < EPS || rates.some((r) => !near(r.x, rate.x, size) || !near(r.y, rate.y, size))) continue;
|
|
146
|
+
out.push({
|
|
147
|
+
axis,
|
|
148
|
+
side,
|
|
149
|
+
rate
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
133
154
|
}
|
|
134
155
|
|
|
135
156
|
//#endregion
|
|
136
|
-
export { EPS, diagonal, edgeAxis,
|
|
157
|
+
export { EPS, diagonal, edgeAxis, resizeProbes, resizeSides, resizeSubject, resizedBox };
|
|
137
158
|
//# sourceMappingURL=resizeBox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resizeBox.js","names":[],"sources":["../../../src/domains/snap/resizeBox.ts"],"sourcesContent":["import type { ResizeAnchor } from '@weasel-js/routing';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { fixedCornerOf, movedEdges, movingPoint } from 'interactions/actions/resize/cornerHandles';\nimport type { SnapResizeBox } from 'interactions/actions/snapDep';\nimport type {
|
|
1
|
+
{"version":3,"file":"resizeBox.js","names":[],"sources":["../../../src/domains/snap/resizeBox.ts"],"sourcesContent":["import type { ResizeAnchor } from '@weasel-js/routing';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { fixedCornerOf, movedEdges, movingPoint } from 'interactions/actions/resize/cornerHandles';\nimport type { SnapResizeBox } from 'interactions/actions/snapDep';\nimport type { SnapProbeAt, SnapSide } from './types';\nimport type { Point } from '@weasel-js/geom';\n\nexport const EPS = 1e-9;\n\n/** A resize's box with its frame read out, in ctx coordinates. */\nexport interface ResizeSubject {\n /** In the box's own frame. */\n bounds: Bounds;\n handle: ResizeAnchor;\n at(p: Point): Point;\n /** Where the frame's unit axes point. */\n ex: Point;\n ey: Point;\n}\n\nexport function resizeSubject(box: SnapResizeBox, handle: ResizeAnchor): ResizeSubject {\n const at = box.toWorld ?? ((p: Point) => p);\n const o = at({ x: 0, y: 0 });\n const ax = at({ x: 1, y: 0 });\n const ay = at({ x: 0, y: 1 });\n return { bounds: box.bounds, handle, at, ex: { x: ax.x - o.x, y: ax.y - o.y }, ey: { x: ay.x - o.x, y: ay.y - o.y } };\n}\n\n/** The dragged corner or edge midpoint as `pointer`, and as `box` the dragged\n * corner or both ends of the dragged edge: the points a drag δ moves by δ. */\nexport function resizeProbes(s: ResizeSubject): SnapProbeAt[] {\n const { bounds: b, handle } = s;\n const out: SnapProbeAt[] = [{ id: 'pointer', at: s.at(movingPoint(b, handle)), role: 'pointer' }];\n const sides = (pin: ResizeAnchor['x'], lo: number, size: number): number[] =>\n pin === 'min' ? [lo + size] : pin === 'max' ? [lo] : [lo, lo + size];\n const xs = sides(handle.x, b.x, b.width);\n const ys = sides(handle.y, b.y, b.height);\n if (xs.length === 2 && ys.length === 2) return out;\n for (const x of xs) for (const y of ys) out.push({ id: `box.${out.length - 1}`, at: s.at({ x, y }), role: 'box' });\n return out;\n}\n\n/** The direction an edge handle's edge moves along; null for a corner. */\nexport function edgeAxis(s: ResizeSubject): Point | null {\n const { x, y } = s.handle;\n if ((x === 'free') === (y === 'free')) return null;\n return y === 'free' ? s.ex : s.ey;\n}\n\n/** From the fixed corner to the dragged one. */\nexport function diagonal(s: ResizeSubject): Point {\n const f = s.at(fixedCornerOf(s.bounds, s.handle));\n const m = s.at(movingPoint(s.bounds, s.handle));\n return { x: m.x - f.x, y: m.y - f.y };\n}\n\n/** The box in its own frame once the handle has moved by `delta`. */\nfunction movedBox(s: ResizeSubject, delta: Point): Bounds {\n const det = s.ex.x * s.ey.y - s.ey.x * s.ex.y;\n const local = Math.abs(det) < EPS\n ? { x: 0, y: 0 }\n : { x: (s.ey.y * delta.x - s.ey.x * delta.y) / det, y: (s.ex.x * delta.y - s.ex.y * delta.x) / det };\n return movedEdges(s.bounds, s.handle, local.x, local.y);\n}\n\n/** The box's corners in ctx coordinates once the handle has moved by `delta`. */\nfunction cornersAt(s: ResizeSubject, delta: Point): Point[] {\n const b = movedBox(s, delta);\n return [\n s.at({ x: b.x, y: b.y }), s.at({ x: b.x + b.width, y: b.y }),\n s.at({ x: b.x, y: b.y + b.height }), s.at({ x: b.x + b.width, y: b.y + b.height }),\n ];\n}\n\n/** The box's axis-aligned extent once the handle has moved by `delta`. */\nexport function resizedBox(s: ResizeSubject, delta: Point): Bounds {\n const corners = cornersAt(s, delta);\n const xs = corners.map((c) => c.x);\n const ys = corners.map((c) => c.y);\n const x = Math.min(...xs);\n const y = Math.min(...ys);\n return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };\n}\n\nconst near = (a: number, b: number, scale: number): boolean => Math.abs(a - b) <= 1e-9 * Math.max(1, scale);\n\n/** The sides of {@link resizedBox} a drag at `delta` moves, and how fast. A\n * side moves with the corners at its extreme; where two of them tie and move\n * apart, as a turned box's do at a kink of its extent, it is left out. */\nexport function resizeSides(s: ResizeSubject, delta: Point): SnapSide[] {\n const now = cornersAt(s, delta);\n // The corners are affine in the drag, so a unit step reads their rate exactly.\n const stepX = cornersAt(s, { x: delta.x + 1, y: delta.y });\n const stepY = cornersAt(s, { x: delta.x, y: delta.y + 1 });\n const out: SnapSide[] = [];\n for (const axis of ['x', 'y'] as const) {\n const vs = now.map((c) => c[axis]);\n const span = Math.max(...vs) - Math.min(...vs);\n for (const side of ['min', 'max'] as const) {\n const edge = side === 'min' ? Math.min(...vs) : Math.max(...vs);\n const rates = now.flatMap((c, i) => (near(c[axis], edge, span)\n ? [{ x: stepX[i]![axis] - c[axis], y: stepY[i]![axis] - c[axis] }]\n : []));\n const rate = rates[0]!;\n const size = Math.hypot(rate.x, rate.y);\n if (size < EPS || rates.some((r) => !near(r.x, rate.x, size) || !near(r.y, rate.y, size))) continue;\n out.push({ axis, side, rate });\n }\n }\n return out;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resizeSides, resizeSubject, resizedBox } from "./resizeBox.js";
|
|
2
2
|
import { markFromPlane } from "./marks.js";
|
|
3
3
|
import { screenOf } from "./screen.js";
|
|
4
4
|
import { angleAt } from "./angle2d.js";
|
|
@@ -113,17 +113,24 @@ function collectAll(sources, probesAtPress, fixed, { delta0, view, plane }, reac
|
|
|
113
113
|
...fixed.origin ? { origin: fixed.origin } : {}
|
|
114
114
|
});
|
|
115
115
|
}
|
|
116
|
-
/** The subject's box where `delta0` puts it,
|
|
116
|
+
/** The subject's box where `delta0` puts it, the sides that move, and where
|
|
117
|
+
* any other move would put it. */
|
|
117
118
|
function subjectAt({ box, resize }, delta0) {
|
|
118
119
|
if (resize) return {
|
|
119
120
|
box: resizedBox(resize, delta0),
|
|
120
|
-
|
|
121
|
+
sides: resizeSides(resize, delta0),
|
|
122
|
+
boxAt: (d) => resizedBox(resize, d)
|
|
121
123
|
};
|
|
122
|
-
|
|
124
|
+
if (!box) return {};
|
|
125
|
+
const boxAt = (d) => ({
|
|
123
126
|
...box,
|
|
124
|
-
x: box.x +
|
|
125
|
-
y: box.y +
|
|
126
|
-
}
|
|
127
|
+
x: box.x + d.x,
|
|
128
|
+
y: box.y + d.y
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
box: boxAt(delta0),
|
|
132
|
+
boxAt
|
|
133
|
+
};
|
|
127
134
|
}
|
|
128
135
|
/** A session no source answers: it only holds the point to the gesture's
|
|
129
136
|
* input subspace, as when a host provides no `snap` dep. */
|