@photonviz/core 0.1.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.
@@ -0,0 +1,961 @@
1
+ /** A single axis tick. Only `value` is required; everything else has sensible defaults. */
2
+ interface Tick {
3
+ /** Position in data space. */
4
+ value: number;
5
+ /** Text shown next to the tick. If omitted, the axis `format` fn is used. */
6
+ label?: string;
7
+ /** Minor ticks are short, unlabeled, and (by default) draw no grid line. */
8
+ minor?: boolean;
9
+ /** Whether this tick draws a grid line. Defaults to `true` for major, `false` for minor. */
10
+ grid?: boolean;
11
+ }
12
+ /** How a user may specify ticks for an axis. */
13
+ type TicksSpec = number[] | Tick[] | ((min: number, max: number) => Array<number | Tick>);
14
+ interface AxisConfig {
15
+ /** Ticks: an array of positions, an array of `Tick` objects, or a generator fn. */
16
+ ticks?: TicksSpec;
17
+ /** Extra ticks layered on top of the auto ticks (only used when `ticks` is not set). */
18
+ addTicks?: Array<number | Tick>;
19
+ /** Formats a numeric tick value into a label when the tick has no explicit `label`. */
20
+ format?: (value: number) => string;
21
+ /** Auto minor ticks: `true` for a default count, or a number of subdivisions per major interval. */
22
+ minorTicks?: boolean | number;
23
+ /** Axis title, drawn along the axis. */
24
+ title?: string;
25
+ }
26
+ type Dim = "x" | "y";
27
+ /**
28
+ * Pointer interaction modes:
29
+ * - `pan` drag pans the view, wheel zooms both axes
30
+ * - `box` drag selects a rectangle and zooms into it (both axes)
31
+ * - `box-x` drag selects an x-range only (full height band) — X-only zoom
32
+ * - `box-y` drag selects a y-range only (full width band) — Y-only zoom
33
+ */
34
+ type InteractionMode = "pan" | "box" | "box-x" | "box-y";
35
+ type Range = readonly [number, number];
36
+ interface Bounds {
37
+ x: Range;
38
+ y: Range;
39
+ }
40
+ /** RGBA in 0..1. */
41
+ type Color = readonly [number, number, number, number];
42
+
43
+ /** Per-axis view state handed to a layer each frame. `lo`/`hi` are raw data-space. */
44
+ interface AxisFrame {
45
+ lo: number;
46
+ hi: number;
47
+ log: boolean;
48
+ }
49
+
50
+ /** State handed to a layer each frame so it can transform data to clip space. */
51
+ interface DrawState {
52
+ gl: WebGL2RenderingContext;
53
+ x: AxisFrame;
54
+ y: AxisFrame;
55
+ /** Plot-region size in device pixels. */
56
+ pixelWidth: number;
57
+ pixelHeight: number;
58
+ /** devicePixelRatio, for converting pixel-space widths/sizes. */
59
+ dpr: number;
60
+ }
61
+ /** A drawable data series backed by GPU buffers. */
62
+ interface Layer {
63
+ readonly id: string;
64
+ /** The y axis this layer is bound to. */
65
+ readonly yAxis: string;
66
+ /** Data-space bounds of this layer, for autoscaling. `null` if empty. */
67
+ bounds(): {
68
+ x: Range;
69
+ y: Range;
70
+ } | null;
71
+ draw(state: DrawState): void;
72
+ dispose(): void;
73
+ }
74
+
75
+ interface AreaOptions {
76
+ x: ArrayLike<number>;
77
+ y: ArrayLike<number>;
78
+ /** Lower edge(s). Number or per-point array — pass cumulative to stack. */
79
+ base?: number | ArrayLike<number>;
80
+ color?: string | Color;
81
+ name?: string;
82
+ yAxis?: string;
83
+ }
84
+ declare class AreaLayer implements Layer {
85
+ readonly id: string;
86
+ readonly name: string;
87
+ readonly colorCss: string;
88
+ readonly yAxis: string;
89
+ private gl;
90
+ private program;
91
+ private vao;
92
+ private buffer;
93
+ private uniforms;
94
+ private vertexCount;
95
+ private color;
96
+ private xRef;
97
+ private yRef;
98
+ private xBounds;
99
+ private yBounds;
100
+ constructor(gl: WebGL2RenderingContext, opts: AreaOptions);
101
+ /** Replace the area data and re-upload (for streaming). */
102
+ setData(x: ArrayLike<number>, y: ArrayLike<number>, base?: number | ArrayLike<number>): void;
103
+ bounds(): {
104
+ x: Range;
105
+ y: Range;
106
+ } | null;
107
+ draw(state: DrawState): void;
108
+ dispose(): void;
109
+ }
110
+
111
+ interface BarOptions {
112
+ /** Bar center positions (data space). */
113
+ x: ArrayLike<number>;
114
+ /** Bar top values. */
115
+ y: ArrayLike<number>;
116
+ /** Baseline(s). Number or per-bar array — pass cumulative values to stack. */
117
+ base?: number | ArrayLike<number>;
118
+ /** Bar width in data units. Defaults to 80% of the median spacing. */
119
+ width?: number;
120
+ /** Shift bars by this many data units (use for grouped bars). */
121
+ offset?: number;
122
+ color?: string | Color;
123
+ name?: string;
124
+ yAxis?: string;
125
+ }
126
+ declare class BarLayer implements Layer {
127
+ readonly id: string;
128
+ readonly name: string;
129
+ readonly colorCss: string;
130
+ readonly yAxis: string;
131
+ private gl;
132
+ private program;
133
+ private vao;
134
+ private buffers;
135
+ private uniforms;
136
+ private count;
137
+ private color;
138
+ private barWidth;
139
+ private offset;
140
+ private xRef;
141
+ private yRef;
142
+ private xBounds;
143
+ private yBounds;
144
+ constructor(gl: WebGL2RenderingContext, opts: BarOptions);
145
+ private buildRects;
146
+ /** Replace bar values and re-upload (for streaming). */
147
+ setData(x: ArrayLike<number>, y: ArrayLike<number>, base?: number | ArrayLike<number>): void;
148
+ bounds(): {
149
+ x: Range;
150
+ y: Range;
151
+ } | null;
152
+ draw(state: DrawState): void;
153
+ dispose(): void;
154
+ }
155
+
156
+ interface BoxGroup {
157
+ /** X-axis center for this group. */
158
+ position: number;
159
+ values: ArrayLike<number>;
160
+ color?: string | Color;
161
+ label?: string;
162
+ }
163
+ interface BoxOptions {
164
+ groups: BoxGroup[];
165
+ /** Group width in data units. */
166
+ width?: number;
167
+ /** Draw the Tukey box + whiskers (default true). */
168
+ box?: boolean;
169
+ /** Draw a violin (KDE) shape behind/instead of the box (default false). */
170
+ violin?: boolean;
171
+ yAxis?: string;
172
+ }
173
+ declare class BoxLayer implements Layer {
174
+ readonly id: string;
175
+ readonly yAxis: string;
176
+ private gl;
177
+ private program;
178
+ private vao;
179
+ private buffer;
180
+ private uniforms;
181
+ private xRef;
182
+ private yRef;
183
+ private xBounds;
184
+ private yBounds;
185
+ private triCount;
186
+ private lineStart;
187
+ private lineCount;
188
+ private pointStart;
189
+ private pointCount;
190
+ constructor(gl: WebGL2RenderingContext, opts: BoxOptions);
191
+ bounds(): {
192
+ x: Range;
193
+ y: Range;
194
+ } | null;
195
+ draw(state: DrawState): void;
196
+ dispose(): void;
197
+ }
198
+
199
+ type RGB = [number, number, number];
200
+ type ColormapName = "viridis" | "plasma" | "coolwarm" | "grayscale";
201
+ /** Returns a `(t in 0..1) => RGB` sampler for the named colormap. */
202
+ declare function colormap(name?: ColormapName): (t: number) => RGB;
203
+
204
+ interface ContourOptions {
205
+ /** Row-major grid values, length `cols * rows` (row 0 at the bottom). */
206
+ values: ArrayLike<number>;
207
+ cols: number;
208
+ rows: number;
209
+ extent: {
210
+ x: Range;
211
+ y: Range;
212
+ };
213
+ /** Iso levels: an explicit list, or a count of evenly-spaced levels. */
214
+ levels?: number[] | number;
215
+ /** Single line color; if omitted, levels are colored by a colormap. */
216
+ color?: string | Color;
217
+ colormap?: ColormapName;
218
+ yAxis?: string;
219
+ }
220
+ declare class ContourLayer implements Layer {
221
+ readonly id: string;
222
+ readonly yAxis: string;
223
+ private gl;
224
+ private program;
225
+ private vao;
226
+ private buffer;
227
+ private uniforms;
228
+ private vertexCount;
229
+ private xRef;
230
+ private yRef;
231
+ private ext;
232
+ constructor(gl: WebGL2RenderingContext, opts: ContourOptions);
233
+ bounds(): {
234
+ x: Range;
235
+ y: Range;
236
+ };
237
+ draw(state: DrawState): void;
238
+ dispose(): void;
239
+ }
240
+
241
+ interface HeatmapOptions {
242
+ /** Row-major grid values, length `cols * rows` (row 0 at the bottom). */
243
+ values: ArrayLike<number>;
244
+ cols: number;
245
+ rows: number;
246
+ /** Data-space extent the grid spans. */
247
+ extent: {
248
+ x: Range;
249
+ y: Range;
250
+ };
251
+ colormap?: ColormapName;
252
+ /** Value range mapped to the colormap. Defaults to the data min/max. */
253
+ domain?: Range;
254
+ /** Bilinear filtering (default true) vs. hard cells. */
255
+ smooth?: boolean;
256
+ yAxis?: string;
257
+ }
258
+ declare class HeatmapLayer implements Layer {
259
+ readonly id: string;
260
+ readonly yAxis: string;
261
+ private gl;
262
+ private program;
263
+ private vao;
264
+ private buffer;
265
+ private texture;
266
+ private uniforms;
267
+ private xRef;
268
+ private yRef;
269
+ private ext;
270
+ constructor(gl: WebGL2RenderingContext, opts: HeatmapOptions);
271
+ bounds(): {
272
+ x: Range;
273
+ y: Range;
274
+ };
275
+ draw(state: DrawState): void;
276
+ dispose(): void;
277
+ }
278
+
279
+ interface HexbinOptions {
280
+ x: ArrayLike<number>;
281
+ y: ArrayLike<number>;
282
+ /** Hex radius in data units. Defaults to ~1/30 of the x-extent. */
283
+ radius?: number;
284
+ colormap?: ColormapName;
285
+ /** Count range mapped to the colormap. Defaults to [1, maxCount]. */
286
+ domain?: Range;
287
+ yAxis?: string;
288
+ }
289
+ declare class HexbinLayer implements Layer {
290
+ readonly id: string;
291
+ readonly yAxis: string;
292
+ private gl;
293
+ private program;
294
+ private vao;
295
+ private buffers;
296
+ private uniforms;
297
+ private radius;
298
+ private cellCount;
299
+ private xRef;
300
+ private yRef;
301
+ private xBounds;
302
+ private yBounds;
303
+ constructor(gl: WebGL2RenderingContext, opts: HexbinOptions);
304
+ bounds(): {
305
+ x: Range;
306
+ y: Range;
307
+ } | null;
308
+ draw(state: DrawState): void;
309
+ dispose(): void;
310
+ }
311
+
312
+ interface LineOptions {
313
+ x: ArrayLike<number>;
314
+ y: ArrayLike<number>;
315
+ color?: string | Color;
316
+ /** Line width in CSS pixels (real thickness via GPU triangle expansion). */
317
+ width?: number;
318
+ name?: string;
319
+ yAxis?: string;
320
+ step?: "before" | "after" | "center";
321
+ /** Round joins/caps (default) or square/butt ends. */
322
+ join?: "round" | "butt";
323
+ /**
324
+ * Min/max decimate very large series to ~2 points per pixel column when
325
+ * zoomed out (preserves peaks). Requires monotonic x; auto-detected. Default true.
326
+ */
327
+ decimate?: boolean;
328
+ }
329
+ declare class LineLayer implements Layer {
330
+ readonly id: string;
331
+ readonly name: string;
332
+ readonly colorCss: string;
333
+ readonly yAxis: string;
334
+ private gl;
335
+ private program;
336
+ private fullVao;
337
+ private decVao;
338
+ private cornerBuf;
339
+ private posBuf;
340
+ private decBuf;
341
+ private uniforms;
342
+ private count;
343
+ private color;
344
+ private width;
345
+ private round;
346
+ private decimateOn;
347
+ private monotonic;
348
+ private step?;
349
+ private xRef;
350
+ private yRef;
351
+ private xs;
352
+ private ys;
353
+ private xBounds;
354
+ private yBounds;
355
+ private decKey;
356
+ private decSegments;
357
+ constructor(gl: WebGL2RenderingContext, opts: LineOptions);
358
+ private configureVao;
359
+ bounds(): {
360
+ x: Range;
361
+ y: Range;
362
+ } | null;
363
+ nearestByX(x: number): {
364
+ x: number;
365
+ y: number;
366
+ index: number;
367
+ } | null;
368
+ /** Replace the series data and re-upload the GPU buffer (for streaming). */
369
+ setData(x: ArrayLike<number>, y: ArrayLike<number>): void;
370
+ /**
371
+ * Rebuild the min/max-decimated buffer for the visible x-window if it changed.
372
+ * Returns the segment count to draw from `decVao`, or null to draw everything.
373
+ */
374
+ private decimate;
375
+ draw(state: DrawState): void;
376
+ dispose(): void;
377
+ }
378
+
379
+ interface ScatterOptions {
380
+ x: ArrayLike<number>;
381
+ y: ArrayLike<number>;
382
+ color?: string | Color;
383
+ /** Marker diameter in CSS pixels. */
384
+ size?: number;
385
+ name?: string;
386
+ yAxis?: string;
387
+ /** Color each point by a value through a colormap. */
388
+ colorBy?: {
389
+ values: ArrayLike<number>;
390
+ colormap?: ColormapName;
391
+ /** Value range mapped to [0,1]. Defaults to the data min/max. */
392
+ domain?: Range;
393
+ };
394
+ }
395
+ declare class ScatterLayer implements Layer {
396
+ readonly id: string;
397
+ readonly name: string;
398
+ readonly colorCss: string;
399
+ readonly yAxis: string;
400
+ private gl;
401
+ private program;
402
+ private vao;
403
+ private buffers;
404
+ private uniforms;
405
+ private count;
406
+ private size;
407
+ private color;
408
+ private useVertexColor;
409
+ private xRef;
410
+ private yRef;
411
+ private xs;
412
+ private ys;
413
+ private xBounds;
414
+ private yBounds;
415
+ constructor(gl: WebGL2RenderingContext, opts: ScatterOptions);
416
+ bounds(): {
417
+ x: Range;
418
+ y: Range;
419
+ } | null;
420
+ nearestByX(x: number): {
421
+ x: number;
422
+ y: number;
423
+ index: number;
424
+ } | null;
425
+ /** Replace point positions and re-upload (for streaming). Keeps uniform color. */
426
+ setData(x: ArrayLike<number>, y: ArrayLike<number>): void;
427
+ draw(state: DrawState): void;
428
+ dispose(): void;
429
+ }
430
+
431
+ /**
432
+ * A scale maps data-space values to normalized [0,1] and back, and knows how to
433
+ * generate its own "nice" ticks + default label format. `log` tells layers to
434
+ * apply a log10 transform on the GPU.
435
+ */
436
+ interface Scale {
437
+ readonly type: string;
438
+ readonly log: boolean;
439
+ domain: Range;
440
+ norm(value: number): number;
441
+ invert(t: number): number;
442
+ /** Auto ticks appropriate for this scale type. */
443
+ ticks(target?: number): Tick[];
444
+ /** Default label formatter for a tick value. */
445
+ formatTick(value: number): string;
446
+ }
447
+ declare class LinearScale implements Scale {
448
+ readonly type = "linear";
449
+ readonly log = false;
450
+ domain: Range;
451
+ constructor(domain?: Range);
452
+ norm(value: number): number;
453
+ invert(t: number): number;
454
+ ticks(target?: number): Tick[];
455
+ formatTick(value: number): string;
456
+ }
457
+ declare class LogScale implements Scale {
458
+ readonly type = "log";
459
+ readonly log = true;
460
+ domain: Range;
461
+ constructor(domain?: Range);
462
+ private static sanitize;
463
+ private get la();
464
+ private get lb();
465
+ norm(value: number): number;
466
+ invert(t: number): number;
467
+ ticks(): Tick[];
468
+ formatTick(value: number): string;
469
+ }
470
+ /** Linear over epoch-millisecond values, with calendar-aware ticks/labels. */
471
+ declare class TimeScale implements Scale {
472
+ readonly type = "time";
473
+ readonly log = false;
474
+ domain: Range;
475
+ constructor(domain?: Range);
476
+ norm(value: number): number;
477
+ invert(t: number): number;
478
+ private chooseStep;
479
+ ticks(target?: number): Tick[];
480
+ formatTick(value: number): string;
481
+ }
482
+ type ScaleType = "linear" | "log" | "time";
483
+ declare function makeScale(type: ScaleType, domain?: Range): Scale;
484
+
485
+ /** Pixel geometry of the plot, in CSS pixels. The plot region excludes margins. */
486
+ interface Layout {
487
+ cssWidth: number;
488
+ cssHeight: number;
489
+ margin: {
490
+ top: number;
491
+ right: number;
492
+ bottom: number;
493
+ left: number;
494
+ };
495
+ }
496
+ interface Theme {
497
+ axis: string;
498
+ grid: string;
499
+ gridMinor: string;
500
+ text: string;
501
+ font: string;
502
+ }
503
+ declare const lightTheme: Theme;
504
+ declare const darkTheme: Theme;
505
+
506
+ interface AxisScaleOptions {
507
+ type?: ScaleType;
508
+ /** Fixed domain. If omitted, the axis autoscales to the data. */
509
+ domain?: Range;
510
+ }
511
+ interface YAxisOptions extends AxisConfig {
512
+ type?: ScaleType;
513
+ domain?: Range;
514
+ /** Which side to draw the axis on. Default `"right"` for secondary axes. */
515
+ side?: "left" | "right";
516
+ /** Axis + label color (secondary axes often match their series). */
517
+ color?: string;
518
+ }
519
+ interface PlotOptions {
520
+ scales?: {
521
+ x?: AxisScaleOptions;
522
+ y?: AxisScaleOptions;
523
+ };
524
+ axes?: {
525
+ x?: AxisConfig;
526
+ y?: AxisConfig;
527
+ };
528
+ theme?: "light" | "dark" | Theme;
529
+ margin?: Partial<Layout["margin"]>;
530
+ /** Enable wheel-zoom and drag interaction. Default true. */
531
+ interactive?: boolean;
532
+ /** Show the built-in toolbar (home + pan/box/X/Y zoom modes). Default true. */
533
+ toolbar?: boolean;
534
+ /** Initial interaction mode. Default `"box"`. */
535
+ mode?: InteractionMode;
536
+ /** Enable hover crosshair + tooltip. Default true. */
537
+ hover?: boolean;
538
+ }
539
+ /**
540
+ * The imperative core plot. Owns a stack of three canvases (grid / WebGL data /
541
+ * axis overlay), a shared x scale, and one-or-more named y axes.
542
+ */
543
+ declare class Plot {
544
+ private container;
545
+ private gridCanvas;
546
+ private dataCanvas;
547
+ private axisCanvas;
548
+ private gridCtx;
549
+ private dataCtx;
550
+ private axisCtx;
551
+ private gl;
552
+ /** Shared offscreen WebGL canvas we render into, then blit onto `dataCanvas`. */
553
+ private sharedCanvas;
554
+ private scaleX;
555
+ private axisX;
556
+ private autoX;
557
+ private initialX;
558
+ /** Named y axes, insertion-ordered. `"y"` is always the primary. */
559
+ private yAxes;
560
+ private layers;
561
+ private theme;
562
+ private isDark;
563
+ private baseMargin;
564
+ private dpr;
565
+ private resizeObserver;
566
+ private frameRequested;
567
+ private mode;
568
+ private selectionDiv;
569
+ private modeChangeCbs;
570
+ private toolbarHandle;
571
+ private hoverEnabled;
572
+ private hoverPx;
573
+ private tooltip;
574
+ constructor(container: HTMLElement, options?: PlotOptions);
575
+ private makeCanvas;
576
+ /** Margins grow to make room for extra y axes on each side. */
577
+ private computeMargin;
578
+ private layout;
579
+ /** Pixel x-position (and title x) for each y axis, by draw order per side. */
580
+ private yAxisPositions;
581
+ private register;
582
+ addLine(opts: LineOptions): LineLayer;
583
+ addScatter(opts: ScatterOptions): ScatterLayer;
584
+ addBar(opts: BarOptions): BarLayer;
585
+ addArea(opts: AreaOptions): AreaLayer;
586
+ addHeatmap(opts: HeatmapOptions): HeatmapLayer;
587
+ addBox(opts: BoxOptions): BoxLayer;
588
+ addHexbin(opts: HexbinOptions): HexbinLayer;
589
+ addContour(opts: ContourOptions): ContourLayer;
590
+ /** Compute an STFT of `signal` and render it as a heatmap (time × frequency). */
591
+ addHeatmapSpectrogram(signal: ArrayLike<number>, opts?: {
592
+ fftSize?: number;
593
+ hop?: number;
594
+ sampleRate?: number;
595
+ colormap?: HeatmapOptions["colormap"];
596
+ }): HeatmapLayer;
597
+ /** Bin raw `values` and render a histogram as bars. */
598
+ addHistogram(values: ArrayLike<number>, opts?: {
599
+ bins?: number;
600
+ range?: [number, number];
601
+ color?: string;
602
+ name?: string;
603
+ yAxis?: string;
604
+ }): BarLayer;
605
+ /** Register an additional named y axis. Series opt in via `addLine({ yAxis })`. */
606
+ addYAxis(id: string, opts?: YAxisOptions): void;
607
+ removeLayer(layer: Layer): void;
608
+ /** Configure ticks/format/title for the x axis or a y axis (default primary "y"). */
609
+ setAxis(dim: Dim | string, config: Partial<AxisConfig>): void;
610
+ /** Set (or lock) the visible domain. `y` targets the primary axis; use `yAxes` for others. */
611
+ setView(view: {
612
+ x?: Range;
613
+ y?: Range;
614
+ yAxes?: Record<string, Range>;
615
+ }): void;
616
+ private setYDomain;
617
+ setMode(mode: InteractionMode): void;
618
+ getMode(): InteractionMode;
619
+ onModeChange(cb: (mode: InteractionMode) => void): void;
620
+ /** Reset to the home view: explicit domains restored, auto axes re-fit to data. */
621
+ home(): void;
622
+ /** Re-fit auto axes to the data: x over all series, each y axis over its own series. */
623
+ autoscale(): void;
624
+ destroy(): void;
625
+ private resize;
626
+ requestRender(): void;
627
+ private primaryY;
628
+ render(): void;
629
+ private renderHover;
630
+ private updateTooltip;
631
+ private updateCursor;
632
+ private axisLock;
633
+ /**
634
+ * Which region the pointer is over: the plot body, the x-axis strip (below),
635
+ * or a specific y-axis strip (in a side margin). Dragging an axis strip pans
636
+ * just that axis.
637
+ */
638
+ private zoneAt;
639
+ private panX;
640
+ private panY;
641
+ private attachInteraction;
642
+ private setHover;
643
+ private drawSelection;
644
+ private applySelection;
645
+ private zoomAround;
646
+ }
647
+
648
+ /** The subset of Plot the toolbar drives. Kept minimal to avoid an import cycle. */
649
+ interface ToolbarHost {
650
+ setMode(mode: InteractionMode): void;
651
+ getMode(): InteractionMode;
652
+ home(): void;
653
+ onModeChange(cb: (mode: InteractionMode) => void): void;
654
+ }
655
+ interface ToolbarTheme {
656
+ bg: string;
657
+ fg: string;
658
+ border: string;
659
+ activeBg: string;
660
+ activeFg: string;
661
+ hoverBg: string;
662
+ }
663
+ /** Build a floating toolbar in `container` and wire it to the plot host. */
664
+ declare function createToolbar(container: HTMLElement, host: ToolbarHost, dark: boolean): {
665
+ element: HTMLElement;
666
+ destroy: () => void;
667
+ };
668
+
669
+ interface PolarOptions {
670
+ theme?: "light" | "dark" | Theme;
671
+ /** Angle unit of input theta values. Default `"rad"`. */
672
+ angleUnit?: "rad" | "deg";
673
+ /** Fixed max radius. If omitted, autoscales to the data. */
674
+ maxRadius?: number;
675
+ margin?: number;
676
+ }
677
+ interface PolarLineOptions {
678
+ theta: ArrayLike<number>;
679
+ r: ArrayLike<number>;
680
+ color?: string | Color;
681
+ width?: number;
682
+ /** Connect the last point back to the first. */
683
+ closed?: boolean;
684
+ }
685
+ interface PolarScatterOptions {
686
+ theta: ArrayLike<number>;
687
+ r: ArrayLike<number>;
688
+ color?: string | Color;
689
+ size?: number;
690
+ }
691
+ /** A live handle to update a polar series with new (theta, r) data. */
692
+ interface PolarSeries {
693
+ setData(theta: ArrayLike<number>, r: ArrayLike<number>): void;
694
+ }
695
+ /** A polar (r, θ) plot: concentric radial grid + angular spokes, WebGL data. */
696
+ declare class PolarPlot {
697
+ private container;
698
+ private gridCanvas;
699
+ private dataCanvas;
700
+ private gridCtx;
701
+ private dataCtx;
702
+ private gl;
703
+ private sharedCanvas;
704
+ private theme;
705
+ private toRad;
706
+ private fixedR?;
707
+ private margin;
708
+ private entries;
709
+ private R;
710
+ private dpr;
711
+ private resizeObserver;
712
+ private frameRequested;
713
+ constructor(container: HTMLElement, options?: PolarOptions);
714
+ private makeCanvas;
715
+ private toXY;
716
+ addLine(opts: PolarLineOptions): PolarSeries;
717
+ addScatter(opts: PolarScatterOptions): PolarSeries;
718
+ private handle;
719
+ private refit;
720
+ destroy(): void;
721
+ private resize;
722
+ requestRender(): void;
723
+ /** The square drawing region (CSS px). */
724
+ private square;
725
+ render(): void;
726
+ private drawGrid;
727
+ }
728
+
729
+ /** Minimal column-major 4×4 matrix helpers for the 3D plots (WebGL convention). */
730
+ type Mat4 = Float32Array;
731
+
732
+ interface Bounds3 {
733
+ x: Range;
734
+ y: Range;
735
+ z: Range;
736
+ }
737
+ /** A drawable in the 3D scene. Positions are in world space; Plot3D supplies the MVP. */
738
+ interface Layer3D {
739
+ readonly id: string;
740
+ bounds3(): Bounds3 | null;
741
+ draw(gl: WebGL2RenderingContext, mvp: Mat4): void;
742
+ dispose(): void;
743
+ }
744
+
745
+ interface PointCloudOptions {
746
+ x: ArrayLike<number>;
747
+ y: ArrayLike<number>;
748
+ z: ArrayLike<number>;
749
+ color?: string | Color;
750
+ size?: number;
751
+ colorBy?: {
752
+ values: ArrayLike<number>;
753
+ colormap?: ColormapName;
754
+ domain?: Range;
755
+ };
756
+ }
757
+ declare class PointCloudLayer implements Layer3D {
758
+ readonly id: string;
759
+ private gl;
760
+ private program;
761
+ private vao;
762
+ private buffer;
763
+ private uniforms;
764
+ private count;
765
+ private size;
766
+ private b3;
767
+ constructor(gl: WebGL2RenderingContext, opts: PointCloudOptions);
768
+ bounds3(): Bounds3 | null;
769
+ draw(gl: WebGL2RenderingContext, mvp: Mat4): void;
770
+ dispose(): void;
771
+ }
772
+
773
+ interface SurfaceOptions {
774
+ /** Row-major height grid, length `cols * rows`. */
775
+ values: ArrayLike<number>;
776
+ cols: number;
777
+ rows: number;
778
+ /** World extent of the grid footprint. Defaults to unit indices. */
779
+ extentX?: Range;
780
+ extentZ?: Range;
781
+ colormap?: ColormapName;
782
+ }
783
+ declare class SurfaceLayer implements Layer3D {
784
+ readonly id: string;
785
+ private gl;
786
+ private program;
787
+ private vao;
788
+ private buffer;
789
+ private uniforms;
790
+ private vertexCount;
791
+ private b3;
792
+ private lightDir;
793
+ private ambient;
794
+ constructor(gl: WebGL2RenderingContext, opts: SurfaceOptions);
795
+ bounds3(): Bounds3;
796
+ /** Set the light direction (world space) and ambient term (0..1). */
797
+ setLight(dir: [number, number, number], ambient: number): void;
798
+ draw(gl: WebGL2RenderingContext, mvp: Mat4): void;
799
+ dispose(): void;
800
+ }
801
+
802
+ interface Plot3DOptions {
803
+ background?: [number, number, number, number];
804
+ azimuth?: number;
805
+ elevation?: number;
806
+ distance?: number;
807
+ /** Axis titles drawn along the x / y (height) / z edges. */
808
+ axisLabels?: {
809
+ x?: string;
810
+ y?: string;
811
+ z?: string;
812
+ };
813
+ /** Render a small on-canvas panel with light-angle + ambient sliders. */
814
+ lightControls?: boolean;
815
+ }
816
+ /** A 3D scatter/surface plot with an orbit camera, axis ticks, and lighting. */
817
+ declare class Plot3D {
818
+ private container;
819
+ private canvas;
820
+ private displayCtx;
821
+ private gl;
822
+ private sharedCanvas;
823
+ private layers;
824
+ private normalize;
825
+ private dataBounds;
826
+ private bg;
827
+ private dpr;
828
+ private azimuth;
829
+ private elevation;
830
+ private distance;
831
+ private axisLabels;
832
+ private resizeObserver;
833
+ private frameRequested;
834
+ private lineProgram;
835
+ private lineUniforms;
836
+ private boxVao;
837
+ private boxBuf;
838
+ private tickVao;
839
+ private tickBuf;
840
+ private tickCount;
841
+ private labels;
842
+ private lightAz;
843
+ private lightEl;
844
+ private ambient;
845
+ private controlsEl;
846
+ constructor(container: HTMLElement, options?: Plot3DOptions);
847
+ private bindLineVao;
848
+ addSurface(opts: SurfaceOptions): SurfaceLayer;
849
+ addPointCloud(opts: PointCloudOptions): PointCloudLayer;
850
+ /** Update the light direction (azimuth/elevation, radians) and ambient (0..1). */
851
+ setLight(params: {
852
+ azimuth?: number;
853
+ elevation?: number;
854
+ ambient?: number;
855
+ }): void;
856
+ private lightDir;
857
+ destroy(): void;
858
+ private recompute;
859
+ /** Build tick-mark line geometry + tick/title labels from the data bounds. */
860
+ private buildAxes;
861
+ private resize;
862
+ requestRender(): void;
863
+ private viewProj;
864
+ render(): void;
865
+ private drawLabels;
866
+ private attachControls;
867
+ private buildControls;
868
+ }
869
+
870
+ /**
871
+ * Owns the tick configuration for one axis and resolves the final tick list
872
+ * against a scale. Modes: auto (delegates to the scale), custom (array), or
873
+ * generator (function of the range) — plus major/minor, per-tick labels, and
874
+ * `addTicks` layering.
875
+ */
876
+ declare class Axis {
877
+ config: AxisConfig;
878
+ constructor(config?: AxisConfig);
879
+ update(patch: Partial<AxisConfig>): void;
880
+ /** Resolve the concrete tick list (labels filled) for the scale's domain. */
881
+ resolve(scale: Scale): Tick[];
882
+ }
883
+
884
+ /**
885
+ * Auto ticks for a linear axis using nice-number rounding.
886
+ * Returns major ticks; minor ticks are added separately.
887
+ */
888
+ declare function autoTicks(min: number, max: number, target?: number): Tick[];
889
+ /** Insert `count` evenly spaced minor ticks between each pair of major ticks. */
890
+ declare function withMinorTicks(major: Tick[], count: number): Tick[];
891
+ /**
892
+ * Resolve a user `TicksSpec` (array | Tick[] | generator) against the current
893
+ * axis range into a concrete, sorted Tick list. Returns `null` if no explicit
894
+ * spec was given, signaling the caller to fall back to auto ticks.
895
+ */
896
+ declare function resolveTicks(spec: TicksSpec | undefined, min: number, max: number): Tick[] | null;
897
+ /** Default number formatter: compact, avoids noisy trailing decimals. */
898
+ declare function defaultFormat(value: number): string;
899
+
900
+ /** Pure statistics helpers backing the histogram, box, and violin layers. */
901
+ interface Histogram {
902
+ edges: Float64Array;
903
+ counts: Float64Array;
904
+ centers: Float64Array;
905
+ binWidth: number;
906
+ }
907
+ /** Bin `values` into `bins` equal-width buckets (or use explicit `edges`). */
908
+ declare function histogram(values: ArrayLike<number>, opts?: {
909
+ bins?: number;
910
+ edges?: ArrayLike<number>;
911
+ range?: [number, number];
912
+ }): Histogram;
913
+ /** Quantile of a *sorted* array via linear interpolation (type-7, like NumPy). */
914
+ declare function quantileSorted(sorted: ArrayLike<number>, q: number): number;
915
+ interface BoxStats {
916
+ min: number;
917
+ q1: number;
918
+ median: number;
919
+ q3: number;
920
+ max: number;
921
+ /** Whisker ends: furthest points within 1.5·IQR of the quartiles. */
922
+ whiskerLo: number;
923
+ whiskerHi: number;
924
+ outliers: number[];
925
+ }
926
+ /** Tukey box-plot statistics for a set of values. */
927
+ declare function boxStats(values: ArrayLike<number>): BoxStats;
928
+ /** In-place iterative radix-2 Cooley–Tukey FFT (length must be a power of two). */
929
+ declare function fft(re: Float64Array, im: Float64Array): void;
930
+ interface Spectrogram {
931
+ /** Row-major magnitudes (dB), rows = freq bins (low at row 0), cols = frames. */
932
+ values: Float64Array;
933
+ cols: number;
934
+ rows: number;
935
+ extent: {
936
+ x: [number, number];
937
+ y: [number, number];
938
+ };
939
+ }
940
+ /** Short-time Fourier transform of a real signal → a time×frequency dB grid. */
941
+ declare function spectrogram(signal: ArrayLike<number>, opts?: {
942
+ fftSize?: number;
943
+ hop?: number;
944
+ sampleRate?: number;
945
+ }): Spectrogram;
946
+ interface Density {
947
+ xs: Float64Array;
948
+ ys: Float64Array;
949
+ }
950
+ /**
951
+ * Gaussian kernel density estimate over `points` grid samples in [lo, hi].
952
+ * Bandwidth defaults to Silverman's rule of thumb.
953
+ */
954
+ declare function kde(values: ArrayLike<number>, lo: number, hi: number, points?: number, bandwidth?: number): Density;
955
+
956
+ /** Parse a CSS hex/rgb color string into normalized RGBA (0..1). */
957
+ declare function parseColor(input: string): [number, number, number, number];
958
+ /** Normalized RGBA (0..1) back to a CSS `rgba(...)` string. */
959
+ declare function toColorCss(c: readonly [number, number, number, number]): string;
960
+
961
+ export { AreaLayer, type AreaOptions, Axis, type AxisConfig, type AxisFrame, type AxisScaleOptions, BarLayer, type BarOptions, type Bounds, type Bounds3, type BoxGroup, BoxLayer, type BoxOptions, type BoxStats, type Color, type ColormapName, ContourLayer, type ContourOptions, type Density, type Dim, type DrawState, HeatmapLayer, type HeatmapOptions, HexbinLayer, type HexbinOptions, type Histogram, type InteractionMode, type Layer, type Layer3D, type Layout, LineLayer, type LineOptions, LinearScale, LogScale, Plot, Plot3D, type Plot3DOptions, type PlotOptions, PointCloudLayer, type PointCloudOptions, type PolarLineOptions, type PolarOptions, PolarPlot, type PolarScatterOptions, type PolarSeries, type RGB, type Range, type Scale, type ScaleType, ScatterLayer, type ScatterOptions, type Spectrogram, SurfaceLayer, type SurfaceOptions, type Theme, type Tick, type TicksSpec, TimeScale, type ToolbarHost, type ToolbarTheme, type YAxisOptions, autoTicks, boxStats, colormap, createToolbar, darkTheme, defaultFormat, fft, histogram, kde, lightTheme, makeScale, parseColor, quantileSorted, resolveTicks, spectrogram, toColorCss, withMinorTicks };