@kanzo-tech/graph 0.10.0 → 0.12.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/README.md +60 -95
- package/dist/core/categories.d.ts +12 -0
- package/dist/core/categories.d.ts.map +1 -0
- package/dist/core/categories.js +14 -0
- package/dist/core/categories.js.map +1 -0
- package/dist/core/channels.d.ts +34 -0
- package/dist/core/channels.d.ts.map +1 -0
- package/dist/core/channels.js +19 -0
- package/dist/core/channels.js.map +1 -0
- package/dist/core/detail.d.ts +18 -0
- package/dist/core/detail.d.ts.map +1 -0
- package/dist/core/detail.js +20 -0
- package/dist/core/detail.js.map +1 -0
- package/dist/core/filter.d.ts +34 -0
- package/dist/core/filter.d.ts.map +1 -0
- package/dist/core/filter.js +99 -0
- package/dist/core/filter.js.map +1 -0
- package/dist/core/refine.d.ts +14 -0
- package/dist/core/refine.d.ts.map +1 -0
- package/dist/core/refine.js +20 -0
- package/dist/core/refine.js.map +1 -0
- package/dist/{resident.d.ts → core/resident.d.ts} +7 -7
- package/dist/core/resident.d.ts.map +1 -0
- package/dist/core/resident.js +44 -0
- package/dist/core/resident.js.map +1 -0
- package/dist/core/scheduler.d.ts +42 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +77 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/state.d.ts +132 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/store.d.ts +6 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +235 -0
- package/dist/core/store.js.map +1 -0
- package/dist/core/tile-matrix.d.ts +37 -0
- package/dist/core/tile-matrix.d.ts.map +1 -0
- package/dist/core/tile-matrix.js +49 -0
- package/dist/core/tile-matrix.js.map +1 -0
- package/dist/core/tile.d.ts +54 -0
- package/dist/core/tile.d.ts.map +1 -0
- package/dist/core/tile.js +76 -0
- package/dist/core/tile.js.map +1 -0
- package/dist/core/tileset.d.ts +59 -0
- package/dist/core/tileset.d.ts.map +1 -0
- package/dist/core/tileset.js +177 -0
- package/dist/core/tileset.js.map +1 -0
- package/dist/{types.d.ts → core/types.d.ts} +9 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/index.d.ts +37 -70
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -34
- package/dist/index.js.map +1 -1
- package/dist/{use-graph-selection.d.ts → parts/gesture.d.ts} +4 -4
- package/dist/parts/gesture.d.ts.map +1 -0
- package/dist/{use-graph-selection.js → parts/gesture.js} +17 -17
- package/dist/parts/gesture.js.map +1 -0
- package/dist/parts/graph-canvas.d.ts +17 -0
- package/dist/parts/graph-canvas.d.ts.map +1 -0
- package/dist/parts/graph-canvas.js +174 -0
- package/dist/parts/graph-canvas.js.map +1 -0
- package/dist/parts/graph-inspector.d.ts +18 -0
- package/dist/parts/graph-inspector.d.ts.map +1 -0
- package/dist/parts/graph-inspector.js +84 -0
- package/dist/parts/graph-inspector.js.map +1 -0
- package/dist/parts/graph-legend.d.ts +12 -0
- package/dist/parts/graph-legend.d.ts.map +1 -0
- package/dist/parts/graph-legend.js +61 -0
- package/dist/parts/graph-legend.js.map +1 -0
- package/dist/parts/graph-toolbar.d.ts +15 -0
- package/dist/parts/graph-toolbar.d.ts.map +1 -0
- package/dist/parts/graph-toolbar.js +94 -0
- package/dist/parts/graph-toolbar.js.map +1 -0
- package/dist/parts/overlays.d.ts +33 -0
- package/dist/parts/overlays.d.ts.map +1 -0
- package/dist/{use-graph-overlays.js → parts/overlays.js} +8 -8
- package/dist/parts/overlays.js.map +1 -0
- package/dist/{shape-glyph.d.ts → parts/shape-glyph.d.ts} +1 -1
- package/dist/parts/shape-glyph.d.ts.map +1 -0
- package/dist/{shape-glyph.js → parts/shape-glyph.js} +1 -1
- package/dist/parts/shape-glyph.js.map +1 -0
- package/dist/react/graph-root.d.ts +15 -0
- package/dist/react/graph-root.d.ts.map +1 -0
- package/dist/react/graph-root.js +23 -0
- package/dist/react/graph-root.js.map +1 -0
- package/dist/{use-graph-prefs.d.ts → react/use-graph-prefs.d.ts} +2 -2
- package/dist/react/use-graph-prefs.d.ts.map +1 -0
- package/dist/{use-graph-prefs.js → react/use-graph-prefs.js} +3 -3
- package/dist/react/use-graph-prefs.js.map +1 -0
- package/dist/react/use-graph-state.d.ts +11 -0
- package/dist/react/use-graph-state.d.ts.map +1 -0
- package/dist/react/use-graph-state.js +16 -0
- package/dist/react/use-graph-state.js.map +1 -0
- package/dist/react/use-graph.d.ts +38 -0
- package/dist/react/use-graph.d.ts.map +1 -0
- package/dist/react/use-graph.js +87 -0
- package/dist/react/use-graph.js.map +1 -0
- package/dist/{adaptive.d.ts → render/adaptive.d.ts} +1 -1
- package/dist/render/adaptive.d.ts.map +1 -0
- package/dist/render/adaptive.js.map +1 -0
- package/dist/render/compose.d.ts +51 -0
- package/dist/render/compose.d.ts.map +1 -0
- package/dist/render/compose.js +107 -0
- package/dist/render/compose.js.map +1 -0
- package/dist/render/css-color.d.ts.map +1 -0
- package/dist/render/css-color.js.map +1 -0
- package/dist/render/encode.d.ts +42 -0
- package/dist/render/encode.d.ts.map +1 -0
- package/dist/render/encode.js +68 -0
- package/dist/render/encode.js.map +1 -0
- package/dist/render/graph-looks.d.ts.map +1 -0
- package/dist/render/graph-looks.js.map +1 -0
- package/dist/render/graph-model.d.ts +44 -0
- package/dist/render/graph-model.d.ts.map +1 -0
- package/dist/render/graph-model.js +80 -0
- package/dist/render/graph-model.js.map +1 -0
- package/dist/{graph-sim.d.ts → render/graph-sim.d.ts} +1 -1
- package/dist/render/graph-sim.d.ts.map +1 -0
- package/dist/render/graph-sim.js.map +1 -0
- package/dist/{obligations.d.ts → render/obligations.d.ts} +1 -1
- package/dist/render/obligations.d.ts.map +1 -0
- package/dist/render/renderer.d.ts +38 -0
- package/dist/render/renderer.d.ts.map +1 -0
- package/dist/render/renderer.js +235 -0
- package/dist/render/renderer.js.map +1 -0
- package/dist/render/webgl.d.ts +14 -0
- package/dist/render/webgl.d.ts.map +1 -0
- package/dist/render/webgl.js +19 -0
- package/dist/render/webgl.js.map +1 -0
- package/dist/render/when-ready.d.ts.map +1 -0
- package/dist/render/when-ready.js.map +1 -0
- package/package.json +11 -21
- package/dist/adaptive.d.ts.map +0 -1
- package/dist/adaptive.js.map +0 -1
- package/dist/bounded.d.ts +0 -364
- package/dist/bounded.d.ts.map +0 -1
- package/dist/bounded.js +0 -18
- package/dist/bounded.js.map +0 -1
- package/dist/cluster-ring.d.ts +0 -25
- package/dist/cluster-ring.d.ts.map +0 -1
- package/dist/cluster-ring.js +0 -16
- package/dist/cluster-ring.js.map +0 -1
- package/dist/css-color.d.ts.map +0 -1
- package/dist/css-color.js.map +0 -1
- package/dist/duck-source.d.ts +0 -198
- package/dist/duck-source.d.ts.map +0 -1
- package/dist/duck-source.js +0 -320
- package/dist/duck-source.js.map +0 -1
- package/dist/graph-canvas.d.ts +0 -50
- package/dist/graph-canvas.d.ts.map +0 -1
- package/dist/graph-canvas.js +0 -35
- package/dist/graph-canvas.js.map +0 -1
- package/dist/graph-looks.d.ts.map +0 -1
- package/dist/graph-looks.js.map +0 -1
- package/dist/graph-model.d.ts +0 -130
- package/dist/graph-model.d.ts.map +0 -1
- package/dist/graph-model.js +0 -104
- package/dist/graph-model.js.map +0 -1
- package/dist/graph-sim.d.ts.map +0 -1
- package/dist/graph-sim.js.map +0 -1
- package/dist/obligations.d.ts.map +0 -1
- package/dist/resident.d.ts.map +0 -1
- package/dist/resident.js +0 -44
- package/dist/resident.js.map +0 -1
- package/dist/shape-glyph.d.ts.map +0 -1
- package/dist/shape-glyph.js.map +0 -1
- package/dist/slice-client.d.ts +0 -78
- package/dist/slice-client.d.ts.map +0 -1
- package/dist/slice-client.js +0 -98
- package/dist/slice-client.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/use-graph-look.d.ts +0 -28
- package/dist/use-graph-look.d.ts.map +0 -1
- package/dist/use-graph-look.js +0 -28
- package/dist/use-graph-look.js.map +0 -1
- package/dist/use-graph-overlays.d.ts +0 -44
- package/dist/use-graph-overlays.d.ts.map +0 -1
- package/dist/use-graph-overlays.js.map +0 -1
- package/dist/use-graph-prefs.d.ts.map +0 -1
- package/dist/use-graph-prefs.js.map +0 -1
- package/dist/use-graph-selection.d.ts.map +0 -1
- package/dist/use-graph-selection.js.map +0 -1
- package/dist/use-graph.d.ts +0 -203
- package/dist/use-graph.d.ts.map +0 -1
- package/dist/use-graph.js +0 -102
- package/dist/use-graph.js.map +0 -1
- package/dist/use-query-loop.d.ts +0 -88
- package/dist/use-query-loop.d.ts.map +0 -1
- package/dist/use-query-loop.js +0 -148
- package/dist/use-query-loop.js.map +0 -1
- package/dist/use-renderer.d.ts +0 -86
- package/dist/use-renderer.d.ts.map +0 -1
- package/dist/use-renderer.js +0 -188
- package/dist/use-renderer.js.map +0 -1
- package/dist/when-ready.d.ts.map +0 -1
- package/dist/when-ready.js.map +0 -1
- /package/dist/{adaptive.js → render/adaptive.js} +0 -0
- /package/dist/{css-color.d.ts → render/css-color.d.ts} +0 -0
- /package/dist/{css-color.js → render/css-color.js} +0 -0
- /package/dist/{graph-looks.d.ts → render/graph-looks.d.ts} +0 -0
- /package/dist/{graph-looks.js → render/graph-looks.js} +0 -0
- /package/dist/{graph-sim.js → render/graph-sim.js} +0 -0
- /package/dist/{when-ready.d.ts → render/when-ready.d.ts} +0 -0
- /package/dist/{when-ready.js → render/when-ready.js} +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Resident, VertexId } from '../core/resident';
|
|
2
|
+
import { GraphOptions, GraphState, GraphStore } from '../core/store';
|
|
3
|
+
import { GraphCommands, SelectionSource, Tool } from '../core/types';
|
|
4
|
+
import { Renderer, RendererEvents } from '../render/renderer';
|
|
5
|
+
export type UseGraphProps = GraphOptions;
|
|
6
|
+
/**
|
|
7
|
+
* **The commands, and the door to the state — stable for the life of the root.** Zag's split of an
|
|
8
|
+
* api from its state, in TanStack Store's shape: the state is read through `useGraphState(selector)`
|
|
9
|
+
* so a part re-renders on what it shows, and a host holding the api never re-renders because a
|
|
10
|
+
* vertex was hovered.
|
|
11
|
+
*/
|
|
12
|
+
export interface GraphApi extends GraphCommands {
|
|
13
|
+
select(vertices: readonly VertexId[] | null, source?: SelectionSource, label?: string): void;
|
|
14
|
+
setFocus(vertex: VertexId | null): void;
|
|
15
|
+
setTool(tool: Tool): void;
|
|
16
|
+
subscribe(listener: () => void): () => void;
|
|
17
|
+
/** The state now, for a callback; a render reads it with `useGraphState`. */
|
|
18
|
+
getState(): GraphState;
|
|
19
|
+
/** Who is drawn right now, to resolve a buffer index to an identity. */
|
|
20
|
+
getResident(): Resident;
|
|
21
|
+
}
|
|
22
|
+
/** What the parts in this package reach and a host does not: the element and the renderer. */
|
|
23
|
+
interface Internals {
|
|
24
|
+
store: GraphStore;
|
|
25
|
+
attach(host: HTMLDivElement, events?: RendererEvents): () => void;
|
|
26
|
+
renderer(): Renderer | null;
|
|
27
|
+
}
|
|
28
|
+
/** The parts' door to the renderer. Not on the barrel, and not on `GraphApi`'s type. */
|
|
29
|
+
export declare function internalsOf(api: GraphApi): Internals;
|
|
30
|
+
/**
|
|
31
|
+
* **`useBaseQuery`'s three moves**: the store is created once in `useState`, handed the latest props
|
|
32
|
+
* with `setOptions` in an effect — whose dependencies are the props themselves, so a render that
|
|
33
|
+
* changed none does not reach it — and read through `useSyncExternalStore`, in `useGraphState`.
|
|
34
|
+
* Callbacks are read through a ref, so a host's inline `onFailure` is never a change of options.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useGraph(props: UseGraphProps): GraphApi;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=use-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-graph.d.ts","sourceRoot":"","sources":["../../src/react/use-graph.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAExF,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,WAAW,QAAS,SAAQ,aAAa;IAC7C,MAAM,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7F,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;IACxC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,6EAA6E;IAC7E,QAAQ,IAAI,UAAU,CAAC;IACvB,wEAAwE;IACxE,WAAW,IAAI,QAAQ,CAAC;CACzB;AAED,8FAA8F;AAC9F,UAAU,SAAS;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IAClE,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAAC;CAC7B;AAID,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,CAIpD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAyBvD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRef as R, useCallback as k, useState as v, useEffect as l } from "react";
|
|
3
|
+
import { createGraph as N } from "../core/store.js";
|
|
4
|
+
import { createRenderer as z } from "../render/renderer.js";
|
|
5
|
+
const f = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
function E(n) {
|
|
7
|
+
const e = f.get(n);
|
|
8
|
+
if (!e) throw new Error("a graph part was given an api useGraph did not build");
|
|
9
|
+
return e;
|
|
10
|
+
}
|
|
11
|
+
function D(n) {
|
|
12
|
+
const e = R(n);
|
|
13
|
+
e.current = n;
|
|
14
|
+
const s = k(
|
|
15
|
+
(F) => ({
|
|
16
|
+
...F,
|
|
17
|
+
onFailure: (u) => e.current.onFailure(u),
|
|
18
|
+
onSelect: (u) => {
|
|
19
|
+
var r, a;
|
|
20
|
+
return (a = (r = e.current).onSelect) == null ? void 0 : a.call(r, u);
|
|
21
|
+
},
|
|
22
|
+
onFocus: (u) => {
|
|
23
|
+
var r, a;
|
|
24
|
+
return (a = (r = e.current).onFocus) == null ? void 0 : a.call(r, u);
|
|
25
|
+
}
|
|
26
|
+
}),
|
|
27
|
+
[]
|
|
28
|
+
), [i] = v(() => G(N(s(n)))), { store: t } = E(i), { categories: c, corpus: o, fill: m, filterBy: p, limit: d, look: b, r: S, sim: g, simulate: h, stroke: y, symbol: B, title: O, type: w } = n;
|
|
29
|
+
return l(() => {
|
|
30
|
+
t.setOptions(s(e.current));
|
|
31
|
+
}, [t, s, c, o, m, p, d, b, S, g, h, y, B, O, w]), l(() => t.subscribe(() => {
|
|
32
|
+
}), [t]), i;
|
|
33
|
+
}
|
|
34
|
+
function G(n) {
|
|
35
|
+
let e = null;
|
|
36
|
+
const s = (t) => (...c) => {
|
|
37
|
+
var o;
|
|
38
|
+
return (o = e == null ? void 0 : e[t]) == null ? void 0 : o.call(e, ...c);
|
|
39
|
+
}, i = {
|
|
40
|
+
zoomBy: s("zoomBy"),
|
|
41
|
+
fit: s("fit"),
|
|
42
|
+
frameBox: s("frameBox"),
|
|
43
|
+
pause: s("pause"),
|
|
44
|
+
resume: s("resume"),
|
|
45
|
+
restart: s("restart"),
|
|
46
|
+
unpin: s("unpin"),
|
|
47
|
+
// Selecting and focusing are state, and hold without a renderer; centring is the camera's.
|
|
48
|
+
reveal: (t) => {
|
|
49
|
+
if (e) return e.reveal(t);
|
|
50
|
+
n.select([t], "node", "Node"), n.focus(t);
|
|
51
|
+
},
|
|
52
|
+
frameSelection: s("frameSelection"),
|
|
53
|
+
clear: () => {
|
|
54
|
+
n.select(null), n.focus(null);
|
|
55
|
+
},
|
|
56
|
+
select: (t, c, o) => n.select(t, c, o),
|
|
57
|
+
setFocus: (t) => n.focus(t),
|
|
58
|
+
setTool: (t) => n.setTool(t),
|
|
59
|
+
subscribe: (t) => n.subscribe(t),
|
|
60
|
+
getState: () => n.getSnapshot(),
|
|
61
|
+
getResident: () => (e == null ? void 0 : e.resident()) ?? T
|
|
62
|
+
};
|
|
63
|
+
return f.set(i, {
|
|
64
|
+
store: n,
|
|
65
|
+
renderer: () => e,
|
|
66
|
+
attach(t, c) {
|
|
67
|
+
const o = z(t, n, c);
|
|
68
|
+
return e = o, n.setRenderable(o !== null), () => {
|
|
69
|
+
o == null || o.destroy(), e === o && (e = null);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}), i;
|
|
73
|
+
}
|
|
74
|
+
const T = {
|
|
75
|
+
size: 0,
|
|
76
|
+
indexOf: () => {
|
|
77
|
+
},
|
|
78
|
+
at: () => {
|
|
79
|
+
},
|
|
80
|
+
indicesOf: () => [],
|
|
81
|
+
verticesAt: () => []
|
|
82
|
+
};
|
|
83
|
+
export {
|
|
84
|
+
E as internalsOf,
|
|
85
|
+
D as useGraph
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=use-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-graph.js","sources":["../../src/react/use-graph.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { Resident, VertexId } from \"../core/resident\";\nimport { createGraph, type GraphOptions, type GraphState, type GraphStore } from \"../core/store\";\nimport type { GraphCommands, SelectionSource, Tool } from \"../core/types\";\nimport { createRenderer, type Renderer, type RendererEvents } from \"../render/renderer\";\n\nexport type UseGraphProps = GraphOptions;\n\n/**\n * **The commands, and the door to the state — stable for the life of the root.** Zag's split of an\n * api from its state, in TanStack Store's shape: the state is read through `useGraphState(selector)`\n * so a part re-renders on what it shows, and a host holding the api never re-renders because a\n * vertex was hovered.\n */\nexport interface GraphApi extends GraphCommands {\n select(vertices: readonly VertexId[] | null, source?: SelectionSource, label?: string): void;\n setFocus(vertex: VertexId | null): void;\n setTool(tool: Tool): void;\n subscribe(listener: () => void): () => void;\n /** The state now, for a callback; a render reads it with `useGraphState`. */\n getState(): GraphState;\n /** Who is drawn right now, to resolve a buffer index to an identity. */\n getResident(): Resident;\n}\n\n/** What the parts in this package reach and a host does not: the element and the renderer. */\ninterface Internals {\n store: GraphStore;\n attach(host: HTMLDivElement, events?: RendererEvents): () => void;\n renderer(): Renderer | null;\n}\n\nconst INTERNALS = new WeakMap<GraphApi, Internals>();\n\n/** The parts' door to the renderer. Not on the barrel, and not on `GraphApi`'s type. */\nexport function internalsOf(api: GraphApi): Internals {\n const found = INTERNALS.get(api);\n if (!found) throw new Error(\"a graph part was given an api useGraph did not build\");\n return found;\n}\n\n/**\n * **`useBaseQuery`'s three moves**: the store is created once in `useState`, handed the latest props\n * with `setOptions` in an effect — whose dependencies are the props themselves, so a render that\n * changed none does not reach it — and read through `useSyncExternalStore`, in `useGraphState`.\n * Callbacks are read through a ref, so a host's inline `onFailure` is never a change of options.\n */\nexport function useGraph(props: UseGraphProps): GraphApi {\n const latest = useRef(props);\n latest.current = props;\n const forward = useCallback(\n (options: UseGraphProps): UseGraphProps => ({\n ...options,\n onFailure: (message) => latest.current.onFailure(message),\n onSelect: (selection) => latest.current.onSelect?.(selection),\n onFocus: (vertex) => latest.current.onFocus?.(vertex),\n }),\n [],\n );\n const [api] = useState<GraphApi>(() => build(createGraph(forward(props))));\n const { store } = internalsOf(api);\n\n const { categories, corpus, fill, filterBy, limit, look, r, sim, simulate, stroke, symbol, title, type } = props;\n useEffect(() => {\n store.setOptions(forward(latest.current));\n }, [store, forward, categories, corpus, fill, filterBy, limit, look, r, sim, simulate, stroke, symbol, title, type]);\n\n // Subscribed here as well as by the parts, so the store's first-subscriber and last-subscriber\n // moves follow the root's lifetime and not whichever part happened to mount first.\n useEffect(() => store.subscribe(() => {}), [store]);\n\n return api;\n}\n\nfunction build(store: GraphStore): GraphApi {\n let renderer: Renderer | null = null;\n const on =\n <K extends keyof GraphCommands>(name: K) =>\n (...args: Parameters<GraphCommands[K]>) =>\n (renderer?.[name] as ((...a: Parameters<GraphCommands[K]>) => void) | undefined)?.(...args);\n const api: GraphApi = {\n zoomBy: on(\"zoomBy\"),\n fit: on(\"fit\"),\n frameBox: on(\"frameBox\"),\n pause: on(\"pause\"),\n resume: on(\"resume\"),\n restart: on(\"restart\"),\n unpin: on(\"unpin\"),\n // Selecting and focusing are state, and hold without a renderer; centring is the camera's.\n reveal: (vertex) => {\n if (renderer) return renderer.reveal(vertex);\n store.select([vertex], \"node\", \"Node\");\n store.focus(vertex);\n },\n frameSelection: on(\"frameSelection\"),\n clear: () => {\n store.select(null);\n store.focus(null);\n },\n select: (vertices, source, label) => store.select(vertices, source, label),\n setFocus: (vertex) => store.focus(vertex),\n setTool: (tool) => store.setTool(tool),\n subscribe: (listener) => store.subscribe(listener),\n getState: () => store.getSnapshot(),\n getResident: () => renderer?.resident() ?? NOBODY,\n };\n INTERNALS.set(api, {\n store,\n renderer: () => renderer,\n attach(host, events) {\n const mounted = createRenderer(host, store, events);\n renderer = mounted;\n store.setRenderable(mounted !== null);\n return () => {\n mounted?.destroy();\n if (renderer === mounted) renderer = null;\n };\n },\n });\n return api;\n}\n\nconst NOBODY: Resident = {\n size: 0,\n indexOf: () => undefined,\n at: () => undefined,\n indicesOf: () => [],\n verticesAt: () => [],\n};\n"],"names":[],"mappings":";;;;AAkCA;AAGO;AACL;AACA;AACA;AACF;AAQO;AACL;AACA;AACA;AAAgB;AAC8B;AACvC;AACqD;;AAC/B;AAA0B;AAAA;;AAC9B;AAAyB;AAAA;AAAM;AAEtD;AAMF;AACE;AAAwC;AAKJ;AAGxC;AAEA;AACE;AACA;;AAGK;AAAqF;AACpE;AACD;AACN;AACU;AACN;AACE;AACE;AACJ;AAAA;AAGf;AACA;AACkB;AACpB;AACmC;AAEjC;AACgB;AAClB;AACyE;AACjC;AACH;AACY;AAC3B;AACqB;AAE7C;AAAmB;AACjB;AACgB;AAEd;AACA;AAGE;AACqC;AACvC;AACF;AAGJ;AAEA;AAAyB;AACjB;AACG;AAAA;AACL;AAAA;AACa;AAEnB;;;;;"}
|
|
@@ -18,7 +18,7 @@ import { Sim } from './graph-sim';
|
|
|
18
18
|
* positions are authority: the box is the coordinate space a source's positions are expressed in and
|
|
19
19
|
* a spatial query is asked against, so resizing it by node count would move the index under the
|
|
20
20
|
* camera. It is not a constant of ours either, for the same reason turned around — it is the
|
|
21
|
-
*
|
|
21
|
+
* tile matrix's extent, and the renderer sets it from there.
|
|
22
22
|
*/
|
|
23
23
|
export declare function adaptive(nodes: number): {
|
|
24
24
|
sim: Sim;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive.d.ts","sourceRoot":"","sources":["../../src/render/adaptive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAgCvC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAgBpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive.js","sources":["../../src/render/adaptive.ts"],"sourcesContent":["import type { Sim } from \"./graph-sim\";\n\n/**\n * How a graph of *this size* should be drawn — force coefficients and two render\n * switches, interpolated continuously against node count.\n *\n * Absorbed from `@fossil-lang/viewer`'s `getAdaptiveConfig` when fossil stopped shipping a viewer, which filed it under\n * \"level-of-detail policy\". Reading it, that is not quite what it is, and the difference matters:\n * almost all of it is **simulation tuning** — repulsion, friction, spring, gravity — plus two\n * genuinely render-side switches. Level of detail in the bounded sense is the zoom of fossil's cell\n * pyramid the tileset draws at, and is a different mechanism entirely.\n *\n * That means most of this only applies under `simulate: true`, which ADR-0001 made the opt-in case.\n * It is still worth having: the host with arrays in hand and no precomputed layout is exactly the\n * host that runs a live simulation, and one set of production-tuned numbers beats each call site\n * inventing its own.\n *\n * **The continuous interpolation is the design**, not an implementation detail. Breakpoints snap —\n * a graph crossing 10,000 nodes would visibly jump — and Cosmograph 1.x is the cautionary example.\n * The constants are tuned across 10 to 100,000 nodes and divergence from them is a defect.\n */\n\nconst clamp = (value: number, lo: number, hi: number): number =>\n Math.min(hi, Math.max(lo, value));\n\nconst lerp = (a: number, b: number, t: number): number => a + (b - a) * clamp(t, 0, 1);\n\n/** `0` at about ten nodes, `1` at about a hundred thousand — log₁₀, because node counts are. */\nfunction scaleOfCount(nodes: number): number {\n return clamp((Math.log10(Math.max(nodes, 1)) - 1) / 4, 0, 1);\n}\n\n/**\n * The coefficients and the drawing options a graph this size wants.\n *\n * Returned together because they are one judgement asked of one number, and splitting them into two\n * exports that must be called with the same argument is how they drift apart. The caller still keeps\n * them apart downstream — a `Sim` rebuilds nothing and `links` is a uniform — which is the split\n * that actually costs something.\n *\n * **The mark scale left with `Display`.** This used to answer a `pointScale` too, interpolated from\n * 1.5 to 0.5 with the corpus, and nothing ever called it: the field it fed was a reader's multiplier\n * over the radius ramp `lookFrom` computes from `marks`, and two ways to size a mark is one too\n * many. What a host with a large corpus wants is the tenant policy — *start your users here* — over\n * the declared axes, which is where a computed fit belongs.\n *\n * **`spaceSize` is deliberately not here.** The original scaled the simulation box from 2,048 to\n * 8,192 with the corpus, which is coherent when a layout is computed on the fly and incoherent once\n * positions are authority: the box is the coordinate space a source's positions are expressed in and\n * a spatial query is asked against, so resizing it by node count would move the index under the\n * camera. It is not a constant of ours either, for the same reason turned around — it is the\n * tile matrix's extent, and the renderer sets it from there.\n */\nexport function adaptive(nodes: number): { sim: Sim; links: boolean } {\n const t = scaleOfCount(nodes);\n return {\n sim: {\n // Bigger graphs need less push and more damping, or they never settle.\n repulsion: lerp(1.2, 0.4, t),\n friction: lerp(0.7, 0.92, t),\n linkSpring: lerp(0.5, 0.25, t),\n linkDistance: lerp(30, 12, t),\n gravity: lerp(0.35, 0.08, t),\n cluster: 0.15,\n },\n // Past a quarter of a million links the edge layer is fog, and fog costs a draw call per frame\n // to render. Below it, links are most of what a reader is actually looking at.\n links: nodes < 250_000,\n };\n}\n"],"names":["clamp","value","lo","hi","lerp","a","b","t","scaleOfCount","nodes","adaptive"],"mappings":"AAsBA,MAAMA,IAAQ,CAACC,GAAeC,GAAYC,MACxC,KAAK,IAAIA,GAAI,KAAK,IAAID,GAAID,CAAK,CAAC,GAE5BG,IAAO,CAACC,GAAWC,GAAWC,MAAsBF,KAAKC,IAAID,KAAKL,EAAMO,GAAG,GAAG,CAAC;AAGrF,SAASC,EAAaC,GAAuB;AAC3C,SAAOT,GAAO,KAAK,MAAM,KAAK,IAAIS,GAAO,CAAC,CAAC,IAAI,KAAK,GAAG,GAAG,CAAC;AAC7D;AAuBO,SAASC,EAASD,GAA6C;AACpE,QAAMF,IAAIC,EAAaC,CAAK;AAC5B,SAAO;AAAA,IACL,KAAK;AAAA;AAAA,MAEH,WAAWL,EAAK,KAAK,KAAKG,CAAC;AAAA,MAC3B,UAAUH,EAAK,KAAK,MAAMG,CAAC;AAAA,MAC3B,YAAYH,EAAK,KAAK,MAAMG,CAAC;AAAA,MAC7B,cAAcH,EAAK,IAAI,IAAIG,CAAC;AAAA,MAC5B,SAASH,EAAK,MAAM,MAAMG,CAAC;AAAA,MAC3B,SAAS;AAAA,IAAA;AAAA;AAAA;AAAA,IAIX,OAAOE,IAAQ;AAAA,EAAA;AAEnB;"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { TileMatrixSet } from '@fossil-lang/corpus';
|
|
2
|
+
import { Binding } from '../core/channels';
|
|
3
|
+
import { Resident } from '../core/resident';
|
|
4
|
+
import { TileView } from '../core/store';
|
|
5
|
+
/** An edge shorter than this many screen pixels is a dot on its own ends, and is not composed. */
|
|
6
|
+
export declare const MIN_LINK_PIXELS = 3;
|
|
7
|
+
/** One buffer set for one cosmos.gl instance. */
|
|
8
|
+
export interface Composition {
|
|
9
|
+
/** `[x, y, …]`: vertices first, then cells, then far ends drawn only so an edge has one. */
|
|
10
|
+
readonly positions: Float32Array;
|
|
11
|
+
/** `[src, dst, …]` as indices into `positions`. */
|
|
12
|
+
readonly links: Float32Array;
|
|
13
|
+
/** How many edges each link stands for; `null` when every link is one edge. */
|
|
14
|
+
readonly weights: Float32Array | null;
|
|
15
|
+
/** Slots that are drawn — vertices and cells. Past it, far ends. */
|
|
16
|
+
readonly marks: number;
|
|
17
|
+
/** How many of the marks are vertices; cells follow them. */
|
|
18
|
+
readonly vertices: number;
|
|
19
|
+
readonly resident: Resident;
|
|
20
|
+
/** Per slot: the category's rank, or `0` when nothing is bound. */
|
|
21
|
+
readonly categories: Uint32Array;
|
|
22
|
+
/** Per slot: the ramp's raw value, or `null` when nothing is bound. */
|
|
23
|
+
readonly sizes: Float32Array | null;
|
|
24
|
+
/** Per vertex slot: its title, when `title` is bound. */
|
|
25
|
+
readonly titles: readonly string[] | null;
|
|
26
|
+
/** What each rank is, for a legend: the seed, then the other values seen, in rank order. */
|
|
27
|
+
readonly domain: readonly unknown[];
|
|
28
|
+
/** Per rank, the vertices the marks of that rank stand for. */
|
|
29
|
+
readonly tally: readonly number[];
|
|
30
|
+
/** Vertices the marks stand for — a cell counts its members. */
|
|
31
|
+
readonly represented: number;
|
|
32
|
+
}
|
|
33
|
+
export interface ComposeInput {
|
|
34
|
+
readonly matrix: TileMatrixSet;
|
|
35
|
+
readonly typeIndex: number;
|
|
36
|
+
readonly binding: Binding;
|
|
37
|
+
readonly modeColumn: string | null;
|
|
38
|
+
/** The categorical domain fixed before any tile: it ranks first, in its own order. */
|
|
39
|
+
readonly domain: readonly unknown[];
|
|
40
|
+
readonly visible: readonly TileView[];
|
|
41
|
+
readonly cached: readonly TileView[];
|
|
42
|
+
/** Graph units per screen pixel when composed, for the stub discard. */
|
|
43
|
+
readonly perPixel: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* **Visible tiles → one buffer set.** cosmos.gl replaces a whole buffer per setter, so what changed
|
|
47
|
+
* is decided at the granularity it offers: the set of tiles. The same visible set under the same
|
|
48
|
+
* binding composes to `null` — nothing new, and nothing to upload.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createComposer(): (input: ComposeInput) => Composition | null;
|
|
51
|
+
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/render/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAwB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,kGAAkG;AAClG,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACtC,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1C,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,sFAAsF;IACtF,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,CAAC,KAAK,EAAE,YAAY,KAAK,WAAW,GAAG,IAAI,CA+B5E"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { vertexId as X, residentOf as Z } from "../core/resident.js";
|
|
2
|
+
import { Dictionary as S, encode as q } from "./encode.js";
|
|
3
|
+
const G = 3;
|
|
4
|
+
function ee() {
|
|
5
|
+
let i = "", a = new S(), h = [], y = /* @__PURE__ */ new WeakMap(), m = "";
|
|
6
|
+
const w = (n, s) => {
|
|
7
|
+
let l = y.get(n.content);
|
|
8
|
+
return l || (l = q(n, s.binding, a, s.modeColumn), y.set(n.content, l)), l;
|
|
9
|
+
};
|
|
10
|
+
return (n) => {
|
|
11
|
+
const s = `${n.binding.category}|${n.binding.size}|${n.binding.title}|${n.modeColumn}`;
|
|
12
|
+
(s !== i || n.domain !== h) && (i = s, h = n.domain, a = new S(h), y = /* @__PURE__ */ new WeakMap(), m = "");
|
|
13
|
+
const l = n.visible.map((u) => J(u.content)).join(",");
|
|
14
|
+
if (l === m) return null;
|
|
15
|
+
m = l;
|
|
16
|
+
const p = n.visible.map((u) => ({ view: u, encoded: w(u, n) }));
|
|
17
|
+
return Q(n, p, a, w);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const U = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
let H = 1;
|
|
22
|
+
function J(i) {
|
|
23
|
+
let a = U.get(i);
|
|
24
|
+
return a === void 0 && U.set(i, a = H++), a;
|
|
25
|
+
}
|
|
26
|
+
function Q(i, a, h, y) {
|
|
27
|
+
var B, P;
|
|
28
|
+
const m = [...a].sort((e, t) => Number(e.encoded.cells) - Number(t.encoded.cells));
|
|
29
|
+
let w = 0, n = 0;
|
|
30
|
+
for (const { encoded: e } of m)
|
|
31
|
+
w += e.n, e.cells || (n += e.n);
|
|
32
|
+
const s = [], l = [], p = [], u = [], x = [], A = new BigUint64Array(n), $ = /* @__PURE__ */ new Map(), I = (e) => {
|
|
33
|
+
let t = $.get(e);
|
|
34
|
+
return t || $.set(e, t = /* @__PURE__ */ new Map()), t;
|
|
35
|
+
}, z = (e, t, o) => {
|
|
36
|
+
var c, d;
|
|
37
|
+
const r = s.length / 2;
|
|
38
|
+
return s.push(t.positions[o * 2], t.positions[o * 2 + 1]), l.push(((c = t.codes) == null ? void 0 : c[o]) ?? 0), p.push(((d = t.sizes) == null ? void 0 : d[o]) ?? 0), I(e).set(t.keys[o], r), r;
|
|
39
|
+
};
|
|
40
|
+
let F = 0;
|
|
41
|
+
for (const { encoded: e, view: t } of m)
|
|
42
|
+
for (let o = 0; o < e.n; o++) {
|
|
43
|
+
const r = z(t.address.z, e, o), c = e.counts[o] ?? 0;
|
|
44
|
+
x[r] = c, F += c, e.cells || (A[r] = X(i.typeIndex, e.keys[o]), u.push(((B = e.titles) == null ? void 0 : B[o]) ?? ""));
|
|
45
|
+
}
|
|
46
|
+
const v = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const e of i.cached) {
|
|
48
|
+
let t = v.get(e.address.z);
|
|
49
|
+
t || v.set(e.address.z, t = /* @__PURE__ */ new Map()), t.set(e.address.tile, e);
|
|
50
|
+
}
|
|
51
|
+
const N = (e, t) => {
|
|
52
|
+
var k, g;
|
|
53
|
+
const o = I(e).get(t);
|
|
54
|
+
if (o !== void 0) return o;
|
|
55
|
+
const r = ((k = i.matrix.tileMatrices[e]) == null ? void 0 : k.tileRows) ?? 1, c = (g = v.get(e)) == null ? void 0 : g.get(Math.floor(t / r));
|
|
56
|
+
if (!c) return;
|
|
57
|
+
const d = y(c, i);
|
|
58
|
+
for (let f = 0; f < d.n; f++) if (d.keys[f] === t) return z(e, d, f);
|
|
59
|
+
}, _ = G * i.perPixel, C = [], E = [], K = /* @__PURE__ */ new Set();
|
|
60
|
+
let L = !1;
|
|
61
|
+
for (const { encoded: e, view: t } of m) {
|
|
62
|
+
const { z: o } = t.address;
|
|
63
|
+
for (let r = 0; r < e.links.length / 2; r++) {
|
|
64
|
+
const c = e.links[r * 2], d = e.links[r * 2 + 1];
|
|
65
|
+
if (c === d) continue;
|
|
66
|
+
const k = `${o}:${c}:${d}`;
|
|
67
|
+
if (K.has(k)) continue;
|
|
68
|
+
const g = N(o, c), f = N(o, d);
|
|
69
|
+
if (g === void 0 || f === void 0) continue;
|
|
70
|
+
K.add(k);
|
|
71
|
+
const D = s[g * 2] - s[f * 2], R = s[g * 2 + 1] - s[f * 2 + 1];
|
|
72
|
+
if (Math.hypot(D, R) < _) continue;
|
|
73
|
+
C.push(g, f);
|
|
74
|
+
const T = ((P = e.weights) == null ? void 0 : P[r]) ?? 1;
|
|
75
|
+
e.weights && (L = !0), E.push(T);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const O = h.ranks(), W = Uint32Array.from(l, (e) => O[e] ?? 0), b = [];
|
|
79
|
+
O.forEach((e, t) => {
|
|
80
|
+
b[e] = h.values[t];
|
|
81
|
+
});
|
|
82
|
+
const M = b.map(() => 0);
|
|
83
|
+
for (let e = 0; e < w; e++) {
|
|
84
|
+
const t = W[e] ?? 0;
|
|
85
|
+
M[t] = (M[t] ?? 0) + (x[e] ?? 0);
|
|
86
|
+
}
|
|
87
|
+
const j = m.some(({ encoded: e }) => e.sizes !== null);
|
|
88
|
+
return {
|
|
89
|
+
positions: Float32Array.from(s),
|
|
90
|
+
links: Float32Array.from(C),
|
|
91
|
+
weights: L ? Float32Array.from(E) : null,
|
|
92
|
+
marks: w,
|
|
93
|
+
vertices: n,
|
|
94
|
+
resident: Z(A, n),
|
|
95
|
+
categories: W,
|
|
96
|
+
sizes: j ? Float32Array.from(p) : null,
|
|
97
|
+
titles: i.binding.title === void 0 ? null : u,
|
|
98
|
+
domain: b,
|
|
99
|
+
tally: M,
|
|
100
|
+
represented: F
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
G as MIN_LINK_PIXELS,
|
|
105
|
+
ee as createComposer
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.js","sources":["../../src/render/compose.ts"],"sourcesContent":["import type { TileMatrixSet } from \"@fossil-lang/corpus\";\nimport type { Binding } from \"../core/channels\";\nimport { residentOf, vertexId, type Resident } from \"../core/resident\";\nimport type { TileContent } from \"../core/tile\";\nimport type { TileView } from \"../core/store\";\nimport { Dictionary, encode, type Encoded } from \"./encode\";\n\n/** An edge shorter than this many screen pixels is a dot on its own ends, and is not composed. */\nexport const MIN_LINK_PIXELS = 3;\n\n/** One buffer set for one cosmos.gl instance. */\nexport interface Composition {\n /** `[x, y, …]`: vertices first, then cells, then far ends drawn only so an edge has one. */\n readonly positions: Float32Array;\n /** `[src, dst, …]` as indices into `positions`. */\n readonly links: Float32Array;\n /** How many edges each link stands for; `null` when every link is one edge. */\n readonly weights: Float32Array | null;\n /** Slots that are drawn — vertices and cells. Past it, far ends. */\n readonly marks: number;\n /** How many of the marks are vertices; cells follow them. */\n readonly vertices: number;\n readonly resident: Resident;\n /** Per slot: the category's rank, or `0` when nothing is bound. */\n readonly categories: Uint32Array;\n /** Per slot: the ramp's raw value, or `null` when nothing is bound. */\n readonly sizes: Float32Array | null;\n /** Per vertex slot: its title, when `title` is bound. */\n readonly titles: readonly string[] | null;\n /** What each rank is, for a legend: the seed, then the other values seen, in rank order. */\n readonly domain: readonly unknown[];\n /** Per rank, the vertices the marks of that rank stand for. */\n readonly tally: readonly number[];\n /** Vertices the marks stand for — a cell counts its members. */\n readonly represented: number;\n}\n\nexport interface ComposeInput {\n readonly matrix: TileMatrixSet;\n readonly typeIndex: number;\n readonly binding: Binding;\n readonly modeColumn: string | null;\n /** The categorical domain fixed before any tile: it ranks first, in its own order. */\n readonly domain: readonly unknown[];\n readonly visible: readonly TileView[];\n readonly cached: readonly TileView[];\n /** Graph units per screen pixel when composed, for the stub discard. */\n readonly perPixel: number;\n}\n\n/**\n * **Visible tiles → one buffer set.** cosmos.gl replaces a whole buffer per setter, so what changed\n * is decided at the granularity it offers: the set of tiles. The same visible set under the same\n * binding composes to `null` — nothing new, and nothing to upload.\n */\nexport function createComposer(): (input: ComposeInput) => Composition | null {\n let bindingKey = \"\";\n let dictionary = new Dictionary();\n let seed: readonly unknown[] = [];\n let encoded = new WeakMap<TileContent, Encoded>();\n let setKey = \"\";\n\n const encodedOf = (view: TileView, input: ComposeInput): Encoded => {\n let found = encoded.get(view.content);\n if (!found) {\n found = encode(view, input.binding, dictionary, input.modeColumn);\n encoded.set(view.content, found);\n }\n return found;\n };\n\n return (input) => {\n const key = `${input.binding.category}|${input.binding.size}|${input.binding.title}|${input.modeColumn}`;\n if (key !== bindingKey || input.domain !== seed) {\n bindingKey = key;\n seed = input.domain;\n dictionary = new Dictionary(seed);\n encoded = new WeakMap();\n setKey = \"\";\n }\n const next = input.visible.map((view) => idOf(view.content)).join(\",\");\n if (next === setKey) return null;\n setKey = next;\n const tiles = input.visible.map((view) => ({ view, encoded: encodedOf(view, input) }));\n return assemble(input, tiles, dictionary, encodedOf);\n };\n}\n\nconst ids = new WeakMap<object, number>();\nlet nextId = 1;\nfunction idOf(content: object): number {\n let id = ids.get(content);\n if (id === undefined) ids.set(content, (id = nextId++));\n return id;\n}\n\nfunction assemble(\n input: ComposeInput,\n tiles: readonly { view: TileView; encoded: Encoded }[],\n dictionary: Dictionary,\n encodedOf: (view: TileView, input: ComposeInput) => Encoded,\n): Composition {\n const ordered = [...tiles].sort((a, b) => Number(a.encoded.cells) - Number(b.encoded.cells));\n let marks = 0;\n let vertexMarks = 0;\n for (const { encoded } of ordered) {\n marks += encoded.n;\n if (!encoded.cells) vertexMarks += encoded.n;\n }\n\n const xy: number[] = [];\n const codes: number[] = [];\n const ramp: number[] = [];\n const titles: string[] = [];\n const counts: number[] = [];\n const vertices = new BigUint64Array(vertexMarks);\n const slot = new Map<number, Map<number, number>>();\n const slotsAt = (z: number) => {\n let found = slot.get(z);\n if (!found) slot.set(z, (found = new Map()));\n return found;\n };\n const place = (z: number, encoded: Encoded, i: number) => {\n const at = xy.length / 2;\n xy.push(encoded.positions[i * 2] as number, encoded.positions[i * 2 + 1] as number);\n codes.push(encoded.codes?.[i] ?? 0);\n ramp.push(encoded.sizes?.[i] ?? 0);\n slotsAt(z).set(encoded.keys[i] as number, at);\n return at;\n };\n\n let represented = 0;\n for (const { encoded, view } of ordered) {\n for (let i = 0; i < encoded.n; i++) {\n const at = place(view.address.z, encoded, i);\n const count = encoded.counts[i] ?? 0;\n counts[at] = count;\n represented += count;\n if (!encoded.cells) {\n vertices[at] = vertexId(input.typeIndex, encoded.keys[i] as number);\n titles.push(encoded.titles?.[i] ?? \"\");\n }\n }\n }\n\n const cachedByZ = new Map<number, Map<number, TileView>>();\n for (const view of input.cached) {\n let byTile = cachedByZ.get(view.address.z);\n if (!byTile) cachedByZ.set(view.address.z, (byTile = new Map()));\n byTile.set(view.address.tile, view);\n }\n /** A far end: in the cache or not at all — never a read of its tile. */\n const farEnd = (z: number, key: number): number | undefined => {\n const known = slotsAt(z).get(key);\n if (known !== undefined) return known;\n const rows = input.matrix.tileMatrices[z]?.tileRows ?? 1;\n const view = cachedByZ.get(z)?.get(Math.floor(key / rows));\n if (!view) return undefined;\n const encoded = encodedOf(view, input);\n for (let i = 0; i < encoded.n; i++) if (encoded.keys[i] === key) return place(z, encoded, i);\n return undefined;\n };\n\n const minLength = MIN_LINK_PIXELS * input.perPixel;\n const links: number[] = [];\n const weights: number[] = [];\n const seen = new Set<string>();\n let weighted = false;\n for (const { encoded, view } of ordered) {\n const { z } = view.address;\n for (let e = 0; e < encoded.links.length / 2; e++) {\n const from = encoded.links[e * 2] as number;\n const to = encoded.links[e * 2 + 1] as number;\n if (from === to) continue;\n const pair = `${z}:${from}:${to}`;\n if (seen.has(pair)) continue;\n const a = farEnd(z, from);\n const b = farEnd(z, to);\n if (a === undefined || b === undefined) continue;\n seen.add(pair);\n const dx = (xy[a * 2] as number) - (xy[b * 2] as number);\n const dy = (xy[a * 2 + 1] as number) - (xy[b * 2 + 1] as number);\n if (Math.hypot(dx, dy) < minLength) continue;\n links.push(a, b);\n const weight = encoded.weights?.[e] ?? 1;\n if (encoded.weights) weighted = true;\n weights.push(weight);\n }\n }\n\n const ranks = dictionary.ranks();\n const categories = Uint32Array.from(codes, (code) => ranks[code] ?? 0);\n const domain: unknown[] = [];\n ranks.forEach((rank, code) => {\n domain[rank] = dictionary.values[code];\n });\n const tally = domain.map(() => 0);\n for (let i = 0; i < marks; i++) {\n const rank = categories[i] ?? 0;\n tally[rank] = (tally[rank] ?? 0) + (counts[i] ?? 0);\n }\n const bound = ordered.some(({ encoded }) => encoded.sizes !== null);\n\n return {\n positions: Float32Array.from(xy),\n links: Float32Array.from(links),\n weights: weighted ? Float32Array.from(weights) : null,\n marks,\n vertices: vertexMarks,\n resident: residentOf(vertices, vertexMarks),\n categories,\n sizes: bound ? Float32Array.from(ramp) : null,\n titles: input.binding.title === undefined ? null : titles,\n domain,\n tally,\n represented,\n };\n}\n"],"names":["MIN_LINK_PIXELS","createComposer","bindingKey","dictionary","Dictionary","seed","encoded","setKey","encodedOf","view","input","found","encode","key","next","idOf","tiles","assemble","ids","nextId","content","id","ordered","a","b","marks","vertexMarks","xy","codes","ramp","titles","counts","vertices","slot","slotsAt","z","place","i","at","_a","_b","represented","count","vertexId","cachedByZ","byTile","farEnd","known","rows","minLength","links","weights","seen","weighted","e","from","to","pair","dx","dy","weight","ranks","categories","code","domain","rank","tally","bound","residentOf"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-color.d.ts","sourceRoot":"","sources":["../../src/render/css-color.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AA0BpD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAE,IAAe,GAAG,IAAI,CAK1F;AAOD,gFAAgF;AAChF,wBAAgB,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-color.js","sources":["../../src/render/css-color.ts"],"sourcesContent":["// Theme colours as numbers a WebGL renderer can take.\n//\n// Resolution is the library's — `resolveTokenColor` from `@kanzo-tech/ui`. This file used\n// to carry its own: a 1×1 canvas painted with the colour and read back through `getImageData`. Two\n// implementations of one job, in two packages, sharing nothing and failing differently, for the\n// same underlying reason — the theme is written in `oklch()` and `color-mix()`, cosmos.gl resolves\n// through d3-color and Observable Plot through its own `isColor`, and neither knows either syntax.\n// One browser probe, one place.\n//\n// What stays here is the part that is genuinely the graph's: the library answers `rgb(r, g, b)`\n// because that is what Plot eats, and `setPointColors` wants four floats in 0..1.\n\nimport { resolveTokenColor } from \"@kanzo-tech/ui\";\n\nexport type Rgba = [number, number, number, number];\n\n/**\n * What a point wears when there is no token to resolve — not a colour this package chose.\n *\n * It cannot be `var(--muted-foreground)`: this is the path taken precisely when the token layer is\n * unreachable — `document` is undefined (a server render), or the browser handed back something\n * `parseRgb` could not read. Pointing it at another token would be pointing at the thing that just\n * failed to answer.\n *\n * So it is a mid grey, and mid is the whole of the reasoning: 0.7 sits between the light page and\n * the dark one, so a point wearing it is visible against either rather than invisible against one.\n * It is a *nothing-resolved* signal, and if it ever reaches a real screen that is a bug in the\n * caller's `host`, not a colour to tune.\n */\nconst FALLBACK: Rgba = [0.7, 0.7, 0.7, 1];\nconst CHANNELS = /(-?[\\d.]+)/g;\n\n/** `rgb(37, 99, 235)` / `rgba(37, 99, 235, 0.5)` → four floats. */\nfunction parseRgb(css: string, fallback: Rgba): Rgba {\n const parts = css.match(CHANNELS)?.map(Number);\n if (!parts || parts.length < 3 || parts.some(Number.isNaN)) return fallback;\n const [r = 0, g = 0, b = 0, a = 1] = parts;\n return [r / 255, g / 255, b / 255, a];\n}\n\n/**\n * Any theme colour — a `var(--token)` or a literal — as GPU floats, resolved against `host`.\n *\n * `host` matters and is not ceremony: resolving against the element the canvas actually sits in is\n * what lets a scoped theme override win, which resolving against `<html>` would quietly lose.\n */\nexport function resolveToken(host: Element, value: string, fallback: Rgba = FALLBACK): Rgba {\n if (typeof document === \"undefined\" || !value) return fallback;\n const token = value.trim().replace(/^var\\(\\s*|\\s*\\)$/g, \"\");\n const resolved = resolveTokenColor(host, token.startsWith(\"--\") ? token : value);\n return parseRgb(resolved, fallback);\n}\n\nconst byte = (n: number) =>\n Math.round(Math.min(1, Math.max(0, n)) * 255)\n .toString(16)\n .padStart(2, \"0\");\n\n/** cosmos.gl's *config* colours go through d3-color, so they have to be hex. */\nexport function toHex([r, g, b, a]: Rgba): string {\n return `#${byte(r)}${byte(g)}${byte(b)}${a >= 1 ? \"\" : byte(a)}`;\n}\n"],"names":["FALLBACK","CHANNELS","parseRgb","css","fallback","parts","_a","r","g","b","a","resolveToken","host","value","token","resolved","resolveTokenColor","byte","n","toHex"],"mappings":";AA6BA,MAAMA,IAAiB,CAAC,KAAK,KAAK,KAAK,CAAC,GAClCC,IAAW;AAGjB,SAASC,EAASC,GAAaC,GAAsB;;AACnD,QAAMC,KAAQC,IAAAH,EAAI,MAAMF,CAAQ,MAAlB,gBAAAK,EAAqB,IAAI;AACvC,MAAI,CAACD,KAASA,EAAM,SAAS,KAAKA,EAAM,KAAK,OAAO,KAAK,EAAG,QAAOD;AACnE,QAAM,CAACG,IAAI,GAAGC,IAAI,GAAGC,IAAI,GAAGC,IAAI,CAAC,IAAIL;AACrC,SAAO,CAACE,IAAI,KAAKC,IAAI,KAAKC,IAAI,KAAKC,CAAC;AACtC;AAQO,SAASC,EAAaC,GAAeC,GAAeT,IAAiBJ,GAAgB;AAC1F,MAAI,OAAO,WAAa,OAAe,CAACa,EAAO,QAAOT;AACtD,QAAMU,IAAQD,EAAM,KAAA,EAAO,QAAQ,qBAAqB,EAAE,GACpDE,IAAWC,EAAkBJ,GAAME,EAAM,WAAW,IAAI,IAAIA,IAAQD,CAAK;AAC/E,SAAOX,EAASa,GAAUX,CAAQ;AACpC;AAEA,MAAMa,IAAO,CAACC,MACZ,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGA,CAAC,CAAC,IAAI,GAAG,EACzC,SAAS,EAAE,EACX,SAAS,GAAG,GAAG;AAGb,SAASC,EAAM,CAAC,GAAGX,GAAGC,GAAGC,CAAC,GAAiB;AAChD,SAAO,IAAIO,EAAK,CAAC,CAAC,GAAGA,EAAKT,CAAC,CAAC,GAAGS,EAAKR,CAAC,CAAC,GAAGC,KAAK,IAAI,KAAKO,EAAKP,CAAC,CAAC;AAChE;"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Binding } from '../core/channels';
|
|
2
|
+
import { TileView } from '../core/store';
|
|
3
|
+
/**
|
|
4
|
+
* Values to codes, in the order first seen — stable for the life of a binding. **The seed ranks
|
|
5
|
+
* first, in its own order**: the manifest's ordinals or the host's named values, so a colour is
|
|
6
|
+
* decided before a tile arrives. Only a value outside it is ranked by value, after the seed.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Dictionary {
|
|
9
|
+
#private;
|
|
10
|
+
readonly values: unknown[];
|
|
11
|
+
constructor(seed?: readonly unknown[]);
|
|
12
|
+
code(value: unknown): number;
|
|
13
|
+
/** Each code's rank: the seed's own order, then every other value seen, numbers before strings. */
|
|
14
|
+
ranks(): Uint32Array;
|
|
15
|
+
}
|
|
16
|
+
/** One tile's rows and edges as the arrays a composition concatenates. */
|
|
17
|
+
export interface Encoded {
|
|
18
|
+
readonly n: number;
|
|
19
|
+
readonly cells: boolean;
|
|
20
|
+
/** `[x0, y0, x1, y1, …]`. */
|
|
21
|
+
readonly positions: Float32Array;
|
|
22
|
+
/** `dense_id` at the payload, `cell_id` on a rung — what an edge's ends name. */
|
|
23
|
+
readonly keys: Float64Array;
|
|
24
|
+
/** Dictionary codes of the categorical column, or `null` when nothing is bound. */
|
|
25
|
+
readonly codes: Uint32Array | null;
|
|
26
|
+
/** The ramp: `r`'s column at the payload, a cell's `count` on a rung. */
|
|
27
|
+
readonly sizes: Float32Array | null;
|
|
28
|
+
/** Vertices each row stands for: one at the payload, a cell's `count` on a rung. */
|
|
29
|
+
readonly counts: Float64Array;
|
|
30
|
+
readonly titles: readonly string[] | null;
|
|
31
|
+
/** `[src, dst, …]` in `keys`' space, every relation read from this tile, both directions. */
|
|
32
|
+
readonly links: Float64Array;
|
|
33
|
+
/** How many edges each link stands for: one at the payload, the quotient's `weight` on a rung. */
|
|
34
|
+
readonly weights: Float32Array | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* **A tile is encoded once per binding**: the arrays are made when it arrives, and a composition is a
|
|
38
|
+
* concatenation of arrays already made. A cell summarises one channel, so its colour is `mode` when
|
|
39
|
+
* `fill` binds the column the tree's mode is of, its size can only be `count`, and it has no title.
|
|
40
|
+
*/
|
|
41
|
+
export declare function encode(view: TileView, binding: Binding, dictionary: Dictionary, modeColumn: string | null): Encoded;
|
|
42
|
+
//# sourceMappingURL=encode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/render/encode.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAY9C;;;;GAIG;AACH,qBAAa,UAAU;;IACrB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAM;gBAIpB,IAAI,GAAE,SAAS,OAAO,EAAO;IAKzC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAW5B,mGAAmG;IACnG,KAAK,IAAI,WAAW;CAUrB;AASD,0EAA0E;AAC1E,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,6BAA6B;IAC7B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IACnC,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1C,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CACvC;AAQD;;;;GAIG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CA2CT"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
var q = Object.defineProperty;
|
|
2
|
+
var M = (e) => {
|
|
3
|
+
throw TypeError(e);
|
|
4
|
+
};
|
|
5
|
+
var x = (e, t, o) => t in e ? q(e, t, { enumerable: !0, configurable: !0, writable: !0, value: o }) : e[t] = o;
|
|
6
|
+
var P = (e, t, o) => x(e, typeof t != "symbol" ? t + "" : t, o), T = (e, t, o) => t.has(e) || M("Cannot " + o);
|
|
7
|
+
var u = (e, t, o) => (T(e, t, "read from private field"), o ? o.call(e) : t.get(e)), C = (e, t, o) => t.has(e) ? M("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, o), U = (e, t, o, r) => (T(e, t, "write to private field"), r ? r.call(e, o) : t.set(e, o), o);
|
|
8
|
+
import { PAYLOAD_COORDINATES as B, PAYLOAD_ADDRESS as G } from "@fossil-lang/corpus";
|
|
9
|
+
const H = "cell_id", J = "count", Q = "mode", V = G[0], [W, Z] = B;
|
|
10
|
+
var f, l;
|
|
11
|
+
class ot {
|
|
12
|
+
constructor(t = []) {
|
|
13
|
+
P(this, "values", []);
|
|
14
|
+
C(this, f, /* @__PURE__ */ new Map());
|
|
15
|
+
C(this, l);
|
|
16
|
+
for (const o of t) this.code(o);
|
|
17
|
+
U(this, l, this.values.length);
|
|
18
|
+
}
|
|
19
|
+
code(t) {
|
|
20
|
+
const o = typeof t == "bigint" ? Number(t) : t;
|
|
21
|
+
let r = u(this, f).get(o);
|
|
22
|
+
return r === void 0 && (r = this.values.length, this.values.push(o), u(this, f).set(o, r)), r;
|
|
23
|
+
}
|
|
24
|
+
/** Each code's rank: the seed's own order, then every other value seen, numbers before strings. */
|
|
25
|
+
ranks() {
|
|
26
|
+
const t = new Uint32Array(this.values.length);
|
|
27
|
+
for (let r = 0; r < u(this, l); r++) t[r] = r;
|
|
28
|
+
const o = this.values.slice(u(this, l)).map((r, n) => ({ value: r, code: u(this, l) + n }));
|
|
29
|
+
return o.sort((r, n) => $(r.value, n.value)), o.forEach(({ code: r }, n) => {
|
|
30
|
+
t[r] = u(this, l) + n;
|
|
31
|
+
}), t;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
f = new WeakMap(), l = new WeakMap();
|
|
35
|
+
function $(e, t) {
|
|
36
|
+
return typeof e == "number" && typeof t == "number" ? e - t : typeof e == "number" ? -1 : typeof t == "number" ? 1 : String(e ?? "").localeCompare(String(t ?? ""));
|
|
37
|
+
}
|
|
38
|
+
const w = (e, t) => {
|
|
39
|
+
const o = new Float64Array(t);
|
|
40
|
+
if (e) for (let r = 0; r < t; r++) o[r] = Number(e[r] ?? Number.NaN);
|
|
41
|
+
return o;
|
|
42
|
+
};
|
|
43
|
+
function rt(e, t, o, r) {
|
|
44
|
+
var k, F, S, Y, O, z, R, K;
|
|
45
|
+
const { rows: n, edges: L } = e.content, i = e.kind === "cells", c = n.numRows, y = (k = n.getChild(W)) == null ? void 0 : k.toArray(), m = (F = n.getChild(Z)) == null ? void 0 : F.toArray(), g = new Float32Array(c * 2);
|
|
46
|
+
for (let s = 0; s < c; s++)
|
|
47
|
+
g[s * 2] = Number((y == null ? void 0 : y[s]) ?? Number.NaN), g[s * 2 + 1] = Number((m == null ? void 0 : m[s]) ?? Number.NaN);
|
|
48
|
+
const I = w((S = n.getChild(i ? H : V)) == null ? void 0 : S.toArray(), c), E = i ? t.category === r ? Q : void 0 : t.category, b = E === void 0 || (Y = n.getChild(E)) == null ? void 0 : Y.toArray();
|
|
49
|
+
let A = null;
|
|
50
|
+
if (b) {
|
|
51
|
+
A = new Uint32Array(c);
|
|
52
|
+
for (let s = 0; s < c; s++) A[s] = o.code(b[s] ?? null);
|
|
53
|
+
}
|
|
54
|
+
const D = i ? w((O = n.getChild(J)) == null ? void 0 : O.toArray(), c) : new Float64Array(c).fill(1), d = i ? t.size !== void 0 ? D : void 0 : t.size, X = d === void 0 ? null : Float32Array.from(typeof d == "string" ? w((z = n.getChild(d)) == null ? void 0 : z.toArray(), c) : d), _ = !i && t.title !== void 0 ? (R = n.getChild(t.title)) == null ? void 0 : R.toArray() : void 0, j = _ ? Array.from({ length: c }, (s, a) => String(_[a] ?? "")) : null;
|
|
55
|
+
let N = 0;
|
|
56
|
+
for (const s of L) N += s.src.length;
|
|
57
|
+
const p = new Float64Array(N * 2), v = i ? new Float32Array(N) : null;
|
|
58
|
+
let h = 0;
|
|
59
|
+
for (const s of L)
|
|
60
|
+
for (let a = 0; a < s.src.length; a++, h++)
|
|
61
|
+
p[h * 2] = Number(s.src[a]), p[h * 2 + 1] = Number(s.dst[a]), v && (v[h] = Number(((K = s.weight) == null ? void 0 : K[a]) ?? 1));
|
|
62
|
+
return { n: c, cells: i, positions: g, keys: I, codes: A, sizes: X, counts: D, titles: j, links: p, weights: v };
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
ot as Dictionary,
|
|
66
|
+
rt as encode
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=encode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encode.js","sources":["../../src/render/encode.ts"],"sourcesContent":["import { PAYLOAD_ADDRESS, PAYLOAD_COORDINATES } from \"@fossil-lang/corpus\";\nimport type { Binding } from \"../core/channels\";\nimport type { TileView } from \"../core/store\";\n\n/**\n * The cell columns a view reads. fossil's barrel publishes the payload's role table and not the\n * cells', so these two are spelled here; `/docs/design/graph` lists it as open on fossil's side.\n */\nconst CELL_KEY = \"cell_id\";\nconst CELL_TALLY = \"count\";\nconst CELL_MODE = \"mode\";\nconst KEY = PAYLOAD_ADDRESS[0] as string;\nconst [X, Y] = PAYLOAD_COORDINATES as [string, string];\n\n/**\n * Values to codes, in the order first seen — stable for the life of a binding. **The seed ranks\n * first, in its own order**: the manifest's ordinals or the host's named values, so a colour is\n * decided before a tile arrives. Only a value outside it is ranked by value, after the seed.\n */\nexport class Dictionary {\n readonly values: unknown[] = [];\n readonly #codes = new Map<unknown, number>();\n readonly #seeded: number;\n\n constructor(seed: readonly unknown[] = []) {\n for (const value of seed) this.code(value);\n this.#seeded = this.values.length;\n }\n\n code(value: unknown): number {\n const key = typeof value === \"bigint\" ? Number(value) : value;\n let code = this.#codes.get(key);\n if (code === undefined) {\n code = this.values.length;\n this.values.push(key);\n this.#codes.set(key, code);\n }\n return code;\n }\n\n /** Each code's rank: the seed's own order, then every other value seen, numbers before strings. */\n ranks(): Uint32Array {\n const ranks = new Uint32Array(this.values.length);\n for (let code = 0; code < this.#seeded; code++) ranks[code] = code;\n const rest = this.values.slice(this.#seeded).map((value, i) => ({ value, code: this.#seeded + i }));\n rest.sort((a, b) => compare(a.value, b.value));\n rest.forEach(({ code }, i) => {\n ranks[code] = this.#seeded + i;\n });\n return ranks;\n }\n}\n\nfunction compare(a: unknown, b: unknown): number {\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (typeof a === \"number\") return -1;\n if (typeof b === \"number\") return 1;\n return String(a ?? \"\").localeCompare(String(b ?? \"\"));\n}\n\n/** One tile's rows and edges as the arrays a composition concatenates. */\nexport interface Encoded {\n readonly n: number;\n readonly cells: boolean;\n /** `[x0, y0, x1, y1, …]`. */\n readonly positions: Float32Array;\n /** `dense_id` at the payload, `cell_id` on a rung — what an edge's ends name. */\n readonly keys: Float64Array;\n /** Dictionary codes of the categorical column, or `null` when nothing is bound. */\n readonly codes: Uint32Array | null;\n /** The ramp: `r`'s column at the payload, a cell's `count` on a rung. */\n readonly sizes: Float32Array | null;\n /** Vertices each row stands for: one at the payload, a cell's `count` on a rung. */\n readonly counts: Float64Array;\n readonly titles: readonly string[] | null;\n /** `[src, dst, …]` in `keys`' space, every relation read from this tile, both directions. */\n readonly links: Float64Array;\n /** How many edges each link stands for: one at the payload, the quotient's `weight` on a rung. */\n readonly weights: Float32Array | null;\n}\n\nconst numbers = (values: ArrayLike<unknown> | undefined, n: number): Float64Array => {\n const out = new Float64Array(n);\n if (values) for (let i = 0; i < n; i++) out[i] = Number(values[i] ?? Number.NaN);\n return out;\n};\n\n/**\n * **A tile is encoded once per binding**: the arrays are made when it arrives, and a composition is a\n * concatenation of arrays already made. A cell summarises one channel, so its colour is `mode` when\n * `fill` binds the column the tree's mode is of, its size can only be `count`, and it has no title.\n */\nexport function encode(\n view: TileView,\n binding: Binding,\n dictionary: Dictionary,\n modeColumn: string | null,\n): Encoded {\n const { rows, edges } = view.content;\n const cells = view.kind === \"cells\";\n const n = rows.numRows;\n const xs = rows.getChild(X)?.toArray();\n const ys = rows.getChild(Y)?.toArray();\n const positions = new Float32Array(n * 2);\n for (let i = 0; i < n; i++) {\n positions[i * 2] = Number(xs?.[i] ?? Number.NaN);\n positions[i * 2 + 1] = Number(ys?.[i] ?? Number.NaN);\n }\n const keys = numbers(rows.getChild(cells ? CELL_KEY : KEY)?.toArray(), n);\n\n const categorical = cells ? (binding.category === modeColumn ? CELL_MODE : undefined) : binding.category;\n const category = categorical === undefined ? undefined : rows.getChild(categorical)?.toArray();\n let codes: Uint32Array | null = null;\n if (category) {\n codes = new Uint32Array(n);\n for (let i = 0; i < n; i++) codes[i] = dictionary.code(category[i] ?? null);\n }\n\n const counts = cells ? numbers(rows.getChild(CELL_TALLY)?.toArray(), n) : new Float64Array(n).fill(1);\n const ramp = cells ? (binding.size !== undefined ? counts : undefined) : binding.size;\n const sizes =\n ramp === undefined ? null : Float32Array.from(typeof ramp === \"string\" ? numbers(rows.getChild(ramp)?.toArray(), n) : ramp);\n\n const named = !cells && binding.title !== undefined ? rows.getChild(binding.title)?.toArray() : undefined;\n const titles = named ? Array.from({ length: n }, (_, i) => String(named[i] ?? \"\")) : null;\n\n let total = 0;\n for (const edge of edges) total += edge.src.length;\n const links = new Float64Array(total * 2);\n const weights = cells ? new Float32Array(total) : null;\n let at = 0;\n for (const edge of edges) {\n for (let e = 0; e < edge.src.length; e++, at++) {\n links[at * 2] = Number(edge.src[e]);\n links[at * 2 + 1] = Number(edge.dst[e]);\n if (weights) weights[at] = Number(edge.weight?.[e] ?? 1);\n }\n }\n\n return { n, cells, positions, keys, codes, sizes, counts, titles, links, weights };\n}\n"],"names":["CELL_KEY","CELL_TALLY","CELL_MODE","KEY","PAYLOAD_ADDRESS","X","Y","PAYLOAD_COORDINATES","Dictionary","seed","__publicField","__privateAdd","_codes","_seeded","value","__privateSet","key","code","__privateGet","ranks","rest","i","a","b","compare","numbers","values","n","out","encode","view","binding","dictionary","modeColumn","rows","edges","cells","xs","_a","ys","_b","positions","keys","_c","categorical","category","_d","codes","counts","_e","ramp","sizes","_f","named","_g","titles","_","total","edge","links","weights","at","e","_h"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-looks.d.ts","sourceRoot":"","sources":["../../src/render/graph-looks.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3E;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAM7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,EAAgD,CAAC;AAEhF;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,KAAe,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,WAAW,IAAI;IACnB,qEAAqE;IACrE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvB,IAAI,EAAE;QACJ;;;;;WAKG;QACH,MAAM,EAAE,OAAO,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd;;;;;;WAMG;QACH,KAAK,EAAE,MAAM,CAAC;QACd;;;;;;;;;;WAUG;QACH,KAAK,EAAE,OAAO,CAAC;QACf;;;;;;WAMG;QACH,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACxB,CAAC;IACF,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;CAuBf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAM,GAAG,IAAI,CA0BxF;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,IAAiB,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,WAAW,SAAU,SAAQ,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5D,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAGnD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAQ5C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-looks.js","sources":["../../src/render/graph-looks.ts"],"sourcesContent":["// Three registers the same graph can be *drawn* in. Not three palettes, and — since a channel is a\n// binding on the request — not three encodings either.\n//\n// **A look is form and nothing else.** No colours: colour belongs to the theme's categorical\n// scheme, the way it belongs to a chart, so every surface showing the same categories gets the same\n// answer. And no encoding: what a channel carries is bound by whoever draws the graph, under Plot's\n// names. A look that decided whether identity reached the GPU as colour or as shape was a theme\n// reaching into an encoding — Vega-Lite states the general rule, that `config` sets defaults for\n// marks, scales, axes and legends and may not touch `encoding` — and its concrete cost was a `fill`\n// binding that painted nothing under the look named Ink.\n//\n// This is the grammar-of-graphics split, and it is the reference model rather than a local idea:\n// in Observable Plot colour is a property of the **scale**, never of the mark, and the mark carries\n// the channels. A look is the mark's geometry.\n//\n// The three names survive as **recommended pairings** — a form plus the bindings that were bundled\n// with it — offered by the host that draws the graph. A host offering three arrangements it authored\n// is not the same act as a preference silently discarding the caller's binding.\n\n/**\n * The glyphs this canvas draws — **by name**, because a name is what the concept is.\n *\n * This was five exports of one idea: a `SHAPE` object mapping names to numbers, a `ShapeId` type\n * that was the union of those numbers, `SHAPE_ORDER`, `SHAPE_OTHER` and `SHAPE_PATH` keyed by them.\n * The numbers were never ours. They are cosmos.gl's `setPointShapes` enum indices, and the jump from\n * `3` (diamond) to `7` (cross) is what gives that away — there is no `4`, `5` or `6` here because\n * Pentagon, Hexagon and Star are members this canvas does not draw. Publishing them made the\n * renderer's internal numbering part of a contract, so a host held `3` where it meant *diamond*, and\n * an upstream enum that renumbered would have moved every legend on every page silently.\n *\n * A string union says the same thing, reads at the call site — `<ShapeGlyph shape=\"cross\" />` — and\n * makes the mapping this file's private business, which is what it always was.\n */\nexport type Shape = \"circle\" | \"square\" | \"triangle\" | \"diamond\" | \"cross\";\n\n/**\n * The name → cosmos.gl enum index, and the one place that translation happens.\n *\n * `None` (`8`) has no name here on purpose: the point fragment shader `discard`s a `NONE` point that\n * carries no image, so \"past capacity\" spelled as `None` would delete the node from the picture, and\n * a category nobody can name is still a node with edges. `obligations.ts` grades that.\n *\n * Module-scoped and off the barrel. `buffers` reads it on the way to the GPU; nothing else needs it,\n * and anything that did would be reaching for the enum this type exists to hide.\n */\nexport const SHAPE_INDEX: Record<Shape, number> = {\n circle: 0,\n square: 1,\n triangle: 2,\n diamond: 3,\n cross: 7,\n};\n\n/**\n * The shape scale, in slot order — the sibling of the colour scale.\n *\n * Plot calls this channel `symbol` and gives it its own legend, which is the tell that it is a peer\n * of colour rather than a decoration. Four slots and no cycling: a fifth category cannot wear circle\n * again without claiming to be the first one.\n *\n * **Four because the shape channel's measured capacity is five, not because small shapes stop being\n * distinguishable.** Giovannangeli et al. (arXiv 2103.06084) put the ceiling at **5 for shape**\n * against **7 for colour**; `SHAPE_ORDER`'s four plus `SHAPE_OTHER` is exactly five distinguishable\n * glyphs, and the two scales differ in cardinality because the channels do. The colour side already\n * says the same thing from the other end — Dracula's document publishes `--chart-capacity: 7`.\n *\n * **This comment used to give the size argument, and the size argument is wrong.** It said a\n * pentagon, a hexagon and a circle are one dot at Ink's floor. Smart & Szafir (CHI 2019,\n * doi:10.1145/3290605.3300899) measured 16 shapes across 6 mark sizes from 6 to 50 px and found\n * shape discrimination *robust* to size: the only significant variation is at 6 px, and it is 4.5\n * accuracy points against 50 px. The conclusion survived the argument that was given for it, which\n * is the most dangerous shape a comment can have — see `OBLIGATIONS` in `./obligations.ts` for what\n * the floor actually protects.\n */\nexport const SHAPE_ORDER: Shape[] = [\"circle\", \"square\", \"triangle\", \"diamond\"];\n\n/**\n * What a category past the scale wears — the shape channel's `--muted-foreground`.\n *\n * No slot wears it, which is the whole point: it says *not one of the four* rather than repeating\n * the first one. This is the half that used to be missing, and the comment above was false without\n * it — `SHAPE_ORDER[4]` is `undefined`, and the fallback was `circle`.\n */\nexport const SHAPE_OTHER: Shape = \"cross\";\n\n/**\n * The geometry a canvas draws — and nothing else.\n *\n * **No `id`, no `label`, no `blurb`.** Those three were a picker's metadata, and the picker was a\n * list of three names that turned out to be two pictures. What a person chooses is declared in\n * `section.ts` as axes and resolved by `lookFrom`; what a renderer consumes is this.\n */\nexport interface Look {\n /** Radius at the lowest degree in the corpus, and at the highest. */\n size: [number, number];\n link: {\n /**\n * Whether the edge layer is drawn at all.\n *\n * It was `Display.links`, a second vocabulary for the picture that sat beside this one with its\n * own default and its own switch. A form that draws no links is a form.\n */\n render: boolean;\n opacity: number;\n width: number;\n /**\n * How far a link bows off the straight line, as a fraction of its length. `0` is straight.\n *\n * Keep it small. Every link curves the same way, so at cosmos.gl's default of `0.5` a few\n * hundred of them read as one pinwheel and the picture looks like it is spinning — motion\n * where there is structure. A hint is enough to tell two parallel edges apart.\n */\n curve: number;\n /**\n * Whether links **add** where they overlap, instead of compositing over one another.\n *\n * cosmos.gl's default is on, which is a choice nobody here made and which the archive made\n * visible: 4,280 grey links at 0.45 summed to a white spray that swallowed 1,543 points of\n * 2–9 px. Every point was uploaded, none was legible, and the picture read as *the nodes are\n * not rendering*.\n *\n * On, it is a real register rather than a bug — additive light is what makes a dense graph read\n * as flow — so it belongs to the form that wants it and not to the renderer's defaults.\n */\n blend: boolean;\n /**\n * Screen lengths between which a link fades out — depth, for free.\n *\n * Keep the far end generous. cosmos.gl measures this in *screen* pixels, so a range that\n * looks reasonable while zoomed in erases the whole edge layer when you zoom out, which\n * reads as \"Show links stopped working\".\n */\n fade: [number, number];\n };\n /** How many of the highest-degree nodes carry a standing label. */\n labels: number;\n /** A darkened rim. Mood rather than a reading aid, which is why it is form and not display. */\n vignette: boolean;\n /**\n * The dot grid behind the graph, which pans and subdivides with the camera.\n *\n * Beside `vignette` because it is the same kind of thing — the backdrop a form sits on — and it\n * arrived from the same place the rim did not: a `Display` interface that spelled the backdrop,\n * the edge layer and two multipliers as a second set of appearance controls.\n */\n grid: boolean;\n // **There is no `pointScale` and no `linkOpacity`**, and they are the two fields `Display` had that\n // this does not. Each was a reader's multiplier over a number this builder already computes from\n // the axis that owns it — `size` from `marks`, `link.opacity` from `marks` — so the panel offered\n // two ways to say one thing and the second could always overrule the measurement. The measured\n // pair (0.28 legible, 0.42 dense) is the whole argument of `marks`; a slider on top of it is the\n // post-process `graph-model.ts` forbids one layer down, wearing a preference's clothes.\n //\n // What is lost with them is the fit-to-corpus case, and it was never a preference: `adaptive`\n // scales a mark by node count, and a computed fit belongs to the tenant's starting point — which\n // is a policy, and which is now expressible.\n\n // There is no `filter`, and its absence is a rule rather than an omission. Nebula carried\n // `saturate(1.1)` on the canvas element — the one thing left in a Look that touched hue, and a\n // chroma multiplier is colour wearing geometry's clothes. Measured over Kanzo's eight slots\n // (2026-08-13, `saturate(1.1)` through the same filter engine the browser applies): it moved\n // every slot, by ΔE 0.85 to **8.05**, which is the size of the separation `deriveScheme`\n // *guarantees* between two different categories. It did not break that separation here —\n // the closest pair went from ΔE 32.36 to 30.38, with room to spare — so the reason it is gone\n // is not a failure it caused, it is that a look must not be able to cause one. The whole claim\n // of the colour layer is that what ships is what was derived and measured; a post-process on\n // the canvas voids it silently, and `graph-model.ts` already forbids the same move one layer\n // down (\"nudging one on the way to the GPU voids all three\").\n}\n\n/**\n * The form a canvas draws, from the axes a person chose.\n *\n * **One function and no table of constants**, which is the shape the axes forced and the reason\n * the three named looks are gone. They were three parallel tables of ten fields; six of those\n * fields separated two of the three by 7–17%, under this file's own threshold for a difference\n * meaning anything — a luminance JND of 6.48–11.30 ΔL*. What is left of them is this: every number\n * appears once, where the axis that owns it is read, and the axis is declared next door in\n * `section.ts` for a panel to draw.\n *\n * **Link opacity and width ride the mark**, and that is what the numbers said rather than a tidy\n * guess: 0.42 and 0.45 on the two dense forms against 0.28 on the legible one. A form that spends\n * more ink on points cannot also spend it on edges.\n *\n * **The legible mark's radius is the whole reason that mark exists.** Its floor protects the other\n * two channels from shape rather than shape from smallness, which is why it is the one to pair\n * `symbol` with: spending shape on identity *and* size on degree at once is what Giovannangeli et\n * al. (arXiv 2103.06084) measure as dropping performance drastically under even minor heterogeneity,\n * and smaller marks worsen it both ways — the luminance JND above, and a square reported larger than\n * any other shape at equal area in 82% of trials (Smart & Szafir, CHI 2019). Not \"a triangle and a\n * square are the same dot below four pixels\", which is what this said for months and is false; see\n * `SHAPE_ORDER` for the measurement that refutes it.\n *\n * **The values are strings because a contributed preference is a string**, in all three kinds, so an\n * unrecognised namespace rides through a write untouched. Parsing what a kind means is the reader's\n * job and it is two lines; `@kanzo-tech/theme` exports the same two, and importing them would add a\n * dependency to this package to carry no code — the same call `GRAPH_SECTION` already makes about the\n * manifest type.\n *\n * A key that is missing, or that carries a value the section never offered, takes the manifest's\n * default. Those defaults are what a graph drew before any of this existed.\n */\nexport function lookFrom(values: Readonly<Record<string, string | undefined>> = {}): Look {\n const on = (key: string, fallback: boolean) => {\n const value = values[key];\n return value === undefined ? fallback : value === \"true\";\n };\n const legible = values.marks === \"legible\";\n const labels = Number.parseFloat(values.labels ?? \"\");\n return {\n size: legible ? [4, 13] : [2, 8],\n link: {\n render: on(\"links\", true),\n opacity: legible ? 0.28 : 0.42,\n width: legible ? 0.5 : 0.6,\n // A hint, and `obligations.ts` says why: every link bows the same way, so cosmos.gl's default\n // of 0.5 reads as one pinwheel. A toggle rather than a range because a reader wants two\n // pictures — straight, and told apart — not a number to tune.\n curve: on(\"bowed-links\", true) ? 0.12 : 0,\n blend: on(\"additive-links\", false),\n // Shared by every form. It was three ranges within ±10% of each other, which is the\n // definition of a field nobody chose.\n fade: [200, 1400],\n },\n labels: Number.isFinite(labels) ? labels : 26,\n vignette: on(\"vignette\", false),\n grid: on(\"grid\", true),\n };\n}\n\n/**\n * What a canvas draws when nobody has chosen anything.\n *\n * **Not on the barrel, and it used to be.** It is literally `lookFrom()` — a second public name for\n * a value the package already hands out on request — and the reason it was exported is the one\n * `resolveLook` below removes: a host that wanted one field different had to start from the whole\n * object, because `look` took a whole `Look`. Spreading a default you were given is a copy of it,\n * and a copy is what stops tracking the original the next time a number here moves.\n */\nexport const DEFAULT_LOOK: Look = lookFrom();\n\n/**\n * A look in the pieces a caller wants different — everything else is this package's answer.\n *\n * Two levels, because a `Look` has exactly two: the fields, and `link`. Deep-merging arbitrarily\n * would be a guess about a shape that is right here in this file, and `size` and `fade` are tuples\n * that must be replaced whole rather than merged element-wise.\n */\nexport interface LookPatch extends Partial<Omit<Look, \"link\">> {\n link?: Partial<Look[\"link\"]>;\n}\n\n/**\n * A patch over the package's own default — the merge `DEFAULT_LOOK` existed so a host could do by\n * hand.\n *\n * **Nothing, and it is the shared constant rather than a copy of it.** That identity matters: the\n * buffers are rebuilt whenever the look's reference changes, so a fresh object per render would\n * mean a full colour/size/shape upload on every render of every canvas that never asked for one.\n */\nexport function resolveLook(patch?: LookPatch): Look {\n if (!patch) return DEFAULT_LOOK;\n return { ...DEFAULT_LOOK, ...patch, link: { ...DEFAULT_LOOK.link, ...patch.link } };\n}\n\n/**\n * The SVG path for a shape glyph inside a 12×12 box — the legend draws what the canvas draws.\n *\n * **Off the barrel, and `ShapeGlyph` is what replaced it.** One host imported this, to fill a\n * `<path>` in a legend key and a hover card. What that host actually wanted was *the glyph*, and\n * handing it the path data made it responsible for the viewBox, the fill and the fact that the box\n * is twelve units — three facts it had to keep in step with this file by reading the comment above.\n * A component carries all three and cannot fall out of step with itself.\n */\nexport const SHAPE_PATH: Record<Shape, string> = {\n circle: \"M6 1.6a4.4 4.4 0 1 0 0 8.8 4.4 4.4 0 0 0 0-8.8Z\",\n square: \"M2 2h8v8H2Z\",\n triangle: \"M6 1.6 10.6 10H1.4Z\",\n diamond: \"M6 1 11 6l-5 5-5-5Z\",\n // The proportions are cosmos.gl's own `crossDistance`: a plus with arms at 0.8 of the radius and\n // a bar 0.3 thick, so the legend's glyph is the shape the shader draws.\n cross: \"M4.2 1.2h3.6v3h3v3.6h-3v3H4.2v-3h-3V4.2h3Z\",\n};\n"],"names":["SHAPE_INDEX","SHAPE_ORDER","SHAPE_OTHER","lookFrom","values","on","key","fallback","value","legible","labels","DEFAULT_LOOK","resolveLook","patch","SHAPE_PATH"],"mappings":"AA6CO,MAAMA,IAAqC;AAAA,EAChD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,OAAO;AACT,GAuBaC,IAAuB,CAAC,UAAU,UAAU,YAAY,SAAS,GASjEC,IAAqB;AAuH3B,SAASC,EAASC,IAAuD,IAAU;AACxF,QAAMC,IAAK,CAACC,GAAaC,MAAsB;AAC7C,UAAMC,IAAQJ,EAAOE,CAAG;AACxB,WAAOE,MAAU,SAAYD,IAAWC,MAAU;AAAA,EACpD,GACMC,IAAUL,EAAO,UAAU,WAC3BM,IAAS,OAAO,WAAWN,EAAO,UAAU,EAAE;AACpD,SAAO;AAAA,IACL,MAAMK,IAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AAAA,IAC/B,MAAM;AAAA,MACJ,QAAQJ,EAAG,SAAS,EAAI;AAAA,MACxB,SAASI,IAAU,OAAO;AAAA,MAC1B,OAAOA,IAAU,MAAM;AAAA;AAAA;AAAA;AAAA,MAIvB,OAAOJ,EAAG,eAAe,EAAI,IAAI,OAAO;AAAA,MACxC,OAAOA,EAAG,kBAAkB,EAAK;AAAA;AAAA;AAAA,MAGjC,MAAM,CAAC,KAAK,IAAI;AAAA,IAAA;AAAA,IAElB,QAAQ,OAAO,SAASK,CAAM,IAAIA,IAAS;AAAA,IAC3C,UAAUL,EAAG,YAAY,EAAK;AAAA,IAC9B,MAAMA,EAAG,QAAQ,EAAI;AAAA,EAAA;AAEzB;AAWO,MAAMM,IAAqBR,EAAA;AAqB3B,SAASS,EAAYC,GAAyB;AACnD,SAAKA,IACE,EAAE,GAAGF,GAAc,GAAGE,GAAO,MAAM,EAAE,GAAGF,EAAa,MAAM,GAAGE,EAAM,OAAK,IAD7DF;AAErB;AAWO,MAAMG,IAAoC;AAAA,EAC/C,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA;AAAA;AAAA,EAGT,OAAO;AACT;"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { GraphConfig } from '@cosmos.gl/graph';
|
|
2
|
+
import { Channels } from '../core/channels';
|
|
3
|
+
import { Composition } from './compose';
|
|
4
|
+
import { Look, Shape } from './graph-looks';
|
|
5
|
+
import { Sim } from './graph-sim';
|
|
6
|
+
/**
|
|
7
|
+
* The categorical scale the bindings imply — what colour and what shape a category rank wears.
|
|
8
|
+
*
|
|
9
|
+
* One answer for the GPU buffers, the hover card and the legend, because three answers is how a
|
|
10
|
+
* legend ends up disagreeing with the canvas it explains. Values are CSS strings: the DOM resolves
|
|
11
|
+
* them itself, and the buffer path resolves them once on its way to the GPU.
|
|
12
|
+
*
|
|
13
|
+
* `capacity` is where Other begins, and it is the document's number rather than the token
|
|
14
|
+
* vocabulary's: a set derived from a client's brand names 6 to 8 real categories, not always 8.
|
|
15
|
+
*/
|
|
16
|
+
export declare function scaleOf(channels: Channels, capacity?: number): {
|
|
17
|
+
color: (ordinal: number) => string;
|
|
18
|
+
shape: (ordinal: number) => Shape;
|
|
19
|
+
};
|
|
20
|
+
export interface Paint {
|
|
21
|
+
colors: Float32Array;
|
|
22
|
+
sizes: Float32Array;
|
|
23
|
+
shapes: Float32Array;
|
|
24
|
+
linkColors: Float32Array;
|
|
25
|
+
linkWidths: Float32Array;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Every per-point and per-link attribute, from one composition and the live theme — what a look or a
|
|
29
|
+
* theme change re-uploads, and all it re-uploads.
|
|
30
|
+
*
|
|
31
|
+
* `host` is the element the tokens are read against, so `var(--primary)` resolves for the tree the
|
|
32
|
+
* canvas sits in. The ramp is `√value` over **this composition** — the biggest node here is what a
|
|
33
|
+
* reader is looking at. A far end past `marks` is drawn at radius zero and alpha zero: it is in the
|
|
34
|
+
* buffers only so an edge has somewhere to end.
|
|
35
|
+
*/
|
|
36
|
+
export declare function paint(composition: Composition, look: Look, host: Element, channels?: Channels): Paint;
|
|
37
|
+
/** The simulation coefficients, in cosmos.gl's spelling. Shared by construction and every change. */
|
|
38
|
+
export declare function forces(sim: Sim): GraphConfig;
|
|
39
|
+
/**
|
|
40
|
+
* Everything the picture needs that is *one number for the whole canvas* — cosmos.gl's uniforms, read
|
|
41
|
+
* fresh on every draw, so a look change costs a `setConfigPartial` and no upload at all.
|
|
42
|
+
*/
|
|
43
|
+
export declare function appearance(look: Look, host: Element): GraphConfig;
|
|
44
|
+
//# sourceMappingURL=graph-model.d.ts.map
|