insomni-plot 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +674 -0
- package/README.md +81 -0
- package/dist/core.d.mts +340 -0
- package/dist/core.mjs +1047 -0
- package/dist/index.d.mts +3426 -0
- package/dist/index.mjs +12762 -0
- package/dist/interactions-DEFL_F4E.mjs +5395 -0
- package/dist/range-presets-CzECsu3V.d.mts +1523 -0
- package/package.json +34 -0
- package/src/annotations.d.ts +121 -0
- package/src/annotations.ts +438 -0
- package/src/axis.d.ts +184 -0
- package/src/axis.test.ts +131 -0
- package/src/axis.ts +765 -0
- package/src/colorbar.d.ts +69 -0
- package/src/colorbar.ts +294 -0
- package/src/colors.d.ts +57 -0
- package/src/colors.test.ts +28 -0
- package/src/colors.ts +486 -0
- package/src/core.ts +299 -0
- package/src/format.d.ts +54 -0
- package/src/format.ts +138 -0
- package/src/grammar/accessibility.d.ts +147 -0
- package/src/grammar/accessibility.test.ts +199 -0
- package/src/grammar/accessibility.ts +443 -0
- package/src/grammar/aes.d.ts +35 -0
- package/src/grammar/aes.test.ts +75 -0
- package/src/grammar/aes.ts +120 -0
- package/src/grammar/annotations.d.ts +86 -0
- package/src/grammar/annotations.test.ts +68 -0
- package/src/grammar/annotations.ts +336 -0
- package/src/grammar/attach-brush.d.ts +44 -0
- package/src/grammar/attach-brush.test.ts +214 -0
- package/src/grammar/attach-brush.ts +111 -0
- package/src/grammar/attach-presets.d.ts +33 -0
- package/src/grammar/attach-presets.test.ts +106 -0
- package/src/grammar/attach-presets.ts +215 -0
- package/src/grammar/chart.d.ts +952 -0
- package/src/grammar/chart.test.ts +118 -0
- package/src/grammar/chart.ts +1172 -0
- package/src/grammar/color-utils.d.ts +29 -0
- package/src/grammar/color-utils.test.ts +53 -0
- package/src/grammar/color-utils.ts +66 -0
- package/src/grammar/constants.d.ts +45 -0
- package/src/grammar/constants.ts +61 -0
- package/src/grammar/coord.d.ts +183 -0
- package/src/grammar/coord.test.ts +355 -0
- package/src/grammar/coord.ts +619 -0
- package/src/grammar/data/pivot.d.ts +57 -0
- package/src/grammar/data/pivot.ts +107 -0
- package/src/grammar/emphasis-driver.d.ts +69 -0
- package/src/grammar/emphasis-driver.test.ts +199 -0
- package/src/grammar/emphasis-driver.ts +205 -0
- package/src/grammar/equality.d.ts +3 -0
- package/src/grammar/equality.ts +40 -0
- package/src/grammar/facet.d.ts +63 -0
- package/src/grammar/facet.test.ts +60 -0
- package/src/grammar/facet.ts +175 -0
- package/src/grammar/geoms/_categorical.d.ts +94 -0
- package/src/grammar/geoms/_categorical.ts +0 -0
- package/src/grammar/geoms/_distribution.d.ts +52 -0
- package/src/grammar/geoms/_distribution.ts +125 -0
- package/src/grammar/geoms/_mark.d.ts +69 -0
- package/src/grammar/geoms/_mark.ts +136 -0
- package/src/grammar/geoms/_shape.d.ts +41 -0
- package/src/grammar/geoms/_shape.ts +74 -0
- package/src/grammar/geoms/aggregate.d.ts +95 -0
- package/src/grammar/geoms/aggregate.test.ts +554 -0
- package/src/grammar/geoms/aggregate.ts +840 -0
- package/src/grammar/geoms/area.d.ts +32 -0
- package/src/grammar/geoms/area.test.ts +165 -0
- package/src/grammar/geoms/area.ts +578 -0
- package/src/grammar/geoms/band.d.ts +27 -0
- package/src/grammar/geoms/band.test.ts +57 -0
- package/src/grammar/geoms/band.ts +126 -0
- package/src/grammar/geoms/bar.d.ts +56 -0
- package/src/grammar/geoms/bar.test.ts +367 -0
- package/src/grammar/geoms/bar.ts +1054 -0
- package/src/grammar/geoms/boxplot.d.ts +129 -0
- package/src/grammar/geoms/boxplot.test.ts +299 -0
- package/src/grammar/geoms/boxplot.ts +834 -0
- package/src/grammar/geoms/connected-scatter.d.ts +27 -0
- package/src/grammar/geoms/connected-scatter.test.ts +157 -0
- package/src/grammar/geoms/connected-scatter.ts +63 -0
- package/src/grammar/geoms/emphasis.d.ts +76 -0
- package/src/grammar/geoms/emphasis.test.ts +135 -0
- package/src/grammar/geoms/emphasis.ts +162 -0
- package/src/grammar/geoms/histogram.d.ts +75 -0
- package/src/grammar/geoms/histogram.test.ts +262 -0
- package/src/grammar/geoms/histogram.ts +740 -0
- package/src/grammar/geoms/index.d.ts +20 -0
- package/src/grammar/geoms/index.ts +77 -0
- package/src/grammar/geoms/interval.d.ts +31 -0
- package/src/grammar/geoms/interval.test.ts +154 -0
- package/src/grammar/geoms/interval.ts +342 -0
- package/src/grammar/geoms/line.d.ts +38 -0
- package/src/grammar/geoms/line.test.ts +247 -0
- package/src/grammar/geoms/line.ts +659 -0
- package/src/grammar/geoms/point.d.ts +57 -0
- package/src/grammar/geoms/point.test.ts +163 -0
- package/src/grammar/geoms/point.ts +545 -0
- package/src/grammar/geoms/polar.test.ts +216 -0
- package/src/grammar/geoms/ribbon.d.ts +21 -0
- package/src/grammar/geoms/ribbon.test.ts +170 -0
- package/src/grammar/geoms/ribbon.ts +87 -0
- package/src/grammar/geoms/ridgeline.d.ts +89 -0
- package/src/grammar/geoms/ridgeline.test.ts +247 -0
- package/src/grammar/geoms/ridgeline.ts +1164 -0
- package/src/grammar/geoms/rolling.d.ts +43 -0
- package/src/grammar/geoms/rolling.test.ts +217 -0
- package/src/grammar/geoms/rolling.ts +387 -0
- package/src/grammar/geoms/rug.d.ts +28 -0
- package/src/grammar/geoms/rug.test.ts +126 -0
- package/src/grammar/geoms/rug.ts +214 -0
- package/src/grammar/geoms/rule.d.ts +23 -0
- package/src/grammar/geoms/rule.test.ts +69 -0
- package/src/grammar/geoms/rule.ts +212 -0
- package/src/grammar/geoms/smooth.d.ts +54 -0
- package/src/grammar/geoms/smooth.test.ts +78 -0
- package/src/grammar/geoms/smooth.ts +337 -0
- package/src/grammar/geoms/text.d.ts +29 -0
- package/src/grammar/geoms/text.test.ts +64 -0
- package/src/grammar/geoms/text.ts +234 -0
- package/src/grammar/geoms/tile.d.ts +61 -0
- package/src/grammar/geoms/tile.test.ts +157 -0
- package/src/grammar/geoms/tile.ts +621 -0
- package/src/grammar/geoms/types.d.ts +319 -0
- package/src/grammar/geoms/types.ts +362 -0
- package/src/grammar/geoms/violin.d.ts +85 -0
- package/src/grammar/geoms/violin.test.ts +187 -0
- package/src/grammar/geoms/violin.ts +672 -0
- package/src/grammar/index.d.ts +22 -0
- package/src/grammar/index.ts +269 -0
- package/src/grammar/interactions/_disposable.d.ts +5 -0
- package/src/grammar/interactions/_disposable.ts +23 -0
- package/src/grammar/interactions/_z.d.ts +4 -0
- package/src/grammar/interactions/_z.ts +16 -0
- package/src/grammar/interactions/brush-selection.test.ts +262 -0
- package/src/grammar/interactions/brush.d.ts +63 -0
- package/src/grammar/interactions/brush.test.ts +483 -0
- package/src/grammar/interactions/brush.ts +452 -0
- package/src/grammar/interactions/crosshair.d.ts +19 -0
- package/src/grammar/interactions/crosshair.test.ts +127 -0
- package/src/grammar/interactions/crosshair.ts +76 -0
- package/src/grammar/interactions/hit-layer.d.ts +64 -0
- package/src/grammar/interactions/hit-layer.ts +246 -0
- package/src/grammar/interactions/legend.d.ts +19 -0
- package/src/grammar/interactions/legend.ts +101 -0
- package/src/grammar/interactions/menu.d.ts +93 -0
- package/src/grammar/interactions/menu.test.ts +373 -0
- package/src/grammar/interactions/menu.ts +342 -0
- package/src/grammar/interactions/selection.d.ts +25 -0
- package/src/grammar/interactions/selection.test.ts +289 -0
- package/src/grammar/interactions/selection.ts +142 -0
- package/src/grammar/interactions/series-readout.d.ts +91 -0
- package/src/grammar/interactions/series-readout.test.ts +668 -0
- package/src/grammar/interactions/series-readout.ts +422 -0
- package/src/grammar/interactions/series-snap.d.ts +70 -0
- package/src/grammar/interactions/series-snap.test.ts +214 -0
- package/src/grammar/interactions/series-snap.ts +218 -0
- package/src/grammar/interactions/tooltip-axis.test.ts +176 -0
- package/src/grammar/interactions/tooltip-touch.browser.test.ts +49 -0
- package/src/grammar/interactions/tooltip-touch.test.ts +161 -0
- package/src/grammar/interactions/tooltip.d.ts +140 -0
- package/src/grammar/interactions/tooltip.test.ts +406 -0
- package/src/grammar/interactions/tooltip.ts +622 -0
- package/src/grammar/interactions/transitions.d.ts +34 -0
- package/src/grammar/interactions/transitions.test.ts +172 -0
- package/src/grammar/interactions/transitions.ts +160 -0
- package/src/grammar/layout.d.ts +68 -0
- package/src/grammar/layout.ts +186 -0
- package/src/grammar/legend-merge.test.ts +332 -0
- package/src/grammar/mount.d.ts +78 -0
- package/src/grammar/mount.test.ts +479 -0
- package/src/grammar/mount.ts +2112 -0
- package/src/grammar/palettes.d.ts +54 -0
- package/src/grammar/palettes.test.ts +80 -0
- package/src/grammar/palettes.ts +167 -0
- package/src/grammar/pan-zoom.test.ts +398 -0
- package/src/grammar/phylo.d.ts +65 -0
- package/src/grammar/phylo.test.ts +59 -0
- package/src/grammar/phylo.ts +112 -0
- package/src/grammar/pipeline.auto-ticks.test.ts +40 -0
- package/src/grammar/pipeline.d.ts +158 -0
- package/src/grammar/pipeline.test.ts +463 -0
- package/src/grammar/pipeline.ts +1233 -0
- package/src/grammar/profiling.d.ts +8 -0
- package/src/grammar/profiling.ts +24 -0
- package/src/grammar/scales.d.ts +188 -0
- package/src/grammar/scales.test.ts +181 -0
- package/src/grammar/scales.ts +800 -0
- package/src/grammar/svg.d.ts +3 -0
- package/src/grammar/svg.ts +39 -0
- package/src/grammar/theme.d.ts +261 -0
- package/src/grammar/theme.test.ts +105 -0
- package/src/grammar/theme.ts +490 -0
- package/src/heatmap/cpu.ts +109 -0
- package/src/heatmap/gpu.ts +565 -0
- package/src/heatmap/types.ts +177 -0
- package/src/heatmap.browser.test.ts +308 -0
- package/src/heatmap.test.ts +320 -0
- package/src/heatmap.ts +123 -0
- package/src/index.d.ts +1 -0
- package/src/index.ts +8 -0
- package/src/interactions.d.ts +48 -0
- package/src/interactions.test.ts +226 -0
- package/src/interactions.ts +394 -0
- package/src/layout/box.d.ts +48 -0
- package/src/layout/box.test.ts +107 -0
- package/src/layout/box.ts +143 -0
- package/src/legend.d.ts +115 -0
- package/src/legend.ts +422 -0
- package/src/marks/curve.d.ts +43 -0
- package/src/marks/curve.ts +244 -0
- package/src/marks/stack.d.ts +53 -0
- package/src/marks/stack.ts +184 -0
- package/src/marks.d.ts +273 -0
- package/src/marks.test.ts +541 -0
- package/src/marks.ts +1292 -0
- package/src/navigator.test.ts +174 -0
- package/src/navigator.ts +393 -0
- package/src/range-presets.d.ts +113 -0
- package/src/range-presets.test.ts +345 -0
- package/src/range-presets.ts +349 -0
- package/src/scales.d.ts +98 -0
- package/src/scales.test.ts +103 -0
- package/src/scales.ts +695 -0
- package/src/stats/index.d.ts +200 -0
- package/src/stats/index.test.ts +349 -0
- package/src/stats/index.ts +740 -0
- package/src/stats/regression.d.ts +38 -0
- package/src/stats/regression.test.ts +56 -0
- package/src/stats/regression.ts +396 -0
- package/src/stats/rolling-window.d.ts +55 -0
- package/src/stats/rolling-window.test.ts +237 -0
- package/src/stats/rolling-window.ts +256 -0
- package/src/test-setup.ts +19 -0
- package/src/viewport/axis-state.d.ts +72 -0
- package/src/viewport/axis-state.ts +476 -0
- package/src/viewport.d.ts +170 -0
- package/src/viewport.test.ts +363 -0
- package/src/viewport.ts +510 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Geom compile helpers
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { Color, Layer } from "insomni";
|
|
6
|
+
import type { MarkBuilder, MarkOrigin } from "../../marks.ts";
|
|
7
|
+
import { coordCartesian, type Coord } from "../coord.ts";
|
|
8
|
+
import type { Theme } from "../theme.ts";
|
|
9
|
+
import type { CompileContext, GeomKind, HoveredHit } from "./types.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Resolve `ctx.coord` to a concrete {@link Coord}. The interface flags `coord`
|
|
13
|
+
* as optional in {@link CompileContext} during the Phase 1 migration to the
|
|
14
|
+
* `Coord` API — geoms call this once at the top of their compile path to get
|
|
15
|
+
* a non-null projection, falling back to {@link coordCartesian} when the
|
|
16
|
+
* pipeline didn't supply one (e.g. legacy direct-compile call sites). Under
|
|
17
|
+
* Cartesian both `project` and `segment` are the identity, so this is a
|
|
18
|
+
* zero-pixel-diff drop-in for every existing geom.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveCoord<T>(ctx: CompileContext<T>): Coord {
|
|
21
|
+
return ctx.coord ?? coordCartesian();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Default alpha applied to non-selected marks while a selection is active.
|
|
26
|
+
* Lifted into a constant so geoms stay consistent without each importing the
|
|
27
|
+
* theme's selection config; per-mount overrides happen via the selection
|
|
28
|
+
* config (see chart.ts:SelectionConfig.dimAlpha).
|
|
29
|
+
*/
|
|
30
|
+
export const SELECTION_DIM_ALPHA = 0.3;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns the set of selected `dataIndex`es that belong to *this* geom (kind
|
|
34
|
+
* matches and the data array reference matches), or `null` when no selection
|
|
35
|
+
* row matches. Geoms use this to push stroke rings on the matching rows.
|
|
36
|
+
*/
|
|
37
|
+
export function selectedIndicesFor<T>(ctx: CompileContext<T>, kind: GeomKind): Set<number> | null {
|
|
38
|
+
const sel = ctx.selected;
|
|
39
|
+
if (!sel || sel.length === 0) return null;
|
|
40
|
+
const data = ctx.data as readonly unknown[];
|
|
41
|
+
const out = new Set<number>();
|
|
42
|
+
for (const h of sel as readonly HoveredHit[]) {
|
|
43
|
+
if (h.geomKind === kind && h.data === data) out.add(h.dataIndex);
|
|
44
|
+
}
|
|
45
|
+
return out.size > 0 ? out : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** True when any selection is active anywhere in the chart. */
|
|
49
|
+
export function selectionActive<T>(ctx: CompileContext<T>): boolean {
|
|
50
|
+
return !!ctx.selected && ctx.selected.length > 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Multi-series equivalent of {@link selectedIndicesFor}: returns the list of
|
|
55
|
+
* `(dataIndex, seriesKey)` pairs selected for *this* geom. Used by stacked /
|
|
56
|
+
* dodged geoms where a single source row produces multiple visible segments
|
|
57
|
+
* and the ring must distinguish them by series key.
|
|
58
|
+
*/
|
|
59
|
+
export function selectedSegmentsFor<T>(
|
|
60
|
+
ctx: CompileContext<T>,
|
|
61
|
+
kind: GeomKind,
|
|
62
|
+
): Array<{ dataIndex: number; seriesKey?: string }> | null {
|
|
63
|
+
const sel = ctx.selected;
|
|
64
|
+
if (!sel || sel.length === 0) return null;
|
|
65
|
+
const data = ctx.data as readonly unknown[];
|
|
66
|
+
const out: Array<{ dataIndex: number; seriesKey?: string }> = [];
|
|
67
|
+
for (const h of sel as readonly HoveredHit[]) {
|
|
68
|
+
if (h.geomKind === kind && h.data === data) {
|
|
69
|
+
out.push({ dataIndex: h.dataIndex, seriesKey: h.seriesKey });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out.length > 0 ? out : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Wrap a `MarkBuilder` so it composes into the `compile()` builder list with
|
|
77
|
+
* a fixed origin (typically the plot frame's top-left). Geoms collect these
|
|
78
|
+
* into the array returned from `compile`. Pass `length` to override the
|
|
79
|
+
* builder's reported length — used for label sub-marks where the visual
|
|
80
|
+
* count differs from the underlying mark's vertex count.
|
|
81
|
+
*/
|
|
82
|
+
export function wrapMark(
|
|
83
|
+
mark: MarkBuilder,
|
|
84
|
+
origin: MarkOrigin,
|
|
85
|
+
length: number = mark.length,
|
|
86
|
+
): { length: number; addTo: (layer: Layer) => Layer } {
|
|
87
|
+
return {
|
|
88
|
+
length,
|
|
89
|
+
addTo: (layer: Layer) => mark.addTo(layer, origin),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build a `MarkBuilder` from a draw callback. Used for one-off shapes that
|
|
95
|
+
* don't fit a mark factory — currently the hover-highlight halo each geom
|
|
96
|
+
* pushes for `ctx.hovered`. Coordinates passed to `draw` are layer-local
|
|
97
|
+
* (the caller has already applied any plot-frame offset).
|
|
98
|
+
*/
|
|
99
|
+
export function inlineMark(draw: (layer: Layer) => void, length: number = 1): MarkBuilder {
|
|
100
|
+
return {
|
|
101
|
+
length,
|
|
102
|
+
addTo(layer: Layer) {
|
|
103
|
+
draw(layer);
|
|
104
|
+
return layer;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function resolveFillAlpha(selected: boolean, theme: Theme): number {
|
|
110
|
+
return selected ? theme.marks.fillAlpha * SELECTION_DIM_ALPHA : theme.marks.fillAlpha;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function defaultMarkFill(theme: Theme): Color {
|
|
114
|
+
return theme.palettes.categorical(0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function selectChannels<X, Y>(
|
|
118
|
+
orientation: "x" | "y",
|
|
119
|
+
channels: { x: X; y: Y },
|
|
120
|
+
): { categoryChannel: X | Y; valueChannel: X | Y } {
|
|
121
|
+
return orientation === "y"
|
|
122
|
+
? { categoryChannel: channels.x, valueChannel: channels.y }
|
|
123
|
+
: { categoryChannel: channels.y, valueChannel: channels.x };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Highlight ring used by point/line/area on `ctx.hovered`. */
|
|
127
|
+
export function haloRing(
|
|
128
|
+
layer: Layer,
|
|
129
|
+
cx: number,
|
|
130
|
+
cy: number,
|
|
131
|
+
radius: number,
|
|
132
|
+
color: Color,
|
|
133
|
+
width: number = 2,
|
|
134
|
+
): void {
|
|
135
|
+
layer.pushCircle({ cx, cy, radius, stroke: color, strokeWidth: width });
|
|
136
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Color, Layer } from "insomni";
|
|
2
|
+
import type { MarkOrigin } from "../../marks.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Halo rect to be painted on top of a geom's main fills, so the active
|
|
5
|
+
* hover row's outline isn't occluded by adjacent cells. `color` is per-halo
|
|
6
|
+
* so multi-series geoms (histogram) can use per-cell highlight colors while
|
|
7
|
+
* single-series geoms (tile) just thread one color through every entry.
|
|
8
|
+
*/
|
|
9
|
+
export interface Halo {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
w: number;
|
|
13
|
+
h: number;
|
|
14
|
+
color: Color;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Push every halo rect as a stroke-only outline on top of `layer`. Caller
|
|
18
|
+
* supplies the shared stroke width (typically `emphasis.haloStrokeWidth`)
|
|
19
|
+
* and an optional corner radius matching the underlying fills.
|
|
20
|
+
*/
|
|
21
|
+
export declare function paintHalos(layer: Layer, halos: readonly Halo[], options: {
|
|
22
|
+
strokeWidth: number;
|
|
23
|
+
cornerRadius?: number;
|
|
24
|
+
}): void;
|
|
25
|
+
/**
|
|
26
|
+
* Orientation-aware bar bbox: takes the band-axis start + bandwidth and the
|
|
27
|
+
* value-axis pixel + baseline, returns the axis-aligned rect in mark-local
|
|
28
|
+
* coordinates (pre-translated by `origin`).
|
|
29
|
+
*
|
|
30
|
+
* `orientation === "y"` means categories on the x-axis (vertical bars rising
|
|
31
|
+
* along y); `"x"` means categories on the y-axis (horizontal bars).
|
|
32
|
+
*
|
|
33
|
+
* Returns `null` when either input pixel isn't finite — callers should
|
|
34
|
+
* `continue` on null so NaN bands don't produce zero-area garbage rects.
|
|
35
|
+
*/
|
|
36
|
+
export declare function barRect(orientation: "x" | "y", bandStart: number, bandwidth: number, valuePx: number, baseline: number, origin: MarkOrigin): {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
} | null;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shape-geom helpers — orientation-aware rect math + halo painting
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Shared by bar / histogram / tile. These geoms all compute axis-aligned
|
|
5
|
+
// rects from a band+value+baseline triple and overlay halo rings on the
|
|
6
|
+
// active hover row. The bbox math and the halo paint loop were copy-pasted
|
|
7
|
+
// across files; this module owns both.
|
|
8
|
+
|
|
9
|
+
import type { Color, Layer } from "insomni";
|
|
10
|
+
import type { MarkOrigin } from "../../marks.ts";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Halo rect to be painted on top of a geom's main fills, so the active
|
|
14
|
+
* hover row's outline isn't occluded by adjacent cells. `color` is per-halo
|
|
15
|
+
* so multi-series geoms (histogram) can use per-cell highlight colors while
|
|
16
|
+
* single-series geoms (tile) just thread one color through every entry.
|
|
17
|
+
*/
|
|
18
|
+
export interface Halo {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
w: number;
|
|
22
|
+
h: number;
|
|
23
|
+
color: Color;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Push every halo rect as a stroke-only outline on top of `layer`. Caller
|
|
28
|
+
* supplies the shared stroke width (typically `emphasis.haloStrokeWidth`)
|
|
29
|
+
* and an optional corner radius matching the underlying fills.
|
|
30
|
+
*/
|
|
31
|
+
export function paintHalos(
|
|
32
|
+
layer: Layer,
|
|
33
|
+
halos: readonly Halo[],
|
|
34
|
+
options: { strokeWidth: number; cornerRadius?: number },
|
|
35
|
+
): void {
|
|
36
|
+
for (const halo of halos) {
|
|
37
|
+
layer.pushRect({
|
|
38
|
+
x: halo.x,
|
|
39
|
+
y: halo.y,
|
|
40
|
+
width: halo.w,
|
|
41
|
+
height: halo.h,
|
|
42
|
+
cornerRadius: options.cornerRadius,
|
|
43
|
+
stroke: halo.color,
|
|
44
|
+
strokeWidth: options.strokeWidth,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Orientation-aware bar bbox: takes the band-axis start + bandwidth and the
|
|
51
|
+
* value-axis pixel + baseline, returns the axis-aligned rect in mark-local
|
|
52
|
+
* coordinates (pre-translated by `origin`).
|
|
53
|
+
*
|
|
54
|
+
* `orientation === "y"` means categories on the x-axis (vertical bars rising
|
|
55
|
+
* along y); `"x"` means categories on the y-axis (horizontal bars).
|
|
56
|
+
*
|
|
57
|
+
* Returns `null` when either input pixel isn't finite — callers should
|
|
58
|
+
* `continue` on null so NaN bands don't produce zero-area garbage rects.
|
|
59
|
+
*/
|
|
60
|
+
export function barRect(
|
|
61
|
+
orientation: "x" | "y",
|
|
62
|
+
bandStart: number,
|
|
63
|
+
bandwidth: number,
|
|
64
|
+
valuePx: number,
|
|
65
|
+
baseline: number,
|
|
66
|
+
origin: MarkOrigin,
|
|
67
|
+
): { x: number; y: number; width: number; height: number } | null {
|
|
68
|
+
if (!Number.isFinite(bandStart) || !Number.isFinite(valuePx)) return null;
|
|
69
|
+
const x = (origin.x ?? 0) + (orientation === "y" ? bandStart : Math.min(baseline, valuePx));
|
|
70
|
+
const y = (origin.y ?? 0) + (orientation === "y" ? Math.min(baseline, valuePx) : bandStart);
|
|
71
|
+
const width = orientation === "y" ? bandwidth : Math.abs(valuePx - baseline);
|
|
72
|
+
const height = orientation === "y" ? Math.abs(valuePx - baseline) : bandwidth;
|
|
73
|
+
return { x, y, width, height };
|
|
74
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type Color } from "insomni";
|
|
2
|
+
import { type LineCurve, type PointShapeKind } from "../../marks.ts";
|
|
3
|
+
import type { Aes } from "../aes.ts";
|
|
4
|
+
import type { Geom } from "./types.ts";
|
|
5
|
+
export type AggregateBinBy = "x" | "y";
|
|
6
|
+
export type AggregateBinSize = number | "auto" | ((info: AutoBinSizeInfo) => number);
|
|
7
|
+
export interface AutoBinSizeInfo {
|
|
8
|
+
/** Pixel span of the binned axis (positive). */
|
|
9
|
+
pixelSpan: number;
|
|
10
|
+
/** Domain span of the binned axis in axis units (positive). For time scales, ms. */
|
|
11
|
+
domainSpan: number;
|
|
12
|
+
/** `pixelSpan / domainSpan` — pixels per domain unit on the binned axis. */
|
|
13
|
+
pixelsPerUnit: number;
|
|
14
|
+
}
|
|
15
|
+
export type AggregateSummaryKind = "mean" | "median" | "count" | "sum" | "first" | "last";
|
|
16
|
+
export interface AggregateBinView<T> {
|
|
17
|
+
/** Values from the *non-binned* axis, in input order within this bin. */
|
|
18
|
+
values: readonly number[];
|
|
19
|
+
/** Source items inside this bin, in input order. */
|
|
20
|
+
items: readonly T[];
|
|
21
|
+
/** Count after filter. */
|
|
22
|
+
count: number;
|
|
23
|
+
/** Bin's lower edge on the binned axis (inclusive). */
|
|
24
|
+
lo: number;
|
|
25
|
+
/** Bin's upper edge on the binned axis (exclusive, except the final bin). */
|
|
26
|
+
hi: number;
|
|
27
|
+
}
|
|
28
|
+
export type AggregateSummary<T> = AggregateSummaryKind | ((bin: AggregateBinView<T>) => number);
|
|
29
|
+
/** Result of a bundle summary — one center + a [lo, hi] range per bin. */
|
|
30
|
+
export interface AggregateBundleResult {
|
|
31
|
+
center: number;
|
|
32
|
+
lo: number;
|
|
33
|
+
hi: number;
|
|
34
|
+
}
|
|
35
|
+
export type AggregateBundleSummaryKind = "mean+ci" | "median+iqr";
|
|
36
|
+
/**
|
|
37
|
+
* Bundle summaries emit `{ center, lo, hi }` per bin and pair with the
|
|
38
|
+
* `interval` or `ribbon` inner geom. Built-in kinds plus a quantile triple
|
|
39
|
+
* `{ quantiles: [loQ, centerQ, hiQ] }` (e.g. `[0.1, 0.5, 0.9]` for an 80%
|
|
40
|
+
* range) or a custom function returning the bundle directly.
|
|
41
|
+
*/
|
|
42
|
+
export type AggregateBundleSummary<T> = AggregateBundleSummaryKind | {
|
|
43
|
+
quantiles: readonly [number, number, number];
|
|
44
|
+
} | ((bin: AggregateBinView<T>) => AggregateBundleResult);
|
|
45
|
+
export interface AggregateChannels<T> {
|
|
46
|
+
x: Aes<T, number | Date>;
|
|
47
|
+
y: Aes<T, number | Date>;
|
|
48
|
+
}
|
|
49
|
+
export type AggregateInnerGeom = "point" | "line" | "bar" | "interval" | "ribbon";
|
|
50
|
+
export interface AggregateOptions<T> {
|
|
51
|
+
/** Axis to slide along. Default `"x"`. */
|
|
52
|
+
binBy?: AggregateBinBy;
|
|
53
|
+
/** Bin width in domain units; `"auto"` targets `~autoTargetPx` px. Default `"auto"`. */
|
|
54
|
+
binSize?: AggregateBinSize;
|
|
55
|
+
/**
|
|
56
|
+
* Reduction applied per bin. Scalar kinds (`"mean"`, `"median"`, …) pair
|
|
57
|
+
* with `geom: "point" | "line" | "bar"`. Bundle kinds (`"mean+ci"`,
|
|
58
|
+
* `"median+iqr"`, `{ quantiles }`, function-returning-`{center, lo, hi}`)
|
|
59
|
+
* pair with `geom: "interval" | "ribbon"`. Default `"mean"`.
|
|
60
|
+
*/
|
|
61
|
+
summary?: AggregateSummary<T> | AggregateBundleSummary<T>;
|
|
62
|
+
/** Row predicate. Rejected rows are excluded from both binning and the
|
|
63
|
+
* raw-fallback path. */
|
|
64
|
+
filter?: (datum: T, index: number) => boolean;
|
|
65
|
+
/** When `avg-points-per-bin < dissolveAt`, render raw geom instead.
|
|
66
|
+
* Default `1.2`. */
|
|
67
|
+
dissolveAt?: number;
|
|
68
|
+
/** Inner mark kind. Default `"point"`. */
|
|
69
|
+
geom?: AggregateInnerGeom;
|
|
70
|
+
/** Fill / stroke applied to every emitted mark. */
|
|
71
|
+
fill?: Color;
|
|
72
|
+
stroke?: Color;
|
|
73
|
+
strokeWidth?: number;
|
|
74
|
+
/** Point-specific. */
|
|
75
|
+
radius?: number;
|
|
76
|
+
shape?: PointShapeKind;
|
|
77
|
+
/** Line-specific. */
|
|
78
|
+
curve?: LineCurve;
|
|
79
|
+
/** Target pixels per bin for `"auto"` sizing. Default `7`. */
|
|
80
|
+
autoTargetPx?: number;
|
|
81
|
+
/** Display label for legend / hit-test (forwarded to inner mark). */
|
|
82
|
+
label?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Confidence level for the `"mean+ci"` bundle summary. Default `0.95`.
|
|
85
|
+
* Ignored for other summaries.
|
|
86
|
+
*/
|
|
87
|
+
ciLevel?: number;
|
|
88
|
+
/** Interval-specific: render perpendicular caps. Default `true`. */
|
|
89
|
+
caps?: boolean;
|
|
90
|
+
/** Interval-specific: cap length in pixels. Default `6`. */
|
|
91
|
+
capWidth?: number;
|
|
92
|
+
/** Ribbon-specific: fill alpha override (otherwise theme.marks.ribbonFillAlpha). */
|
|
93
|
+
fillAlpha?: number;
|
|
94
|
+
}
|
|
95
|
+
export declare function aggregate<T>(channels: AggregateChannels<T>, options?: AggregateOptions<T>): Geom<T>;
|