@photonviz/core 0.4.0 → 0.5.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 CHANGED
@@ -10,7 +10,9 @@
10
10
  <a href="https://www.npmjs.com/package/@photonviz/core"><img src="https://img.shields.io/npm/v/@photonviz/core?color=cb3837&logo=npm" alt="npm"/></a>
11
11
  <a href="https://www.npmjs.com/package/@photonviz/core"><img src="https://img.shields.io/npm/dm/@photonviz/core?color=cb3837" alt="downloads"/></a>
12
12
  <a href="https://bundlephobia.com/package/@photonviz/core"><img src="https://img.shields.io/bundlephobia/minzip/@photonviz/core?label=minzip" alt="size"/></a>
13
+ · <a href="https://coredumpdev.github.io/photon/docs/">📖 Docs</a>
13
14
  · <a href="https://coredumpdev.github.io/photon/">▶ Live demo</a>
15
+ · <a href="https://coredumpdev.github.io/photon/llms-full.txt">Docs for AI agents</a>
14
16
  </p>
15
17
 
16
18
  <p align="center">
@@ -49,8 +51,8 @@ plot.addLine({ x: xs, y: ys, color: "#60a5fa", width: 2, name: "signal" });
49
51
 
50
52
  | Type | API |
51
53
  | --- | --- |
52
- | Line / Step | `plot.addLine({ x, y, color, width, step })` — real thick lines, round/miter/bevel joins |
53
- | Scatter | `plot.addScatter({ x, y, size, colorBy })` — instanced, colormap by value |
54
+ | Line / Step | `plot.addLine({ x, y, color, width, step, dash })` — real thick lines, round/miter/bevel joins, dashed guides |
55
+ | Scatter / Bubble | `plot.addScatter({ x, y, size, sizes, colors, colorBy })` — instanced; per-point size/colour or colormap by value |
54
56
  | Bar | `plot.addBar({ x, y, width, offset, base })` — grouped / stacked |
55
57
  | Area | `plot.addArea({ x, y, base })` |
56
58
  | Histogram | `plot.addHistogram(values, { bins })` |
@@ -61,11 +63,117 @@ plot.addLine({ x: xs, y: ys, color: "#60a5fa", width: 2, name: "signal" });
61
63
  | Error bar / Stem / Quiver | `plot.addErrorBar` · `addStem` · `addQuiver` |
62
64
  | Candlestick / OHLC | `plot.addCandlestick({ x, open, high, low, close })` · `addOhlc(...)` — live via `updateLast` / `appendCandle` |
63
65
  | Pie / Patches / Graph / Image | `plot.addPie` · `addPatches` · `addGraph` · `addImage` |
64
- | **Finance** | `addHeikinAshi` · `addRenko` · `addBollinger` · `addVolumeProfile` · `addDepth` + indicators `sma`/`ema`/`wma`/`rsi`/`macd`/`vwap`/`atr`/`stochastic`/`keltner`/`obv`/`ichimoku`/`adx`/`superTrend`/`fibRetracements` |
66
+ | **Finance** | `addHeikinAshi` · `addRenko` · `addBollinger` · `addVolumeProfile` · `addDepth` · `addDrawdown` + indicators `sma`/`ema`/`wma`/`rsi`/`macd`/`vwap`/`atr`/`stochastic`/`keltner`/`obv`/`ichimoku`/`adx`/`superTrend`/`fibRetracements`/`cci`/`mfi`/`williamsR`/`aroon`/`donchian`/`parabolicSar`/`pivotPoints` + `resampleOhlc`/`drawdown` |
65
67
  | **Diagrams** | `plot.addTreemap` · `addFunnel` · `addSunburst` · `addGauge` · `addSankey` · `addChord` · `addParallelCoordinates` — pure `*Layout` fns exported too |
