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
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/geom/shapes.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Geometry figure parts. These work directly in PIXELS (y down), the way a\n * compass-and-ruler figure is drawn, so a polygon's vertices are just your state.\n */\nimport { handle } from '../handle';\nimport { Scene, SceneNode, Tone, circle as circleNode, path, text } from '../scene';\nimport { V2, Vec, vadd, vnorm, vscale, vsub } from '../vec';\nimport { angleAt, arcPoints, centroid, toDegrees } from './math';\n\nconst pt = (v: Vec): [number, number] => [v.x, v.y];\n\nexport interface PolygonOptions {\n id?: string;\n tone?: Tone;\n /** Vertex names (A, B, C \u2026) placed just outside each corner. */\n labels?: string[];\n /** Give every vertex a drag handle; drag events carry `{ id, index }`. */\n draggable?: boolean;\n /** Fill the inside lightly. Default true. */\n fill?: boolean;\n width?: number;\n /** Pulse the drag handles until the learner touches one. */\n hint?: boolean;\n}\n\n/** A closed polygon with optional vertex labels and drag handles. */\nexport function polygon(points: readonly Vec[], o: PolygonOptions = {}): Scene {\n const id = o.id ?? 'poly';\n const nodes: SceneNode[] = [\n path({ id: `${id}:shape`, points: points.map(pt), closed: true, role: o.fill === false ? 'outline' : 'region', tone: o.tone, width: o.width ?? 1.75 }),\n path({ id: `${id}:edge`, points: points.map(pt), closed: true, role: 'curve', tone: o.tone, width: o.width ?? 1.75 }),\n ];\n const c = centroid(points);\n points.forEach((p, i) => {\n if (o.labels?.[i]) {\n const out = vadd(p, vscale(vnorm(vsub(p, c)), 16));\n nodes.push(text({ id: `${id}:label:${i}`, x: out.x, y: out.y, text: o.labels[i], size: 15, role: 'math' }));\n }\n nodes.push(...handle({ id: `${id}:v:${i}`, x: p.x, y: p.y, r: 5, draggable: !!o.draggable, hint: o.hint, tone: o.tone, data: { id, index: i } }));\n });\n return { nodes };\n}\n\nexport interface SegmentOptions {\n id: string;\n tone?: Tone;\n dashed?: boolean;\n width?: number;\n /** Equal-length tick marks (1, 2 or 3) across the middle. */\n ticks?: number;\n label?: string;\n}\n\n/** A segment, optionally with equal-length tick marks and a label beside its midpoint. */\nexport function segment(a: Vec, b: Vec, o: SegmentOptions): Scene {\n const nodes: SceneNode[] = [path({ id: o.id, points: [pt(a), pt(b)], role: 'curve', tone: o.tone, dashed: o.dashed, width: o.width ?? 1.75 })];\n if (o.ticks) nodes.push(...tickMarks(a, b, o.ticks, { id: `${o.id}:ticks`, tone: o.tone }).nodes);\n if (o.label) {\n const m = vscale(vadd(a, b), 0.5);\n const n = vnorm(V2(-(b.y - a.y), b.x - a.x));\n nodes.push(text({ id: `${o.id}:label`, x: m.x + n.x * 14, y: m.y + n.y * 14, text: o.label, size: 13, role: 'math', tone: o.tone }));\n }\n return { nodes };\n}\n\n/** The little hash marks that say \"these sides are equal\". */\nexport function tickMarks(a: Vec, b: Vec, count: number, o: { id: string; tone?: Tone; size?: number }): Scene {\n const m = vscale(vadd(a, b), 0.5);\n const d = vnorm(vsub(b, a));\n const n = V2(-d.y, d.x);\n const s = o.size ?? 6, gap = 4;\n return {\n nodes: Array.from({ length: count }, (_, i) => {\n const c = vadd(m, vscale(d, (i - (count - 1) / 2) * gap));\n return path({ id: `${o.id}:${i}`, points: [pt(vadd(c, vscale(n, -s))), pt(vadd(c, vscale(n, s)))], role: 'curve', tone: o.tone, width: 1.5 });\n }),\n };\n}\n\nexport interface AngleMarkOptions {\n id: string;\n r?: number;\n tone?: Tone;\n /** Text at the arc, e.g. '\u03B1', or true to show the measure in degrees. */\n label?: string | boolean;\n /** Draw a square for (near) right angles. Default true. */\n rightSquare?: boolean;\n /** How many arcs (1\u20133) to mark equal angles. Default 1. */\n arcs?: number;\n}\n\n/**\n * Mark the angle ABC at vertex B: an arc between rays BA and BC (always the\n * smaller side), or a little square when the angle is 90\u00B0. The arc and the\n * square have the same number of points so they morph into each other as you\n * drag through 90\u00B0.\n */\nexport function angleMark(a: Vec, b: Vec, c: Vec, o: AngleMarkOptions): Scene {\n const r = o.r ?? 22;\n const ang = angleAt(a, b, c);\n const a0 = Math.atan2(a.y - b.y, a.x - b.x);\n let a1 = Math.atan2(c.y - b.y, c.x - b.x);\n // sweep the short way round\n let delta = a1 - a0;\n while (delta > Math.PI) delta -= 2 * Math.PI;\n while (delta < -Math.PI) delta += 2 * Math.PI;\n a1 = a0 + delta;\n const right = (o.rightSquare ?? true) && Math.abs(toDegrees(ang) - 90) < 0.5;\n const nodes: SceneNode[] = [];\n const N = 24;\n const arcs = o.arcs ?? 1;\n for (let k = 0; k < arcs; k++) {\n const rr = r + k * 4;\n let pts: Array<[number, number]>;\n if (right) {\n const s = rr * 0.7;\n const u = V2(Math.cos(a0), Math.sin(a0)), v = V2(Math.cos(a1), Math.sin(a1));\n const p1 = vadd(b, vscale(u, s)), p2 = vadd(b, vadd(vscale(u, s), vscale(v, s))), p3 = vadd(b, vscale(v, s));\n // 3 straight legs resampled to N points so it tweens with the arc\n pts = Array.from({ length: N }, (_, i) => {\n const t = (i / (N - 1)) * 2;\n const q = t < 1 ? vadd(p1, vscale(vsub(p2, p1), t)) : vadd(p2, vscale(vsub(p3, p2), t - 1));\n return pt(q);\n });\n } else pts = arcPoints(b, rr, a0, a1, N);\n nodes.push(path({ id: `${o.id}:arc:${k}`, points: pts, role: 'curve', tone: o.tone, width: 1.5 }));\n }\n // a light wedge fill behind the arc\n nodes.unshift(path({ id: `${o.id}:wedge`, points: [pt(b), ...arcPoints(b, r, a0, a1, N)], closed: true, role: 'region', tone: o.tone }));\n if (o.label) {\n const mid = (a0 + a1) / 2;\n const lr = r + 12 + (arcs - 1) * 4;\n const s = o.label === true ? `${Math.round(toDegrees(ang))}\u00B0` : o.label;\n nodes.push(text({ id: `${o.id}:label`, x: b.x + lr * Math.cos(mid), y: b.y + lr * Math.sin(mid), text: s, size: 14, role: 'math', tone: o.tone }));\n }\n return { nodes };\n}\n\n/** A circle outline (and light fill) with an optional centre dot. */\nexport function circle(center: Vec, r: number, o: { id: string; tone?: Tone; dashed?: boolean; fill?: boolean; showCenter?: boolean }): Scene {\n const nodes: SceneNode[] = [\n circleNode({ id: o.id, x: center.x, y: center.y, r, tone: o.tone ?? 'ghost', dashed: o.dashed, role: o.fill ? undefined : 'outline' }),\n ];\n if (o.showCenter) nodes.push(circleNode({ id: `${o.id}:c`, x: center.x, y: center.y, r: 2.5, role: 'dot', tone: o.tone }));\n return { nodes };\n}\n\n/** A labelled point. */\nexport function pointLabel(p: Vec, s: string, o: { id: string; tone?: Tone; offset?: Vec; draggable?: boolean; hint?: boolean; data?: unknown }): Scene {\n const off = o.offset ?? V2(10, -12);\n return {\n nodes: [\n ...handle({ id: o.id, x: p.x, y: p.y, r: 4.5, tone: o.tone, draggable: !!o.draggable, hint: o.hint, data: o.data ?? { id: o.id } }),\n text({ id: `${o.id}:label`, x: p.x + off.x, y: p.y + off.y, text: s, size: 14, role: 'math', tone: o.tone }),\n ],\n };\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/handle.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A draggable handle: the dot you see, plus a larger invisible circle that
|
|
3
|
+
* actually receives the pointer. Small targets are the #1 reason drag
|
|
4
|
+
* interactions feel bad (and on touch screens a 5px dot is nearly impossible).
|
|
5
|
+
*
|
|
6
|
+
* The hit circle comes FIRST so CSS can say "when the hit area is hovered,
|
|
7
|
+
* grow the dot that follows it" (`.dv-role-hit:hover + .dv-role-handle`).
|
|
8
|
+
* Drag events report the hit circle's id and `data`, so put what you need there.
|
|
9
|
+
*/
|
|
10
|
+
import { CircleNode, Tone } from './scene';
|
|
11
|
+
export interface HandleOptions {
|
|
12
|
+
id: string;
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
/** Visible radius. Default 6. */
|
|
16
|
+
r?: number;
|
|
17
|
+
/** Hit radius. Default 16. */
|
|
18
|
+
hit?: number;
|
|
19
|
+
tone?: Tone;
|
|
20
|
+
data?: unknown;
|
|
21
|
+
/** Set false to show a dot that can't be dragged. */
|
|
22
|
+
draggable?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Pulse a ring around the handle: "you can drag me". Turn it off once the
|
|
25
|
+
* learner has touched it (that's what Brilliant does, and it's why people
|
|
26
|
+
* find the interaction without being told).
|
|
27
|
+
*/
|
|
28
|
+
hint?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function handle(o: HandleOptions): CircleNode[];
|
package/dist/handle.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { circle } from "./scene.js";
|
|
2
|
+
function handle(o) {
|
|
3
|
+
const draggable = o.draggable ?? true;
|
|
4
|
+
const dot = circle({ id: `${o.id}:dot`, x: o.x, y: o.y, r: o.r ?? 6, role: "handle", tone: o.tone });
|
|
5
|
+
if (!draggable) return [dot];
|
|
6
|
+
const out = [circle({ id: o.id, x: o.x, y: o.y, r: o.hit ?? 16, role: "hit", draggable: true, data: o.data }), dot];
|
|
7
|
+
if (o.hint) out.unshift(circle({ id: `${o.id}:hint`, x: o.x, y: o.y, r: (o.r ?? 6) + 2, role: "pulse", tone: o.tone }));
|
|
8
|
+
return out;
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
handle
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=handle.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/handle.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * A draggable handle: the dot you see, plus a larger invisible circle that\n * actually receives the pointer. Small targets are the #1 reason drag\n * interactions feel bad (and on touch screens a 5px dot is nearly impossible).\n *\n * The hit circle comes FIRST so CSS can say \"when the hit area is hovered,\n * grow the dot that follows it\" (`.dv-role-hit:hover + .dv-role-handle`).\n * Drag events report the hit circle's id and `data`, so put what you need there.\n */\nimport { CircleNode, Tone, circle } from './scene';\n\nexport interface HandleOptions {\n id: string;\n x: number;\n y: number;\n /** Visible radius. Default 6. */\n r?: number;\n /** Hit radius. Default 16. */\n hit?: number;\n tone?: Tone;\n data?: unknown;\n /** Set false to show a dot that can't be dragged. */\n draggable?: boolean;\n /**\n * Pulse a ring around the handle: \"you can drag me\". Turn it off once the\n * learner has touched it (that's what Brilliant does, and it's why people\n * find the interaction without being told).\n */\n hint?: boolean;\n}\n\nexport function handle(o: HandleOptions): CircleNode[] {\n const draggable = o.draggable ?? true;\n const dot = circle({ id: `${o.id}:dot`, x: o.x, y: o.y, r: o.r ?? 6, role: 'handle', tone: o.tone });\n if (!draggable) return [dot];\n const out = [circle({ id: o.id, x: o.x, y: o.y, r: o.hit ?? 16, role: 'hit', draggable: true, data: o.data }), dot];\n // the pulse ring goes first so it sits underneath\n if (o.hint) out.unshift(circle({ id: `${o.id}:hint`, x: o.x, y: o.y, r: (o.r ?? 6) + 2, role: 'pulse', tone: o.tone }));\n return out;\n}\n"],
|
|
5
|
+
"mappings": "AASA,SAA2B,cAAc;AAsBlC,SAAS,OAAO,GAAgC;AACrD,QAAM,YAAY,EAAE,aAAa;AACjC,QAAM,MAAM,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,KAAK,GAAG,MAAM,UAAU,MAAM,EAAE,KAAK,CAAC;AACnG,MAAI,CAAC,UAAW,QAAO,CAAC,GAAG;AAC3B,QAAM,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,OAAO,IAAI,MAAM,OAAO,WAAW,MAAM,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG;AAElH,MAAI,EAAE,KAAM,KAAI,QAAQ,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,KAAK,GAAG,MAAM,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC;AACtH,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsviz — interactive, animated SVG teaching diagrams.
|
|
3
|
+
*
|
|
4
|
+
* state ──▶ layout functions ──▶ Scene (plain data, stable ids) ──▶ renderer
|
|
5
|
+
*
|
|
6
|
+
* Core (primitives, renderer, player) and the data-structure layouts are
|
|
7
|
+
* exported at the top level. The other families are namespaces, because
|
|
8
|
+
* several of them have a `segment` or `circle` and the namespace says which:
|
|
9
|
+
*
|
|
10
|
+
* import { plot, prob, geom, sched, mem, uml, sys } from 'dsviz';
|
|
11
|
+
* plot.fn(p, Math.sin) geom.angleMark(A, B, C, …) sched.simulate(procs, 'rr')
|
|
12
|
+
*/
|
|
13
|
+
export * from './vec';
|
|
14
|
+
export * from './easing';
|
|
15
|
+
export * from './themes';
|
|
16
|
+
export * from './scene';
|
|
17
|
+
export * from './path';
|
|
18
|
+
export * from './mathtext';
|
|
19
|
+
export * from './handle';
|
|
20
|
+
export * from './fx';
|
|
21
|
+
export * from './describe';
|
|
22
|
+
export * from './styles';
|
|
23
|
+
export * from './render';
|
|
24
|
+
export * from './camera';
|
|
25
|
+
export * from './connector';
|
|
26
|
+
export * from './static';
|
|
27
|
+
export * from './player';
|
|
28
|
+
export * from './ds';
|
|
29
|
+
export * as ds from './ds';
|
|
30
|
+
export * as plot from './plot';
|
|
31
|
+
export * as prob from './prob';
|
|
32
|
+
export * as geom from './geom';
|
|
33
|
+
export * as sched from './sched';
|
|
34
|
+
export * as mem from './mem';
|
|
35
|
+
export * as uml from './uml';
|
|
36
|
+
export * as sys from './sys';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export * from "./vec.js";
|
|
2
|
+
export * from "./easing.js";
|
|
3
|
+
export * from "./themes.js";
|
|
4
|
+
export * from "./scene.js";
|
|
5
|
+
export * from "./path.js";
|
|
6
|
+
export * from "./mathtext.js";
|
|
7
|
+
export * from "./handle.js";
|
|
8
|
+
export * from "./fx.js";
|
|
9
|
+
export * from "./describe.js";
|
|
10
|
+
export * from "./styles.js";
|
|
11
|
+
export * from "./render.js";
|
|
12
|
+
export * from "./camera.js";
|
|
13
|
+
export * from "./connector.js";
|
|
14
|
+
export * from "./static.js";
|
|
15
|
+
export * from "./player.js";
|
|
16
|
+
export * from "./ds/index.js";
|
|
17
|
+
import * as ds from "./ds/index.js";
|
|
18
|
+
import * as plot from "./plot/index.js";
|
|
19
|
+
import * as prob from "./prob/index.js";
|
|
20
|
+
import * as geom from "./geom/index.js";
|
|
21
|
+
import * as sched from "./sched/index.js";
|
|
22
|
+
import * as mem from "./mem/index.js";
|
|
23
|
+
import * as uml from "./uml/index.js";
|
|
24
|
+
import * as sys from "./sys/index.js";
|
|
25
|
+
export {
|
|
26
|
+
ds,
|
|
27
|
+
geom,
|
|
28
|
+
mem,
|
|
29
|
+
plot,
|
|
30
|
+
prob,
|
|
31
|
+
sched,
|
|
32
|
+
sys,
|
|
33
|
+
uml
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * dsviz \u2014 interactive, animated SVG teaching diagrams.\n *\n * state \u2500\u2500\u25B6 layout functions \u2500\u2500\u25B6 Scene (plain data, stable ids) \u2500\u2500\u25B6 renderer\n *\n * Core (primitives, renderer, player) and the data-structure layouts are\n * exported at the top level. The other families are namespaces, because\n * several of them have a `segment` or `circle` and the namespace says which:\n *\n * import { plot, prob, geom, sched, mem, uml, sys } from 'dsviz';\n * plot.fn(p, Math.sin) geom.angleMark(A, B, C, \u2026) sched.simulate(procs, 'rr')\n */\nexport * from './vec';\nexport * from './easing';\nexport * from './themes';\nexport * from './scene';\nexport * from './path';\nexport * from './mathtext';\nexport * from './handle';\nexport * from './fx';\nexport * from './describe';\nexport * from './styles';\nexport * from './render';\nexport * from './camera';\nexport * from './connector';\nexport * from './static';\nexport * from './player';\nexport * from './ds';\n\nexport * as ds from './ds';\nexport * as plot from './plot';\nexport * as prob from './prob';\nexport * as geom from './geom';\nexport * as sched from './sched';\nexport * as mem from './mem';\nexport * as uml from './uml';\nexport * as sys from './sys';\n"],
|
|
5
|
+
"mappings": "AAYA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAEd,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,YAAY,WAAW;AACvB,YAAY,SAAS;AACrB,YAAY,SAAS;AACrB,YAAY,SAAS;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TeX-lite: just enough maths typesetting for diagram labels, no KaTeX needed.
|
|
3
|
+
*
|
|
4
|
+
* 'a^2 + b^2 = c^2' 'f′(x)' 'P(A \cap B)' 'x_{i+1}' '\theta = 90°'
|
|
5
|
+
*
|
|
6
|
+
* The conventions are the ones every maths textbook uses:
|
|
7
|
+
* - single-letter variables are italic a x θ
|
|
8
|
+
* - numbers, operators, brackets are upright 2 + = (
|
|
9
|
+
* - named functions are upright words sin log max
|
|
10
|
+
* - binary operators get a little space either side, and '-' becomes a real minus '−'
|
|
11
|
+
* - ^ and _ make super- and subscripts, {…} groups them
|
|
12
|
+
*
|
|
13
|
+
* parseMath() turns the string into spans; describe() turns spans into SVG
|
|
14
|
+
* <tspan>s, and <MathText> in dsviz/react turns the same spans into HTML.
|
|
15
|
+
*/
|
|
16
|
+
export interface MathSpan {
|
|
17
|
+
text: string;
|
|
18
|
+
italic: boolean;
|
|
19
|
+
/** 1 = superscript, -1 = subscript. */
|
|
20
|
+
shift?: 1 | -1;
|
|
21
|
+
}
|
|
22
|
+
export declare function parseMath(src: string): MathSpan[];
|
|
23
|
+
/** Approximate width in px, for bounds (sub/superscripts are smaller). */
|
|
24
|
+
export declare function mathWidth(spans: MathSpan[], size: number): number;
|
|
25
|
+
/**
|
|
26
|
+
* SVG markup for the spans, as <tspan>s. Super/subscripts move with `dy` in px
|
|
27
|
+
* (not baseline-shift, which Firefox ignores), and the next span moves back.
|
|
28
|
+
*/
|
|
29
|
+
export declare function mathTspans(spans: MathSpan[], size: number): string;
|
package/dist/mathtext.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const FUNCTIONS = /* @__PURE__ */ new Set(["sin", "cos", "tan", "log", "ln", "exp", "max", "min", "lim", "det", "gcd", "mod", "proj", "arg", "Pr", "E", "Var"]);
|
|
2
|
+
const COMMANDS = {
|
|
3
|
+
theta: "\u03B8",
|
|
4
|
+
alpha: "\u03B1",
|
|
5
|
+
beta: "\u03B2",
|
|
6
|
+
gamma: "\u03B3",
|
|
7
|
+
delta: "\u03B4",
|
|
8
|
+
epsilon: "\u03B5",
|
|
9
|
+
lambda: "\u03BB",
|
|
10
|
+
mu: "\u03BC",
|
|
11
|
+
pi: "\u03C0",
|
|
12
|
+
sigma: "\u03C3",
|
|
13
|
+
phi: "\u03C6",
|
|
14
|
+
omega: "\u03C9",
|
|
15
|
+
Delta: "\u0394",
|
|
16
|
+
Sigma: "\u03A3",
|
|
17
|
+
Omega: "\u03A9",
|
|
18
|
+
cap: "\u2229",
|
|
19
|
+
cup: "\u222A",
|
|
20
|
+
setminus: "\u2212",
|
|
21
|
+
triangle: "\u25B3",
|
|
22
|
+
cdot: "\xB7",
|
|
23
|
+
times: "\xD7",
|
|
24
|
+
le: "\u2264",
|
|
25
|
+
ge: "\u2265",
|
|
26
|
+
ne: "\u2260",
|
|
27
|
+
approx: "\u2248",
|
|
28
|
+
to: "\u2192",
|
|
29
|
+
in: "\u2208",
|
|
30
|
+
infty: "\u221E",
|
|
31
|
+
pm: "\xB1",
|
|
32
|
+
angle: "\u2220",
|
|
33
|
+
circ: "\xB0",
|
|
34
|
+
prime: "\u2032",
|
|
35
|
+
mid: "\u2223",
|
|
36
|
+
ldots: "\u2026",
|
|
37
|
+
sqrt: "\u221A",
|
|
38
|
+
int: "\u222B",
|
|
39
|
+
sum: "\u2211"
|
|
40
|
+
};
|
|
41
|
+
const RELATIONS = /* @__PURE__ */ new Set(["=", "<", ">", "\u2264", "\u2265", "\u2260", "\u2248", "\u2192", "\u2208", "\u2223"]);
|
|
42
|
+
const BINARY = /* @__PURE__ */ new Set([...RELATIONS, "+", "\u2212", "\xD7", "\xB7", "\u2229", "\u222A", "\u25B3", "\xB1"]);
|
|
43
|
+
const GREEK_UPRIGHT = /[ΔΣΩ∑∫√]/;
|
|
44
|
+
const MEDIUM = "\u205F";
|
|
45
|
+
const THICK = "\u2005";
|
|
46
|
+
function parseMath(src) {
|
|
47
|
+
const out = [];
|
|
48
|
+
const push = (text, italic, shift) => {
|
|
49
|
+
const last = out[out.length - 1];
|
|
50
|
+
if (last && last.italic === italic && last.shift === shift) last.text += text;
|
|
51
|
+
else out.push({ text, italic, ...shift ? { shift } : {} });
|
|
52
|
+
};
|
|
53
|
+
const walk = (s, shift) => {
|
|
54
|
+
let i = 0;
|
|
55
|
+
while (i < s.length) {
|
|
56
|
+
const ch = s[i];
|
|
57
|
+
if (ch === " ") {
|
|
58
|
+
i++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if ((ch === "^" || ch === "_") && i + 1 < s.length) {
|
|
62
|
+
const sh = ch === "^" ? 1 : -1;
|
|
63
|
+
if (s[i + 1] === "{") {
|
|
64
|
+
const end = matchBrace(s, i + 1);
|
|
65
|
+
walk(s.slice(i + 2, end), sh);
|
|
66
|
+
i = end + 1;
|
|
67
|
+
} else {
|
|
68
|
+
walk(s[i + 1], sh);
|
|
69
|
+
i += 2;
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (ch === "\\") {
|
|
74
|
+
const m = /^\\([A-Za-z]+)/.exec(s.slice(i));
|
|
75
|
+
if (m) {
|
|
76
|
+
if (m[1] === "text" && s[i + 5] === "{") {
|
|
77
|
+
const end = matchBrace(s, i + 5);
|
|
78
|
+
push(s.slice(i + 6, end), false, shift);
|
|
79
|
+
i = end + 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const sym = COMMANDS[m[1]] ?? m[1];
|
|
83
|
+
emitSymbol(sym, shift);
|
|
84
|
+
i += m[0].length;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (/[A-Za-z]/.test(ch)) {
|
|
89
|
+
const word = /^[A-Za-z]+/.exec(s.slice(i))[0];
|
|
90
|
+
if (FUNCTIONS.has(word)) push(word, false, shift);
|
|
91
|
+
else push(word, true, shift);
|
|
92
|
+
i += word.length;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
emitSymbol(ch === "-" ? "\u2212" : ch === "*" ? "\xB7" : ch === "'" ? "\u2032" : ch, shift);
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
function emitSymbol(sym, shift) {
|
|
100
|
+
if (BINARY.has(sym) && !shift) {
|
|
101
|
+
const prev = out[out.length - 1]?.text.trimEnd().slice(-1);
|
|
102
|
+
const unary = sym === "\u2212" && (!prev || BINARY.has(prev) || "([{,".includes(prev));
|
|
103
|
+
const sp = RELATIONS.has(sym) ? THICK : MEDIUM;
|
|
104
|
+
push(unary ? sym : `${sp}${sym}${sp}`, false, shift);
|
|
105
|
+
} else if (/^[α-ωϑϕ]$/.test(sym)) push(sym, true, shift);
|
|
106
|
+
else push(sym, !GREEK_UPRIGHT.test(sym) && /^\p{L}$/u.test(sym), shift);
|
|
107
|
+
}
|
|
108
|
+
walk(src);
|
|
109
|
+
if (out.length) out[0].text = out[0].text.trimStart(), out[out.length - 1].text = out[out.length - 1].text.trimEnd();
|
|
110
|
+
return out.filter((s) => s.text);
|
|
111
|
+
}
|
|
112
|
+
function matchBrace(s, open) {
|
|
113
|
+
let depth = 0;
|
|
114
|
+
for (let i = open; i < s.length; i++) {
|
|
115
|
+
if (s[i] === "{") depth++;
|
|
116
|
+
else if (s[i] === "}" && --depth === 0) return i;
|
|
117
|
+
}
|
|
118
|
+
return s.length;
|
|
119
|
+
}
|
|
120
|
+
function mathWidth(spans, size) {
|
|
121
|
+
return spans.reduce((w, s) => w + [...s.text].length * size * (s.shift ? 0.36 : 0.5), 0);
|
|
122
|
+
}
|
|
123
|
+
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
124
|
+
function mathTspans(spans, size) {
|
|
125
|
+
let out = "", level = 0;
|
|
126
|
+
for (const s of spans) {
|
|
127
|
+
const target = s.shift ? s.shift * size * 0.42 : 0;
|
|
128
|
+
const dy = level - target;
|
|
129
|
+
level = target;
|
|
130
|
+
const attrs = [s.italic ? 'font-style="italic"' : 'font-style="normal"'];
|
|
131
|
+
if (s.shift) attrs.push(`font-size="${Math.round(size * 0.68 * 10) / 10}"`);
|
|
132
|
+
if (Math.abs(dy) > 0.01) attrs.push(`dy="${Math.round(dy * 100) / 100}"`);
|
|
133
|
+
out += `<tspan ${attrs.join(" ")}>${esc(s.text)}</tspan>`;
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
mathTspans,
|
|
139
|
+
mathWidth,
|
|
140
|
+
parseMath
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=mathtext.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/mathtext.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * TeX-lite: just enough maths typesetting for diagram labels, no KaTeX needed.\n *\n * 'a^2 + b^2 = c^2' 'f\u2032(x)' 'P(A \\cap B)' 'x_{i+1}' '\\theta = 90\u00B0'\n *\n * The conventions are the ones every maths textbook uses:\n * - single-letter variables are italic a x \u03B8\n * - numbers, operators, brackets are upright 2 + = (\n * - named functions are upright words sin log max\n * - binary operators get a little space either side, and '-' becomes a real minus '\u2212'\n * - ^ and _ make super- and subscripts, {\u2026} groups them\n *\n * parseMath() turns the string into spans; describe() turns spans into SVG\n * <tspan>s, and <MathText> in dsviz/react turns the same spans into HTML.\n */\nexport interface MathSpan {\n text: string;\n italic: boolean;\n /** 1 = superscript, -1 = subscript. */\n shift?: 1 | -1;\n}\n\nconst FUNCTIONS = new Set(['sin', 'cos', 'tan', 'log', 'ln', 'exp', 'max', 'min', 'lim', 'det', 'gcd', 'mod', 'proj', 'arg', 'Pr', 'E', 'Var']);\nconst COMMANDS: Record<string, string> = {\n theta: '\u03B8', alpha: '\u03B1', beta: '\u03B2', gamma: '\u03B3', delta: '\u03B4', epsilon: '\u03B5', lambda: '\u03BB', mu: '\u03BC', pi: '\u03C0', sigma: '\u03C3', phi: '\u03C6', omega: '\u03C9',\n Delta: '\u0394', Sigma: '\u03A3', Omega: '\u03A9', cap: '\u2229', cup: '\u222A', setminus: '\u2212', triangle: '\u25B3', cdot: '\u00B7', times: '\u00D7', le: '\u2264', ge: '\u2265', ne: '\u2260',\n approx: '\u2248', to: '\u2192', in: '\u2208', infty: '\u221E', pm: '\u00B1', angle: '\u2220', circ: '\u00B0', prime: '\u2032', mid: '\u2223', ldots: '\u2026', sqrt: '\u221A', int: '\u222B', sum: '\u2211',\n};\n// TeX's spacing classes: binary operators get a medium space (4/18 em) either\n// side, relations a thick one (5/18 em), which is why \"a + b = c\" reads as\n// (a + b) = (c) at a glance.\nconst RELATIONS = new Set(['=', '<', '>', '\u2264', '\u2265', '\u2260', '\u2248', '\u2192', '\u2208', '\u2223']);\nconst BINARY = new Set([...RELATIONS, '+', '\u2212', '\u00D7', '\u00B7', '\u2229', '\u222A', '\u25B3', '\u00B1']);\nconst GREEK_UPRIGHT = /[\u0394\u03A3\u03A9\u2211\u222B\u221A]/; // capital Greek and big operators stay upright\nconst MEDIUM = '\\u205f'; // medium mathematical space, 4/18 em\nconst THICK = '\\u2005'; // four-per-em space, close to TeX's 5/18 em\n\nexport function parseMath(src: string): MathSpan[] {\n const out: MathSpan[] = [];\n const push = (text: string, italic: boolean, shift?: 1 | -1) => {\n const last = out[out.length - 1];\n if (last && last.italic === italic && last.shift === shift) last.text += text;\n else out.push({ text, italic, ...(shift ? { shift } : {}) });\n };\n\n const walk = (s: string, shift?: 1 | -1) => {\n let i = 0;\n while (i < s.length) {\n const ch = s[i];\n if (ch === ' ') { i++; continue; } // spacing is ours to decide\n if ((ch === '^' || ch === '_') && i + 1 < s.length) {\n const sh = ch === '^' ? 1 : -1;\n if (s[i + 1] === '{') {\n const end = matchBrace(s, i + 1);\n walk(s.slice(i + 2, end), sh);\n i = end + 1;\n } else {\n walk(s[i + 1], sh);\n i += 2;\n }\n continue;\n }\n if (ch === '\\\\') {\n const m = /^\\\\([A-Za-z]+)/.exec(s.slice(i));\n if (m) {\n if (m[1] === 'text' && s[i + 5] === '{') {\n const end = matchBrace(s, i + 5);\n push(s.slice(i + 6, end), false, shift);\n i = end + 1;\n continue;\n }\n const sym = COMMANDS[m[1]] ?? m[1];\n emitSymbol(sym, shift);\n i += m[0].length;\n continue;\n }\n }\n if (/[A-Za-z]/.test(ch)) {\n const word = /^[A-Za-z]+/.exec(s.slice(i))![0];\n if (FUNCTIONS.has(word)) push(word, false, shift);\n else push(word, true, shift); // \"ab\" is a\u00B7b: every letter italic\n i += word.length;\n continue;\n }\n emitSymbol(ch === '-' ? '\u2212' : ch === '*' ? '\u00B7' : ch === \"'\" ? '\u2032' : ch, shift);\n i++;\n }\n };\n\n function emitSymbol(sym: string, shift?: 1 | -1) {\n if (BINARY.has(sym) && !shift) {\n // unary minus at the start or after an operator / open bracket: no spaces\n const prev = out[out.length - 1]?.text.trimEnd().slice(-1);\n const unary = sym === '\u2212' && (!prev || BINARY.has(prev) || '([{,'.includes(prev));\n const sp = RELATIONS.has(sym) ? THICK : MEDIUM;\n push(unary ? sym : `${sp}${sym}${sp}`, false, shift);\n } else if (/^[\u03B1-\u03C9\u03D1\u03D5]$/.test(sym)) push(sym, true, shift);\n else push(sym, !GREEK_UPRIGHT.test(sym) && /^\\p{L}$/u.test(sym), shift);\n }\n\n walk(src);\n // an operator at either end has nothing to be spaced from (\"P =\" then a number in HTML)\n if (out.length) out[0].text = out[0].text.trimStart(), (out[out.length - 1].text = out[out.length - 1].text.trimEnd());\n return out.filter((s) => s.text);\n}\n\nfunction matchBrace(s: string, open: number): number {\n let depth = 0;\n for (let i = open; i < s.length; i++) {\n if (s[i] === '{') depth++;\n else if (s[i] === '}' && --depth === 0) return i;\n }\n return s.length;\n}\n\n/** Approximate width in px, for bounds (sub/superscripts are smaller). */\nexport function mathWidth(spans: MathSpan[], size: number): number {\n return spans.reduce((w, s) => w + [...s.text].length * size * (s.shift ? 0.36 : 0.5), 0);\n}\n\nconst esc = (s: string) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n\n/**\n * SVG markup for the spans, as <tspan>s. Super/subscripts move with `dy` in px\n * (not baseline-shift, which Firefox ignores), and the next span moves back.\n */\nexport function mathTspans(spans: MathSpan[], size: number): string {\n let out = '', level = 0;\n for (const s of spans) {\n const target = s.shift ? s.shift * size * 0.42 : 0;\n const dy = level - target; // y grows downward: up = negative dy\n level = target;\n const attrs = [s.italic ? 'font-style=\"italic\"' : 'font-style=\"normal\"'];\n if (s.shift) attrs.push(`font-size=\"${Math.round(size * 0.68 * 10) / 10}\"`);\n if (Math.abs(dy) > 0.01) attrs.push(`dy=\"${Math.round(dy * 100) / 100}\"`);\n out += `<tspan ${attrs.join(' ')}>${esc(s.text)}</tspan>`;\n }\n return out;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw memory: a strip of bytes with hex addresses, and typed values laid over
|
|
3
|
+
* them. This is the picture behind pointer arithmetic: `p + 1` on an `int*`
|
|
4
|
+
* moves 4 bytes, on a `char*` 1 byte, on a `double*` 8, because the compiler
|
|
5
|
+
* multiplies by the size of what `p` points at.
|
|
6
|
+
*/
|
|
7
|
+
import { Scene, Tone } from '../scene';
|
|
8
|
+
import { Vec } from '../vec';
|
|
9
|
+
export interface MemoryGroup {
|
|
10
|
+
/** Stable identity (e.g. 'a[2]'): resizing a group tweens it. */
|
|
11
|
+
key: string;
|
|
12
|
+
/** Byte offset from `base`. */
|
|
13
|
+
offset: number;
|
|
14
|
+
/** Size in bytes. */
|
|
15
|
+
size: number;
|
|
16
|
+
value?: string;
|
|
17
|
+
tone?: Tone;
|
|
18
|
+
/** Small label above the group, e.g. 'a[2]'. */
|
|
19
|
+
label?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface MemoryOptions {
|
|
22
|
+
id?: string;
|
|
23
|
+
x?: number;
|
|
24
|
+
y?: number;
|
|
25
|
+
/** Address of byte 0. */
|
|
26
|
+
base?: number;
|
|
27
|
+
/** Total bytes in the strip. */
|
|
28
|
+
bytes: number;
|
|
29
|
+
/** Pixels per byte. Default 14. */
|
|
30
|
+
byteWidth?: number;
|
|
31
|
+
height?: number;
|
|
32
|
+
groups?: readonly MemoryGroup[];
|
|
33
|
+
/** Hex digits to show in addresses. Default 4 (the low digits). */
|
|
34
|
+
digits?: number;
|
|
35
|
+
/** Address under every Nth byte as well as at each group start. Default: groups only. */
|
|
36
|
+
addressEvery?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface MemoryDiagram extends Scene {
|
|
39
|
+
xAt(offset: number): number;
|
|
40
|
+
/** Top-centre of a group (for pointer arrows). */
|
|
41
|
+
top(key: string): Vec;
|
|
42
|
+
bottom(key: string): Vec;
|
|
43
|
+
/** Top of an arbitrary byte. */
|
|
44
|
+
byteTop(offset: number): Vec;
|
|
45
|
+
}
|
|
46
|
+
export declare const hex: (n: number, digits?: number) => string;
|
|
47
|
+
export declare function memory(o: MemoryOptions): MemoryDiagram;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { path, rect, text } from "../scene.js";
|
|
2
|
+
import { V2 } from "../vec.js";
|
|
3
|
+
const hex = (n, digits = 4) => "0x" + (n >>> 0).toString(16).padStart(digits, "0").slice(-digits);
|
|
4
|
+
function memory(o) {
|
|
5
|
+
const id = o.id ?? "memory";
|
|
6
|
+
const x0 = o.x ?? 0, y0 = o.y ?? 0;
|
|
7
|
+
const bw = o.byteWidth ?? 14, h = o.height ?? 34;
|
|
8
|
+
const base = o.base ?? 4096;
|
|
9
|
+
const digits = o.digits ?? 4;
|
|
10
|
+
const xAt = (off) => x0 + off * bw;
|
|
11
|
+
const nodes = [];
|
|
12
|
+
nodes.push(rect({ id: `${id}:strip`, x: xAt(o.bytes / 2), y: y0 + h / 2, w: o.bytes * bw, h, rx: 3, role: "track" }));
|
|
13
|
+
for (let b = 1; b < o.bytes; b++) nodes.push(path({ id: `${id}:byte:${b}`, points: [[xAt(b), y0 + h - 6], [xAt(b), y0 + h]], role: "tick", width: 1 }));
|
|
14
|
+
const groups = /* @__PURE__ */ new Map();
|
|
15
|
+
const addrAt = /* @__PURE__ */ new Set();
|
|
16
|
+
for (const g2 of o.groups ?? []) {
|
|
17
|
+
groups.set(g2.key, g2);
|
|
18
|
+
addrAt.add(g2.offset);
|
|
19
|
+
const cx = xAt(g2.offset + g2.size / 2);
|
|
20
|
+
nodes.push(rect({ id: `${id}:g:${g2.key}`, x: cx, y: y0 + h / 2, w: g2.size * bw - 2, h: h - 4, rx: 4, tone: g2.tone }));
|
|
21
|
+
if (g2.value !== void 0) nodes.push(text({ id: `${id}:g:${g2.key}:v`, x: cx, y: y0 + h / 2, text: g2.value, size: 12, role: "value", tone: g2.tone }));
|
|
22
|
+
if (g2.label) nodes.push(text({ id: `${id}:g:${g2.key}:l`, x: cx, y: y0 - 10, text: g2.label, size: 11, role: "caption", tone: g2.tone }));
|
|
23
|
+
}
|
|
24
|
+
if (o.addressEvery) for (let b = 0; b <= o.bytes; b += o.addressEvery) addrAt.add(b);
|
|
25
|
+
for (const b of [...addrAt].sort((a, c) => a - c)) {
|
|
26
|
+
nodes.push(text({ id: `${id}:addr:${b}`, x: xAt(b), y: y0 + h + 12, text: hex(base + b, digits), size: 9, role: "index", anchor: "start" }));
|
|
27
|
+
}
|
|
28
|
+
const g = (k) => groups.get(k) ?? { offset: 0, size: 0 };
|
|
29
|
+
return {
|
|
30
|
+
nodes,
|
|
31
|
+
xAt,
|
|
32
|
+
top: (k) => V2(xAt(g(k).offset + g(k).size / 2), y0),
|
|
33
|
+
bottom: (k) => V2(xAt(g(k).offset + g(k).size / 2), y0 + h),
|
|
34
|
+
byteTop: (b) => V2(xAt(b), y0)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
hex,
|
|
39
|
+
memory
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/mem/memory.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Raw memory: a strip of bytes with hex addresses, and typed values laid over\n * them. This is the picture behind pointer arithmetic: `p + 1` on an `int*`\n * moves 4 bytes, on a `char*` 1 byte, on a `double*` 8, because the compiler\n * multiplies by the size of what `p` points at.\n */\nimport { Scene, SceneNode, Tone, path, rect, text } from '../scene';\nimport { V2, Vec } from '../vec';\n\nexport interface MemoryGroup {\n /** Stable identity (e.g. 'a[2]'): resizing a group tweens it. */\n key: string;\n /** Byte offset from `base`. */\n offset: number;\n /** Size in bytes. */\n size: number;\n value?: string;\n tone?: Tone;\n /** Small label above the group, e.g. 'a[2]'. */\n label?: string;\n}\n\nexport interface MemoryOptions {\n id?: string;\n x?: number;\n y?: number;\n /** Address of byte 0. */\n base?: number;\n /** Total bytes in the strip. */\n bytes: number;\n /** Pixels per byte. Default 14. */\n byteWidth?: number;\n height?: number;\n groups?: readonly MemoryGroup[];\n /** Hex digits to show in addresses. Default 4 (the low digits). */\n digits?: number;\n /** Address under every Nth byte as well as at each group start. Default: groups only. */\n addressEvery?: number;\n}\n\nexport interface MemoryDiagram extends Scene {\n xAt(offset: number): number;\n /** Top-centre of a group (for pointer arrows). */\n top(key: string): Vec;\n bottom(key: string): Vec;\n /** Top of an arbitrary byte. */\n byteTop(offset: number): Vec;\n}\n\nexport const hex = (n: number, digits = 4) => '0x' + (n >>> 0).toString(16).padStart(digits, '0').slice(-digits);\n\nexport function memory(o: MemoryOptions): MemoryDiagram {\n const id = o.id ?? 'memory';\n const x0 = o.x ?? 0, y0 = o.y ?? 0;\n const bw = o.byteWidth ?? 14, h = o.height ?? 34;\n const base = o.base ?? 0x1000;\n const digits = o.digits ?? 4;\n const xAt = (off: number) => x0 + off * bw;\n const nodes: SceneNode[] = [];\n // the byte strip: one hairline per byte boundary\n nodes.push(rect({ id: `${id}:strip`, x: xAt(o.bytes / 2), y: y0 + h / 2, w: o.bytes * bw, h, rx: 3, role: 'track' }));\n for (let b = 1; b < o.bytes; b++) nodes.push(path({ id: `${id}:byte:${b}`, points: [[xAt(b), y0 + h - 6], [xAt(b), y0 + h]], role: 'tick', width: 1 }));\n const groups = new Map<string, MemoryGroup>();\n const addrAt = new Set<number>();\n for (const g of o.groups ?? []) {\n groups.set(g.key, g);\n addrAt.add(g.offset);\n const cx = xAt(g.offset + g.size / 2);\n nodes.push(rect({ id: `${id}:g:${g.key}`, x: cx, y: y0 + h / 2, w: g.size * bw - 2, h: h - 4, rx: 4, tone: g.tone }));\n if (g.value !== undefined) nodes.push(text({ id: `${id}:g:${g.key}:v`, x: cx, y: y0 + h / 2, text: g.value, size: 12, role: 'value', tone: g.tone }));\n if (g.label) nodes.push(text({ id: `${id}:g:${g.key}:l`, x: cx, y: y0 - 10, text: g.label, size: 11, role: 'caption', tone: g.tone }));\n }\n if (o.addressEvery) for (let b = 0; b <= o.bytes; b += o.addressEvery) addrAt.add(b);\n for (const b of [...addrAt].sort((a, c) => a - c)) {\n nodes.push(text({ id: `${id}:addr:${b}`, x: xAt(b), y: y0 + h + 12, text: hex(base + b, digits), size: 9, role: 'index', anchor: 'start' }));\n }\n const g = (k: string) => groups.get(k) ?? { offset: 0, size: 0 };\n return {\n nodes,\n xAt,\n top: (k) => V2(xAt(g(k).offset + g(k).size / 2), y0),\n bottom: (k) => V2(xAt(g(k).offset + g(k).size / 2), y0 + h),\n byteTop: (b) => V2(xAt(b), y0),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["g"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python-Tutor-style memory pictures: stack frames on the left, heap objects
|
|
3
|
+
* on the right, and an arrow for every reference.
|
|
4
|
+
*
|
|
5
|
+
* The thing this makes visible is ALIASING. `b = a` doesn't copy a list; it
|
|
6
|
+
* adds a second arrow to the same object. `b.append(3)` then changes what `a`
|
|
7
|
+
* points at too, and you can see why.
|
|
8
|
+
*
|
|
9
|
+
* Primitives (numbers, strings, None) are drawn inline in their slot. A
|
|
10
|
+
* `{ ref: 'id' }` value draws a dot in the slot and an arrow to heap object `id`.
|
|
11
|
+
*/
|
|
12
|
+
import { Scene, Tone } from '../scene';
|
|
13
|
+
import { Vec } from '../vec';
|
|
14
|
+
export type MemValue = number | string | boolean | null | {
|
|
15
|
+
ref: string;
|
|
16
|
+
};
|
|
17
|
+
export interface StackFrame {
|
|
18
|
+
name: string;
|
|
19
|
+
vars: ReadonlyArray<{
|
|
20
|
+
name: string;
|
|
21
|
+
value: MemValue;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export interface HeapObject {
|
|
25
|
+
id: string;
|
|
26
|
+
/** Shown above the object: 'list', 'tuple', 'dict', a class name… */
|
|
27
|
+
kind: string;
|
|
28
|
+
/** Sequence items, or [key, value] pairs for dicts / instances. */
|
|
29
|
+
items: ReadonlyArray<MemValue> | ReadonlyArray<readonly [string, MemValue]>;
|
|
30
|
+
}
|
|
31
|
+
export interface StackHeapOptions {
|
|
32
|
+
id?: string;
|
|
33
|
+
x?: number;
|
|
34
|
+
y?: number;
|
|
35
|
+
frameWidth?: number;
|
|
36
|
+
/** x of the heap column. Default frameWidth + 110. */
|
|
37
|
+
heapX?: number;
|
|
38
|
+
rowHeight?: number;
|
|
39
|
+
/** Tones keyed by `frame.var` (e.g. 'Global.a') or heap object id. */
|
|
40
|
+
tones?: Record<string, Tone | undefined>;
|
|
41
|
+
/** Heap layout: explicit positions per object id, relative to (heapX, y). */
|
|
42
|
+
positions?: Record<string, Vec>;
|
|
43
|
+
}
|
|
44
|
+
export interface StackHeapDiagram extends Scene {
|
|
45
|
+
slot(frame: string, name: string): Vec;
|
|
46
|
+
object(id: string): Vec;
|
|
47
|
+
}
|
|
48
|
+
export declare function stackHeap(frames: readonly StackFrame[], heap: readonly HeapObject[], o?: StackHeapOptions): StackHeapDiagram;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { arrow, circle, estimateTextWidth, rect, text } from "../scene.js";
|
|
2
|
+
import { V2 } from "../vec.js";
|
|
3
|
+
const show = (v) => v === null ? "None" : typeof v === "string" ? `"${v}"` : typeof v === "boolean" ? v ? "True" : "False" : String(v);
|
|
4
|
+
const isRef = (v) => typeof v === "object" && v !== null;
|
|
5
|
+
function stackHeap(frames, heap, o = {}) {
|
|
6
|
+
const id = o.id ?? "mem";
|
|
7
|
+
const x0 = o.x ?? 0, y0 = o.y ?? 0;
|
|
8
|
+
const fw = o.frameWidth ?? 150;
|
|
9
|
+
const hx = o.heapX ?? x0 + fw + 110;
|
|
10
|
+
const rh = o.rowHeight ?? 28;
|
|
11
|
+
const cellW = 40;
|
|
12
|
+
const nodes = [];
|
|
13
|
+
const arrows = [];
|
|
14
|
+
const slots = /* @__PURE__ */ new Map();
|
|
15
|
+
const objPos = /* @__PURE__ */ new Map();
|
|
16
|
+
let hy = y0 + 18;
|
|
17
|
+
for (const obj of heap) {
|
|
18
|
+
const p = o.positions?.[obj.id];
|
|
19
|
+
const at = p ? V2(hx + p.x, y0 + p.y) : V2(hx, hy);
|
|
20
|
+
objPos.set(obj.id, at);
|
|
21
|
+
if (!p) hy += rh + 40;
|
|
22
|
+
}
|
|
23
|
+
let fy = y0;
|
|
24
|
+
for (const f of frames) {
|
|
25
|
+
const h = 26 + f.vars.length * rh + 8;
|
|
26
|
+
nodes.push(rect({ id: `${id}:frame:${f.name}`, x: x0 + fw / 2, y: fy + h / 2, w: fw, h, rx: 6, role: "frame" }));
|
|
27
|
+
nodes.push(text({ id: `${id}:frame:${f.name}:t`, x: x0 + 10, y: fy + 13, text: f.name, size: 11, role: "caption", anchor: "start" }));
|
|
28
|
+
f.vars.forEach((v, i) => {
|
|
29
|
+
const cy = fy + 26 + i * rh + rh / 2;
|
|
30
|
+
const key = `${f.name}.${v.name}`;
|
|
31
|
+
const tone = o.tones?.[key];
|
|
32
|
+
const slotX = x0 + fw - 34;
|
|
33
|
+
slots.set(key, V2(slotX, cy));
|
|
34
|
+
nodes.push(text({ id: `${id}:var:${key}`, x: slotX - 26, y: cy, text: v.name, size: 13, role: "value", anchor: "end", tone }));
|
|
35
|
+
nodes.push(rect({ id: `${id}:slot:${key}`, x: slotX, y: cy, w: 40, h: rh - 6, rx: 4, tone }));
|
|
36
|
+
if (isRef(v.value)) {
|
|
37
|
+
nodes.push(circle({ id: `${id}:dot:${key}`, x: slotX, y: cy, r: 3.5, role: "dot", tone }));
|
|
38
|
+
const t = objPos.get(v.value.ref);
|
|
39
|
+
if (t) arrows.push(refArrow(`${id}:ref:${key}`, V2(slotX, cy), V2(t.x - 4, t.y), tone));
|
|
40
|
+
} else {
|
|
41
|
+
nodes.push(text({ id: `${id}:val:${key}`, x: slotX, y: cy, text: show(v.value), size: 12, role: "value", tone }));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
fy += h + 12;
|
|
45
|
+
}
|
|
46
|
+
for (const obj of heap) {
|
|
47
|
+
const at = objPos.get(obj.id);
|
|
48
|
+
const tone = o.tones?.[obj.id];
|
|
49
|
+
nodes.push(text({ id: `${id}:obj:${obj.id}:kind`, x: at.x, y: at.y - rh / 2 - 9, text: obj.kind, size: 11, role: "caption", anchor: "start" }));
|
|
50
|
+
const pairs = obj.items.length && Array.isArray(obj.items[0]);
|
|
51
|
+
obj.items.forEach((item, i) => {
|
|
52
|
+
const [k, v] = pairs ? item : [String(i), item];
|
|
53
|
+
const w = pairs ? Math.max(cellW, estimateTextWidth(k, 11) + 14) + cellW : cellW;
|
|
54
|
+
const cx = at.x + i * (pairs ? w : cellW) + (pairs ? w : cellW) / 2;
|
|
55
|
+
nodes.push(rect({ id: `${id}:obj:${obj.id}:${i}`, x: cx, y: at.y, w: pairs ? w : cellW, h: rh - 4, rx: 3, tone, enter: { x: cx + 20 } }));
|
|
56
|
+
nodes.push(text({ id: `${id}:obj:${obj.id}:${i}:i`, x: pairs ? cx - w / 2 + 7 : cx, y: pairs ? at.y : at.y + rh / 2 + 7, text: k, size: pairs ? 11 : 9, role: "index", anchor: pairs ? "start" : "middle" }));
|
|
57
|
+
const vx = pairs ? cx + w / 2 - cellW / 2 : cx;
|
|
58
|
+
if (isRef(v)) {
|
|
59
|
+
nodes.push(circle({ id: `${id}:obj:${obj.id}:${i}:dot`, x: vx, y: at.y, r: 3.5, role: "dot" }));
|
|
60
|
+
const t = objPos.get(v.ref);
|
|
61
|
+
if (t) arrows.push(refArrow(`${id}:ref:${obj.id}:${i}`, V2(vx, at.y), V2(t.x - 4, t.y), tone, true));
|
|
62
|
+
} else nodes.push(text({ id: `${id}:obj:${obj.id}:${i}:v`, x: vx, y: at.y, text: show(v), size: 12, role: "value", tone, enter: { x: vx + 20 } }));
|
|
63
|
+
});
|
|
64
|
+
if (!obj.items.length) nodes.push(rect({ id: `${id}:obj:${obj.id}:empty`, x: at.x + 14, y: at.y, w: 28, h: rh - 4, rx: 3, tone: "ghost" }));
|
|
65
|
+
}
|
|
66
|
+
return { nodes: [...nodes, ...arrows], slot: (f, n) => slots.get(`${f}.${n}`) ?? V2(0, 0), object: (oid) => objPos.get(oid) ?? V2(0, 0) };
|
|
67
|
+
}
|
|
68
|
+
function refArrow(id, from, to, tone, fromHeap = false) {
|
|
69
|
+
const dy = to.y - from.y;
|
|
70
|
+
const bend = fromHeap ? -40 : Math.abs(dy) < 4 ? 0 : Math.sign(dy) * -Math.min(30, Math.abs(dy) * 0.25);
|
|
71
|
+
return arrow({ id, x1: from.x, y1: from.y, x2: to.x, y2: to.y, bend, width: 1.5, tone });
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
stackHeap
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=stackHeap.js.map
|