dsviz 0.0.0-stage → 2.0.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/LICENSE +21 -0
- package/README.md +35 -2
- package/dist/camera.d.ts +25 -0
- package/dist/camera.js +27 -0
- package/dist/camera.js.map +7 -0
- package/dist/connector.d.ts +65 -0
- package/dist/connector.js +108 -0
- package/dist/connector.js.map +7 -0
- package/dist/describe.d.ts +44 -0
- package/dist/describe.js +131 -0
- package/dist/describe.js.map +7 -0
- package/dist/ds/array.d.ts +51 -0
- package/dist/ds/array.js +62 -0
- package/dist/ds/array.js.map +7 -0
- package/dist/ds/common.d.ts +9 -0
- package/dist/ds/common.js +10 -0
- package/dist/ds/common.js.map +7 -0
- package/dist/ds/graph.d.ts +39 -0
- package/dist/ds/graph.js +57 -0
- package/dist/ds/graph.js.map +7 -0
- package/dist/ds/grid.d.ts +30 -0
- package/dist/ds/grid.js +41 -0
- package/dist/ds/grid.js.map +7 -0
- package/dist/ds/index.d.ts +8 -0
- package/dist/ds/index.js +9 -0
- package/dist/ds/index.js.map +7 -0
- package/dist/ds/linkedList.d.ts +46 -0
- package/dist/ds/linkedList.js +68 -0
- package/dist/ds/linkedList.js.map +7 -0
- package/dist/ds/pointer.d.ts +23 -0
- package/dist/ds/pointer.js +35 -0
- package/dist/ds/pointer.js.map +7 -0
- package/dist/ds/stackQueue.d.ts +5 -0
- package/dist/ds/stackQueue.js +49 -0
- package/dist/ds/stackQueue.js.map +7 -0
- package/dist/ds/tree.d.ts +53 -0
- package/dist/ds/tree.js +87 -0
- package/dist/ds/tree.js.map +7 -0
- package/dist/easing.d.ts +14 -0
- package/dist/easing.js +21 -0
- package/dist/easing.js.map +7 -0
- package/dist/fx.d.ts +21 -0
- package/dist/fx.js +27 -0
- package/dist/fx.js.map +7 -0
- package/dist/geom/index.d.ts +2 -0
- package/dist/geom/index.js +3 -0
- package/dist/geom/index.js.map +7 -0
- package/dist/geom/math.d.ts +34 -0
- package/dist/geom/math.js +74 -0
- package/dist/geom/math.js.map +7 -0
- package/dist/geom/shapes.d.ts +69 -0
- package/dist/geom/shapes.js +105 -0
- package/dist/geom/shapes.js.map +7 -0
- package/dist/handle.d.ts +30 -0
- package/dist/handle.js +13 -0
- package/dist/handle.js.map +7 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +7 -0
- package/dist/mathtext.d.ts +29 -0
- package/dist/mathtext.js +142 -0
- package/dist/mathtext.js.map +7 -0
- package/dist/mem/index.d.ts +2 -0
- package/dist/mem/index.js +3 -0
- package/dist/mem/index.js.map +7 -0
- package/dist/mem/memory.d.ts +47 -0
- package/dist/mem/memory.js +41 -0
- package/dist/mem/memory.js.map +7 -0
- package/dist/mem/stackHeap.d.ts +48 -0
- package/dist/mem/stackHeap.js +76 -0
- package/dist/mem/stackHeap.js.map +7 -0
- package/dist/path.d.ts +23 -0
- package/dist/path.js +49 -0
- package/dist/path.js.map +7 -0
- package/dist/player.d.ts +49 -0
- package/dist/player.js +100 -0
- package/dist/player.js.map +7 -0
- package/dist/plot/index.d.ts +2 -0
- package/dist/plot/index.js +3 -0
- package/dist/plot/index.js.map +7 -0
- package/dist/plot/marks.d.ts +120 -0
- package/dist/plot/marks.js +200 -0
- package/dist/plot/marks.js.map +7 -0
- package/dist/plot/plane.d.ts +45 -0
- package/dist/plot/plane.js +41 -0
- package/dist/plot/plane.js.map +7 -0
- package/dist/prob/bars.d.ts +67 -0
- package/dist/prob/bars.js +82 -0
- package/dist/prob/bars.js.map +7 -0
- package/dist/prob/index.d.ts +3 -0
- package/dist/prob/index.js +4 -0
- package/dist/prob/index.js.map +7 -0
- package/dist/prob/tree.d.ts +39 -0
- package/dist/prob/tree.js +57 -0
- package/dist/prob/tree.js.map +7 -0
- package/dist/prob/venn.d.ts +38 -0
- package/dist/prob/venn.js +89 -0
- package/dist/prob/venn.js.map +7 -0
- package/dist/react/Diagram.d.ts +47 -0
- package/dist/react/Diagram.js +41 -0
- package/dist/react/Diagram.js.map +7 -0
- package/dist/react/MathText.d.ts +9 -0
- package/dist/react/MathText.js +15 -0
- package/dist/react/MathText.js.map +7 -0
- package/dist/react/controls-styles.d.ts +9 -0
- package/dist/react/controls-styles.js +109 -0
- package/dist/react/controls-styles.js.map +7 -0
- package/dist/react/controls.d.ts +84 -0
- package/dist/react/controls.js +146 -0
- package/dist/react/controls.js.map +7 -0
- package/dist/react/hooks.d.ts +48 -0
- package/dist/react/hooks.js +67 -0
- package/dist/react/hooks.js.map +7 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.js +5 -0
- package/dist/react/index.js.map +7 -0
- package/dist/render.d.ts +61 -0
- package/dist/render.js +294 -0
- package/dist/render.js.map +7 -0
- package/dist/scene.d.ts +138 -0
- package/dist/scene.js +94 -0
- package/dist/scene.js.map +7 -0
- package/dist/sched/gantt.d.ts +49 -0
- package/dist/sched/gantt.js +65 -0
- package/dist/sched/gantt.js.map +7 -0
- package/dist/sched/index.d.ts +2 -0
- package/dist/sched/index.js +3 -0
- package/dist/sched/index.js.map +7 -0
- package/dist/sched/simulate.d.ts +55 -0
- package/dist/sched/simulate.js +78 -0
- package/dist/sched/simulate.js.map +7 -0
- package/dist/static.d.ts +8 -0
- package/dist/static.js +26 -0
- package/dist/static.js.map +7 -0
- package/dist/styles.d.ts +3 -0
- package/dist/styles.js +241 -0
- package/dist/styles.js.map +7 -0
- package/dist/sys/architecture.d.ts +104 -0
- package/dist/sys/architecture.js +167 -0
- package/dist/sys/architecture.js.map +7 -0
- package/dist/sys/index.d.ts +1 -0
- package/dist/sys/index.js +2 -0
- package/dist/sys/index.js.map +7 -0
- package/dist/themes.d.ts +13 -0
- package/dist/themes.js +8 -0
- package/dist/themes.js.map +7 -0
- package/dist/uml/classDiagram.d.ts +72 -0
- package/dist/uml/classDiagram.js +84 -0
- package/dist/uml/classDiagram.js.map +7 -0
- package/dist/uml/index.d.ts +3 -0
- package/dist/uml/index.js +4 -0
- package/dist/uml/index.js.map +7 -0
- package/dist/uml/sequence.d.ts +58 -0
- package/dist/uml/sequence.js +121 -0
- package/dist/uml/sequence.js.map +7 -0
- package/dist/uml/stateMachine.d.ts +40 -0
- package/dist/uml/stateMachine.js +89 -0
- package/dist/uml/stateMachine.js.map +7 -0
- package/dist/vec.d.ts +21 -0
- package/dist/vec.js +24 -0
- package/dist/vec.js.map +7 -0
- package/package.json +93 -4
- package/src/camera.ts +50 -0
- package/src/connector.ts +182 -0
- package/src/describe.ts +181 -0
- package/src/ds/array.ts +108 -0
- package/src/ds/common.ts +16 -0
- package/src/ds/graph.ts +104 -0
- package/src/ds/grid.ts +70 -0
- package/src/ds/index.ts +8 -0
- package/src/ds/linkedList.ts +117 -0
- package/src/ds/pointer.ts +55 -0
- package/src/ds/stackQueue.ts +59 -0
- package/src/ds/tree.ts +139 -0
- package/src/easing.ts +24 -0
- package/src/fx.ts +46 -0
- package/src/geom/index.ts +2 -0
- package/src/geom/math.ts +85 -0
- package/src/geom/shapes.ts +158 -0
- package/src/handle.ts +40 -0
- package/src/index.ts +37 -0
- package/src/mathtext.ts +139 -0
- package/src/mem/index.ts +2 -0
- package/src/mem/memory.ts +85 -0
- package/src/mem/stackHeap.ts +127 -0
- package/src/path.ts +68 -0
- package/src/player.ts +153 -0
- package/src/plot/index.ts +2 -0
- package/src/plot/marks.ts +298 -0
- package/src/plot/plane.ts +82 -0
- package/src/prob/bars.ts +132 -0
- package/src/prob/index.ts +3 -0
- package/src/prob/tree.ts +94 -0
- package/src/prob/venn.ts +144 -0
- package/src/react/Diagram.tsx +97 -0
- package/src/react/MathText.tsx +21 -0
- package/src/react/controls-styles.ts +111 -0
- package/src/react/controls.tsx +297 -0
- package/src/react/hooks.ts +112 -0
- package/src/react/index.ts +4 -0
- package/src/render.ts +445 -0
- package/src/scene.ts +238 -0
- package/src/sched/gantt.ts +110 -0
- package/src/sched/index.ts +2 -0
- package/src/sched/simulate.ts +140 -0
- package/src/static.ts +35 -0
- package/src/styles.ts +272 -0
- package/src/sys/architecture.ts +257 -0
- package/src/sys/index.ts +1 -0
- package/src/themes.ts +26 -0
- package/src/uml/classDiagram.ts +145 -0
- package/src/uml/index.ts +3 -0
- package/src/uml/sequence.ts +218 -0
- package/src/uml/stateMachine.ts +133 -0
- package/src/vec.ts +28 -0
package/dist/ds/array.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { rect, text } from "../scene.js";
|
|
2
|
+
import { V2 } from "../vec.js";
|
|
3
|
+
import { resolveTone, show } from "./common.js";
|
|
4
|
+
function array(values, o = {}) {
|
|
5
|
+
const id = o.id ?? "arr";
|
|
6
|
+
const x0 = o.x ?? 0, y0 = o.y ?? 0;
|
|
7
|
+
const cell = o.cell ?? 48;
|
|
8
|
+
const gap = o.gap ?? 6;
|
|
9
|
+
const step = cell + gap;
|
|
10
|
+
const keys = o.keys ?? values.map((_, i) => i);
|
|
11
|
+
const size = Math.round(cell * 0.4);
|
|
12
|
+
const center = (i) => o.vertical ? V2(x0 + cell / 2, y0 + i * step + cell / 2) : V2(x0 + i * step + cell / 2, y0 + cell / 2);
|
|
13
|
+
const nodes = [];
|
|
14
|
+
values.forEach((v, i) => {
|
|
15
|
+
const c = center(i);
|
|
16
|
+
const key = keys[i];
|
|
17
|
+
const tone = resolveTone(o.tones, i, v);
|
|
18
|
+
const enter = o.enterOffset ? { x: c.x + o.enterOffset.x, y: c.y + o.enterOffset.y } : void 0;
|
|
19
|
+
const exit = o.exitOffset ? { x: c.x + o.exitOffset.x, y: c.y + o.exitOffset.y } : void 0;
|
|
20
|
+
nodes.push(
|
|
21
|
+
rect({ id: `${id}:cell:${key}`, x: c.x, y: c.y, w: cell, h: cell, rx: cell * 0.16, tone, enter, exit, interactive: o.interactive, data: { index: i, key, value: v } }),
|
|
22
|
+
text({ id: `${id}:val:${key}`, x: c.x, y: c.y, text: o.format ? o.format(v, i) : show(v), size, weight: 600, role: "value", tone, enter, exit })
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
if (o.showIndices ?? true) {
|
|
26
|
+
values.forEach((_, i) => {
|
|
27
|
+
const c = center(i);
|
|
28
|
+
const p = o.vertical ? V2(c.x - cell / 2 - 10, c.y) : V2(c.x, c.y + cell / 2 + 14);
|
|
29
|
+
nodes.push(text({ id: `${id}:idx:${i}`, x: p.x, y: p.y, text: String(i), size: 12, role: "index", anchor: o.vertical ? "end" : "middle" }));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const half = cell / 2;
|
|
33
|
+
return {
|
|
34
|
+
nodes,
|
|
35
|
+
length: values.length,
|
|
36
|
+
cell,
|
|
37
|
+
center,
|
|
38
|
+
top: (i) => V2(center(i).x, center(i).y - half),
|
|
39
|
+
bottom: (i) => V2(center(i).x, center(i).y + half),
|
|
40
|
+
left: (i) => V2(center(i).x - half, center(i).y),
|
|
41
|
+
right: (i) => V2(center(i).x + half, center(i).y),
|
|
42
|
+
window(lo, hi, w = {}) {
|
|
43
|
+
if (lo > hi || hi < 0 || lo >= values.length) return null;
|
|
44
|
+
const pad = w.pad ?? 5;
|
|
45
|
+
const a = center(Math.max(0, lo)), b = center(Math.min(values.length - 1, hi));
|
|
46
|
+
return rect({
|
|
47
|
+
id: w.id ?? `${id}:window`,
|
|
48
|
+
x: (a.x + b.x) / 2,
|
|
49
|
+
y: (a.y + b.y) / 2,
|
|
50
|
+
w: Math.abs(b.x - a.x) + cell + 2 * pad,
|
|
51
|
+
h: Math.abs(b.y - a.y) + cell + 2 * pad,
|
|
52
|
+
rx: cell * 0.22,
|
|
53
|
+
role: "window",
|
|
54
|
+
tone: w.tone
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
array
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=array.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ds/array.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Arrays: a row of cells, with index labels underneath.\n *\n * The important design decision is WHAT A CELL'S ID IS:\n * - the cell box + value text are identified by the element's `key`\n * (default: its index). Give elements stable keys and a swap becomes two\n * cells sliding past each other instead of two numbers changing in place.\n * - the index labels (0, 1, 2\u2026) are identified by POSITION, because index 3\n * is always index 3 no matter what value sits in it.\n */\nimport { Part, Scene, SceneNode, Tone, rect, text } from '../scene';\nimport { V2, Vec } from '../vec';\nimport { Key, ToneSpec, resolveTone, show } from './common';\n\nexport interface ArrayOptions<T = unknown> {\n id?: string;\n /** Top-left corner of the first cell. */\n x?: number;\n y?: number;\n /** Cell size in px. Default 48. */\n cell?: number;\n gap?: number;\n /** Stable identity per element (must be unique). Default: the index. */\n keys?: Key[];\n /** Tone per INDEX: `{ 2: 'active' }` or `(i, v) => \u2026`. */\n tones?: ToneSpec<number, T>;\n showIndices?: boolean;\n /** Text to show instead of String(value). */\n format?: (v: T, i: number) => string;\n /** Lay the cells out top-to-bottom instead of left-to-right. */\n vertical?: boolean;\n /** Where new cells fly in from / old cells fly out to (offset in px). */\n enterOffset?: Vec;\n exitOffset?: Vec;\n interactive?: boolean;\n}\n\nexport interface ArrayDiagram extends Scene {\n length: number;\n cell: number;\n center(i: number): Vec;\n top(i: number): Vec;\n bottom(i: number): Vec;\n left(i: number): Vec;\n right(i: number): Vec;\n /** A highlight band behind cells lo..hi (inclusive). Put it BEFORE the array in combine(). */\n window(lo: number, hi: number, o?: { id?: string; tone?: Tone; pad?: number }): Part;\n}\n\nexport function array<T>(values: readonly T[], o: ArrayOptions<T> = {}): ArrayDiagram {\n const id = o.id ?? 'arr';\n const x0 = o.x ?? 0, y0 = o.y ?? 0;\n const cell = o.cell ?? 48;\n const gap = o.gap ?? 6;\n const step = cell + gap;\n const keys = o.keys ?? values.map((_, i) => i);\n const size = Math.round(cell * 0.4);\n\n const center = (i: number) =>\n o.vertical ? V2(x0 + cell / 2, y0 + i * step + cell / 2) : V2(x0 + i * step + cell / 2, y0 + cell / 2);\n\n const nodes: SceneNode[] = [];\n values.forEach((v, i) => {\n const c = center(i);\n const key = keys[i];\n const tone = resolveTone(o.tones, i, v);\n const enter = o.enterOffset ? { x: c.x + o.enterOffset.x, y: c.y + o.enterOffset.y } : undefined;\n const exit = o.exitOffset ? { x: c.x + o.exitOffset.x, y: c.y + o.exitOffset.y } : undefined;\n nodes.push(\n rect({ id: `${id}:cell:${key}`, x: c.x, y: c.y, w: cell, h: cell, rx: cell * 0.16, tone, enter, exit, interactive: o.interactive, data: { index: i, key, value: v } }),\n text({ id: `${id}:val:${key}`, x: c.x, y: c.y, text: o.format ? o.format(v, i) : show(v), size, weight: 600, role: 'value', tone, enter, exit }),\n );\n });\n if (o.showIndices ?? true) {\n values.forEach((_, i) => {\n const c = center(i);\n const p = o.vertical ? V2(c.x - cell / 2 - 10, c.y) : V2(c.x, c.y + cell / 2 + 14);\n nodes.push(text({ id: `${id}:idx:${i}`, x: p.x, y: p.y, text: String(i), size: 12, role: 'index', anchor: o.vertical ? 'end' : 'middle' }));\n });\n }\n\n const half = cell / 2;\n return {\n nodes,\n length: values.length,\n cell,\n center,\n top: (i) => V2(center(i).x, center(i).y - half),\n bottom: (i) => V2(center(i).x, center(i).y + half),\n left: (i) => V2(center(i).x - half, center(i).y),\n right: (i) => V2(center(i).x + half, center(i).y),\n window(lo, hi, w = {}) {\n if (lo > hi || hi < 0 || lo >= values.length) return null;\n const pad = w.pad ?? 5;\n const a = center(Math.max(0, lo)), b = center(Math.min(values.length - 1, hi));\n return rect({\n id: w.id ?? `${id}:window`,\n x: (a.x + b.x) / 2,\n y: (a.y + b.y) / 2,\n w: Math.abs(b.x - a.x) + cell + 2 * pad,\n h: Math.abs(b.y - a.y) + cell + 2 * pad,\n rx: cell * 0.22,\n role: 'window',\n tone: w.tone,\n });\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Tone } from '../scene';
|
|
2
|
+
export type Key = string | number;
|
|
3
|
+
/**
|
|
4
|
+
* Most layouts accept tones either as a lookup object (`{ 3: 'active' }`)
|
|
5
|
+
* or a function (`(i, value) => i === mid ? 'active' : undefined`).
|
|
6
|
+
*/
|
|
7
|
+
export type ToneSpec<K, V = unknown> = Partial<Record<string, Tone>> | ((key: K, value: V) => Tone | undefined);
|
|
8
|
+
export declare function resolveTone<K extends Key, V>(spec: ToneSpec<K, V> | undefined, key: K, value: V): Tone | undefined;
|
|
9
|
+
export declare const show: (v: unknown) => string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
function resolveTone(spec, key, value) {
|
|
2
|
+
if (!spec) return void 0;
|
|
3
|
+
return typeof spec === "function" ? spec(key, value) : spec[String(key)];
|
|
4
|
+
}
|
|
5
|
+
const show = (v) => v === null || v === void 0 ? "" : String(v);
|
|
6
|
+
export {
|
|
7
|
+
resolveTone,
|
|
8
|
+
show
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ds/common.ts"],
|
|
4
|
+
"sourcesContent": ["import { Tone } from '../scene';\n\nexport type Key = string | number;\n\n/**\n * Most layouts accept tones either as a lookup object (`{ 3: 'active' }`)\n * or a function (`(i, value) => i === mid ? 'active' : undefined`).\n */\nexport type ToneSpec<K, V = unknown> = Partial<Record<string, Tone>> | ((key: K, value: V) => Tone | undefined);\n\nexport function resolveTone<K extends Key, V>(spec: ToneSpec<K, V> | undefined, key: K, value: V): Tone | undefined {\n if (!spec) return undefined;\n return typeof spec === 'function' ? spec(key, value) : spec[String(key)];\n}\n\nexport const show = (v: unknown): string => (v === null || v === undefined ? '' : String(v));\n"],
|
|
5
|
+
"mappings": "AAUO,SAAS,YAA8B,MAAkC,KAAQ,OAA4B;AAClH,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,OAAO,SAAS,aAAa,KAAK,KAAK,KAAK,IAAI,KAAK,OAAO,GAAG,CAAC;AACzE;AAEO,MAAM,OAAO,CAAC,MAAwB,MAAM,QAAQ,MAAM,SAAY,KAAK,OAAO,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Scene, Tone } from '../scene';
|
|
2
|
+
import { Vec } from '../vec';
|
|
3
|
+
import { Key } from './common';
|
|
4
|
+
export interface GraphNodeSpec {
|
|
5
|
+
id: Key;
|
|
6
|
+
label?: string | number;
|
|
7
|
+
x?: number;
|
|
8
|
+
y?: number;
|
|
9
|
+
tone?: Tone;
|
|
10
|
+
}
|
|
11
|
+
export interface GraphEdgeSpec {
|
|
12
|
+
from: Key;
|
|
13
|
+
to: Key;
|
|
14
|
+
label?: string | number;
|
|
15
|
+
tone?: Tone;
|
|
16
|
+
/** Override the graph-wide `directed` for this edge. */
|
|
17
|
+
directed?: boolean;
|
|
18
|
+
/** Force a curve. Antiparallel directed pairs (a→b, b→a) curve automatically. */
|
|
19
|
+
bend?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface GraphOptions {
|
|
22
|
+
id?: string;
|
|
23
|
+
r?: number;
|
|
24
|
+
directed?: boolean;
|
|
25
|
+
/** Centre + radius for the circular fallback layout. */
|
|
26
|
+
cx?: number;
|
|
27
|
+
cy?: number;
|
|
28
|
+
radius?: number;
|
|
29
|
+
draggable?: boolean;
|
|
30
|
+
interactive?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface GraphDiagram extends Scene {
|
|
33
|
+
pos(id: Key): Vec;
|
|
34
|
+
top(id: Key): Vec;
|
|
35
|
+
bottom(id: Key): Vec;
|
|
36
|
+
}
|
|
37
|
+
/** Place n points evenly on a circle, starting at 12 o'clock. */
|
|
38
|
+
export declare function circularPositions(n: number, cx?: number, cy?: number, radius?: number): Vec[];
|
|
39
|
+
export declare function graph(nodes: readonly GraphNodeSpec[], edges: readonly GraphEdgeSpec[], o?: GraphOptions): GraphDiagram;
|
package/dist/ds/graph.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { bendControl, pointOnArrow } from "../describe.js";
|
|
2
|
+
import { arrow, circle, text } from "../scene.js";
|
|
3
|
+
import { V2, vnorm, vsub } from "../vec.js";
|
|
4
|
+
import { show } from "./common.js";
|
|
5
|
+
function circularPositions(n, cx = 0, cy = 0, radius = 120) {
|
|
6
|
+
return Array.from({ length: n }, (_, i) => {
|
|
7
|
+
const a = -Math.PI / 2 + 2 * Math.PI * i / Math.max(1, n);
|
|
8
|
+
return V2(cx + radius * Math.cos(a), cy + radius * Math.sin(a));
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function graph(nodes, edges, o = {}) {
|
|
12
|
+
const id = o.id ?? "graph";
|
|
13
|
+
const r = o.r ?? 22;
|
|
14
|
+
const ring = circularPositions(nodes.length, o.cx ?? 0, o.cy ?? 0, o.radius ?? Math.max(90, nodes.length * 22));
|
|
15
|
+
const pos = /* @__PURE__ */ new Map();
|
|
16
|
+
nodes.forEach((n, i) => pos.set(String(n.id), n.x != null && n.y != null ? V2(n.x, n.y) : ring[i]));
|
|
17
|
+
const P = (k) => pos.get(String(k)) ?? V2(0, 0);
|
|
18
|
+
const pairs = new Set(edges.map((e) => `${e.from}\u2192${e.to}`));
|
|
19
|
+
const out = [];
|
|
20
|
+
const labels = [];
|
|
21
|
+
for (const e of edges) {
|
|
22
|
+
const directed = e.directed ?? o.directed ?? false;
|
|
23
|
+
const a = P(e.from), b = P(e.to);
|
|
24
|
+
const antiparallel = directed && pairs.has(`${e.to}\u2192${e.from}`);
|
|
25
|
+
const bend = e.bend ?? (antiparallel ? 22 : 0);
|
|
26
|
+
const c = bendControl(a, b, bend);
|
|
27
|
+
const da = vnorm(vsub(Math.abs(bend) > 0.5 ? c : b, a));
|
|
28
|
+
const db = vnorm(vsub(Math.abs(bend) > 0.5 ? c : a, b));
|
|
29
|
+
const pad = directed ? 3 : 0;
|
|
30
|
+
const s = V2(a.x + da.x * r, a.y + da.y * r);
|
|
31
|
+
const t = V2(b.x + db.x * (r + pad), b.y + db.y * (r + pad));
|
|
32
|
+
const eid = `${id}:edge:${e.from}-${e.to}`;
|
|
33
|
+
out.push(arrow({ id: eid, x1: s.x, y1: s.y, x2: t.x, y2: t.y, bend, head: directed, tone: e.tone, data: e }));
|
|
34
|
+
if (e.label != null) {
|
|
35
|
+
const m = pointOnArrow(s, t, bend, 0.5);
|
|
36
|
+
labels.push(text({ id: `${eid}:label`, x: m.x, y: m.y, text: show(e.label), size: 13, role: "edge-label", tone: e.tone }));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const n of nodes) {
|
|
40
|
+
const p = P(n.id);
|
|
41
|
+
out.push(
|
|
42
|
+
circle({ id: `${id}:node:${n.id}`, x: p.x, y: p.y, r, tone: n.tone, draggable: o.draggable, interactive: o.interactive, data: n })
|
|
43
|
+
);
|
|
44
|
+
out.push(text({ id: `${id}:label:${n.id}`, x: p.x, y: p.y, text: show(n.label ?? n.id), size: 15, weight: 600, role: "value", tone: n.tone }));
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
nodes: [...out, ...labels],
|
|
48
|
+
pos: P,
|
|
49
|
+
top: (k) => V2(P(k).x, P(k).y - r),
|
|
50
|
+
bottom: (k) => V2(P(k).x, P(k).y + r)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
circularPositions,
|
|
55
|
+
graph
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ds/graph.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * General graphs: nodes as circles, edges as (optionally curved, optionally\n * directed, optionally weighted) lines.\n *\n * Positions are YOUR state. Give nodes x/y (e.g. updated from drag events) or\n * let the layout place unpositioned nodes on a circle. Real force-directed\n * layout is out of scope \u2014 but because positions are just data, you can plug\n * one in (d3-force, or your own) and feed the results back in.\n */\nimport { bendControl, pointOnArrow } from '../describe';\nimport { Scene, SceneNode, Tone, arrow, circle, text } from '../scene';\nimport { V2, Vec, vnorm, vsub } from '../vec';\nimport { Key, show } from './common';\n\nexport interface GraphNodeSpec {\n id: Key;\n label?: string | number;\n x?: number;\n y?: number;\n tone?: Tone;\n}\n\nexport interface GraphEdgeSpec {\n from: Key;\n to: Key;\n label?: string | number;\n tone?: Tone;\n /** Override the graph-wide `directed` for this edge. */\n directed?: boolean;\n /** Force a curve. Antiparallel directed pairs (a\u2192b, b\u2192a) curve automatically. */\n bend?: number;\n}\n\nexport interface GraphOptions {\n id?: string;\n r?: number;\n directed?: boolean;\n /** Centre + radius for the circular fallback layout. */\n cx?: number;\n cy?: number;\n radius?: number;\n draggable?: boolean;\n interactive?: boolean;\n}\n\nexport interface GraphDiagram extends Scene {\n pos(id: Key): Vec;\n top(id: Key): Vec;\n bottom(id: Key): Vec;\n}\n\n/** Place n points evenly on a circle, starting at 12 o'clock. */\nexport function circularPositions(n: number, cx = 0, cy = 0, radius = 120): Vec[] {\n return Array.from({ length: n }, (_, i) => {\n const a = -Math.PI / 2 + (2 * Math.PI * i) / Math.max(1, n);\n return V2(cx + radius * Math.cos(a), cy + radius * Math.sin(a));\n });\n}\n\nexport function graph(nodes: readonly GraphNodeSpec[], edges: readonly GraphEdgeSpec[], o: GraphOptions = {}): GraphDiagram {\n const id = o.id ?? 'graph';\n const r = o.r ?? 22;\n const ring = circularPositions(nodes.length, o.cx ?? 0, o.cy ?? 0, o.radius ?? Math.max(90, nodes.length * 22));\n const pos = new Map<string, Vec>();\n nodes.forEach((n, i) => pos.set(String(n.id), n.x != null && n.y != null ? V2(n.x, n.y) : ring[i]));\n const P = (k: Key) => pos.get(String(k)) ?? V2(0, 0);\n\n const pairs = new Set(edges.map((e) => `${e.from}\u2192${e.to}`));\n const out: SceneNode[] = [];\n const labels: SceneNode[] = [];\n\n for (const e of edges) {\n const directed = e.directed ?? o.directed ?? false;\n const a = P(e.from), b = P(e.to);\n const antiparallel = directed && pairs.has(`${e.to}\u2192${e.from}`);\n const bend = e.bend ?? (antiparallel ? 22 : 0);\n // Trim both ends to the circle boundary, along the curve's actual tangent.\n const c = bendControl(a, b, bend);\n const da = vnorm(vsub(Math.abs(bend) > 0.5 ? c : b, a));\n const db = vnorm(vsub(Math.abs(bend) > 0.5 ? c : a, b));\n const pad = directed ? 3 : 0;\n const s = V2(a.x + da.x * r, a.y + da.y * r);\n const t = V2(b.x + db.x * (r + pad), b.y + db.y * (r + pad));\n const eid = `${id}:edge:${e.from}-${e.to}`;\n out.push(arrow({ id: eid, x1: s.x, y1: s.y, x2: t.x, y2: t.y, bend, head: directed, tone: e.tone, data: e }));\n if (e.label != null) {\n const m = pointOnArrow(s, t, bend, 0.5);\n labels.push(text({ id: `${eid}:label`, x: m.x, y: m.y, text: show(e.label), size: 13, role: 'edge-label', tone: e.tone }));\n }\n }\n for (const n of nodes) {\n const p = P(n.id);\n out.push(\n circle({ id: `${id}:node:${n.id}`, x: p.x, y: p.y, r, tone: n.tone, draggable: o.draggable, interactive: o.interactive, data: n }),\n );\n out.push(text({ id: `${id}:label:${n.id}`, x: p.x, y: p.y, text: show(n.label ?? n.id), size: 15, weight: 600, role: 'value', tone: n.tone }));\n }\n return {\n nodes: [...out, ...labels],\n pos: P,\n top: (k) => V2(P(k).x, P(k).y - r),\n bottom: (k) => V2(P(k).x, P(k).y + r),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2D grids: DP tables, BFS on a maze, matrices, game boards.
|
|
3
|
+
* Cells are identified by (row, col), with optional row/column headers
|
|
4
|
+
* (e.g. the two strings of an LCS / edit-distance table).
|
|
5
|
+
*/
|
|
6
|
+
import { Scene, Tone } from '../scene';
|
|
7
|
+
import { Vec } from '../vec';
|
|
8
|
+
export interface GridOptions<T = unknown> {
|
|
9
|
+
id?: string;
|
|
10
|
+
x?: number;
|
|
11
|
+
y?: number;
|
|
12
|
+
cell?: number;
|
|
13
|
+
/** Width and height of a cell when it is not square: a table of words. Default `cell`. */
|
|
14
|
+
cellWidth?: number;
|
|
15
|
+
cellHeight?: number;
|
|
16
|
+
gap?: number;
|
|
17
|
+
tones?: (r: number, c: number, v: T) => Tone | undefined;
|
|
18
|
+
rowLabels?: readonly (string | number)[];
|
|
19
|
+
colLabels?: readonly (string | number)[];
|
|
20
|
+
format?: (v: T, r: number, c: number) => string;
|
|
21
|
+
interactive?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface GridDiagram extends Scene {
|
|
24
|
+
center(r: number, c: number): Vec;
|
|
25
|
+
top(r: number, c: number): Vec;
|
|
26
|
+
bottom(r: number, c: number): Vec;
|
|
27
|
+
left(r: number, c: number): Vec;
|
|
28
|
+
right(r: number, c: number): Vec;
|
|
29
|
+
}
|
|
30
|
+
export declare function grid<T>(rows: readonly (readonly T[])[], o?: GridOptions<T>): GridDiagram;
|
package/dist/ds/grid.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { rect, text } from "../scene.js";
|
|
2
|
+
import { V2 } from "../vec.js";
|
|
3
|
+
import { show } from "./common.js";
|
|
4
|
+
function grid(rows, o = {}) {
|
|
5
|
+
const id = o.id ?? "grid";
|
|
6
|
+
const cell = o.cell ?? 40, gap = o.gap ?? 4;
|
|
7
|
+
const cw = o.cellWidth ?? cell, ch = o.cellHeight ?? cell;
|
|
8
|
+
const x0 = o.x ?? 0, y0 = o.y ?? 0;
|
|
9
|
+
const center = (r, c) => V2(x0 + c * (cw + gap) + cw / 2, y0 + r * (ch + gap) + ch / 2);
|
|
10
|
+
const nodes = [];
|
|
11
|
+
const texts = [];
|
|
12
|
+
rows.forEach(
|
|
13
|
+
(row, r) => row.forEach((v, c) => {
|
|
14
|
+
const p = center(r, c);
|
|
15
|
+
const tone = o.tones?.(r, c, v);
|
|
16
|
+
nodes.push(rect({ id: `${id}:cell:${r},${c}`, x: p.x, y: p.y, w: cw, h: ch, rx: 6, tone, interactive: o.interactive, data: { r, c, value: v } }));
|
|
17
|
+
const s = o.format ? o.format(v, r, c) : show(v);
|
|
18
|
+
if (s) nodes.push(text({ id: `${id}:val:${r},${c}`, x: p.x, y: p.y, text: s, size: Math.round(Math.min(cw, ch) * 0.38), weight: 600, role: "value", tone }));
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
o.colLabels?.forEach((l, c) => {
|
|
22
|
+
const p = center(0, c);
|
|
23
|
+
texts.push(text({ id: `${id}:col:${c}`, x: p.x, y: p.y - ch / 2 - 14, text: show(l), size: 13, role: "caption" }));
|
|
24
|
+
});
|
|
25
|
+
o.rowLabels?.forEach((l, r) => {
|
|
26
|
+
const p = center(r, 0);
|
|
27
|
+
texts.push(text({ id: `${id}:row:${r}`, x: p.x - cw / 2 - 12, y: p.y, text: show(l), size: 13, role: "caption", anchor: "end" }));
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
nodes: [...nodes, ...texts],
|
|
31
|
+
center,
|
|
32
|
+
top: (r, c) => V2(center(r, c).x, center(r, c).y - ch / 2),
|
|
33
|
+
bottom: (r, c) => V2(center(r, c).x, center(r, c).y + ch / 2),
|
|
34
|
+
left: (r, c) => V2(center(r, c).x - cw / 2, center(r, c).y),
|
|
35
|
+
right: (r, c) => V2(center(r, c).x + cw / 2, center(r, c).y)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
grid
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=grid.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ds/grid.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * 2D grids: DP tables, BFS on a maze, matrices, game boards.\n * Cells are identified by (row, col), with optional row/column headers\n * (e.g. the two strings of an LCS / edit-distance table).\n */\nimport { Scene, SceneNode, Tone, rect, text } from '../scene';\nimport { V2, Vec } from '../vec';\nimport { show } from './common';\n\nexport interface GridOptions<T = unknown> {\n id?: string;\n x?: number;\n y?: number;\n cell?: number;\n /** Width and height of a cell when it is not square: a table of words. Default `cell`. */\n cellWidth?: number;\n cellHeight?: number;\n gap?: number;\n tones?: (r: number, c: number, v: T) => Tone | undefined;\n rowLabels?: readonly (string | number)[];\n colLabels?: readonly (string | number)[];\n format?: (v: T, r: number, c: number) => string;\n interactive?: boolean;\n}\n\nexport interface GridDiagram extends Scene {\n center(r: number, c: number): Vec;\n top(r: number, c: number): Vec;\n bottom(r: number, c: number): Vec;\n left(r: number, c: number): Vec;\n right(r: number, c: number): Vec;\n}\n\nexport function grid<T>(rows: readonly (readonly T[])[], o: GridOptions<T> = {}): GridDiagram {\n const id = o.id ?? 'grid';\n const cell = o.cell ?? 40, gap = o.gap ?? 4;\n const cw = o.cellWidth ?? cell, ch = o.cellHeight ?? cell;\n const x0 = o.x ?? 0, y0 = o.y ?? 0;\n const center = (r: number, c: number) => V2(x0 + c * (cw + gap) + cw / 2, y0 + r * (ch + gap) + ch / 2);\n const nodes: SceneNode[] = [];\n const texts: SceneNode[] = [];\n\n rows.forEach((row, r) =>\n row.forEach((v, c) => {\n const p = center(r, c);\n const tone = o.tones?.(r, c, v);\n nodes.push(rect({ id: `${id}:cell:${r},${c}`, x: p.x, y: p.y, w: cw, h: ch, rx: 6, tone, interactive: o.interactive, data: { r, c, value: v } }));\n const s = o.format ? o.format(v, r, c) : show(v);\n // the value goes right after its cell: CSS moves it with the cell (`cell:active + text`)\n if (s) nodes.push(text({ id: `${id}:val:${r},${c}`, x: p.x, y: p.y, text: s, size: Math.round(Math.min(cw, ch) * 0.38), weight: 600, role: 'value', tone }));\n }),\n );\n o.colLabels?.forEach((l, c) => {\n const p = center(0, c);\n texts.push(text({ id: `${id}:col:${c}`, x: p.x, y: p.y - ch / 2 - 14, text: show(l), size: 13, role: 'caption' }));\n });\n o.rowLabels?.forEach((l, r) => {\n const p = center(r, 0);\n texts.push(text({ id: `${id}:row:${r}`, x: p.x - cw / 2 - 12, y: p.y, text: show(l), size: 13, role: 'caption', anchor: 'end' }));\n });\n\n return {\n nodes: [...nodes, ...texts],\n center,\n top: (r, c) => V2(center(r, c).x, center(r, c).y - ch / 2),\n bottom: (r, c) => V2(center(r, c).x, center(r, c).y + ch / 2),\n left: (r, c) => V2(center(r, c).x - cw / 2, center(r, c).y),\n right: (r, c) => V2(center(r, c).x + cw / 2, center(r, c).y),\n };\n}\n"],
|
|
5
|
+
"mappings": "AAKA,SAAiC,MAAM,YAAY;AACnD,SAAS,UAAe;AACxB,SAAS,YAAY;AA0Bd,SAAS,KAAQ,MAAiC,IAAoB,CAAC,GAAgB;AAC5F,QAAM,KAAK,EAAE,MAAM;AACnB,QAAM,OAAO,EAAE,QAAQ,IAAI,MAAM,EAAE,OAAO;AAC1C,QAAM,KAAK,EAAE,aAAa,MAAM,KAAK,EAAE,cAAc;AACrD,QAAM,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK;AACjC,QAAM,SAAS,CAAC,GAAW,MAAc,GAAG,KAAK,KAAK,KAAK,OAAO,KAAK,GAAG,KAAK,KAAK,KAAK,OAAO,KAAK,CAAC;AACtG,QAAM,QAAqB,CAAC;AAC5B,QAAM,QAAqB,CAAC;AAE5B,OAAK;AAAA,IAAQ,CAAC,KAAK,MACjB,IAAI,QAAQ,CAAC,GAAG,MAAM;AACpB,YAAM,IAAI,OAAO,GAAG,CAAC;AACrB,YAAM,OAAO,EAAE,QAAQ,GAAG,GAAG,CAAC;AAC9B,YAAM,KAAK,KAAK,EAAE,IAAI,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,MAAM,aAAa,EAAE,aAAa,MAAM,EAAE,GAAG,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC;AAChJ,YAAM,IAAI,EAAE,SAAS,EAAE,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC;AAE/C,UAAI,EAAG,OAAM,KAAK,KAAK,EAAE,IAAI,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,MAAM,GAAG,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,IAAI,IAAI,GAAG,QAAQ,KAAK,MAAM,SAAS,KAAK,CAAC,CAAC;AAAA,IAC7J,CAAC;AAAA,EACH;AACA,IAAE,WAAW,QAAQ,CAAC,GAAG,MAAM;AAC7B,UAAM,IAAI,OAAO,GAAG,CAAC;AACrB,UAAM,KAAK,KAAK,EAAE,IAAI,GAAG,EAAE,QAAQ,CAAC,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,IAAI,KAAK,IAAI,IAAI,MAAM,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,UAAU,CAAC,CAAC;AAAA,EACnH,CAAC;AACD,IAAE,WAAW,QAAQ,CAAC,GAAG,MAAM;AAC7B,UAAM,IAAI,OAAO,GAAG,CAAC;AACrB,UAAM,KAAK,KAAK,EAAE,IAAI,GAAG,EAAE,QAAQ,CAAC,IAAI,GAAG,EAAE,IAAI,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,WAAW,QAAQ,MAAM,CAAC,CAAC;AAAA,EAClI,CAAC;AAED,SAAO;AAAA,IACL,OAAO,CAAC,GAAG,OAAO,GAAG,KAAK;AAAA,IAC1B;AAAA,IACA,KAAK,CAAC,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,EAAE,GAAG,OAAO,GAAG,CAAC,EAAE,IAAI,KAAK,CAAC;AAAA,IACzD,QAAQ,CAAC,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,EAAE,GAAG,OAAO,GAAG,CAAC,EAAE,IAAI,KAAK,CAAC;AAAA,IAC5D,MAAM,CAAC,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,EAAE,IAAI,KAAK,GAAG,OAAO,GAAG,CAAC,EAAE,CAAC;AAAA,IAC1D,OAAO,CAAC,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,EAAE,IAAI,KAAK,GAAG,OAAO,GAAG,CAAC,EAAE,CAAC;AAAA,EAC7D;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/ds/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./common.js";
|
|
2
|
+
export * from "./array.js";
|
|
3
|
+
export * from "./pointer.js";
|
|
4
|
+
export * from "./linkedList.js";
|
|
5
|
+
export * from "./tree.js";
|
|
6
|
+
export * from "./graph.js";
|
|
7
|
+
export * from "./grid.js";
|
|
8
|
+
export * from "./stackQueue.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ds/index.ts"],
|
|
4
|
+
"sourcesContent": ["export * from './common';\nexport * from './array';\nexport * from './pointer';\nexport * from './linkedList';\nexport * from './tree';\nexport * from './graph';\nexport * from './grid';\nexport * from './stackQueue';\n"],
|
|
5
|
+
"mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singly linked lists drawn as [value|•]──▶ boxes.
|
|
3
|
+
*
|
|
4
|
+
* You describe the list as data — each node says who its `next` is — and the
|
|
5
|
+
* layout draws whatever those pointers say, even mid-algorithm states where the
|
|
6
|
+
* list is broken in two (that's exactly what you want for "reverse a list").
|
|
7
|
+
*
|
|
8
|
+
* - Boxes are placed left→right in `order` (default: walk from the first node).
|
|
9
|
+
* - Each `next` arrow is identified by the node it STARTS from, so when you
|
|
10
|
+
* change `a.next` from b to z the arrow swings over instead of popping.
|
|
11
|
+
* - Arrows pointing backwards curve underneath the row.
|
|
12
|
+
*/
|
|
13
|
+
import { Scene, Tone } from '../scene';
|
|
14
|
+
import { Vec } from '../vec';
|
|
15
|
+
import { Key } from './common';
|
|
16
|
+
export interface ListNodeSpec {
|
|
17
|
+
key: Key;
|
|
18
|
+
value: unknown;
|
|
19
|
+
next: Key | null;
|
|
20
|
+
tone?: Tone;
|
|
21
|
+
}
|
|
22
|
+
export interface LinkedListOptions {
|
|
23
|
+
id?: string;
|
|
24
|
+
x?: number;
|
|
25
|
+
y?: number;
|
|
26
|
+
/** Width of the value part. Default 52. */
|
|
27
|
+
w?: number;
|
|
28
|
+
h?: number;
|
|
29
|
+
/** Horizontal space between boxes (room for the arrow). Default 44. */
|
|
30
|
+
gap?: number;
|
|
31
|
+
/** Explicit left→right placement. Default: follow `next` from nodes[0]. */
|
|
32
|
+
order?: Key[];
|
|
33
|
+
/** Tone for the next-arrow leaving a node. */
|
|
34
|
+
arrowTones?: Partial<Record<string, Tone>>;
|
|
35
|
+
}
|
|
36
|
+
export interface LinkedListDiagram extends Scene {
|
|
37
|
+
/** Centre of a node's value box. */
|
|
38
|
+
center(key: Key): Vec;
|
|
39
|
+
top(key: Key): Vec;
|
|
40
|
+
bottom(key: Key): Vec;
|
|
41
|
+
/** A point left of slot 0 / right of the last slot (for null pointers). */
|
|
42
|
+
slot(i: number): Vec;
|
|
43
|
+
}
|
|
44
|
+
/** Convenience: turn [1, 2, 3] into node specs keyed 'n0', 'n1', … */
|
|
45
|
+
export declare function listFromValues(values: readonly unknown[], prefix?: string): ListNodeSpec[];
|
|
46
|
+
export declare function linkedList(list: readonly ListNodeSpec[], o?: LinkedListOptions): LinkedListDiagram;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { arrow, circle, rect, text } from "../scene.js";
|
|
2
|
+
import { V2 } from "../vec.js";
|
|
3
|
+
import { show } from "./common.js";
|
|
4
|
+
function listFromValues(values, prefix = "n") {
|
|
5
|
+
return values.map((value, i) => ({ key: `${prefix}${i}`, value, next: i + 1 < values.length ? `${prefix}${i + 1}` : null }));
|
|
6
|
+
}
|
|
7
|
+
function linkedList(list, o = {}) {
|
|
8
|
+
const id = o.id ?? "list";
|
|
9
|
+
const x0 = o.x ?? 0, y0 = o.y ?? 0;
|
|
10
|
+
const w = o.w ?? 44, h = o.h ?? 38, gap = o.gap ?? 34;
|
|
11
|
+
const pw = h * 0.62;
|
|
12
|
+
const boxW = w + pw;
|
|
13
|
+
const step = boxW + gap;
|
|
14
|
+
const byKey = new Map(list.map((n) => [String(n.key), n]));
|
|
15
|
+
let order = o.order ? [...o.order] : [];
|
|
16
|
+
if (!o.order && list.length) {
|
|
17
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18
|
+
let cur = list[0];
|
|
19
|
+
while (cur && !seen.has(String(cur.key))) {
|
|
20
|
+
seen.add(String(cur.key));
|
|
21
|
+
order.push(cur.key);
|
|
22
|
+
cur = cur.next == null ? void 0 : byKey.get(String(cur.next));
|
|
23
|
+
}
|
|
24
|
+
for (const n of list) if (!seen.has(String(n.key))) order.push(n.key);
|
|
25
|
+
}
|
|
26
|
+
const slotOf = new Map(order.map((k, i) => [String(k), i]));
|
|
27
|
+
const slot = (i) => V2(x0 + i * step + w / 2, y0 + h / 2);
|
|
28
|
+
const center = (k) => slot(slotOf.get(String(k)) ?? 0);
|
|
29
|
+
const boxes = [];
|
|
30
|
+
const arrows = [];
|
|
31
|
+
for (const n of list) {
|
|
32
|
+
const c = center(n.key);
|
|
33
|
+
const pc = V2(c.x + w / 2 + pw / 2, c.y);
|
|
34
|
+
boxes.push(
|
|
35
|
+
rect({ id: `${id}:val:${n.key}`, x: c.x, y: c.y, w, h, rx: 6, tone: n.tone, data: n }),
|
|
36
|
+
text({ id: `${id}:txt:${n.key}`, x: c.x, y: c.y, text: show(n.value), size: 15, role: "value", tone: n.tone }),
|
|
37
|
+
rect({ id: `${id}:ptr:${n.key}`, x: pc.x, y: pc.y, w: pw, h, rx: 6, tone: n.tone })
|
|
38
|
+
);
|
|
39
|
+
const target = n.next == null ? void 0 : byKey.get(String(n.next));
|
|
40
|
+
if (!target) {
|
|
41
|
+
boxes.push(text({ id: `${id}:null:${n.key}`, x: pc.x, y: pc.y + 1, text: "\u2205", size: 14, role: "null" }));
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
boxes.push(circle({ id: `${id}:dot:${n.key}`, x: pc.x, y: pc.y, r: 3.5, role: "dot" }));
|
|
45
|
+
const t = center(target.key);
|
|
46
|
+
const tone = o.arrowTones?.[String(n.key)];
|
|
47
|
+
if (t.x > pc.x) {
|
|
48
|
+
arrows.push(arrow({ id: `${id}:next:${n.key}`, x1: pc.x, y1: pc.y, x2: t.x - w / 2 - 3, y2: t.y, tone }));
|
|
49
|
+
} else {
|
|
50
|
+
const span = Math.abs(t.x - pc.x);
|
|
51
|
+
arrows.push(
|
|
52
|
+
arrow({ id: `${id}:next:${n.key}`, x1: pc.x, y1: pc.y + h / 2, x2: t.x, y2: t.y + h / 2 + 3, bend: -(26 + span * 0.18), tone })
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
nodes: [...arrows, ...boxes],
|
|
58
|
+
center,
|
|
59
|
+
top: (k) => V2(center(k).x + pw / 2, center(k).y - h / 2),
|
|
60
|
+
bottom: (k) => V2(center(k).x + pw / 2, center(k).y + h / 2),
|
|
61
|
+
slot
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
linkedList,
|
|
66
|
+
listFromValues
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=linkedList.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ds/linkedList.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Singly linked lists drawn as [value|\u2022]\u2500\u2500\u25B6 boxes.\n *\n * You describe the list as data \u2014 each node says who its `next` is \u2014 and the\n * layout draws whatever those pointers say, even mid-algorithm states where the\n * list is broken in two (that's exactly what you want for \"reverse a list\").\n *\n * - Boxes are placed left\u2192right in `order` (default: walk from the first node).\n * - Each `next` arrow is identified by the node it STARTS from, so when you\n * change `a.next` from b to z the arrow swings over instead of popping.\n * - Arrows pointing backwards curve underneath the row.\n */\nimport { Scene, SceneNode, Tone, arrow, circle, rect, text } from '../scene';\nimport { V2, Vec } from '../vec';\nimport { Key, show } from './common';\n\nexport interface ListNodeSpec {\n key: Key;\n value: unknown;\n next: Key | null;\n tone?: Tone;\n}\n\nexport interface LinkedListOptions {\n id?: string;\n x?: number;\n y?: number;\n /** Width of the value part. Default 52. */\n w?: number;\n h?: number;\n /** Horizontal space between boxes (room for the arrow). Default 44. */\n gap?: number;\n /** Explicit left\u2192right placement. Default: follow `next` from nodes[0]. */\n order?: Key[];\n /** Tone for the next-arrow leaving a node. */\n arrowTones?: Partial<Record<string, Tone>>;\n}\n\nexport interface LinkedListDiagram extends Scene {\n /** Centre of a node's value box. */\n center(key: Key): Vec;\n top(key: Key): Vec;\n bottom(key: Key): Vec;\n /** A point left of slot 0 / right of the last slot (for null pointers). */\n slot(i: number): Vec;\n}\n\n/** Convenience: turn [1, 2, 3] into node specs keyed 'n0', 'n1', \u2026 */\nexport function listFromValues(values: readonly unknown[], prefix = 'n'): ListNodeSpec[] {\n return values.map((value, i) => ({ key: `${prefix}${i}`, value, next: i + 1 < values.length ? `${prefix}${i + 1}` : null }));\n}\n\nexport function linkedList(list: readonly ListNodeSpec[], o: LinkedListOptions = {}): LinkedListDiagram {\n const id = o.id ?? 'list';\n const x0 = o.x ?? 0, y0 = o.y ?? 0;\n const w = o.w ?? 44, h = o.h ?? 38, gap = o.gap ?? 34;\n const pw = h * 0.62; // width of the \"next\" compartment\n const boxW = w + pw;\n const step = boxW + gap;\n const byKey = new Map(list.map((n) => [String(n.key), n]));\n\n // Placement order: explicit, or walk from the head, then any stragglers.\n let order: Key[] = o.order ? [...o.order] : [];\n if (!o.order && list.length) {\n const seen = new Set<string>();\n let cur: ListNodeSpec | undefined = list[0];\n while (cur && !seen.has(String(cur.key))) {\n seen.add(String(cur.key));\n order.push(cur.key);\n cur = cur.next == null ? undefined : byKey.get(String(cur.next));\n }\n for (const n of list) if (!seen.has(String(n.key))) order.push(n.key);\n }\n const slotOf = new Map(order.map((k, i) => [String(k), i]));\n\n // Slot i's value box centre; the pointer compartment sits to its right.\n const slot = (i: number) => V2(x0 + i * step + w / 2, y0 + h / 2);\n const center = (k: Key) => slot(slotOf.get(String(k)) ?? 0);\n\n const boxes: SceneNode[] = [];\n const arrows: SceneNode[] = [];\n for (const n of list) {\n const c = center(n.key);\n const pc = V2(c.x + w / 2 + pw / 2, c.y); // pointer compartment centre\n boxes.push(\n rect({ id: `${id}:val:${n.key}`, x: c.x, y: c.y, w, h, rx: 6, tone: n.tone, data: n }),\n text({ id: `${id}:txt:${n.key}`, x: c.x, y: c.y, text: show(n.value), size: 15, role: 'value', tone: n.tone }),\n rect({ id: `${id}:ptr:${n.key}`, x: pc.x, y: pc.y, w: pw, h, rx: 6, tone: n.tone }),\n );\n const target = n.next == null ? undefined : byKey.get(String(n.next));\n if (!target) {\n boxes.push(text({ id: `${id}:null:${n.key}`, x: pc.x, y: pc.y + 1, text: '\u2205', size: 14, role: 'null' }));\n continue;\n }\n boxes.push(circle({ id: `${id}:dot:${n.key}`, x: pc.x, y: pc.y, r: 3.5, role: 'dot' }));\n const t = center(target.key);\n const tone = o.arrowTones?.[String(n.key)];\n if (t.x > pc.x) {\n // forward: straight across to the target's left edge\n arrows.push(arrow({ id: `${id}:next:${n.key}`, x1: pc.x, y1: pc.y, x2: t.x - w / 2 - 3, y2: t.y, tone }));\n } else {\n // backward (or self): leave from the bottom and curve under the row\n const span = Math.abs(t.x - pc.x);\n arrows.push(\n arrow({ id: `${id}:next:${n.key}`, x1: pc.x, y1: pc.y + h / 2, x2: t.x, y2: t.y + h / 2 + 3, bend: -(26 + span * 0.18), tone }),\n );\n }\n }\n\n return {\n nodes: [...arrows, ...boxes],\n center,\n top: (k) => V2(center(k).x + pw / 2, center(k).y - h / 2),\n bottom: (k) => V2(center(k).x + pw / 2, center(k).y + h / 2),\n slot,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A labelled pointer arrow (lo / hi / mid / curr / prev …).
|
|
3
|
+
*
|
|
4
|
+
* Give each pointer a stable `id` (e.g. 'mid') and just change `at` between
|
|
5
|
+
* renders — the arrow and its label slide to the new target.
|
|
6
|
+
*/
|
|
7
|
+
import { Scene, Tone } from '../scene';
|
|
8
|
+
import { Vec } from '../vec';
|
|
9
|
+
export interface PointerOptions {
|
|
10
|
+
id: string;
|
|
11
|
+
/** The point the arrow tip touches, e.g. `arr.top(i)`. */
|
|
12
|
+
at: Vec;
|
|
13
|
+
label?: string;
|
|
14
|
+
/** Which side the pointer comes from. Default 'top'. */
|
|
15
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
16
|
+
length?: number;
|
|
17
|
+
/** Gap between the tip and the target. Default 4. */
|
|
18
|
+
gap?: number;
|
|
19
|
+
/** Stack several pointers at the same spot: level 1 sits one label further out. */
|
|
20
|
+
level?: number;
|
|
21
|
+
tone?: Tone;
|
|
22
|
+
}
|
|
23
|
+
export declare function pointer(o: PointerOptions): Scene;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { arrow, estimateTextWidth, rect, text } from "../scene.js";
|
|
2
|
+
function pointer(o) {
|
|
3
|
+
const len = o.length ?? 26;
|
|
4
|
+
const gap = o.gap ?? 4;
|
|
5
|
+
const side = o.side ?? "top";
|
|
6
|
+
const out = (o.level ?? 0) * 18;
|
|
7
|
+
const dir = { top: [0, -1], bottom: [0, 1], left: [-1, 0], right: [1, 0] }[side];
|
|
8
|
+
const tip = { x: o.at.x + dir[0] * gap, y: o.at.y + dir[1] * gap };
|
|
9
|
+
const tail = { x: tip.x + dir[0] * len, y: tip.y + dir[1] * len };
|
|
10
|
+
const tone = o.tone ?? "accent";
|
|
11
|
+
const lab = { x: tail.x + dir[0] * (13 + out), y: tail.y + dir[1] * (13 + out) };
|
|
12
|
+
const nodes = [arrow({ id: `${o.id}:arrow`, x1: tail.x, y1: tail.y, x2: tip.x, y2: tip.y, tone, width: 2 })];
|
|
13
|
+
if (o.label) {
|
|
14
|
+
const w = estimateTextWidth(o.label, 13) + 16;
|
|
15
|
+
const tx = side === "left" ? lab.x - w / 2 : side === "right" ? lab.x + w / 2 : lab.x;
|
|
16
|
+
nodes.push(rect({ id: `${o.id}:tag`, x: tx, y: lab.y, w, h: 21, rx: 10.5, role: "tag", tone }));
|
|
17
|
+
nodes.push(
|
|
18
|
+
text({
|
|
19
|
+
id: `${o.id}:label`,
|
|
20
|
+
x: lab.x,
|
|
21
|
+
y: lab.y,
|
|
22
|
+
text: o.label,
|
|
23
|
+
size: 13,
|
|
24
|
+
role: "pointer-label",
|
|
25
|
+
tone,
|
|
26
|
+
anchor: side === "left" ? "end" : side === "right" ? "start" : "middle"
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return { nodes };
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
pointer
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=pointer.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ds/pointer.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * A labelled pointer arrow (lo / hi / mid / curr / prev \u2026).\n *\n * Give each pointer a stable `id` (e.g. 'mid') and just change `at` between\n * renders \u2014 the arrow and its label slide to the new target.\n */\nimport { Scene, SceneNode, Tone, arrow, estimateTextWidth, rect, text } from '../scene';\nimport { Vec } from '../vec';\n\nexport interface PointerOptions {\n id: string;\n /** The point the arrow tip touches, e.g. `arr.top(i)`. */\n at: Vec;\n label?: string;\n /** Which side the pointer comes from. Default 'top'. */\n side?: 'top' | 'bottom' | 'left' | 'right';\n length?: number;\n /** Gap between the tip and the target. Default 4. */\n gap?: number;\n /** Stack several pointers at the same spot: level 1 sits one label further out. */\n level?: number;\n tone?: Tone;\n}\n\nexport function pointer(o: PointerOptions): Scene {\n const len = o.length ?? 26;\n const gap = o.gap ?? 4;\n const side = o.side ?? 'top';\n const out = (o.level ?? 0) * 18; // labels stack outward, arrows stay put\n const dir = { top: [0, -1], bottom: [0, 1], left: [-1, 0], right: [1, 0] }[side];\n const tip = { x: o.at.x + dir[0] * gap, y: o.at.y + dir[1] * gap };\n const tail = { x: tip.x + dir[0] * len, y: tip.y + dir[1] * len };\n const tone = o.tone ?? 'accent';\n const lab = { x: tail.x + dir[0] * (13 + out), y: tail.y + dir[1] * (13 + out) };\n const nodes: SceneNode[] = [arrow({ id: `${o.id}:arrow`, x1: tail.x, y1: tail.y, x2: tip.x, y2: tip.y, tone, width: 2 })];\n if (o.label) {\n // A pill behind the label. Hidden by default; apps can show it with `.dv-role-tag { display: inline }`.\n const w = estimateTextWidth(o.label, 13) + 16;\n const tx = side === 'left' ? lab.x - w / 2 : side === 'right' ? lab.x + w / 2 : lab.x;\n nodes.push(rect({ id: `${o.id}:tag`, x: tx, y: lab.y, w, h: 21, rx: 10.5, role: 'tag', tone }));\n nodes.push(\n text({\n id: `${o.id}:label`,\n x: lab.x,\n y: lab.y,\n text: o.label,\n size: 13,\n role: 'pointer-label',\n tone,\n anchor: side === 'left' ? 'end' : side === 'right' ? 'start' : 'middle',\n }),\n );\n }\n return { nodes };\n}\n"],
|
|
5
|
+
"mappings": "AAMA,SAAiC,OAAO,mBAAmB,MAAM,YAAY;AAkBtE,SAAS,QAAQ,GAA0B;AAChD,QAAM,MAAM,EAAE,UAAU;AACxB,QAAM,MAAM,EAAE,OAAO;AACrB,QAAM,OAAO,EAAE,QAAQ;AACvB,QAAM,OAAO,EAAE,SAAS,KAAK;AAC7B,QAAM,MAAM,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI;AAC/E,QAAM,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,IAAI;AACjE,QAAM,OAAO,EAAE,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI;AAChE,QAAM,OAAO,EAAE,QAAQ;AACvB,QAAM,MAAM,EAAE,GAAG,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,GAAG,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;AAC/E,QAAM,QAAqB,CAAC,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,IAAI,KAAK,GAAG,IAAI,KAAK,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,MAAM,OAAO,EAAE,CAAC,CAAC;AACxH,MAAI,EAAE,OAAO;AAEX,UAAM,IAAI,kBAAkB,EAAE,OAAO,EAAE,IAAI;AAC3C,UAAM,KAAK,SAAS,SAAS,IAAI,IAAI,IAAI,IAAI,SAAS,UAAU,IAAI,IAAI,IAAI,IAAI,IAAI;AACpF,UAAM,KAAK,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,MAAM,MAAM,OAAO,KAAK,CAAC,CAAC;AAC9F,UAAM;AAAA,MACJ,KAAK;AAAA,QACH,IAAI,GAAG,EAAE,EAAE;AAAA,QACX,GAAG,IAAI;AAAA,QACP,GAAG,IAAI;AAAA,QACP,MAAM,EAAE;AAAA,QACR,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,QAAQ,SAAS,SAAS,QAAQ,SAAS,UAAU,UAAU;AAAA,MACjE,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,EAAE,MAAM;AACjB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ArrayDiagram, ArrayOptions } from './array';
|
|
2
|
+
/** Bottom of the stack is index 0, drawn at the bottom. */
|
|
3
|
+
export declare function stack<T>(values: readonly T[], o?: ArrayOptions<T>): ArrayDiagram;
|
|
4
|
+
/** Front of the queue is index 0, on the left. Enqueue enters right, dequeue exits left. */
|
|
5
|
+
export declare function queue<T>(values: readonly T[], o?: ArrayOptions<T>): ArrayDiagram;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { V2 } from "../vec.js";
|
|
2
|
+
import { array } from "./array.js";
|
|
3
|
+
import { resolveTone } from "./common.js";
|
|
4
|
+
function stack(values, o = {}) {
|
|
5
|
+
const cell = o.cell ?? 44;
|
|
6
|
+
const gap = o.gap ?? 4;
|
|
7
|
+
const n = values.length;
|
|
8
|
+
const reversed = [...values].reverse();
|
|
9
|
+
const keys = (o.keys ?? values.map((_, i) => i)).slice().reverse();
|
|
10
|
+
const bottomY = o.y ?? 0;
|
|
11
|
+
const d = array(reversed, {
|
|
12
|
+
showIndices: false,
|
|
13
|
+
...o,
|
|
14
|
+
id: o.id ?? "stack",
|
|
15
|
+
cell,
|
|
16
|
+
gap,
|
|
17
|
+
keys,
|
|
18
|
+
vertical: true,
|
|
19
|
+
y: bottomY - n * (cell + gap) + gap,
|
|
20
|
+
tones: o.tones ? (i, v) => resolveTone(o.tones, n - 1 - i, v) : void 0,
|
|
21
|
+
enterOffset: V2(0, -cell * 1.5),
|
|
22
|
+
exitOffset: V2(0, -cell * 1.5)
|
|
23
|
+
});
|
|
24
|
+
const flip = (i) => n - 1 - i;
|
|
25
|
+
return {
|
|
26
|
+
...d,
|
|
27
|
+
center: (i) => d.center(flip(i)),
|
|
28
|
+
top: (i) => d.top(flip(i)),
|
|
29
|
+
bottom: (i) => d.bottom(flip(i)),
|
|
30
|
+
left: (i) => d.left(flip(i)),
|
|
31
|
+
right: (i) => d.right(flip(i))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function queue(values, o = {}) {
|
|
35
|
+
const cell = o.cell ?? 44;
|
|
36
|
+
return array(values, {
|
|
37
|
+
showIndices: false,
|
|
38
|
+
...o,
|
|
39
|
+
id: o.id ?? "queue",
|
|
40
|
+
cell,
|
|
41
|
+
enterOffset: V2(cell * 1.5, 0),
|
|
42
|
+
exitOffset: V2(-cell * 1.5, 0)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
queue,
|
|
47
|
+
stack
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=stackQueue.js.map
|