68
+ | **ML / DL** | `addTrainingCurves` · `addConfusionMatrix` · `addRocCurve` · `addPrCurve` · `addCalibration` · `addEmbedding` · `addDecisionBoundary` · `addFeatureImportance` · `addShapBeeswarm` · `addPartialDependence` · `addAttentionMap` · `addRidgeline` · `addPredVsActual` · `addResiduals` · `addLiftCurve` · `addLearningCurve` + pure `confusionMatrix`/`rocCurve`/`prCurve`/`calibrationCurve`/`classificationReport`/`rocCurveOvR`/`liftCurve`/`r2`/`rmse`/`mae`/`logLoss`/`brierScore`/`pca` |
69
+ | **Statistics** | `addRegression` (OLS + CI band or LOESS) · `addEcdf` · `addCorrMatrix` · `addPsd` + pure `linearRegression`/`loess`/`ecdf`/`zscore`/`corrMatrix`/`welch`/`savitzkyGolay`/`crossCorrelate`/`windowFunction` |
70
+ | **Model architecture** | `addModelGraph(plot, { graph })` — layered DAG · `addModelGraph3D(plot3d, { graph })` — tensor-shaped blocks (see below) |
66
71
 
67
72
  **Polar** — `new PolarPlot(el)` with `addLine` / `addScatter` (drag to rotate, wheel to zoom).
68
- **3D** — `new Plot3D(el)` with `addSurface` / `addPointCloud` / `addLine3D` / `addBar3D` / `addQuiver3D` / `addContour3D` / `addIsosurface` / `addVolume` (orbit camera).
73
+ **3D** — `new Plot3D(el)` with `addSurface` / `addPointCloud` / `addLine3D` / `addBar3D` / `addBoxes3D` / `addQuiver3D` / `addContour3D` / `addIsosurface` / `addVolume` (orbit camera, perspective or orthographic).
74
+
75
+ ## Model architecture graphs
76
+
77
+ Render the layers of a real model — in 2D as a Netron-style DAG, or in 3D as
78
+ cuboids sized from each layer's output tensor. Both consume the same
79
+ `ModelGraph`, so one export drives either view.
80
+
81
+ ```ts
82
+ import { Plot, Plot3D, addModelGraph, addModelGraph3D, modelGraphFromTorchFx } from "@photonviz/core";
83
+
84
+ const graph = modelGraphFromTorchFx(await (await fetch("/model.json")).json());
85
+
86
+ // Flat DAG — residual/skip edges route around the trunk, boxes colored by layer family.
87
+ addModelGraph(new Plot(el, { hover: false }), { graph, direction: "horizontal", sizeBy: "params" });
88
+
89
+ // Tensor blocks — feature maps shrink while channel depth grows.
90
+ addModelGraph3D(new Plot3D(el3d, { aspectMode: "data", showAxes: false, projection: "orthographic" }), { graph });
91
+ ```
92
+
93
+ Adapters (all pure, all zero-dependency) — dump the JSON in Python, load it in the browser:
94
+
95
+ <details><summary><b>PyTorch</b> — <code>modelGraphFromTorchFx</code> (keeps skip connections)</summary>
96
+
97
+ ```python
98
+ import json, torch
99
+ from torch.fx import symbolic_trace
100
+ from torch.fx.passes.shape_prop import ShapeProp
101
+
102
+ gm = symbolic_trace(model)
103
+ ShapeProp(gm).propagate(torch.randn(1, 3, 224, 224)) # optional: fills in shapes
104
+ mods = dict(gm.named_modules())
105
+
106
+ def shape_of(n):
107
+ t = n.meta.get("tensor_meta")
108
+ return list(t.shape)[1:] if t is not None else None # drop the batch dim
109
+
110
+ json.dump([{
111
+ "name": n.name,
112
+ "op": n.op,
113
+ "target": str(n.target),
114
+ "args": [a.name for a in n.all_input_nodes],
115
+ "moduleType": type(mods[str(n.target)]).__name__ if n.op == "call_module" else None,
116
+ "shape": shape_of(n),
117
+ "params": sum(p.numel() for p in mods[str(n.target)].parameters())
118
+ if n.op == "call_module" else None,
119
+ } for n in gm.graph.nodes], open("model.json", "w"))
120
+ ```
121
+ </details>
122
+
123
+ <details><summary><b>TensorFlow / Keras</b> — <code>modelGraphFromKeras</code></summary>
124
+
125
+ ```python
126
+ import json
127
+ json.dump({
128
+ "model": json.loads(model.to_json()),
129
+ "shapes": {l.name: list(l.output_shape)[1:] for l in model.layers},
130
+ "params": {l.name: l.count_params() for l in model.layers},
131
+ }, open("model.json", "w"))
132
+ ```
133
+
134
+ Sequential models chain in declaration order; functional models are wired from
135
+ `inbound_nodes` (both the Keras 2 and Keras 3 nestings are understood).
136
+ </details>
137
+
138
+ <details><summary><b>scikit-learn</b> — <code>modelGraphFromSklearn</code> / <code>mlpModel</code></summary>
139
+
140
+ ```python
141
+ from sklearn.pipeline import Pipeline, FeatureUnion
142
+ from sklearn.compose import ColumnTransformer
143
+
144
+ def to_step(name, est, columns=None):
145
+ node = {"name": name, "type": type(est).__name__}
146
+ if columns is not None: node["columns"] = list(columns)
147
+ if isinstance(est, Pipeline):
148
+ node.update(steps=[to_step(n, e) for n, e in est.steps], mode="sequential")
149
+ elif isinstance(est, ColumnTransformer):
150
+ node.update(steps=[to_step(n, e, c) for n, e, c in est.transformers], mode="parallel")
151
+ elif isinstance(est, FeatureUnion):
152
+ node.update(steps=[to_step(n, e) for n, e in est.transformer_list], mode="parallel")
153
+ return node
154
+
155
+ json.dump(to_step("pipe", pipeline), open("model.json", "w"))
156
+ ```
157
+
158
+ An `MLPClassifier` is a real layer stack instead:
159
+ `mlpModel([n_features, *clf.hidden_layer_sizes, clf.n_outputs_])`.
160
+ </details>
161
+
162
+ <details><summary><b>ONNX</b> — <code>modelGraphFromOnnx</code> (any framework)</summary>
163
+
164
+ ```python
165
+ import json, onnx
166
+ from google.protobuf.json_format import MessageToDict
167
+
168
+ m = onnx.shape_inference.infer_shapes(onnx.load("model.onnx"))
169
+ json.dump(MessageToDict(m.graph), open("model.json", "w"))
170
+ ```
171
+ </details>
172
+
173
+ Layers are colored by family (`layerCategory` → conv / linear / norm / activation
174
+ / pool / attention / …); override with `colors`. The layout itself is pure and
175
+ exported — `modelLayout(graph, opts)` gives box centers, sizes, ranks and routed
176
+ edge paths if you want to draw it yourself.
69
177
 
