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.
Files changed (242) hide show
  1. package/LICENSE.md +674 -0
  2. package/README.md +81 -0
  3. package/dist/core.d.mts +340 -0
  4. package/dist/core.mjs +1047 -0
  5. package/dist/index.d.mts +3426 -0
  6. package/dist/index.mjs +12762 -0
  7. package/dist/interactions-DEFL_F4E.mjs +5395 -0
  8. package/dist/range-presets-CzECsu3V.d.mts +1523 -0
  9. package/package.json +34 -0
  10. package/src/annotations.d.ts +121 -0
  11. package/src/annotations.ts +438 -0
  12. package/src/axis.d.ts +184 -0
  13. package/src/axis.test.ts +131 -0
  14. package/src/axis.ts +765 -0
  15. package/src/colorbar.d.ts +69 -0
  16. package/src/colorbar.ts +294 -0
  17. package/src/colors.d.ts +57 -0
  18. package/src/colors.test.ts +28 -0
  19. package/src/colors.ts +486 -0
  20. package/src/core.ts +299 -0
  21. package/src/format.d.ts +54 -0
  22. package/src/format.ts +138 -0
  23. package/src/grammar/accessibility.d.ts +147 -0
  24. package/src/grammar/accessibility.test.ts +199 -0
  25. package/src/grammar/accessibility.ts +443 -0
  26. package/src/grammar/aes.d.ts +35 -0
  27. package/src/grammar/aes.test.ts +75 -0
  28. package/src/grammar/aes.ts +120 -0
  29. package/src/grammar/annotations.d.ts +86 -0
  30. package/src/grammar/annotations.test.ts +68 -0
  31. package/src/grammar/annotations.ts +336 -0
  32. package/src/grammar/attach-brush.d.ts +44 -0
  33. package/src/grammar/attach-brush.test.ts +214 -0
  34. package/src/grammar/attach-brush.ts +111 -0
  35. package/src/grammar/attach-presets.d.ts +33 -0
  36. package/src/grammar/attach-presets.test.ts +106 -0
  37. package/src/grammar/attach-presets.ts +215 -0
  38. package/src/grammar/chart.d.ts +952 -0
  39. package/src/grammar/chart.test.ts +118 -0
  40. package/src/grammar/chart.ts +1172 -0
  41. package/src/grammar/color-utils.d.ts +29 -0
  42. package/src/grammar/color-utils.test.ts +53 -0
  43. package/src/grammar/color-utils.ts +66 -0
  44. package/src/grammar/constants.d.ts +45 -0
  45. package/src/grammar/constants.ts +61 -0
  46. package/src/grammar/coord.d.ts +183 -0
  47. package/src/grammar/coord.test.ts +355 -0
  48. package/src/grammar/coord.ts +619 -0
  49. package/src/grammar/data/pivot.d.ts +57 -0
  50. package/src/grammar/data/pivot.ts +107 -0
  51. package/src/grammar/emphasis-driver.d.ts +69 -0
  52. package/src/grammar/emphasis-driver.test.ts +199 -0
  53. package/src/grammar/emphasis-driver.ts +205 -0
  54. package/src/grammar/equality.d.ts +3 -0
  55. package/src/grammar/equality.ts +40 -0
  56. package/src/grammar/facet.d.ts +63 -0
  57. package/src/grammar/facet.test.ts +60 -0
  58. package/src/grammar/facet.ts +175 -0
  59. package/src/grammar/geoms/_categorical.d.ts +94 -0
  60. package/src/grammar/geoms/_categorical.ts +0 -0
  61. package/src/grammar/geoms/_distribution.d.ts +52 -0
  62. package/src/grammar/geoms/_distribution.ts +125 -0
  63. package/src/grammar/geoms/_mark.d.ts +69 -0
  64. package/src/grammar/geoms/_mark.ts +136 -0
  65. package/src/grammar/geoms/_shape.d.ts +41 -0
  66. package/src/grammar/geoms/_shape.ts +74 -0
  67. package/src/grammar/geoms/aggregate.d.ts +95 -0
  68. package/src/grammar/geoms/aggregate.test.ts +554 -0
  69. package/src/grammar/geoms/aggregate.ts +840 -0
  70. package/src/grammar/geoms/area.d.ts +32 -0
  71. package/src/grammar/geoms/area.test.ts +165 -0
  72. package/src/grammar/geoms/area.ts +578 -0
  73. package/src/grammar/geoms/band.d.ts +27 -0
  74. package/src/grammar/geoms/band.test.ts +57 -0
  75. package/src/grammar/geoms/band.ts +126 -0
  76. package/src/grammar/geoms/bar.d.ts +56 -0
  77. package/src/grammar/geoms/bar.test.ts +367 -0
  78. package/src/grammar/geoms/bar.ts +1054 -0
  79. package/src/grammar/geoms/boxplot.d.ts +129 -0
  80. package/src/grammar/geoms/boxplot.test.ts +299 -0
  81. package/src/grammar/geoms/boxplot.ts +834 -0
  82. package/src/grammar/geoms/connected-scatter.d.ts +27 -0
  83. package/src/grammar/geoms/connected-scatter.test.ts +157 -0
  84. package/src/grammar/geoms/connected-scatter.ts +63 -0
  85. package/src/grammar/geoms/emphasis.d.ts +76 -0
  86. package/src/grammar/geoms/emphasis.test.ts +135 -0
  87. package/src/grammar/geoms/emphasis.ts +162 -0
  88. package/src/grammar/geoms/histogram.d.ts +75 -0
  89. package/src/grammar/geoms/histogram.test.ts +262 -0
  90. package/src/grammar/geoms/histogram.ts +740 -0
  91. package/src/grammar/geoms/index.d.ts +20 -0
  92. package/src/grammar/geoms/index.ts +77 -0
  93. package/src/grammar/geoms/interval.d.ts +31 -0
  94. package/src/grammar/geoms/interval.test.ts +154 -0
  95. package/src/grammar/geoms/interval.ts +342 -0
  96. package/src/grammar/geoms/line.d.ts +38 -0
  97. package/src/grammar/geoms/line.test.ts +247 -0
  98. package/src/grammar/geoms/line.ts +659 -0
  99. package/src/grammar/geoms/point.d.ts +57 -0
  100. package/src/grammar/geoms/point.test.ts +163 -0
  101. package/src/grammar/geoms/point.ts +545 -0
  102. package/src/grammar/geoms/polar.test.ts +216 -0
  103. package/src/grammar/geoms/ribbon.d.ts +21 -0
  104. package/src/grammar/geoms/ribbon.test.ts +170 -0
  105. package/src/grammar/geoms/ribbon.ts +87 -0
  106. package/src/grammar/geoms/ridgeline.d.ts +89 -0
  107. package/src/grammar/geoms/ridgeline.test.ts +247 -0
  108. package/src/grammar/geoms/ridgeline.ts +1164 -0
  109. package/src/grammar/geoms/rolling.d.ts +43 -0
  110. package/src/grammar/geoms/rolling.test.ts +217 -0
  111. package/src/grammar/geoms/rolling.ts +387 -0
  112. package/src/grammar/geoms/rug.d.ts +28 -0
  113. package/src/grammar/geoms/rug.test.ts +126 -0
  114. package/src/grammar/geoms/rug.ts +214 -0
  115. package/src/grammar/geoms/rule.d.ts +23 -0
  116. package/src/grammar/geoms/rule.test.ts +69 -0
  117. package/src/grammar/geoms/rule.ts +212 -0
  118. package/src/grammar/geoms/smooth.d.ts +54 -0
  119. package/src/grammar/geoms/smooth.test.ts +78 -0
  120. package/src/grammar/geoms/smooth.ts +337 -0
  121. package/src/grammar/geoms/text.d.ts +29 -0
  122. package/src/grammar/geoms/text.test.ts +64 -0
  123. package/src/grammar/geoms/text.ts +234 -0
  124. package/src/grammar/geoms/tile.d.ts +61 -0
  125. package/src/grammar/geoms/tile.test.ts +157 -0
  126. package/src/grammar/geoms/tile.ts +621 -0
  127. package/src/grammar/geoms/types.d.ts +319 -0
  128. package/src/grammar/geoms/types.ts +362 -0
  129. package/src/grammar/geoms/violin.d.ts +85 -0
  130. package/src/grammar/geoms/violin.test.ts +187 -0
  131. package/src/grammar/geoms/violin.ts +672 -0
  132. package/src/grammar/index.d.ts +22 -0
  133. package/src/grammar/index.ts +269 -0
  134. package/src/grammar/interactions/_disposable.d.ts +5 -0
  135. package/src/grammar/interactions/_disposable.ts +23 -0
  136. package/src/grammar/interactions/_z.d.ts +4 -0
  137. package/src/grammar/interactions/_z.ts +16 -0
  138. package/src/grammar/interactions/brush-selection.test.ts +262 -0
  139. package/src/grammar/interactions/brush.d.ts +63 -0
  140. package/src/grammar/interactions/brush.test.ts +483 -0
  141. package/src/grammar/interactions/brush.ts +452 -0
  142. package/src/grammar/interactions/crosshair.d.ts +19 -0
  143. package/src/grammar/interactions/crosshair.test.ts +127 -0
  144. package/src/grammar/interactions/crosshair.ts +76 -0
  145. package/src/grammar/interactions/hit-layer.d.ts +64 -0
  146. package/src/grammar/interactions/hit-layer.ts +246 -0
  147. package/src/grammar/interactions/legend.d.ts +19 -0
  148. package/src/grammar/interactions/legend.ts +101 -0
  149. package/src/grammar/interactions/menu.d.ts +93 -0
  150. package/src/grammar/interactions/menu.test.ts +373 -0
  151. package/src/grammar/interactions/menu.ts +342 -0
  152. package/src/grammar/interactions/selection.d.ts +25 -0
  153. package/src/grammar/interactions/selection.test.ts +289 -0
  154. package/src/grammar/interactions/selection.ts +142 -0
  155. package/src/grammar/interactions/series-readout.d.ts +91 -0
  156. package/src/grammar/interactions/series-readout.test.ts +668 -0
  157. package/src/grammar/interactions/series-readout.ts +422 -0
  158. package/src/grammar/interactions/series-snap.d.ts +70 -0
  159. package/src/grammar/interactions/series-snap.test.ts +214 -0
  160. package/src/grammar/interactions/series-snap.ts +218 -0
  161. package/src/grammar/interactions/tooltip-axis.test.ts +176 -0
  162. package/src/grammar/interactions/tooltip-touch.browser.test.ts +49 -0
  163. package/src/grammar/interactions/tooltip-touch.test.ts +161 -0
  164. package/src/grammar/interactions/tooltip.d.ts +140 -0
  165. package/src/grammar/interactions/tooltip.test.ts +406 -0
  166. package/src/grammar/interactions/tooltip.ts +622 -0
  167. package/src/grammar/interactions/transitions.d.ts +34 -0
  168. package/src/grammar/interactions/transitions.test.ts +172 -0
  169. package/src/grammar/interactions/transitions.ts +160 -0
  170. package/src/grammar/layout.d.ts +68 -0
  171. package/src/grammar/layout.ts +186 -0
  172. package/src/grammar/legend-merge.test.ts +332 -0
  173. package/src/grammar/mount.d.ts +78 -0
  174. package/src/grammar/mount.test.ts +479 -0
  175. package/src/grammar/mount.ts +2112 -0
  176. package/src/grammar/palettes.d.ts +54 -0
  177. package/src/grammar/palettes.test.ts +80 -0
  178. package/src/grammar/palettes.ts +167 -0
  179. package/src/grammar/pan-zoom.test.ts +398 -0
  180. package/src/grammar/phylo.d.ts +65 -0
  181. package/src/grammar/phylo.test.ts +59 -0
  182. package/src/grammar/phylo.ts +112 -0
  183. package/src/grammar/pipeline.auto-ticks.test.ts +40 -0
  184. package/src/grammar/pipeline.d.ts +158 -0
  185. package/src/grammar/pipeline.test.ts +463 -0
  186. package/src/grammar/pipeline.ts +1233 -0
  187. package/src/grammar/profiling.d.ts +8 -0
  188. package/src/grammar/profiling.ts +24 -0
  189. package/src/grammar/scales.d.ts +188 -0
  190. package/src/grammar/scales.test.ts +181 -0
  191. package/src/grammar/scales.ts +800 -0
  192. package/src/grammar/svg.d.ts +3 -0
  193. package/src/grammar/svg.ts +39 -0
  194. package/src/grammar/theme.d.ts +261 -0
  195. package/src/grammar/theme.test.ts +105 -0
  196. package/src/grammar/theme.ts +490 -0
  197. package/src/heatmap/cpu.ts +109 -0
  198. package/src/heatmap/gpu.ts +565 -0
  199. package/src/heatmap/types.ts +177 -0
  200. package/src/heatmap.browser.test.ts +308 -0
  201. package/src/heatmap.test.ts +320 -0
  202. package/src/heatmap.ts +123 -0
  203. package/src/index.d.ts +1 -0
  204. package/src/index.ts +8 -0
  205. package/src/interactions.d.ts +48 -0
  206. package/src/interactions.test.ts +226 -0
  207. package/src/interactions.ts +394 -0
  208. package/src/layout/box.d.ts +48 -0
  209. package/src/layout/box.test.ts +107 -0
  210. package/src/layout/box.ts +143 -0
  211. package/src/legend.d.ts +115 -0
  212. package/src/legend.ts +422 -0
  213. package/src/marks/curve.d.ts +43 -0
  214. package/src/marks/curve.ts +244 -0
  215. package/src/marks/stack.d.ts +53 -0
  216. package/src/marks/stack.ts +184 -0
  217. package/src/marks.d.ts +273 -0
  218. package/src/marks.test.ts +541 -0
  219. package/src/marks.ts +1292 -0
  220. package/src/navigator.test.ts +174 -0
  221. package/src/navigator.ts +393 -0
  222. package/src/range-presets.d.ts +113 -0
  223. package/src/range-presets.test.ts +345 -0
  224. package/src/range-presets.ts +349 -0
  225. package/src/scales.d.ts +98 -0
  226. package/src/scales.test.ts +103 -0
  227. package/src/scales.ts +695 -0
  228. package/src/stats/index.d.ts +200 -0
  229. package/src/stats/index.test.ts +349 -0
  230. package/src/stats/index.ts +740 -0
  231. package/src/stats/regression.d.ts +38 -0
  232. package/src/stats/regression.test.ts +56 -0
  233. package/src/stats/regression.ts +396 -0
  234. package/src/stats/rolling-window.d.ts +55 -0
  235. package/src/stats/rolling-window.test.ts +237 -0
  236. package/src/stats/rolling-window.ts +256 -0
  237. package/src/test-setup.ts +19 -0
  238. package/src/viewport/axis-state.d.ts +72 -0
  239. package/src/viewport/axis-state.ts +476 -0
  240. package/src/viewport.d.ts +170 -0
  241. package/src/viewport.test.ts +363 -0
  242. package/src/viewport.ts +510 -0
