@kanzo-tech/graph 0.13.1 → 0.15.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 +12 -15
- package/dist/core/categories.d.ts +18 -6
- package/dist/core/categories.d.ts.map +1 -1
- package/dist/core/categories.js +41 -10
- package/dist/core/categories.js.map +1 -1
- package/dist/core/channels.d.ts +10 -5
- package/dist/core/channels.d.ts.map +1 -1
- package/dist/core/channels.js +11 -13
- package/dist/core/channels.js.map +1 -1
- package/dist/core/detail.d.ts +14 -8
- package/dist/core/detail.d.ts.map +1 -1
- package/dist/core/detail.js +33 -15
- package/dist/core/detail.js.map +1 -1
- package/dist/core/filter.d.ts +3 -1
- package/dist/core/filter.d.ts.map +1 -1
- package/dist/core/filter.js +54 -50
- package/dist/core/filter.js.map +1 -1
- package/dist/core/load.d.ts +58 -0
- package/dist/core/load.d.ts.map +1 -0
- package/dist/core/load.js +115 -0
- package/dist/core/load.js.map +1 -0
- package/dist/core/state.d.ts +31 -56
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/store.d.ts +1 -3
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +162 -172
- package/dist/core/store.js.map +1 -1
- package/dist/core/types.d.ts +21 -17
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -25
- package/dist/index.js.map +1 -1
- package/dist/parts/gesture.d.ts +1 -10
- package/dist/parts/gesture.d.ts.map +1 -1
- package/dist/parts/gesture.js +26 -28
- package/dist/parts/gesture.js.map +1 -1
- package/dist/parts/graph-canvas.d.ts.map +1 -1
- package/dist/parts/graph-canvas.js +128 -106
- package/dist/parts/graph-canvas.js.map +1 -1
- package/dist/parts/graph-inspector.d.ts +4 -4
- package/dist/parts/graph-inspector.d.ts.map +1 -1
- package/dist/parts/graph-inspector.js +55 -57
- package/dist/parts/graph-inspector.js.map +1 -1
- package/dist/parts/graph-legend.d.ts +4 -4
- package/dist/parts/graph-legend.d.ts.map +1 -1
- package/dist/parts/graph-legend.js +21 -21
- package/dist/parts/graph-legend.js.map +1 -1
- package/dist/parts/graph-toolbar.d.ts +1 -1
- package/dist/parts/graph-toolbar.d.ts.map +1 -1
- package/dist/parts/graph-toolbar.js +40 -40
- package/dist/parts/graph-toolbar.js.map +1 -1
- package/dist/parts/overlays.d.ts +6 -5
- package/dist/parts/overlays.d.ts.map +1 -1
- package/dist/parts/overlays.js +70 -68
- package/dist/parts/overlays.js.map +1 -1
- package/dist/react/use-graph-state.d.ts +4 -2
- package/dist/react/use-graph-state.d.ts.map +1 -1
- package/dist/react/use-graph-state.js +10 -6
- package/dist/react/use-graph-state.js.map +1 -1
- package/dist/react/use-graph.d.ts +1 -4
- package/dist/react/use-graph.d.ts.map +1 -1
- package/dist/react/use-graph.js +38 -49
- package/dist/react/use-graph.js.map +1 -1
- package/dist/render/graph-looks.d.ts.map +1 -1
- package/dist/render/graph-looks.js.map +1 -1
- package/dist/render/graph-model.d.ts +6 -8
- package/dist/render/graph-model.d.ts.map +1 -1
- package/dist/render/graph-model.js +51 -57
- package/dist/render/graph-model.js.map +1 -1
- package/dist/render/graph-sim.d.ts +2 -3
- package/dist/render/graph-sim.d.ts.map +1 -1
- package/dist/render/graph-sim.js.map +1 -1
- package/dist/render/renderer.d.ts +10 -10
- package/dist/render/renderer.d.ts.map +1 -1
- package/dist/render/renderer.js +121 -177
- package/dist/render/renderer.js.map +1 -1
- package/dist/section.d.ts.map +1 -1
- package/dist/section.js +3 -7
- package/dist/section.js.map +1 -1
- package/package.json +29 -10
- package/dist/core/refine.d.ts +0 -14
- package/dist/core/refine.d.ts.map +0 -1
- package/dist/core/refine.js +0 -20
- package/dist/core/refine.js.map +0 -1
- package/dist/core/resident.d.ts +0 -79
- package/dist/core/resident.d.ts.map +0 -1
- package/dist/core/resident.js +0 -44
- package/dist/core/resident.js.map +0 -1
- package/dist/core/scheduler.d.ts +0 -42
- package/dist/core/scheduler.d.ts.map +0 -1
- package/dist/core/scheduler.js +0 -77
- package/dist/core/scheduler.js.map +0 -1
- package/dist/core/tile-matrix.d.ts +0 -37
- package/dist/core/tile-matrix.d.ts.map +0 -1
- package/dist/core/tile-matrix.js +0 -49
- package/dist/core/tile-matrix.js.map +0 -1
- package/dist/core/tile.d.ts +0 -54
- package/dist/core/tile.d.ts.map +0 -1
- package/dist/core/tile.js +0 -76
- package/dist/core/tile.js.map +0 -1
- package/dist/core/tileset.d.ts +0 -59
- package/dist/core/tileset.d.ts.map +0 -1
- package/dist/core/tileset.js +0 -177
- package/dist/core/tileset.js.map +0 -1
- package/dist/render/adaptive.d.ts +0 -27
- package/dist/render/adaptive.d.ts.map +0 -1
- package/dist/render/adaptive.js +0 -25
- package/dist/render/adaptive.js.map +0 -1
- package/dist/render/compose.d.ts +0 -51
- package/dist/render/compose.d.ts.map +0 -1
- package/dist/render/compose.js +0 -107
- package/dist/render/compose.js.map +0 -1
- package/dist/render/encode.d.ts +0 -42
- package/dist/render/encode.d.ts.map +0 -1
- package/dist/render/encode.js +0 -68
- package/dist/render/encode.js.map +0 -1
- package/dist/render/when-ready.d.ts +0 -35
- package/dist/render/when-ready.d.ts.map +0 -1
- package/dist/render/when-ready.js +0 -17
- package/dist/render/when-ready.js.map +0 -1
|
@@ -1 +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;"}
|
|
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: a computed fit\n // belongs to the tenant's starting point — which 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;AAsH3B,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;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GraphConfig } from '@cosmos.gl/graph';
|
|
2
2
|
import { Channels } from '../core/channels';
|
|
3
|
-
import {
|
|
3
|
+
import { Encoding, Geometry } from '../core/load';
|
|
4
4
|
import { Look, Shape } from './graph-looks';
|
|
5
5
|
import { Sim } from './graph-sim';
|
|
6
6
|
/**
|
|
@@ -22,18 +22,16 @@ export interface Paint {
|
|
|
22
22
|
sizes: Float32Array;
|
|
23
23
|
shapes: Float32Array;
|
|
24
24
|
linkColors: Float32Array;
|
|
25
|
-
linkWidths: Float32Array;
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
28
|
-
* Every per-point and per-link attribute, from
|
|
29
|
-
* theme change re-uploads, and all it re-uploads.
|
|
27
|
+
* Every per-point and per-link attribute, from the loaded graph and the live theme — what a binding,
|
|
28
|
+
* a look or a theme change re-uploads, and all it re-uploads.
|
|
30
29
|
*
|
|
31
30
|
* `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
|
|
33
|
-
*
|
|
34
|
-
* buffers only so an edge has somewhere to end.
|
|
31
|
+
* canvas sits in. The ramp is `√value` over the whole graph, and a vertex with no value takes the
|
|
32
|
+
* smallest radius.
|
|
35
33
|
*/
|
|
36
|
-
export declare function paint(
|
|
34
|
+
export declare function paint(geometry: Geometry, encoding: Encoding, look: Look, host: Element, channels?: Channels): Paint;
|
|
37
35
|
/** The simulation coefficients, in cosmos.gl's spelling. Shared by construction and every change. */
|
|
38
36
|
export declare function forces(sim: Sim): GraphConfig;
|
|
39
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-model.d.ts","sourceRoot":"","sources":["../../src/render/graph-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"graph-model.d.ts","sourceRoot":"","sources":["../../src/render/graph-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAyC,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,SAAc;qBAI7C,MAAM,KAAG,MAAM;qBAIf,MAAM,KAAG,KAAK;EAElC;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,YAAY,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,GAAE,QAAa,GAAG,KAAK,CAgDvH;AAED,qGAAqG;AACrG,wBAAgB,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,WAAW,CAS5C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,GAAG,WAAW,CAkBjE"}
|
|
@@ -1,80 +1,74 @@
|
|
|
1
|
-
import { CHART_SLOTS as
|
|
2
|
-
import { isColour as
|
|
3
|
-
import { toHex as
|
|
4
|
-
import { SHAPE_ORDER as
|
|
5
|
-
function
|
|
6
|
-
const
|
|
1
|
+
import { CHART_SLOTS as R, categoricalColor as A, categoricalCapacity as S } from "@kanzo-tech/ui";
|
|
2
|
+
import { isColour as D } from "../core/channels.js";
|
|
3
|
+
import { toHex as v, resolveToken as k } from "./css-color.js";
|
|
4
|
+
import { SHAPE_ORDER as E, SHAPE_OTHER as F, SHAPE_INDEX as H } from "./graph-looks.js";
|
|
5
|
+
function T(n, t = R) {
|
|
6
|
+
const o = D(n.fill) ? n.fill : void 0, c = n.symbol !== void 0;
|
|
7
7
|
return {
|
|
8
|
-
color: (e) =>
|
|
9
|
-
shape: (e) =>
|
|
8
|
+
color: (e) => o || (e >= t ? "var(--muted-foreground)" : A(e, void 0, t)),
|
|
9
|
+
shape: (e) => c ? E[e] ?? F : "circle"
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
k < t && (t = k), k > n && (n = k);
|
|
12
|
+
function M(n, t, o, c, e = {}) {
|
|
13
|
+
const y = T(e, S(c)), g = /* @__PURE__ */ new Map(), P = (i) => {
|
|
14
|
+
let r = g.get(i);
|
|
15
|
+
return r || g.set(i, r = k(c, y.color(i))), r;
|
|
16
|
+
}, u = n.size, f = new Float32Array(u * 4), b = new Float32Array(u), N = new Float32Array(u), d = t.sizes;
|
|
17
|
+
let m = 0, C = 1;
|
|
18
|
+
if (d) {
|
|
19
|
+
let i = Number.POSITIVE_INFINITY, r = 0;
|
|
20
|
+
for (let l = 0; l < u; l++) {
|
|
21
|
+
const a = d[l];
|
|
22
|
+
a < i && (i = a), a > r && (r = a);
|
|
24
23
|
}
|
|
25
|
-
|
|
24
|
+
m = Math.sqrt(Number.isFinite(i) ? i : 0), C = Math.sqrt(r) - m || 1;
|
|
26
25
|
}
|
|
27
|
-
for (let
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
const a =
|
|
31
|
-
|
|
26
|
+
for (let i = 0; i < u; i++) {
|
|
27
|
+
const r = t.ranks[i] ?? 0;
|
|
28
|
+
f.set(P(r), i * 4);
|
|
29
|
+
const l = d ? d[i] : Number.NaN, a = Number.isNaN(l) ? 0 : (Math.sqrt(l) - m) / C;
|
|
30
|
+
b[i] = o.size[0] + a * (o.size[1] - o.size[0]), N[i] = H[y.shape(r)];
|
|
32
31
|
}
|
|
33
|
-
const
|
|
34
|
-
for (let
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
f ? [f[0], f[1], f[2], 1] : [o[n * 4] ?? 0.7, o[n * 4 + 1] ?? 0.7, o[n * 4 + 2] ?? 0.7, 1],
|
|
38
|
-
t * 4
|
|
39
|
-
);
|
|
40
|
-
const a = ((b = r.weights) == null ? void 0 : b[t]) ?? 1;
|
|
41
|
-
O[t] = i.link.width * (1 + Math.log2(Math.max(1, a)) / 2);
|
|
32
|
+
const O = n.links.length / 2, p = new Float32Array(O * 4), s = e.stroke ? k(c, e.stroke) : null;
|
|
33
|
+
for (let i = 0; i < O; i++) {
|
|
34
|
+
const r = n.links[i * 2] ?? 0;
|
|
35
|
+
p[i * 4] = s ? s[0] : f[r * 4] ?? 0.7, p[i * 4 + 1] = s ? s[1] : f[r * 4 + 1] ?? 0.7, p[i * 4 + 2] = s ? s[2] : f[r * 4 + 2] ?? 0.7, p[i * 4 + 3] = 1;
|
|
42
36
|
}
|
|
43
|
-
return { colors:
|
|
37
|
+
return { colors: f, sizes: b, shapes: N, linkColors: p };
|
|
44
38
|
}
|
|
45
|
-
function _(
|
|
39
|
+
function _(n) {
|
|
46
40
|
return {
|
|
47
|
-
simulationGravity:
|
|
48
|
-
simulationRepulsion:
|
|
49
|
-
simulationLinkSpring:
|
|
50
|
-
simulationLinkDistance:
|
|
51
|
-
simulationFriction:
|
|
52
|
-
simulationCluster:
|
|
41
|
+
simulationGravity: n.gravity,
|
|
42
|
+
simulationRepulsion: n.repulsion,
|
|
43
|
+
simulationLinkSpring: n.linkSpring,
|
|
44
|
+
simulationLinkDistance: n.linkDistance,
|
|
45
|
+
simulationFriction: n.friction,
|
|
46
|
+
simulationCluster: n.cluster
|
|
53
47
|
};
|
|
54
48
|
}
|
|
55
|
-
function
|
|
49
|
+
function h(n, t) {
|
|
56
50
|
return {
|
|
57
|
-
backgroundColor:
|
|
51
|
+
backgroundColor: v(k(t, "var(--background)")),
|
|
58
52
|
scalePointsOnZoom: !1,
|
|
59
|
-
renderLinks:
|
|
60
|
-
linkOpacity:
|
|
61
|
-
linkDefaultWidth:
|
|
62
|
-
linkBlending:
|
|
63
|
-
curvedLinks:
|
|
64
|
-
curvedLinkControlPointDistance:
|
|
65
|
-
linkVisibilityDistanceRange:
|
|
53
|
+
renderLinks: n.link.render,
|
|
54
|
+
linkOpacity: n.link.opacity,
|
|
55
|
+
linkDefaultWidth: n.link.width,
|
|
56
|
+
linkBlending: n.link.blend,
|
|
57
|
+
curvedLinks: n.link.curve > 0,
|
|
58
|
+
curvedLinkControlPointDistance: n.link.curve,
|
|
59
|
+
linkVisibilityDistanceRange: n.link.fade,
|
|
66
60
|
linkVisibilityMinTransparency: 0.12,
|
|
67
61
|
renderHoveredPointRing: !0,
|
|
68
|
-
hoveredPointRingColor:
|
|
69
|
-
focusedPointRingColor:
|
|
62
|
+
hoveredPointRingColor: v(k(t, "var(--primary)")),
|
|
63
|
+
focusedPointRingColor: v(k(t, "var(--primary)")),
|
|
70
64
|
pointGreyoutOpacity: 0.1,
|
|
71
65
|
linkGreyoutOpacity: 0.025
|
|
72
66
|
};
|
|
73
67
|
}
|
|
74
68
|
export {
|
|
75
|
-
|
|
69
|
+
h as appearance,
|
|
76
70
|
_ as forces,
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
M as paint,
|
|
72
|
+
T as scaleOf
|
|
79
73
|
};
|
|
80
74
|
//# sourceMappingURL=graph-model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-model.js","sources":["../../src/render/graph-model.ts"],"sourcesContent":["import type { GraphConfig } from \"@cosmos.gl/graph\";\nimport { CHART_SLOTS, categoricalCapacity, categoricalColor } from \"@kanzo-tech/ui\";\nimport { isColour, type Channels } from \"../core/channels\";\nimport type {
|
|
1
|
+
{"version":3,"file":"graph-model.js","sources":["../../src/render/graph-model.ts"],"sourcesContent":["import type { GraphConfig } from \"@cosmos.gl/graph\";\nimport { CHART_SLOTS, categoricalCapacity, categoricalColor } from \"@kanzo-tech/ui\";\nimport { isColour, type Channels } from \"../core/channels\";\nimport type { Encoding, Geometry } from \"../core/load\";\nimport { resolveToken, toHex, type Rgba } from \"./css-color\";\nimport { SHAPE_INDEX, SHAPE_ORDER, SHAPE_OTHER, type Look, type Shape } from \"./graph-looks\";\nimport type { Sim } from \"./graph-sim\";\n\n/**\n * The categorical scale the bindings imply — what colour and what shape a category rank wears.\n *\n * One answer for the GPU buffers, the hover card and the legend, because three answers is how a\n * legend ends up disagreeing with the canvas it explains. Values are CSS strings: the DOM resolves\n * them itself, and the buffer path resolves them once on its way to the GPU.\n *\n * `capacity` is where Other begins, and it is the document's number rather than the token\n * vocabulary's: a set derived from a client's brand names 6 to 8 real categories, not always 8.\n */\nexport function scaleOf(channels: Channels, capacity = CHART_SLOTS) {\n const constant = isColour(channels.fill) ? channels.fill : undefined;\n const shaped = channels.symbol !== undefined;\n return {\n color: (ordinal: number): string => {\n if (constant) return constant;\n return ordinal >= capacity ? \"var(--muted-foreground)\" : categoricalColor(ordinal, undefined, capacity);\n },\n shape: (ordinal: number): Shape => (shaped ? (SHAPE_ORDER[ordinal] ?? SHAPE_OTHER) : \"circle\"),\n };\n}\n\nexport interface Paint {\n colors: Float32Array;\n sizes: Float32Array;\n shapes: Float32Array;\n linkColors: Float32Array;\n}\n\n/**\n * Every per-point and per-link attribute, from the loaded graph and the live theme — what a binding,\n * a look or a theme change re-uploads, and all it re-uploads.\n *\n * `host` is the element the tokens are read against, so `var(--primary)` resolves for the tree the\n * canvas sits in. The ramp is `√value` over the whole graph, and a vertex with no value takes the\n * smallest radius.\n */\nexport function paint(geometry: Geometry, encoding: Encoding, look: Look, host: Element, channels: Channels = {}): Paint {\n const scale = scaleOf(channels, categoricalCapacity(host));\n const rgba = new Map<number, Rgba>();\n const colourOf = (ordinal: number): Rgba => {\n let resolved = rgba.get(ordinal);\n if (!resolved) rgba.set(ordinal, (resolved = resolveToken(host, scale.color(ordinal))));\n return resolved;\n };\n const n = geometry.size;\n const colors = new Float32Array(n * 4);\n const sizes = new Float32Array(n);\n const shapes = new Float32Array(n);\n const ramp = encoding.sizes;\n let lo = 0;\n let span = 1;\n if (ramp) {\n let min = Number.POSITIVE_INFINITY;\n let max = 0;\n for (let i = 0; i < n; i++) {\n const value = ramp[i] as number;\n if (value < min) min = value;\n if (value > max) max = value;\n }\n lo = Math.sqrt(Number.isFinite(min) ? min : 0);\n span = Math.sqrt(max) - lo || 1;\n }\n for (let i = 0; i < n; i++) {\n const ordinal = encoding.ranks[i] ?? 0;\n colors.set(colourOf(ordinal), i * 4);\n const value = ramp ? (ramp[i] as number) : Number.NaN;\n const t = Number.isNaN(value) ? 0 : (Math.sqrt(value) - lo) / span;\n sizes[i] = look.size[0] + t * (look.size[1] - look.size[0]);\n shapes[i] = SHAPE_INDEX[scale.shape(ordinal)] as number;\n }\n\n const count = geometry.links.length / 2;\n const linkColors = new Float32Array(count * 4);\n const neutral = channels.stroke ? resolveToken(host, channels.stroke) : null;\n for (let e = 0; e < count; e++) {\n const src = geometry.links[e * 2] ?? 0;\n // Link alpha stays 1: `linkOpacity` and the distance fade multiply into it, and a second\n // opacity here would compound with both.\n linkColors[e * 4] = neutral ? neutral[0] : (colors[src * 4] ?? 0.7);\n linkColors[e * 4 + 1] = neutral ? neutral[1] : (colors[src * 4 + 1] ?? 0.7);\n linkColors[e * 4 + 2] = neutral ? neutral[2] : (colors[src * 4 + 2] ?? 0.7);\n linkColors[e * 4 + 3] = 1;\n }\n return { colors, sizes, shapes, linkColors };\n}\n\n/** The simulation coefficients, in cosmos.gl's spelling. Shared by construction and every change. */\nexport function forces(sim: Sim): GraphConfig {\n return {\n simulationGravity: sim.gravity,\n simulationRepulsion: sim.repulsion,\n simulationLinkSpring: sim.linkSpring,\n simulationLinkDistance: sim.linkDistance,\n simulationFriction: sim.friction,\n simulationCluster: sim.cluster,\n };\n}\n\n/**\n * Everything the picture needs that is *one number for the whole canvas* — cosmos.gl's uniforms, read\n * fresh on every draw, so a look change costs a `setConfigPartial` and no upload at all.\n */\nexport function appearance(look: Look, host: Element): GraphConfig {\n return {\n backgroundColor: toHex(resolveToken(host, \"var(--background)\")),\n scalePointsOnZoom: false,\n renderLinks: look.link.render,\n linkOpacity: look.link.opacity,\n linkDefaultWidth: look.link.width,\n linkBlending: look.link.blend,\n curvedLinks: look.link.curve > 0,\n curvedLinkControlPointDistance: look.link.curve,\n linkVisibilityDistanceRange: look.link.fade,\n linkVisibilityMinTransparency: 0.12,\n renderHoveredPointRing: true,\n hoveredPointRingColor: toHex(resolveToken(host, \"var(--primary)\")),\n focusedPointRingColor: toHex(resolveToken(host, \"var(--primary)\")),\n pointGreyoutOpacity: 0.1,\n linkGreyoutOpacity: 0.025,\n };\n}\n"],"names":["scaleOf","channels","capacity","CHART_SLOTS","constant","isColour","shaped","ordinal","categoricalColor","SHAPE_ORDER","SHAPE_OTHER","paint","geometry","encoding","look","host","scale","categoricalCapacity","rgba","colourOf","resolved","resolveToken","n","colors","sizes","shapes","ramp","lo","span","min","max","i","value","t","SHAPE_INDEX","count","linkColors","neutral","e","src","forces","sim","appearance","toHex"],"mappings":";;;;AAkBO,SAASA,EAAQC,GAAoBC,IAAWC,GAAa;AAClE,QAAMC,IAAWC,EAASJ,EAAS,IAAI,IAAIA,EAAS,OAAO,QACrDK,IAASL,EAAS,WAAW;AACnC,SAAO;AAAA,IACL,OAAO,CAACM,MACFH,MACGG,KAAWL,IAAW,4BAA4BM,EAAiBD,GAAS,QAAWL,CAAQ;AAAA,IAExG,OAAO,CAACK,MAA4BD,IAAUG,EAAYF,CAAO,KAAKG,IAAe;AAAA,EAAA;AAEzF;AAiBO,SAASC,EAAMC,GAAoBC,GAAoBC,GAAYC,GAAed,IAAqB,IAAW;AACvH,QAAMe,IAAQhB,EAAQC,GAAUgB,EAAoBF,CAAI,CAAC,GACnDG,wBAAW,IAAA,GACXC,IAAW,CAACZ,MAA0B;AAC1C,QAAIa,IAAWF,EAAK,IAAIX,CAAO;AAC/B,WAAKa,KAAUF,EAAK,IAAIX,GAAUa,IAAWC,EAAaN,GAAMC,EAAM,MAAMT,CAAO,CAAC,CAAE,GAC/Ea;AAAA,EACT,GACME,IAAIV,EAAS,MACbW,IAAS,IAAI,aAAaD,IAAI,CAAC,GAC/BE,IAAQ,IAAI,aAAaF,CAAC,GAC1BG,IAAS,IAAI,aAAaH,CAAC,GAC3BI,IAAOb,EAAS;AACtB,MAAIc,IAAK,GACLC,IAAO;AACX,MAAIF,GAAM;AACR,QAAIG,IAAM,OAAO,mBACbC,IAAM;AACV,aAASC,IAAI,GAAGA,IAAIT,GAAGS,KAAK;AAC1B,YAAMC,IAAQN,EAAKK,CAAC;AACpB,MAAIC,IAAQH,MAAKA,IAAMG,IACnBA,IAAQF,MAAKA,IAAME;AAAA,IACzB;AACA,IAAAL,IAAK,KAAK,KAAK,OAAO,SAASE,CAAG,IAAIA,IAAM,CAAC,GAC7CD,IAAO,KAAK,KAAKE,CAAG,IAAIH,KAAM;AAAA,EAChC;AACA,WAAS,IAAI,GAAG,IAAIL,GAAG,KAAK;AAC1B,UAAMf,IAAUM,EAAS,MAAM,CAAC,KAAK;AACrC,IAAAU,EAAO,IAAIJ,EAASZ,CAAO,GAAG,IAAI,CAAC;AACnC,UAAMyB,IAAQN,IAAQA,EAAK,CAAC,IAAe,OAAO,KAC5CO,IAAI,OAAO,MAAMD,CAAK,IAAI,KAAK,KAAK,KAAKA,CAAK,IAAIL,KAAMC;AAC9D,IAAAJ,EAAM,CAAC,IAAIV,EAAK,KAAK,CAAC,IAAImB,KAAKnB,EAAK,KAAK,CAAC,IAAIA,EAAK,KAAK,CAAC,IACzDW,EAAO,CAAC,IAAIS,EAAYlB,EAAM,MAAMT,CAAO,CAAC;AAAA,EAC9C;AAEA,QAAM4B,IAAQvB,EAAS,MAAM,SAAS,GAChCwB,IAAa,IAAI,aAAaD,IAAQ,CAAC,GACvCE,IAAUpC,EAAS,SAASoB,EAAaN,GAAMd,EAAS,MAAM,IAAI;AACxE,WAASqC,IAAI,GAAGA,IAAIH,GAAOG,KAAK;AAC9B,UAAMC,IAAM3B,EAAS,MAAM0B,IAAI,CAAC,KAAK;AAGrC,IAAAF,EAAWE,IAAI,CAAC,IAAID,IAAUA,EAAQ,CAAC,IAAKd,EAAOgB,IAAM,CAAC,KAAK,KAC/DH,EAAWE,IAAI,IAAI,CAAC,IAAID,IAAUA,EAAQ,CAAC,IAAKd,EAAOgB,IAAM,IAAI,CAAC,KAAK,KACvEH,EAAWE,IAAI,IAAI,CAAC,IAAID,IAAUA,EAAQ,CAAC,IAAKd,EAAOgB,IAAM,IAAI,CAAC,KAAK,KACvEH,EAAWE,IAAI,IAAI,CAAC,IAAI;AAAA,EAC1B;AACA,SAAO,EAAE,QAAAf,GAAQ,OAAAC,GAAO,QAAAC,GAAQ,YAAAW,EAAA;AAClC;AAGO,SAASI,EAAOC,GAAuB;AAC5C,SAAO;AAAA,IACL,mBAAmBA,EAAI;AAAA,IACvB,qBAAqBA,EAAI;AAAA,IACzB,sBAAsBA,EAAI;AAAA,IAC1B,wBAAwBA,EAAI;AAAA,IAC5B,oBAAoBA,EAAI;AAAA,IACxB,mBAAmBA,EAAI;AAAA,EAAA;AAE3B;AAMO,SAASC,EAAW5B,GAAYC,GAA4B;AACjE,SAAO;AAAA,IACL,iBAAiB4B,EAAMtB,EAAaN,GAAM,mBAAmB,CAAC;AAAA,IAC9D,mBAAmB;AAAA,IACnB,aAAaD,EAAK,KAAK;AAAA,IACvB,aAAaA,EAAK,KAAK;AAAA,IACvB,kBAAkBA,EAAK,KAAK;AAAA,IAC5B,cAAcA,EAAK,KAAK;AAAA,IACxB,aAAaA,EAAK,KAAK,QAAQ;AAAA,IAC/B,gCAAgCA,EAAK,KAAK;AAAA,IAC1C,6BAA6BA,EAAK,KAAK;AAAA,IACvC,+BAA+B;AAAA,IAC/B,wBAAwB;AAAA,IACxB,uBAAuB6B,EAAMtB,EAAaN,GAAM,gBAAgB,CAAC;AAAA,IACjE,uBAAuB4B,EAAMtB,EAAaN,GAAM,gBAAgB,CAAC;AAAA,IACjE,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,EAAA;AAExB;"}
|
|
@@ -25,9 +25,8 @@ export interface Sim {
|
|
|
25
25
|
*
|
|
26
26
|
* Chosen against a corpus of that size, and they are a starting point rather than a law: a graph
|
|
27
27
|
* two orders of magnitude larger wants less repulsion and more friction, and the measurements in
|
|
28
|
-
* `/docs/graph/benchmarks` say a live simulation is finished by around 200,000 points regardless.
|
|
29
|
-
*
|
|
30
|
-
* knows its corpus should start its users at that answer through the tenant policy.
|
|
28
|
+
* `/docs/graph/benchmarks` say a live simulation is finished by around 200,000 points regardless. A
|
|
29
|
+
* host that knows its corpus starts its users elsewhere through the tenant policy.
|
|
31
30
|
*
|
|
32
31
|
* The values are strings for the reason `lookFrom`'s are: a contributed preference is a string in
|
|
33
32
|
* all three kinds, so an unrecognised namespace rides through a write untouched. A key that is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-sim.d.ts","sourceRoot":"","sources":["../../src/render/graph-sim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iEAAiE;AACjE,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,OAAO,EAAE,MAAM,CAAC;CACjB;AAED
|
|
1
|
+
{"version":3,"file":"graph-sim.d.ts","sourceRoot":"","sources":["../../src/render/graph-sim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iEAAiE;AACjE,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAM,GAAG,GAAG,CAatF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,GAAe,CAAC;AAE1C;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAGpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-sim.js","sources":["../../src/render/graph-sim.ts"],"sourcesContent":["/**\n * The force coefficients, and the one function that builds them from the axes a person chose.\n *\n * `graph-looks.ts`'s sibling, and deliberately its twin: a type the renderer consumes, one builder\n * that reads declared string values, and a default that is the builder called with nothing. What was\n * here before is what was there before — an interface, a `DEFAULT_SIM` table beside it, and a dock\n * that held six of them in React state and drew six sliders by hand.\n *\n * **A coefficient is not appearance, and it is still a preference.** `section.ts` says why it shares\n * the manifest: `tokens` are the colours a document may move, `prefs` are what the person on the\n * screen decides, and somebody tuning a layout until it settles is deciding.\n */\n\n/** Force coefficients, handed straight to the GPU simulation. */\nexport interface Sim {\n gravity: number;\n repulsion: number;\n linkSpring: number;\n linkDistance: number;\n friction: number;\n /** Pull toward the node's group position on the cluster ring. Zero lets the links decide alone. */\n cluster: number;\n}\n\n/**\n * Coefficients that settle a few-hundred-node graph into something readable.\n *\n * Chosen against a corpus of that size, and they are a starting point rather than a law: a graph\n * two orders of magnitude larger wants less repulsion and more friction, and the measurements in\n * `/docs/graph/benchmarks` say a live simulation is finished by around 200,000 points regardless.
|
|
1
|
+
{"version":3,"file":"graph-sim.js","sources":["../../src/render/graph-sim.ts"],"sourcesContent":["/**\n * The force coefficients, and the one function that builds them from the axes a person chose.\n *\n * `graph-looks.ts`'s sibling, and deliberately its twin: a type the renderer consumes, one builder\n * that reads declared string values, and a default that is the builder called with nothing. What was\n * here before is what was there before — an interface, a `DEFAULT_SIM` table beside it, and a dock\n * that held six of them in React state and drew six sliders by hand.\n *\n * **A coefficient is not appearance, and it is still a preference.** `section.ts` says why it shares\n * the manifest: `tokens` are the colours a document may move, `prefs` are what the person on the\n * screen decides, and somebody tuning a layout until it settles is deciding.\n */\n\n/** Force coefficients, handed straight to the GPU simulation. */\nexport interface Sim {\n gravity: number;\n repulsion: number;\n linkSpring: number;\n linkDistance: number;\n friction: number;\n /** Pull toward the node's group position on the cluster ring. Zero lets the links decide alone. */\n cluster: number;\n}\n\n/**\n * Coefficients that settle a few-hundred-node graph into something readable.\n *\n * Chosen against a corpus of that size, and they are a starting point rather than a law: a graph\n * two orders of magnitude larger wants less repulsion and more friction, and the measurements in\n * `/docs/graph/benchmarks` say a live simulation is finished by around 200,000 points regardless. A\n * host that knows its corpus starts its users elsewhere through the tenant policy.\n *\n * The values are strings for the reason `lookFrom`'s are: a contributed preference is a string in\n * all three kinds, so an unrecognised namespace rides through a write untouched. A key that is\n * missing, or that carries a value the section never offered, takes the default below — which is\n * what a graph simulated before any of this existed.\n */\nexport function simFrom(values: Readonly<Record<string, string | undefined>> = {}): Sim {\n const num = (key: string, fallback: number) => {\n const value = Number.parseFloat(values[key] ?? \"\");\n return Number.isFinite(value) ? value : fallback;\n };\n return {\n gravity: num(\"gravity\", 0.14),\n repulsion: num(\"repulsion\", 1.1),\n linkSpring: num(\"link-spring\", 0.6),\n linkDistance: num(\"link-distance\", 18),\n friction: num(\"friction\", 0.86),\n cluster: num(\"cluster\", 0.1),\n };\n}\n\n/**\n * What a canvas simulates with when nobody has chosen anything.\n *\n * **Not on the barrel, and it used to be** — for the reason its twin was: it is `simFrom()`, a\n * second public name for a value already available on request, exported only because `sim` took a\n * whole `Sim` and a host changing `gravity` had to supply the other five.\n */\nexport const DEFAULT_SIM: Sim = simFrom();\n\n/**\n * The coefficients a caller wants different, over the ones this package chose.\n *\n * Flat, so a spread is the whole merge. `undefined` returns the shared constant rather than a copy:\n * the renderer re-heats when the forces change, so a fresh object\n * per render would put energy back into a settled layout on every render.\n */\nexport function resolveSim(patch?: Partial<Sim>): Sim {\n if (!patch) return DEFAULT_SIM;\n return { ...DEFAULT_SIM, ...patch };\n}\n"],"names":["simFrom","values","num","key","fallback","value","DEFAULT_SIM","resolveSim","patch"],"mappings":"AAqCO,SAASA,EAAQC,IAAuD,IAAS;AACtF,QAAMC,IAAM,CAACC,GAAaC,MAAqB;AAC7C,UAAMC,IAAQ,OAAO,WAAWJ,EAAOE,CAAG,KAAK,EAAE;AACjD,WAAO,OAAO,SAASE,CAAK,IAAIA,IAAQD;AAAA,EAC1C;AACA,SAAO;AAAA,IACL,SAASF,EAAI,WAAW,IAAI;AAAA,IAC5B,WAAWA,EAAI,aAAa,GAAG;AAAA,IAC/B,YAAYA,EAAI,eAAe,GAAG;AAAA,IAClC,cAAcA,EAAI,iBAAiB,EAAE;AAAA,IACrC,UAAUA,EAAI,YAAY,IAAI;AAAA,IAC9B,SAASA,EAAI,WAAW,GAAG;AAAA,EAAA;AAE/B;AASO,MAAMI,IAAmBN,EAAA;AASzB,SAASO,EAAWC,GAA2B;AACpD,SAAKA,IACE,EAAE,GAAGF,GAAa,GAAGE,EAAA,IADTF;AAErB;"}
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import { Graph } from '@cosmos.gl/graph';
|
|
2
|
-
import { Resident } from '../core/resident';
|
|
3
2
|
import { GraphStore } from '../core/store';
|
|
4
3
|
import { GraphCommands } from '../core/types';
|
|
5
|
-
import { Composition } from './compose';
|
|
6
4
|
export interface RendererEvents {
|
|
7
5
|
/** After every frame and every camera move — where the overlays repaint. */
|
|
8
6
|
onFrame?: () => void;
|
|
9
|
-
/**
|
|
10
|
-
|
|
7
|
+
/** Where the hovered point is in space, as cosmos.gl reports it: no GPU readback per hover. */
|
|
8
|
+
onHover?: (position: [number, number] | null) => void;
|
|
11
9
|
}
|
|
12
10
|
export interface Renderer extends GraphCommands {
|
|
13
11
|
readonly graph: Graph;
|
|
14
|
-
resident(): Resident;
|
|
15
|
-
composition(): Composition | null;
|
|
16
12
|
/** The theme moved under the canvas: colours are resolved again, nothing else. */
|
|
17
13
|
repaint(): void;
|
|
18
14
|
/** The drawn points inside a screen rectangle or polygon, once the device is ready. */
|
|
@@ -27,11 +23,15 @@ export interface Renderer extends GraphCommands {
|
|
|
27
23
|
* **cosmos.gl's lifetime, and the frame that renders once.** Built once per element; it subscribes to
|
|
28
24
|
* the store and discharges what changed in one `requestAnimationFrame`:
|
|
29
25
|
*
|
|
30
|
-
* - a
|
|
31
|
-
* - a look or a theme change
|
|
32
|
-
* - a selection, a focus or a pin sets
|
|
33
|
-
*
|
|
26
|
+
* - a new graph uploads positions and links, once per corpus; a filter uploads positions alone;
|
|
27
|
+
* - a binding, a look or a theme change uploads colours, sizes and shapes and nothing else;
|
|
28
|
+
* - a selection, a focus or a pin sets config and uploads nothing, and never calls `render()`:
|
|
29
|
+
* `render()` walks every point and link in JS, and `setConfigPartial` asks for its own frame;
|
|
30
|
+
* - the camera reaches nothing: cosmos.gl moves it, and the overlays follow.
|
|
34
31
|
*
|
|
32
|
+
* A vertex's id is its index here, so nothing is resolved between the store and the buffers.
|
|
33
|
+
* `transitionDuration` is 0: the default animates every upload for 800 ms and keeps the loop awake.
|
|
34
|
+
* The live layout is off unless `simulate` or the toolbar asks, and runs from the current positions.
|
|
35
35
|
* Every callback reads the store's latest options, so a host's inline `onFailure` never rebuilds it.
|
|
36
36
|
*/
|
|
37
37
|
export declare function createRenderer(host: HTMLDivElement, store: GraphStore, events?: RendererEvents): Renderer | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/render/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/render/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,eAAe,CAAC;AAM7D,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,QAAS,SAAQ,aAAa;IAC7C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,kFAAkF;IAClF,OAAO,IAAI,IAAI,CAAC;IAChB,uFAAuF;IACvF,GAAG,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;KAAE,GAAG,MAAM,EAAE,CAAC;IACvG,OAAO,IAAI,IAAI,CAAC;CACjB;AAqBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,GAAE,cAAmB,GAAG,QAAQ,GAAG,IAAI,CA8QpH"}
|