70
178
  ## Features
71
179
 
@@ -76,6 +184,9 @@ plot.addLine({ x: xs, y: ys, color: "#60a5fa", width: 2, name: "signal" });
76
184
  - **Drawing tools** — `new Plot(el, { drawingTools: true })` adds trendline / horizontal / ray / Fibonacci / rectangle tools; drawings are editable (drag handles, relabel, recolor, delete). API: `setDrawTool`/`getDrawTool`/`addDrawing`/`clearDrawings`.
77
185
  - **Image export** — every plot has `toDataURL()` / `toBlob()` / `downloadImage()` / `copyToClipboard()` + a toolbar download-PNG button; helpers `canvasToBlob` / `downloadCanvas` / `copyCanvasToClipboard` exported.
78
186
  - **Data adapters** — `parseCSV(text)` → a `Table` (`.column()` / `.numeric()`), and `lttb(x, y, threshold)` for downsampling long line series.
187
+ - **Colorbar** — layers that map values to colours report `colorInfo()` and the plot draws a bar per scale (on by default; `colorbar: false` opts out).
188
+ - **Colour** — 12 colormaps (sequential / diverging / cyclic) + 4 categorical palettes; pass inline colours or register your own with `registerColormap` / `registerPalette`.
189
+ - **Interactive legend** — click an entry to hide/show a series; the auto axes re-fit to what is left. `plot.toggleLayer()` / `setLayerVisible()` / `onVisibilityChange()`.
79
190
  - **Accessibility** — plots render as `role="img"` with an auto-summarized `aria-label`; override via `ariaLabel` / `setAriaLabel()` / `describe()`.
80
191
  - **Many charts, one context** — a single shared WebGL2 context backs every plot, so a page can hold dozens.
81
192