@@ -0,0 +1,53 @@
1
+ export interface StackSegment<T, K extends string = string> {
2
+ /** Original datum the segment came from. */
3
+ readonly datum: T;
4
+ /** Key of this segment within the datum (matches `keys[subIndex]`). */
5
+ readonly key: K;
6
+ /** Numeric value of this segment. */
7
+ readonly value: number;
8
+ /** Cumulative offset — where this segment starts (0 for the first key). */
9
+ readonly base: number;
10
+ /** `base + value` — where this segment ends. */
11
+ readonly top: number;
12
+ /** Index of this segment within its datum (0-based, same order as `keys`). */
13
+ readonly subIndex: number;
14
+ /** Index of the source datum in the input array. */
15
+ readonly datumIndex: number;
16
+ }
17
+ export type StackOffset = "zero" | "expand" | "silhouette" | "wiggle";
18
+ export type StackOrder = "input" | "ascending" | "descending" | "inside-out" | "appearance";
19
+ export interface StackOptions<T, K extends string = string> {
20
+ /**
21
+ * Extract the numeric value for `key` from `datum`. Default: `datum[key]` —
22
+ * works when the data is a plain object keyed by string.
23
+ */
24
+ value?: (datum: T, key: K, datumIndex: number) => number;
25
+ /**
26
+ * Baseline offset:
27
+ * - `"zero"` (default) — bases at 0 going up.
28
+ * - `"expand"` — normalize each datum to `[0, 1]` (100% stacked).
29
+ * - `"silhouette"` — center the stack on 0 (`-rowSum/2 .. +rowSum/2`).
30
+ * - `"wiggle"` — minimize layer wiggle (Byron–Wattenberg streamgraph).
31
+ */
32
+ offset?: StackOffset;
33
+ /**
34
+ * Series ordering. Reorders the stack from inner-out:
35
+ * - `"input"` (default) — preserve `keys` order.
36
+ * - `"ascending"` — smallest total first.
37
+ * - `"descending"` — largest total first.
38
+ * - `"inside-out"` — largest in the middle (good for streamgraphs).
39
+ * - `"appearance"` — by index of each series' max value.
40
+ */
41
+ order?: StackOrder;
42
+ }
43
+ /**
44
+ * Flatten `data` into a list of `StackSegment`s — one per (datum, key) pair —
45
+ * with cumulative `base` / `top` offsets computed per datum. Supports
46
+ * `offset` (zero / expand / silhouette / wiggle) and `order` (input /
47
+ * ascending / descending / inside-out / appearance).
48
+ *
49
+ * Output order is `data[0].keys[0], data[0].keys[1], ..., data[1].keys[0],
50
+ * ...` — convenient for `barMark` / `areaMark`. `subIndex` reflects the
51
+ * post-`order` stacking position (0 = inner-most).
52
+ */
53
+ export declare function stack<T, K extends string = string>(data: readonly T[], keys: readonly K[], options?: StackOptions<T, K>): StackSegment<T, K>[];
@@ -0,0 +1,184 @@
1
+ // ---------------------------------------------------------------------------
2
+ // stack() — data helper for stacked bars / areas
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export interface StackSegment<T, K extends string = string> {
6
+ /** Original datum the segment came from. */
7
+ readonly datum: T;
8
+ /** Key of this segment within the datum (matches `keys[subIndex]`). */
9
+ readonly key: K;
10
+ /** Numeric value of this segment. */
11
+ readonly value: number;
12
+ /** Cumulative offset — where this segment starts (0 for the first key). */
13
+ readonly base: number;
14
+ /** `base + value` — where this segment ends. */
15
+ readonly top: number;
16
+ /** Index of this segment within its datum (0-based, same order as `keys`). */
17
+ readonly subIndex: number;
18
+ /** Index of the source datum in the input array. */
19
+ readonly datumIndex: number;
20
+ }
21
+
22
+ export type StackOffset = "zero" | "expand" | "silhouette" | "wiggle";
23
+ export type StackOrder = "input" | "ascending" | "descending" | "inside-out" | "appearance";
24
+
25
+ export interface StackOptions<T, K extends string = string> {
26
+ /**
27
+ * Extract the numeric value for `key` from `datum`. Default: `datum[key]` —
28
+ * works when the data is a plain object keyed by string.
29
+ */
30
+ value?: (datum: T, key: K, datumIndex: number) => number;
31
+ /**
32
+ * Baseline offset:
33
+ * - `"zero"` (default) — bases at 0 going up.
34
+ * - `"expand"` — normalize each datum to `[0, 1]` (100% stacked).
35
+ * - `"silhouette"` — center the stack on 0 (`-rowSum/2 .. +rowSum/2`).
36
+ * - `"wiggle"` — minimize layer wiggle (Byron–Wattenberg streamgraph).
37
+ */
38
+ offset?: StackOffset;
39
+ /**
40
+ * Series ordering. Reorders the stack from inner-out:
41
+ * - `"input"` (default) — preserve `keys` order.
42
+ * - `"ascending"` — smallest total first.
43
+ * - `"descending"` — largest total first.
44
+ * - `"inside-out"` — largest in the middle (good for streamgraphs).
45
+ * - `"appearance"` — by index of each series' max value.
46
+ */
47
+ order?: StackOrder;
48
+ }
49
+
50
+ function orderKeys<T, K extends string>(
51
+ data: readonly T[],
52
+ keys: readonly K[],
53
+ value: (datum: T, key: K, index: number) => number,
54
+ order: StackOrder,
55
+ ): readonly K[] {
56
+ if (order === "input") return keys;
57
+
58
+ const sums = keys.map((k) => {
59
+ let s = 0;
60
+ for (let i = 0; i < data.length; i++) s += value(data[i]!, k, i);
61
+ return s;
62
+ });
63
+
64
+ if (order === "ascending") {
65
+ return [...keys].sort((a, b) => sums[keys.indexOf(a)]! - sums[keys.indexOf(b)]!);
66
+ }
67
+ if (order === "descending") {
68
+ return [...keys].sort((a, b) => sums[keys.indexOf(b)]! - sums[keys.indexOf(a)]!);
69
+ }
70
+ if (order === "appearance") {
71
+ const peakIndex = keys.map((k) => {
72
+ let bestIdx = 0;
73
+ let bestVal = -Number.POSITIVE_INFINITY;
74
+ for (let i = 0; i < data.length; i++) {
75
+ const v = value(data[i]!, k, i);
76
+ if (v > bestVal) {
77
+ bestVal = v;
78
+ bestIdx = i;
79
+ }
80
+ }
81
+ return bestIdx;
82
+ });
83
+ return [...keys].sort((a, b) => peakIndex[keys.indexOf(a)]! - peakIndex[keys.indexOf(b)]!);
84
+ }
85
+ // inside-out: ascending sums to bottom + top alternately so largest sits in middle
86
+ if (order === "inside-out") {
87
+ const indexed = keys.map((k, idx) => ({ k, sum: sums[idx]! }));
88
+ indexed.sort((a, b) => a.sum - b.sum);
89
+ const bottom: K[] = [];
90
+ const top: K[] = [];
91
+ let bottomSum = 0;
92
+ let topSum = 0;
93
+ for (const e of indexed) {
94
+ if (bottomSum < topSum) {
95
+ bottom.push(e.k);
96
+ bottomSum += e.sum;
97
+ } else {
98
+ top.unshift(e.k);
99
+ topSum += e.sum;
100
+ }
101
+ }
102
+ return [...bottom, ...top];
103
+ }
104
+ return keys;
105
+ }
106
+
107
+ /**
108
+ * Flatten `data` into a list of `StackSegment`s — one per (datum, key) pair —
109
+ * with cumulative `base` / `top` offsets computed per datum. Supports
110
+ * `offset` (zero / expand / silhouette / wiggle) and `order` (input /
111
+ * ascending / descending / inside-out / appearance).
112
+ *
113
+ * Output order is `data[0].keys[0], data[0].keys[1], ..., data[1].keys[0],
114
+ * ...` — convenient for `barMark` / `areaMark`. `subIndex` reflects the
115
+ * post-`order` stacking position (0 = inner-most).
116
+ */
117
+ export function stack<T, K extends string = string>(
118
+ data: readonly T[],
119
+ keys: readonly K[],
120
+ options: StackOptions<T, K> = {},
121
+ ): StackSegment<T, K>[] {
122
+ const value =
123
+ options.value ?? ((datum: T, key: K) => (datum as Record<string, number>)[key] ?? 0);
124
+ const offset = options.offset ?? "zero";
125
+ const order = options.order ?? "input";
126
+ const ordered = orderKeys(data, keys, value, order);
127
+
128
+ // Per-datum precomputed values + row totals (needed for expand / silhouette / wiggle).
129
+ const rows: number[][] = data.map((d, i) => ordered.map((k) => value(d, k, i)));
130
+ const rowSums = rows.map((row) => row.reduce((s, v) => s + v, 0));
131
+
132
+ // Compute per-datum baseline (the value subtracted from `base` for each segment).
133
+ const baselines: number[] = data.map(() => 0);
134
+ if (offset === "silhouette") {
135
+ for (let i = 0; i < data.length; i++) baselines[i] = -rowSums[i]! / 2;
136
+ } else if (offset === "wiggle") {
137
+ // Byron–Wattenberg wiggle: y[0] = -∑(n-i) * Δprev_i / 2 / ∑Δprev_*
138
+ // Iteratively compute baseline that minimizes weighted wiggle.
139
+ if (data.length > 0) {
140
+ baselines[0] = 0;
141
+ for (let i = 1; i < data.length; i++) {
142
+ let n1 = 0;
143
+ let n2 = 0;
144
+ for (let k = 0; k < ordered.length; k++) {
145
+ const di = rows[i]![k]!;
146
+ const di1 = rows[i - 1]![k]!;
147
+ let s = 0;
148
+ for (let j = 0; j < k; j++) s += rows[i]![j]! - rows[i - 1]![j]!;
149
+ n1 += (s + (di - di1) / 2) * di;
150
+ n2 += di;
151
+ }
152
+ baselines[i] = baselines[i - 1]! - (n2 === 0 ? 0 : n1 / n2);
153
+ }
154
+ // Center the wiggle baseline visually.
155
+ const minB = Math.min(...baselines);
156
+ const maxB = Math.max(...baselines.map((b, i) => b + rowSums[i]!));
157
+ const center = (minB + maxB) / 2;
158
+ for (let i = 0; i < baselines.length; i++) baselines[i] -= center - (maxB - minB) / 2;
159
+ }
160
+ }
161
+
162
+ const out: StackSegment<T, K>[] = [];
163
+ for (let i = 0; i < data.length; i++) {
164
+ const datum = data[i]!;
165
+ const sum = rowSums[i]!;
166
+ const scale = offset === "expand" && sum !== 0 ? 1 / sum : 1;
167
+ let base = baselines[i]!;
168
+ for (let j = 0; j < ordered.length; j++) {
169
+ const key = ordered[j]!;
170
+ const v = rows[i]![j]! * scale;
171
+ out.push({
172
+ datum,
173
+ key,
174
+ value: v,
175
+ base,
176
+ top: base + v,
177
+ subIndex: j,
178
+ datumIndex: i,
179
+ });
180
+ base += v;
181
+ }
182
+ }
183
+ return out;
184
+ }
package/src/marks.d.ts ADDED
@@ -0,0 +1,273 @@
1
+ import { type Color, type Group, type Layer, type LineCap, type LineJoin, type Vec2 } from "insomni";
2
+ import { type LineCurve } from "./marks/curve.ts";
3
+ import { type StackOffset, type StackOrder, type StackSegment } from "./marks/stack.ts";
4
+ import type { BandScale, ContinuousScale, GroupedBandScale } from "./scales.ts";
5
+ export { cardinalCurve, defineCurve, resamplePoints, type CustomCurve, type LineCurve, type LineCurvePreset, } from "./marks/curve.ts";
6
+ export { stack, type StackOffset, type StackOptions, type StackOrder, type StackSegment, } from "./marks/stack.ts";
7
+ export interface MarkOrigin {
8
+ x?: number;
9
+ y?: number;
10
+ }
11
+ export interface MarkBuilder {
12
+ addTo(layer: Layer, origin?: MarkOrigin): Layer;
13
+ readonly length: number;
14
+ }
15
+ export type Accessor<T, V> = (datum: T, index: number) => V;
16
+ export type ValueOrAccessor<T, V> = V | Accessor<T, V>;
17
+ declare function resolve<T, V>(value: ValueOrAccessor<T, V>, datum: T, index: number): V;
18
+ export { resolve as resolveValueOrAccessor };
19
+ /**
20
+ * Discrete marker glyphs for `pointMark`. The first four are filled solids;
21
+ * the `-open` variants render the same outline with no fill (caller must
22
+ * supply a stroke). `cross`, `plus`, and `star` are filled glyphs whose
23
+ * "arm" thickness scales with `radius`.
24
+ */
25
+ export type PointShapeKind = "circle" | "square" | "triangle" | "diamond" | "circle-open" | "square-open" | "triangle-open" | "diamond-open" | "cross" | "plus" | "star";
26
+ /** Shape ordering used by the default categorical shape scale. */
27
+ export declare const POINT_SHAPE_PALETTE: readonly PointShapeKind[];
28
+ /**
29
+ * Border treatment applied orthogonally to a point's shape kind.
30
+ *
31
+ * - `"solid"` (default): existing behavior — fill + optional solid stroke.
32
+ * - `"open"`: fill is suppressed; stroke uses `stroke ?? fill` as the color
33
+ * (equivalent to the `-open` shape variants, but composable with any kind).
34
+ * - `"dashed"` / `"dotted"`: fill is kept; the stroke is rendered as a polyline
35
+ * ring with a dash pattern. Required because SDF strokes don't support
36
+ * per-shape dashes; this routes the border through `tessellatePolyline`.
37
+ */
38
+ export type PointBorderStyle = "solid" | "dashed" | "dotted" | "open";
39
+ /** Line-stroke dash treatment. Maps to polyline `dashPattern`. */
40
+ export type LineDashStyle = "solid" | "dashed" | "dotted";
41
+ /** Default dash pattern emitted for `"dashed"` border/line styles. */
42
+ export declare const DASHED_PATTERN: readonly number[];
43
+ /** Default dash pattern emitted for `"dotted"` border/line styles. */
44
+ export declare const DOTTED_PATTERN: readonly number[];
45
+ export interface PointMarkOptions<T> {
46
+ x: Accessor<T, number>;
47
+ y: Accessor<T, number>;
48
+ radius?: ValueOrAccessor<T, number>;
49
+ fill?: ValueOrAccessor<T, Color>;
50
+ stroke?: ValueOrAccessor<T, Color>;
51
+ strokeWidth?: ValueOrAccessor<T, number>;
52
+ shape?: ValueOrAccessor<T, PointShapeKind>;
53
+ /**
54
+ * Per-datum border treatment (`"solid"` | `"dashed"` | `"dotted"` | `"open"`).
55
+ * Composes with any shape kind; e.g. `shape: "circle", borderStyle: "open"`
56
+ * is equivalent to `shape: "circle-open"`. Dashed/dotted borders bypass the
57
+ * SDF shader (which has no dash support) and route through a polyline ring.
58
+ */
59
+ borderStyle?: ValueOrAccessor<T, PointBorderStyle>;
60
+ /**
61
+ * Optional secondary glyph drawn on top of the base shape at the same anchor.
62
+ * Pass `null` to skip overlay for a specific datum. The overlay's radius is
63
+ * `radius * overlayScale`; its color falls back to the base stroke (or fill
64
+ * if no stroke) so badges contrast against the base shape.
65
+ */
66
+ overlayGlyph?: ValueOrAccessor<T, PointShapeKind | null>;
67
+ /** Overlay radius as a fraction of the base radius. Default `0.6`. */
68
+ overlayScale?: ValueOrAccessor<T, number>;
69
+ /**
70
+ * Per-datum GPU emphasis key (P5-T3) applied to every primitive a point emits
71
+ * (base shape + overlay glyph). Used by boxplot/violin point overlays + mean
72
+ * markers to tag a whole entity's dots with the entity's key. `undefined` →
73
+ * untagged (no dim).
74
+ */
75
+ emphasisKey?: Accessor<T, number | undefined>;
76
+ group?: Group;
77
+ }
78
+ /**
79
+ * Emit a filled polygon plus an optional solid stroke outline.
80
+ *
81
+ * v3's `PolygonShape` is fill-only (stroke is a separate polyline ring, not a
82
+ * field), so this mirrors the rect/ellipse stroked-shape pattern: push a
83
+ * fill-only polygon when a real fill is present, and push a closed-loop
84
+ * polyline ring over the same points when a stroke is present. A stroke-only
85
+ * mark (no fill) emits only the ring — v3 requires `fill`, so `pushPolygon` is
86
+ * never called without one.
87
+ */
88
+ export declare function pushFilledPolygon(layer: Layer, points: readonly Vec2[], opts: PolygonOpts & {
89
+ holes?: readonly (readonly Vec2[])[];
90
+ }): void;
91
+ export interface PolygonOpts {
92
+ fill?: Color;
93
+ stroke?: Color;
94
+ strokeWidth?: number;
95
+ group?: Group;
96
+ /**
97
+ * GPU emphasis key (P5-T3) threaded onto the polygon fill AND its stroke-ring
98
+ * polyline so the whole filled shape dims as one unit. `undefined` → untagged
99
+ * (no dim). Used by boxplot/violin/ridgeline to tag a logical entity's fill.
100
+ */
101
+ emphasisKey?: number;
102
+ }
103
+ /**
104
+ * Single-point swatch emission for legends and other one-off renders.
105
+ *
106
+ * Internal helper exported so the legend renderer can produce a swatch that
107
+ * matches the on-chart point exactly — same shape, border treatment, and
108
+ * overlay glyph — without re-implementing the dispatch. Composes the same
109
+ * primitives that `pointMark` calls per datum.
110
+ */
111
+ export interface PointSwatchEmitOptions {
112
+ cx: number;
113
+ cy: number;
114
+ radius: number;
115
+ fill: Color;
116
+ stroke?: Color;
117
+ strokeWidth?: number;
118
+ shape?: PointShapeKind;
119
+ borderStyle?: PointBorderStyle;
120
+ overlayGlyph?: PointShapeKind | null;
121
+ overlayScale?: number;
122
+ group?: Group;
123
+ }
124
+ export declare function emitPointSwatch(layer: Layer, opts: PointSwatchEmitOptions): void;
125
+ export declare function pointMark<T>(data: readonly T[], options: PointMarkOptions<T>): MarkBuilder;
126
+ export interface LineMarkOptions<T> {
127
+ x: Accessor<T, number>;
128
+ y: Accessor<T, number>;
129
+ stroke?: Color;
130
+ strokeWidth?: number;
131
+ curve?: LineCurve;
132
+ /** Resample density for smooth curves (`monotone-x`, `basis`). Default `16`. */
133
+ curveSamples?: number;
134
+ cap?: LineCap;
135
+ join?: LineJoin;
136
+ dashPattern?: readonly number[];
137
+ dashOffset?: number;
138
+ /**
139
+ * Categorical dash treatment. `"solid"` (default) leaves the stroke as a
140
+ * continuous line; `"dashed"` and `"dotted"` map to default dash patterns.
141
+ * `dashPattern` takes precedence when both are supplied.
142
+ */
143
+ dashStyle?: LineDashStyle;
144
+ defined?: Accessor<T, boolean>;
145
+ /**
146
+ * GPU emphasis key (P5-T3) applied to every polyline segment of this line.
147
+ * A whole line/series shares one key so it dims/un-dims together. `undefined`
148
+ * → untagged (no dim).
149
+ */
150
+ emphasisKey?: number;
151
+ group?: Group;
152
+ }
153
+ export declare function lineMark<T>(data: readonly T[], options: LineMarkOptions<T>): MarkBuilder;
154
+ export interface AreaMarkOptions<T> {
155
+ x: Accessor<T, number>;
156
+ y0: Accessor<T, number>;
157
+ y1: Accessor<T, number>;
158
+ fill?: Color;
159
+ stroke?: Color;
160
+ strokeWidth?: number;
161
+ defined?: Accessor<T, boolean>;
162
+ group?: Group;
163
+ }
164
+ export declare function areaMark<T>(data: readonly T[], options: AreaMarkOptions<T>): MarkBuilder;
165
+ /**
166
+ * Bar border treatment — shared between `barMark`, `categoricalBarMark`,
167
+ * `stackedBarMark`, and `groupedBarMark`. Same routing as point borders:
168
+ * `"open"` suppresses fill; `"dashed"` / `"dotted"` emit a polyline ring
169
+ * around the rect because SDF rect strokes have no native dash support.
170
+ */
171
+ export type BarBorderStyle = "solid" | "dashed" | "dotted" | "open";
172
+ export interface BarMarkOptions<T> {
173
+ x: Accessor<T, number>;
174
+ y: Accessor<T, number>;
175
+ width: ValueOrAccessor<T, number>;
176
+ height: ValueOrAccessor<T, number>;
177
+ fill?: ValueOrAccessor<T, Color>;
178
+ stroke?: ValueOrAccessor<T, Color>;
179
+ strokeWidth?: ValueOrAccessor<T, number>;
180
+ cornerRadius?: ValueOrAccessor<T, number>;
181
+ rotation?: ValueOrAccessor<T, number>;
182
+ /** Per-datum border treatment. See {@link BarBorderStyle}. */
183
+ borderStyle?: ValueOrAccessor<T, BarBorderStyle>;
184
+ group?: Group;
185
+ }
186
+ export declare function barMark<T>(data: readonly T[], options: BarMarkOptions<T>): MarkBuilder;
187
+ export type BarOrientation = "y" | "x";
188
+ export interface CategoricalBarMarkOptions<T, C> {
189
+ /**
190
+ * `"y"` (default) → vertical bars, value mapped to height.
191
+ * `"x"` → horizontal bars, value mapped to width.
192
+ */
193
+ orientation?: BarOrientation;
194
+ category: Accessor<T, C>;
195
+ value: Accessor<T, number>;
196
+ /** Band scale resolving categories to one of the layer's axes. */
197
+ categoryScale: BandScale<C>;
198
+ /** Continuous scale resolving values to the perpendicular axis. */
199
+ valueScale: ContinuousScale;
200
+ /** Baseline value (in domain units). Default `0`. */
201
+ baseline?: number;
202
+ fill?: ValueOrAccessor<T, Color>;
203
+ stroke?: ValueOrAccessor<T, Color>;
204
+ strokeWidth?: ValueOrAccessor<T, number>;
205
+ cornerRadius?: ValueOrAccessor<T, number>;
206
+ /** Per-datum border treatment. See {@link BarBorderStyle}. */
207
+ borderStyle?: ValueOrAccessor<T, BarBorderStyle>;
208
+ /** Per-datum GPU emphasis key (P5-T3). `undefined` → untagged (no dim). */
209
+ emphasisKey?: Accessor<T, number | undefined>;
210
+ group?: Group;
211
+ }
212
+ export declare function categoricalBarMark<T, C>(data: readonly T[], options: CategoricalBarMarkOptions<T, C>): MarkBuilder;
213
+ export interface StackedBarMarkOptions<T, C, K extends string> {
214
+ orientation?: BarOrientation;
215
+ category: Accessor<T, C>;
216
+ categoryScale: BandScale<C>;
217
+ valueScale: ContinuousScale;
218
+ /** Per-segment fill — typically `colorScale(palette, keys)` (a function of `K`). */
219
+ color: (key: K, segment: StackSegment<T, K>) => Color;
220
+ offset?: StackOffset;
221
+ order?: StackOrder;
222
+ value?: (datum: T, key: K, datumIndex: number) => number;
223
+ cornerRadius?: number;
224
+ stroke?: Color;
225
+ strokeWidth?: number;
226
+ /** Border treatment shared across every segment. See {@link BarBorderStyle}. */
227
+ borderStyle?: BarBorderStyle;
228
+ /** Per-segment GPU emphasis key (P5-T3). `undefined` → untagged (no dim). */
229
+ emphasisKey?: (key: K, segment: StackSegment<T, K>) => number | undefined;
230
+ group?: Group;
231
+ }
232
+ export interface StackedMarkBuilder<T, K extends string> extends MarkBuilder {
233
+ /** Per-(datum, key) layout segments — useful for `valueLabelMark` over totals. */
234
+ readonly segments: readonly StackSegment<T, K>[];
235
+ }
236
+ export declare function stackedBarMark<T, C, K extends string>(data: readonly T[], keys: readonly K[], options: StackedBarMarkOptions<T, C, K>): StackedMarkBuilder<T, K>;
237
+ export interface GroupedBarMarkOptions<T, C, K extends string> {
238
+ orientation?: BarOrientation;
239
+ category: Accessor<T, C>;
240
+ groupedScale: GroupedBandScale<C, K>;
241
+ valueScale: ContinuousScale;
242
+ /** Per-key fill — typically `colorScale(palette, keys)`. */
243
+ color: (key: K) => Color;
244
+ /** Per-(datum, key) value resolver. Default `datum[key]`. */
245
+ value?: (datum: T, key: K, datumIndex: number) => number;
246
+ /** Domain-space baseline. Default `0`. */
247
+ baseline?: number;
248
+ cornerRadius?: number;
249
+ stroke?: Color;
250
+ strokeWidth?: number;
251
+ /** Border treatment shared across every bar in the grouped layout. */
252
+ borderStyle?: BarBorderStyle;
253
+ /** Per-(datum, key) GPU emphasis key (P5-T3). `undefined` → untagged. */
254
+ emphasisKey?: (datum: T, key: K, datumIndex: number) => number | undefined;
255
+ group?: Group;
256
+ }
257
+ export declare function groupedBarMark<T, C, K extends string>(data: readonly T[], options: GroupedBarMarkOptions<T, C, K>): MarkBuilder;
258
+ export interface StackedAreaMarkOptions<T, K extends string> {
259
+ /** Continuous x position resolver — already in layer-space pixels. */
260
+ x: Accessor<T, number>;
261
+ /** Continuous scale that maps stack base/top values to layer-space pixels. */
262
+ valueScale: ContinuousScale;
263
+ /** Per-series fill. */
264
+ color: (key: K) => Color;
265
+ offset?: StackOffset;
266
+ order?: StackOrder;
267
+ value?: (datum: T, key: K, datumIndex: number) => number;
268
+ defined?: Accessor<T, boolean>;
269
+ stroke?: Color;
270
+ strokeWidth?: number;
271
+ group?: Group;
272
+ }
273
+ export declare function stackedAreaMark<T, K extends string>(data: readonly T[], keys: readonly K[], options: StackedAreaMarkOptions<T, K>): StackedMarkBuilder<T, K>;