@motionscript/charts 0.1.0 → 0.1.1-alpha.1

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 (215) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +98 -28
  3. package/dist/bar-chart/bar-chart.d.ts +242 -119
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -1
  5. package/dist/bar-chart/bar-chart.js +231 -260
  6. package/dist/bar-chart/bar-chart.js.map +1 -1
  7. package/dist/bar-chart/category-axis.d.ts +16 -38
  8. package/dist/bar-chart/category-axis.d.ts.map +1 -1
  9. package/dist/bar-chart/category-axis.js +59 -68
  10. package/dist/bar-chart/category-axis.js.map +1 -1
  11. package/dist/bar-chart/chart-bars.d.ts +60 -91
  12. package/dist/bar-chart/chart-bars.d.ts.map +1 -1
  13. package/dist/bar-chart/chart-bars.js +88 -125
  14. package/dist/bar-chart/chart-bars.js.map +1 -1
  15. package/dist/bar-chart/index.d.ts +5 -13
  16. package/dist/bar-chart/index.d.ts.map +1 -1
  17. package/dist/bar-chart/index.js +2 -9
  18. package/dist/bar-chart/index.js.map +1 -1
  19. package/dist/bar-chart/shared.d.ts +40 -132
  20. package/dist/bar-chart/shared.d.ts.map +1 -1
  21. package/dist/bar-chart/shared.js +70 -158
  22. package/dist/bar-chart/shared.js.map +1 -1
  23. package/dist/browser/chunks/chunk-R3Q5EE6E.js +2 -0
  24. package/dist/browser/chunks/chunk-R3Q5EE6E.js.map +7 -0
  25. package/dist/browser/index.js +1 -1
  26. package/dist/browser/index.js.map +4 -4
  27. package/dist/browser/kit.js +1 -1
  28. package/dist/browser/manifest.json +1 -1
  29. package/dist/engine.d.ts +8 -17
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +8 -17
  32. package/dist/engine.js.map +1 -1
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/kit/cartesian.d.ts +49 -35
  38. package/dist/kit/cartesian.d.ts.map +1 -1
  39. package/dist/kit/cartesian.js +80 -42
  40. package/dist/kit/cartesian.js.map +1 -1
  41. package/dist/kit/chart-canvas.d.ts +37 -40
  42. package/dist/kit/chart-canvas.d.ts.map +1 -1
  43. package/dist/kit/chart-canvas.js +53 -47
  44. package/dist/kit/chart-canvas.js.map +1 -1
  45. package/dist/kit/chart-legend.d.ts +16 -59
  46. package/dist/kit/chart-legend.d.ts.map +1 -1
  47. package/dist/kit/chart-legend.js +38 -76
  48. package/dist/kit/chart-legend.js.map +1 -1
  49. package/dist/kit/entries.d.ts +2 -0
  50. package/dist/kit/entries.d.ts.map +1 -1
  51. package/dist/kit/entries.js +2 -0
  52. package/dist/kit/entries.js.map +1 -1
  53. package/dist/kit/horizontal-axis.d.ts +77 -0
  54. package/dist/kit/horizontal-axis.d.ts.map +1 -0
  55. package/dist/kit/horizontal-axis.js +99 -0
  56. package/dist/kit/horizontal-axis.js.map +1 -0
  57. package/dist/kit/motion.d.ts +16 -3
  58. package/dist/kit/motion.d.ts.map +1 -1
  59. package/dist/kit/motion.js +15 -1
  60. package/dist/kit/motion.js.map +1 -1
  61. package/dist/{pie-chart → kit}/pie-legend.d.ts +14 -29
  62. package/dist/kit/pie-legend.d.ts.map +1 -0
  63. package/dist/kit/pie-legend.js +93 -0
  64. package/dist/kit/pie-legend.js.map +1 -0
  65. package/dist/kit/polar.d.ts +31 -0
  66. package/dist/kit/polar.d.ts.map +1 -0
  67. package/dist/kit/polar.js +33 -0
  68. package/dist/kit/polar.js.map +1 -0
  69. package/dist/kit/shared.d.ts +60 -214
  70. package/dist/kit/shared.d.ts.map +1 -1
  71. package/dist/kit/shared.js +91 -291
  72. package/dist/kit/shared.js.map +1 -1
  73. package/dist/kit/style.d.ts +254 -0
  74. package/dist/kit/style.d.ts.map +1 -0
  75. package/dist/kit/style.js +197 -0
  76. package/dist/kit/style.js.map +1 -0
  77. package/dist/kit/vertical-axis.d.ts +36 -45
  78. package/dist/kit/vertical-axis.d.ts.map +1 -1
  79. package/dist/kit/vertical-axis.js +69 -81
  80. package/dist/kit/vertical-axis.js.map +1 -1
  81. package/dist/line-chart/chart-line.d.ts +107 -40
  82. package/dist/line-chart/chart-line.d.ts.map +1 -1
  83. package/dist/line-chart/chart-line.js +172 -89
  84. package/dist/line-chart/chart-line.js.map +1 -1
  85. package/dist/line-chart/chart-region.d.ts +14 -27
  86. package/dist/line-chart/chart-region.d.ts.map +1 -1
  87. package/dist/line-chart/chart-region.js +13 -28
  88. package/dist/line-chart/chart-region.js.map +1 -1
  89. package/dist/line-chart/index.d.ts +5 -18
  90. package/dist/line-chart/index.d.ts.map +1 -1
  91. package/dist/line-chart/index.js +3 -16
  92. package/dist/line-chart/index.js.map +1 -1
  93. package/dist/line-chart/line-chart.d.ts +272 -119
  94. package/dist/line-chart/line-chart.d.ts.map +1 -1
  95. package/dist/line-chart/line-chart.js +309 -257
  96. package/dist/line-chart/line-chart.js.map +1 -1
  97. package/dist/line-chart/shared.d.ts +73 -130
  98. package/dist/line-chart/shared.d.ts.map +1 -1
  99. package/dist/line-chart/shared.js +195 -156
  100. package/dist/line-chart/shared.js.map +1 -1
  101. package/dist/line-chart/x-scale.d.ts +16 -55
  102. package/dist/line-chart/x-scale.d.ts.map +1 -1
  103. package/dist/line-chart/x-scale.js +44 -111
  104. package/dist/line-chart/x-scale.js.map +1 -1
  105. package/dist/nodes.d.ts +3 -13
  106. package/dist/nodes.d.ts.map +1 -1
  107. package/dist/nodes.js +9 -13
  108. package/dist/nodes.js.map +1 -1
  109. package/dist/pie-chart/index.d.ts +3 -15
  110. package/dist/pie-chart/index.d.ts.map +1 -1
  111. package/dist/pie-chart/index.js +1 -11
  112. package/dist/pie-chart/index.js.map +1 -1
  113. package/dist/pie-chart/pie-chart.d.ts +147 -85
  114. package/dist/pie-chart/pie-chart.d.ts.map +1 -1
  115. package/dist/pie-chart/pie-chart.js +65 -86
  116. package/dist/pie-chart/pie-chart.js.map +1 -1
  117. package/dist/pie-chart/pie-ring.d.ts +65 -196
  118. package/dist/pie-chart/pie-ring.d.ts.map +1 -1
  119. package/dist/pie-chart/pie-ring.js +154 -292
  120. package/dist/pie-chart/pie-ring.js.map +1 -1
  121. package/dist/pie-chart/shared.d.ts +78 -172
  122. package/dist/pie-chart/shared.d.ts.map +1 -1
  123. package/dist/pie-chart/shared.js +54 -177
  124. package/dist/pie-chart/shared.js.map +1 -1
  125. package/dist/radar-chart/index.d.ts +5 -0
  126. package/dist/radar-chart/index.d.ts.map +1 -0
  127. package/dist/radar-chart/index.js +5 -0
  128. package/dist/radar-chart/index.js.map +1 -0
  129. package/dist/radar-chart/radar-chart.d.ts +218 -0
  130. package/dist/radar-chart/radar-chart.d.ts.map +1 -0
  131. package/dist/radar-chart/radar-chart.js +224 -0
  132. package/dist/radar-chart/radar-chart.js.map +1 -0
  133. package/dist/radar-chart/radar-grid.d.ts +43 -0
  134. package/dist/radar-chart/radar-grid.d.ts.map +1 -0
  135. package/dist/radar-chart/radar-grid.js +61 -0
  136. package/dist/radar-chart/radar-grid.js.map +1 -0
  137. package/dist/radar-chart/radar-series.d.ts +59 -0
  138. package/dist/radar-chart/radar-series.d.ts.map +1 -0
  139. package/dist/radar-chart/radar-series.js +71 -0
  140. package/dist/radar-chart/radar-series.js.map +1 -0
  141. package/dist/radar-chart/shared.d.ts +73 -0
  142. package/dist/radar-chart/shared.d.ts.map +1 -0
  143. package/dist/radar-chart/shared.js +123 -0
  144. package/dist/radar-chart/shared.js.map +1 -0
  145. package/dist/radial-chart/index.d.ts +6 -0
  146. package/dist/radial-chart/index.d.ts.map +1 -0
  147. package/dist/radial-chart/index.js +4 -0
  148. package/dist/radial-chart/index.js.map +1 -0
  149. package/dist/radial-chart/radial-chart.d.ts +216 -0
  150. package/dist/radial-chart/radial-chart.d.ts.map +1 -0
  151. package/dist/radial-chart/radial-chart.js +174 -0
  152. package/dist/radial-chart/radial-chart.js.map +1 -0
  153. package/dist/radial-chart/radial-rings.d.ts +121 -0
  154. package/dist/radial-chart/radial-rings.d.ts.map +1 -0
  155. package/dist/radial-chart/radial-rings.js +273 -0
  156. package/dist/radial-chart/radial-rings.js.map +1 -0
  157. package/dist/radial-chart/shared.d.ts +73 -0
  158. package/dist/radial-chart/shared.d.ts.map +1 -0
  159. package/dist/radial-chart/shared.js +79 -0
  160. package/dist/radial-chart/shared.js.map +1 -0
  161. package/package.json +4 -4
  162. package/registry.json +21 -6
  163. package/src/bar-chart/bar-chart.ts +274 -315
  164. package/src/bar-chart/category-axis.ts +53 -88
  165. package/src/bar-chart/chart-bars.ts +108 -205
  166. package/src/bar-chart/index.ts +8 -19
  167. package/src/bar-chart/shared.ts +81 -309
  168. package/src/engine.ts +8 -17
  169. package/src/index.ts +2 -0
  170. package/src/kit/cartesian.ts +118 -55
  171. package/src/kit/chart-canvas.ts +50 -80
  172. package/src/kit/chart-legend.ts +36 -97
  173. package/src/kit/entries.ts +4 -1
  174. package/src/kit/horizontal-axis.ts +114 -0
  175. package/src/kit/motion.ts +18 -2
  176. package/src/{pie-chart → kit}/pie-legend.ts +25 -65
  177. package/src/kit/polar.ts +54 -0
  178. package/src/kit/shared.ts +104 -487
  179. package/src/kit/style.ts +257 -0
  180. package/src/kit/vertical-axis.ts +81 -115
  181. package/src/line-chart/chart-line.ts +185 -91
  182. package/src/line-chart/chart-region.ts +14 -31
  183. package/src/line-chart/index.ts +14 -22
  184. package/src/line-chart/line-chart.ts +377 -315
  185. package/src/line-chart/shared.ts +229 -304
  186. package/src/line-chart/x-scale.ts +44 -121
  187. package/src/nodes.ts +9 -13
  188. package/src/pie-chart/index.ts +3 -35
  189. package/src/pie-chart/pie-chart.ts +83 -134
  190. package/src/pie-chart/pie-ring.ts +162 -368
  191. package/src/pie-chart/shared.ts +92 -379
  192. package/src/radar-chart/index.ts +4 -0
  193. package/src/radar-chart/radar-chart.ts +287 -0
  194. package/src/radar-chart/radar-grid.ts +78 -0
  195. package/src/radar-chart/radar-series.ts +88 -0
  196. package/src/radar-chart/shared.ts +146 -0
  197. package/src/radial-chart/index.ts +5 -0
  198. package/src/radial-chart/radial-chart.ts +210 -0
  199. package/src/radial-chart/radial-rings.ts +328 -0
  200. package/src/radial-chart/shared.ts +114 -0
  201. package/dist/browser/chunks/chunk-TJNHLQGV.js +0 -2
  202. package/dist/browser/chunks/chunk-TJNHLQGV.js.map +0 -7
  203. package/dist/kit/chart-border.d.ts +0 -27
  204. package/dist/kit/chart-border.d.ts.map +0 -1
  205. package/dist/kit/chart-border.js +0 -28
  206. package/dist/kit/chart-border.js.map +0 -1
  207. package/dist/line-chart/horizontal-axis.d.ts +0 -79
  208. package/dist/line-chart/horizontal-axis.d.ts.map +0 -1
  209. package/dist/line-chart/horizontal-axis.js +0 -129
  210. package/dist/line-chart/horizontal-axis.js.map +0 -1
  211. package/dist/pie-chart/pie-legend.d.ts.map +0 -1
  212. package/dist/pie-chart/pie-legend.js +0 -117
  213. package/dist/pie-chart/pie-legend.js.map +0 -1
  214. package/src/kit/chart-border.ts +0 -50
  215. package/src/line-chart/horizontal-axis.ts +0 -151
@@ -1,22 +1,6 @@
1
- /**
2
- * Types and pure helpers specific to the {@link LineChart} family: the traced
3
- * polylines, the spotlit region they dim outside of, and the theme those two
4
- * add to the shared chart vocabulary.
5
- *
6
- * Everything a bar or a pie chart would want too — what a series is, how an axis
7
- * is scaled and formatted, the theme leaf resolvers, the legend and plot-panel
8
- * branches — lives in `../kit/shared.ts` and is imported here. What's
9
- * left below is what only a line chart has: points sorted along an x column,
10
- * a region clipped out of a polyline, and the `region` branch of the theme.
11
- *
12
- * Kept renderer-agnostic (no `Node` imports) so every part shares one
13
- * scale/format/palette/theme vocabulary, and so a host's own inspector
14
- * mapping can build a partial {@link ChartTheme} without
15
- * pulling the components in.
16
- */
17
- import { Fills, fillOps, lerpNumber, strokeOps, } from "@motionscript/sdk";
18
- import { CHART_PALETTE, DEFAULT_LEGEND, DEFAULT_PLOT_AREA, DEFAULT_SERIES_STROKES, DEFAULT_X_AXIS, DEFAULT_Y_AXIS, SPOTLIGHT_DIM, lerpAxisConfig, lerpLegendConfig, lerpPlotArea, lerpSeriesStrokes, niceStep, num, resolveAxisConfig, resolveLegendConfig, resolvePlotArea, resolveSeriesStrokes, } from "../kit/shared.js";
19
- /** The empty box, used as the "no region" resting value. */
1
+ import { Types } from "@motionscript/sdk";
2
+ import { niceRange, num } from "../kit/shared.js";
3
+ /** The "no region" resting value. */
20
4
  export const EMPTY_REGION_BOX = {
21
5
  left: 0,
22
6
  right: 0,
@@ -25,18 +9,14 @@ export const EMPTY_REGION_BOX = {
25
9
  xBounded: false,
26
10
  yBounded: false,
27
11
  };
28
- /** Is `box` a real, non-degenerate region — i.e. is there anything to spotlight? */
12
+ /** Whether `box` is non-degenerate, so there is something to spotlight. */
29
13
  export function hasRegion(box) {
30
14
  return box.right > box.left && box.top > box.bottom;
31
15
  }
32
16
  /**
33
- * Resolve a {@link PlotRegion} against the plot's pixel box and the chart's
34
- * scales. `plot` is the plot rectangle in centred pixel space, and
35
- * `xScale`/`yScale` the data ranges those edges span.
36
- *
37
- * An omitted axis takes the plot's own edges, so the region spans it. A given
38
- * axis is clamped to the plot, so a range running off the scale still produces
39
- * a box inside the panel.
17
+ * Resolve a {@link PlotRegion} against the plot rectangle (centred pixel space)
18
+ * and the data ranges its edges span. An omitted axis takes the plot's own
19
+ * edges; a given one is clamped to the plot.
40
20
  */
41
21
  export function resolveRegionBox(region, plot, xScale, yScale) {
42
22
  if (!region || (region.x === undefined && region.y === undefined)) {
@@ -59,12 +39,10 @@ export function resolveRegionBox(region, plot, xScale, yScale) {
59
39
  return { left, right, bottom, top, xBounded, yBounded };
60
40
  }
61
41
  /**
62
- * The parts of `points` that fall **inside** `box`, as a list of contiguous
63
- * sub-polylines — the geometry behind the region spotlight: a series is drawn
64
- * dimmed in full, then these runs are re-drawn over it at full strength.
65
- *
66
- * Segments are clipped against the box rather than filtered by endpoint, so a
67
- * line entering the region mid-segment is cut exactly on the boundary.
42
+ * The parts of `points` inside `box`, as contiguous sub-polylines: a series is
43
+ * drawn dimmed in full, then these runs are re-drawn at full strength. Segments
44
+ * are clipped, not filtered by endpoint, so a line entering mid-segment is cut
45
+ * exactly on the boundary.
68
46
  */
69
47
  export function clipPolylineToBox(points, box) {
70
48
  if (points.length < 2 || !hasRegion(box))
@@ -74,15 +52,14 @@ export function clipPolylineToBox(points, box) {
74
52
  for (let i = 1; i < points.length; i++) {
75
53
  const seg = clipSegment(points[i - 1], points[i], box);
76
54
  if (!seg) {
77
- // Segment misses the box entirely — close whatever run was open.
55
+ // Misses the box: close the open run.
78
56
  if (run.length >= 2)
79
57
  runs.push(run);
80
58
  run = [];
81
59
  continue;
82
60
  }
83
61
  const [a, b] = seg;
84
- // Continue the open run when this clipped segment starts where the last one
85
- // ended; otherwise the line left the box and came back, which is a new run.
62
+ // Continues the open run if it starts where the last ended; otherwise the line left and came back.
86
63
  if (run.length > 0 && samePoint(run[run.length - 1], a))
87
64
  run.push(b);
88
65
  else {
@@ -99,16 +76,13 @@ export function clipPolylineToBox(points, box) {
99
76
  function samePoint(a, b) {
100
77
  return Math.abs(a.x - b.x) < 0.5 && Math.abs(a.y - b.y) < 0.5;
101
78
  }
102
- /**
103
- * Liang–Barsky clip of one segment against the axis-aligned `box`. Returns the
104
- * surviving `[start, end]`, or `null` when the segment lies wholly outside.
105
- */
79
+ /** Liang-Barsky clip of one segment against `box`: the surviving `[start, end]`, or `null` when wholly outside. */
106
80
  function clipSegment(a, b, box) {
107
81
  const dx = b.x - a.x;
108
82
  const dy = b.y - a.y;
109
83
  let t0 = 0;
110
84
  let t1 = 1;
111
- // Each edge as (p, q): the segment survives where p·t <= q.
85
+ // Each edge as (p, q): the segment survives where p*t <= q.
112
86
  const edges = [
113
87
  [-dx, a.x - box.left],
114
88
  [dx, box.right - a.x],
@@ -117,7 +91,7 @@ function clipSegment(a, b, box) {
117
91
  ];
118
92
  for (const [p, q] of edges) {
119
93
  if (p === 0) {
120
- // Parallel to this edge: outside it means the whole segment is out.
94
+ // Parallel to this edge: outside it, the whole segment is out.
121
95
  if (q < 0)
122
96
  return null;
123
97
  continue;
@@ -142,103 +116,15 @@ function clipSegment(a, b, box) {
142
116
  ];
143
117
  }
144
118
  /**
145
- * The legend of a line chart: the shared one, with a small rounded-square
146
- * swatch in place of the wide chip. A bar's or a wedge's swatch stands in for
147
- * an area of paint and is drawn like one; a line's stands in for a colour, and
148
- * a square reads as the sample it is.
149
- */
150
- const LINE_CHART_LEGEND = {
151
- ...DEFAULT_LEGEND,
152
- markerStyle: {
153
- ...DEFAULT_LEGEND.markerStyle,
154
- width: 20,
155
- height: 20,
156
- borderRadius: 6,
157
- },
158
- };
159
- /** The chart's out-of-the-box look. */
160
- export const DEFAULT_CHART_THEME = {
161
- seriesStyles: DEFAULT_SERIES_STROKES,
162
- xAxis: DEFAULT_X_AXIS,
163
- yAxis: DEFAULT_Y_AXIS,
164
- plotArea: DEFAULT_PLOT_AREA,
165
- legend: LINE_CHART_LEGEND,
166
- region: {
167
- // Brighter than the grid and dashed, so the boundary reads as a call-out
168
- // rather than another axis line; `butt` caps keep the dash lengths exact.
169
- stroke: {
170
- weight: 3,
171
- fill: CHART_PALETTE.regionColor,
172
- dash: [14, 12],
173
- cap: "butt",
174
- },
175
- // A wash barely above the panel — enough to tint the band, not enough to
176
- // mute the series drawn over it.
177
- fill: Fills.color(CHART_PALETTE.regionColor, { opacity: 0.08 }),
178
- dimOpacity: SPOTLIGHT_DIM,
179
- },
180
- };
181
- function resolveRegionConfig(region, previous) {
182
- return {
183
- stroke: strokeOps.resolve(region?.stroke ?? previous.stroke),
184
- fill: fillOps.resolve(region?.fill ?? previous.fill),
185
- dimOpacity: region?.dimOpacity ?? previous.dimOpacity,
186
- };
187
- }
188
- function lerpRegionConfig(from, to, t) {
189
- return {
190
- stroke: strokeOps.lerp(from.stroke, to.stroke, t),
191
- fill: fillOps.lerp(from.fill, to.fill, t),
192
- dimOpacity: lerpNumber(from.dimOpacity, to.dimOpacity, t),
193
- };
194
- }
195
- /**
196
- * {@link LineChart}'s `theme` entry's resolve: merges the author's partial
197
- * {@link ChartTheme} onto `previous` (falling back to
198
- * {@link DEFAULT_CHART_THEME} on first resolution), fully resolving every leaf.
199
- */
200
- export function resolveChartTheme(theme, previous) {
201
- const base = previous ?? DEFAULT_CHART_THEME;
202
- return {
203
- seriesStyles: resolveSeriesStrokes(theme?.seriesStyles, base.seriesStyles),
204
- xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),
205
- yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),
206
- plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),
207
- legend: resolveLegendConfig(theme?.legend, base.legend),
208
- region: resolveRegionConfig(theme?.region, base.region),
209
- };
210
- }
211
- /** {@link LineChart}'s `theme` entry's lerp — leaf by leaf. */
212
- export function lerpChartTheme(from, to, t) {
213
- return {
214
- seriesStyles: lerpSeriesStrokes(from.seriesStyles, to.seriesStyles, t),
215
- xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),
216
- yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),
217
- plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),
218
- legend: lerpLegendConfig(from.legend, to.legend, t),
219
- region: lerpRegionConfig(from.region, to.region, t),
220
- };
221
- }
222
- // ---- Scales --------------------------------------------------------------
223
- /**
224
- * Resolve one series' `[x, y]` points out of the chart's rows: each row's
225
- * position on the {@link XScale} paired with its own `field` column, sorted
226
- * along x so the polyline traces left-to-right whatever order the rows arrived
227
- * in.
228
- *
229
- * A row missing its y column contributes a 0 rather than a gap — these charts
230
- * draw continuous lines, so dropping the point would silently bridge the hole.
231
- *
232
- * Takes the resolved scale rather than the column *name*, because what a row's x
233
- * is depends on what kind of column it came out of: a number, an instant, or a
234
- * place in the file. See `x-scale.ts`.
119
+ * One series' `[x, y]` points, sorted along x so the polyline traces left to
120
+ * right. A row missing its y contributes 0 rather than a gap, since dropping it
121
+ * would silently bridge the hole.
235
122
  */
236
123
  export function seriesPoints(data, x, series) {
237
124
  return data
238
125
  .map((row, i) => [x.at(i), num(row, series.field)])
239
126
  .sort((a, b) => a[0] - b[0]);
240
127
  }
241
- /** Min/max across every series' column, the fallback when unset. */
242
128
  export function yExtent(data, series) {
243
129
  let lo = Infinity;
244
130
  let hi = -Infinity;
@@ -253,33 +139,186 @@ export function yExtent(data, series) {
253
139
  return lo <= hi ? [lo, hi] : [0, 1];
254
140
  }
255
141
  /**
256
- * Effective vertical scale: the explicit `range`, else the data's y extent
257
- * nudged out to tidy step bounds so markers read as round numbers.
258
- *
259
- * `divisions` is how many segments the auto-range is stepped into (one fewer
260
- * than a marker count) — it only shapes the tidy step when `range` is omitted.
142
+ * The explicit `range`, else the data's y extent nudged out to tidy step bounds.
143
+ * `divisions` (one fewer than the marker count) only shapes the step when
144
+ * `range` is omitted.
261
145
  */
262
146
  export function resolveYScale(data, series, range, divisions) {
263
- if (range)
264
- return range;
265
- const [lo, hi] = yExtent(data, series);
266
- const segments = Math.max(1, Math.round(divisions));
267
- if (hi === lo)
268
- return constantScale(lo, segments);
269
- const step = niceStep((hi - lo) / segments);
270
- return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step];
147
+ return range ?? niceRange(yExtent(data, series), divisions);
148
+ }
149
+ /**
150
+ * Every series' points under `stack`, rows in x order. A series contributes its
151
+ * values times its weight (`1` when missing), so a series sinking out of a stack
152
+ * lowers the ones above it. `percent` reads each row as shares of its total
153
+ * magnitude, so the top of a full stack is 100.
154
+ */
155
+ export function stackSeries(data, x, series, stack, weights = []) {
156
+ const order = data.map((_, i) => i).sort((a, b) => x.at(a) - x.at(b));
157
+ if (stack !== "stacked" && stack !== "percent") {
158
+ return series.map((s) => ({ points: order.map((r) => [x.at(r), num(data[r], s.field)]), base: null }));
159
+ }
160
+ const weight = (i) => Math.max(0, weights[i] ?? 1);
161
+ const totals = order.map((r) => series.reduce((sum, s, i) => sum + Math.abs(num(data[r], s.field) * weight(i)), 0));
162
+ const running = order.map(() => 0);
163
+ return series.map((s, i) => {
164
+ const base = order.map((r, k) => [x.at(r), running[k]]);
165
+ const points = order.map((r, k) => {
166
+ const value = num(data[r], s.field) * weight(i);
167
+ running[k] += stack === "percent" ? (totals[k] > 0 ? (value / totals[k]) * 100 : 0) : value;
168
+ return [x.at(r), running[k]];
169
+ });
170
+ return { points, base };
171
+ });
172
+ }
173
+ /** The lowest and highest y anything in `stacks` reaches, its bases included. */
174
+ export function stackExtent(stacks) {
175
+ let lo = Infinity;
176
+ let hi = -Infinity;
177
+ for (const { points, base } of stacks)
178
+ for (const [, y] of [...points, ...(base ?? [])]) {
179
+ if (y < lo)
180
+ lo = y;
181
+ if (y > hi)
182
+ hi = y;
183
+ }
184
+ return lo <= hi ? [lo, hi] : [0, 1];
185
+ }
186
+ /** Points along a cubic Bezier segment, its start left out. */
187
+ const SAMPLES = 16;
188
+ /**
189
+ * The drawn path through `points` (ordered along x) as a polyline: the points
190
+ * themselves for `linear`; for `step`, level runs that change at the midpoint
191
+ * between neighbours; for `smooth`, a monotone cubic (Fritsch-Carlson, as d3's
192
+ * `curveMonotoneX`), which never overshoots the data between two points.
193
+ */
194
+ export function curvePoints(points, curve) {
195
+ if (curve === "step") {
196
+ const out = points.length > 0 ? [points[0]] : [];
197
+ for (let i = 1; i < points.length; i++) {
198
+ const mid = (points[i - 1].x + points[i].x) / 2;
199
+ out.push({ x: mid, y: points[i - 1].y }, { x: mid, y: points[i].y }, points[i]);
200
+ }
201
+ return out;
202
+ }
203
+ if (curve !== "smooth" || points.length < 3)
204
+ return [...points];
205
+ const tangents = monotoneTangents(points);
206
+ const out = [points[0]];
207
+ for (let i = 1; i < points.length; i++) {
208
+ const a = points[i - 1];
209
+ const b = points[i];
210
+ const dx = (b.x - a.x) / 3;
211
+ const c1 = { x: a.x + dx, y: a.y + dx * tangents[i - 1] };
212
+ const c2 = { x: b.x - dx, y: b.y - dx * tangents[i] };
213
+ for (let k = 1; k <= SAMPLES; k++) {
214
+ const t = k / SAMPLES;
215
+ const u = 1 - t;
216
+ out.push({
217
+ x: u * u * u * a.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * b.x,
218
+ y: u * u * u * a.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * b.y,
219
+ });
220
+ }
221
+ }
222
+ return out;
223
+ }
224
+ function monotoneTangents(points) {
225
+ const n = points.length;
226
+ const slope = (i) => {
227
+ const h = points[i + 1].x - points[i].x;
228
+ return h === 0 ? 0 : (points[i + 1].y - points[i].y) / h;
229
+ };
230
+ const m = new Array(n).fill(0);
231
+ for (let i = 1; i < n - 1; i++) {
232
+ const h0 = points[i].x - points[i - 1].x;
233
+ const h1 = points[i + 1].x - points[i].x;
234
+ const s0 = slope(i - 1);
235
+ const s1 = slope(i);
236
+ const p = h0 + h1 === 0 ? 0 : (s0 * h1 + s1 * h0) / (h0 + h1);
237
+ m[i] = (Math.sign(s0) + Math.sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
238
+ }
239
+ m[0] = endTangent(slope(0), m[1]);
240
+ m[n - 1] = endTangent(slope(n - 2), m[n - 2]);
241
+ return m;
242
+ }
243
+ /** d3's one-sided end tangent, held to the end segment's own direction and to three times its slope, past which it would overshoot. */
244
+ function endTangent(secant, inner) {
245
+ const t = (3 * secant - inner) / 2;
246
+ if (secant === 0 || Math.sign(t) !== Math.sign(secant))
247
+ return 0;
248
+ return Math.abs(t) > 3 * Math.abs(secant) ? 3 * secant : t;
249
+ }
250
+ /** The part of `points` (ordered along x) left of `x`, ending on the point where it crosses `x`. */
251
+ export function cutAt(points, x) {
252
+ if (points.length === 0 || x < points[0].x)
253
+ return [];
254
+ const out = [];
255
+ for (let i = 0; i < points.length; i++) {
256
+ const p = points[i];
257
+ if (p.x <= x) {
258
+ out.push(p);
259
+ continue;
260
+ }
261
+ const a = points[i - 1];
262
+ if (a && p.x > a.x)
263
+ out.push({ x, y: a.y + ((p.y - a.y) * (x - a.x)) / (p.x - a.x) });
264
+ break;
265
+ }
266
+ return out;
271
267
  }
272
268
  /**
273
- * The scale for data that never moves. A zero-width scale puts every tick on the
274
- * same value and the line on the plot's floor, so it is widened to a few steps
275
- * either side of the value (the step sized to its magnitude, so `5` gets
276
- * `[3, 7]` and `5000` gets `[3000, 7000]`), or to `[0, 1]` for all zeros, as for
277
- * no data at all.
269
+ * `polygon` clipped to `box` (Sutherland-Hodgman against each edge in turn).
270
+ * The box is convex, so the result is the part inside, possibly with seams of
271
+ * no width where a concave outline leaves and re-enters, which fill as nothing.
278
272
  */
279
- function constantScale(value, segments) {
280
- if (value === 0)
281
- return [0, 1];
282
- const step = niceStep(Math.abs(value) / segments);
283
- return [value - step, value + step];
273
+ export function clipPolygonToBox(polygon, box) {
274
+ if (polygon.length < 3 || !hasRegion(box))
275
+ return [];
276
+ const edges = [
277
+ (p) => p.x - box.left,
278
+ (p) => box.right - p.x,
279
+ (p) => p.y - box.bottom,
280
+ (p) => box.top - p.y,
281
+ ];
282
+ let out = [...polygon];
283
+ for (const inside of edges) {
284
+ const input = out;
285
+ out = [];
286
+ for (let i = 0; i < input.length; i++) {
287
+ const cur = input[i];
288
+ const prev = input[(i + input.length - 1) % input.length];
289
+ const dc = inside(cur);
290
+ const dp = inside(prev);
291
+ if (dc >= 0) {
292
+ if (dp < 0)
293
+ out.push(crossing(prev, cur, dp, dc));
294
+ out.push(cur);
295
+ }
296
+ else if (dp >= 0)
297
+ out.push(crossing(prev, cur, dp, dc));
298
+ }
299
+ if (out.length === 0)
300
+ return [];
301
+ }
302
+ return out;
284
303
  }
304
+ function crossing(a, b, da, db) {
305
+ const t = da / (da - db);
306
+ return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
307
+ }
308
+ /** A line chart's own look: how each series is stroked, dotted and filled. */
309
+ export const LINE_MARKS = {
310
+ strokeWidth: Types.number(4, { min: 0, max: 24, step: 0.5, unit: "px" }),
311
+ curve: Types.oneOf(["smooth", "linear", "step"]),
312
+ dots: Types.boolean(false),
313
+ /** Each dot's diameter. */
314
+ dotSize: Types.number(12, { min: 0, max: 48, step: 1, unit: "px" }),
315
+ /** Fill under each line: none, the series colour, or the colour fading to nothing at the baseline. */
316
+ area: Types.oneOf(["none", "solid", "gradient"]),
317
+ /** The fill's opacity at the line. */
318
+ areaOpacity: Types.number(0.4, { min: 0, max: 1, step: 0.05 }),
319
+ };
320
+ export const LINE_LABELS = {
321
+ /** Read as the chart is built: each point's value printed above or below it, or none. */
322
+ values: Types.oneOf(["none", "above", "below"], { animatable: false }),
323
+ };
285
324
  //# sourceMappingURL=shared.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/line-chart/shared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Types and pure helpers specific to the {@link LineChart} family: the traced\n * polylines, the spotlit region they dim outside of, and the theme those two\n * add to the shared chart vocabulary.\n *\n * Everything a bar or a pie chart would want too — what a series is, how an axis\n * is scaled and formatted, the theme leaf resolvers, the legend and plot-panel\n * branches — lives in `../kit/shared.ts` and is imported here. What's\n * left below is what only a line chart has: points sorted along an x column,\n * a region clipped out of a polyline, and the `region` branch of the theme.\n *\n * Kept renderer-agnostic (no `Node` imports) so every part shares one\n * scale/format/palette/theme vocabulary, and so a host's own inspector\n * mapping can build a partial {@link ChartTheme} without\n * pulling the components in.\n */\n\nimport {\n Fills,\n fillOps,\n lerpNumber,\n strokeOps,\n type DataRecord,\n type Fill,\n type FillResolved,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/sdk\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_STROKES,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesStrokes,\n niceStep,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesStrokes,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\nimport type { XScale } from \"./x-scale\"\n\n/**\n * One series in a {@link LineChart}: a polyline traced through the values in one\n * column of the chart's `data`, drawn in `fill` (or the theme's `seriesStyles`\n * palette when omitted) and captioned by `label` in the legend.\n *\n * The family-wide {@link ChartSeries} under a name that says what it draws here;\n * the chart's own `xField` prop names the column carrying the shared x values,\n * so every series is read off the same rows — one row per sample, one column per\n * series.\n */\nexport type LineSeries = ChartSeries\n\n// ---- Plot regions -------------------------------------------------------\n\n/**\n * A rectangular slice of the plot, in **data units** — the author-facing input\n * to {@link LineChart.spotlightRegion} / {@link LineChart.zoomTo}.\n *\n * Either axis may be omitted, and that's the point: `{ x: [60, 100] }` is a\n * vertical band spanning the full height, `{ y: [8, 10] }` a horizontal band\n * spanning the full width, and both together a box. A reversed range is read\n * the same as the forward one — a region is a span, not a direction.\n */\nexport interface PlotRegion {\n /** Horizontal span in x-axis data units. Omit to span the full plot width. */\n x?: Range\n /** Vertical span in y-axis data units. Omit to span the full plot height. */\n y?: Range\n}\n\n/**\n * A {@link PlotRegion} resolved into the plot's **centred pixel space** (origin\n * at the plot centre, y-up) — the same local frame the series points live in,\n * so a line can be clipped against it directly.\n *\n * `xBounded`/`yBounded` carry which axes the author actually constrained: an\n * unbounded axis has its edges pinned to the plot's own and draws no rule.\n */\nexport interface RegionBox {\n left: number\n right: number\n bottom: number\n top: number\n /** The author gave an x range — the left/right edges are real data bounds. */\n xBounded: boolean\n /** The author gave a y range — the bottom/top edges are real data bounds. */\n yBounded: boolean\n}\n\n/** The empty box, used as the \"no region\" resting value. */\nexport const EMPTY_REGION_BOX: RegionBox = {\n left: 0,\n right: 0,\n bottom: 0,\n top: 0,\n xBounded: false,\n yBounded: false,\n}\n\n/** Is `box` a real, non-degenerate region — i.e. is there anything to spotlight? */\nexport function hasRegion(box: RegionBox): boolean {\n return box.right > box.left && box.top > box.bottom\n}\n\n/**\n * Resolve a {@link PlotRegion} against the plot's pixel box and the chart's\n * scales. `plot` is the plot rectangle in centred pixel space, and\n * `xScale`/`yScale` the data ranges those edges span.\n *\n * An omitted axis takes the plot's own edges, so the region spans it. A given\n * axis is clamped to the plot, so a range running off the scale still produces\n * a box inside the panel.\n */\nexport function resolveRegionBox(\n region: PlotRegion | null | undefined,\n plot: { left: number; right: number; bottom: number; top: number },\n xScale: Range,\n yScale: Range\n): RegionBox {\n if (!region || (region.x === undefined && region.y === undefined)) {\n return EMPTY_REGION_BOX\n }\n\n const span = (\n range: Range | undefined,\n [min, max]: Range,\n lo: number,\n hi: number\n ): [number, number, boolean] => {\n if (range === undefined || max <= min) return [lo, hi, false]\n const a = Math.min(range[0], range[1])\n const b = Math.max(range[0], range[1])\n const toPx = (v: number) => lo + ((v - min) / (max - min)) * (hi - lo)\n return [\n Math.max(lo, Math.min(hi, toPx(a))),\n Math.max(lo, Math.min(hi, toPx(b))),\n true,\n ]\n }\n\n const [left, right, xBounded] = span(region.x, xScale, plot.left, plot.right)\n const [bottom, top, yBounded] = span(region.y, yScale, plot.bottom, plot.top)\n return { left, right, bottom, top, xBounded, yBounded }\n}\n\n/** A bare `{x, y}` — declared locally so this module stays free of node imports. */\ninterface Vector2Like {\n x: number\n y: number\n}\n\n/**\n * The parts of `points` that fall **inside** `box`, as a list of contiguous\n * sub-polylines — the geometry behind the region spotlight: a series is drawn\n * dimmed in full, then these runs are re-drawn over it at full strength.\n *\n * Segments are clipped against the box rather than filtered by endpoint, so a\n * line entering the region mid-segment is cut exactly on the boundary.\n */\nexport function clipPolylineToBox(\n points: Vector2Like[],\n box: RegionBox\n): Vector2Like[][] {\n if (points.length < 2 || !hasRegion(box)) return []\n\n const runs: Vector2Like[][] = []\n let run: Vector2Like[] = []\n\n for (let i = 1; i < points.length; i++) {\n const seg = clipSegment(points[i - 1], points[i], box)\n if (!seg) {\n // Segment misses the box entirely — close whatever run was open.\n if (run.length >= 2) runs.push(run)\n run = []\n continue\n }\n const [a, b] = seg\n // Continue the open run when this clipped segment starts where the last one\n // ended; otherwise the line left the box and came back, which is a new run.\n if (run.length > 0 && samePoint(run[run.length - 1], a)) run.push(b)\n else {\n if (run.length >= 2) runs.push(run)\n run = [a, b]\n }\n }\n if (run.length >= 2) runs.push(run)\n return runs\n}\n\n/** Points within half a pixel are the same point, for run-joining purposes. */\nfunction samePoint(a: Vector2Like, b: Vector2Like): boolean {\n return Math.abs(a.x - b.x) < 0.5 && Math.abs(a.y - b.y) < 0.5\n}\n\n/**\n * Liang–Barsky clip of one segment against the axis-aligned `box`. Returns the\n * surviving `[start, end]`, or `null` when the segment lies wholly outside.\n */\nfunction clipSegment(\n a: Vector2Like,\n b: Vector2Like,\n box: RegionBox\n): [Vector2Like, Vector2Like] | null {\n const dx = b.x - a.x\n const dy = b.y - a.y\n let t0 = 0\n let t1 = 1\n\n // Each edge as (p, q): the segment survives where p·t <= q.\n const edges: [number, number][] = [\n [-dx, a.x - box.left],\n [dx, box.right - a.x],\n [-dy, a.y - box.bottom],\n [dy, box.top - a.y],\n ]\n for (const [p, q] of edges) {\n if (p === 0) {\n // Parallel to this edge: outside it means the whole segment is out.\n if (q < 0) return null\n continue\n }\n const r = q / p\n if (p < 0) {\n if (r > t1) return null\n if (r > t0) t0 = r\n } else {\n if (r < t0) return null\n if (r < t1) t1 = r\n }\n }\n return [\n { x: a.x + t0 * dx, y: a.y + t0 * dy },\n { x: a.x + t1 * dx, y: a.y + t1 * dy },\n ]\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/**\n * How a spotlit {@link PlotRegion} is drawn — the dashed rules on its bounded\n * edges, the wash filling it, and how far the series fade outside it.\n */\nexport interface RegionConfig {\n /** The dashed rule marking each bounded edge. */\n stroke: Stroke\n /** Wash filling the region, drawn under the series lines. */\n fill: Fill\n /** Opacity the stretch of every series *outside* the region fades to. */\n dimOpacity: number\n}\n\n/** A {@link LineChart}'s full, resolved visual theme. */\nexport interface ChartConfig {\n /**\n * Default per-series line stroke, cycled by series index (`i % length`). A\n * series' own `fill`, when set, overrides just the colour.\n */\n seriesStyles: Stroke[]\n xAxis: AxisConfig\n yAxis: AxisConfig\n /** The plot panel: border, fill, and drop shadow. */\n plotArea: PlotAreaConfig\n legend: LegendConfig\n /** The spotlit-region annotation — see {@link RegionConfig}. */\n region: RegionConfig\n}\n\n/**\n * Author-facing partial theme, passed as {@link LineChart}'s `theme` prop —\n * every branch optional and deep-merged onto {@link DEFAULT_CHART_THEME}.\n */\nexport type ChartTheme = {\n seriesStyles?: Stroke[]\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n region?: Partial<RegionConfig>\n}\n\n/**\n * The legend of a line chart: the shared one, with a small rounded-square\n * swatch in place of the wide chip. A bar's or a wedge's swatch stands in for\n * an area of paint and is drawn like one; a line's stands in for a colour, and\n * a square reads as the sample it is.\n */\nconst LINE_CHART_LEGEND: LegendConfig = {\n ...DEFAULT_LEGEND,\n markerStyle: {\n ...DEFAULT_LEGEND.markerStyle,\n width: 20,\n height: 20,\n borderRadius: 6,\n },\n}\n\n/** The chart's out-of-the-box look. */\nexport const DEFAULT_CHART_THEME: ChartConfig = {\n seriesStyles: DEFAULT_SERIES_STROKES,\n xAxis: DEFAULT_X_AXIS,\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: LINE_CHART_LEGEND,\n region: {\n // Brighter than the grid and dashed, so the boundary reads as a call-out\n // rather than another axis line; `butt` caps keep the dash lengths exact.\n stroke: {\n weight: 3,\n fill: CHART_PALETTE.regionColor,\n dash: [14, 12],\n cap: \"butt\",\n },\n // A wash barely above the panel — enough to tint the band, not enough to\n // mute the series drawn over it.\n fill: Fills.color(CHART_PALETTE.regionColor, { opacity: 0.08 }),\n dimOpacity: SPOTLIGHT_DIM,\n },\n}\n\nfunction resolveRegionConfig(\n region: Partial<RegionConfig> | undefined,\n previous: RegionConfig\n): RegionConfig {\n return {\n stroke: strokeOps.resolve(region?.stroke ?? previous.stroke),\n fill: fillOps.resolve(region?.fill ?? previous.fill),\n dimOpacity: region?.dimOpacity ?? previous.dimOpacity,\n }\n}\n\nfunction lerpRegionConfig(\n from: RegionConfig,\n to: RegionConfig,\n t: number\n): RegionConfig {\n return {\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n fill: fillOps.lerp(\n from.fill as FillResolved[],\n to.fill as FillResolved[],\n t\n ),\n dimOpacity: lerpNumber(from.dimOpacity, to.dimOpacity, t),\n }\n}\n\n/**\n * {@link LineChart}'s `theme` entry's resolve: merges the author's partial\n * {@link ChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_CHART_THEME} on first resolution), fully resolving every leaf.\n */\nexport function resolveChartTheme(\n theme: ChartTheme | undefined,\n previous?: ChartConfig\n): ChartConfig {\n const base = previous ?? DEFAULT_CHART_THEME\n return {\n seriesStyles: resolveSeriesStrokes(theme?.seriesStyles, base.seriesStyles),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n region: resolveRegionConfig(theme?.region, base.region),\n }\n}\n\n/** {@link LineChart}'s `theme` entry's lerp — leaf by leaf. */\nexport function lerpChartTheme(\n from: ChartConfig,\n to: ChartConfig,\n t: number\n): ChartConfig {\n return {\n seriesStyles: lerpSeriesStrokes(from.seriesStyles, to.seriesStyles, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n region: lerpRegionConfig(from.region, to.region, t),\n }\n}\n\n// ---- Scales --------------------------------------------------------------\n\n/**\n * Resolve one series' `[x, y]` points out of the chart's rows: each row's\n * position on the {@link XScale} paired with its own `field` column, sorted\n * along x so the polyline traces left-to-right whatever order the rows arrived\n * in.\n *\n * A row missing its y column contributes a 0 rather than a gap — these charts\n * draw continuous lines, so dropping the point would silently bridge the hole.\n *\n * Takes the resolved scale rather than the column *name*, because what a row's x\n * is depends on what kind of column it came out of: a number, an instant, or a\n * place in the file. See `x-scale.ts`.\n */\nexport function seriesPoints(\n data: DataRecord[],\n x: XScale,\n series: LineSeries\n): [x: number, y: number][] {\n return data\n .map((row, i): [number, number] => [x.at(i), num(row, series.field)])\n .sort((a, b) => a[0] - b[0])\n}\n\n/** Min/max across every series' column, the fallback when unset. */\nexport function yExtent(data: DataRecord[], series: LineSeries[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const row of data)\n for (const s of series) {\n const v = num(row, s.field)\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Effective vertical scale: the explicit `range`, else the data's y extent\n * nudged out to tidy step bounds so markers read as round numbers.\n *\n * `divisions` is how many segments the auto-range is stepped into (one fewer\n * than a marker count) — it only shapes the tidy step when `range` is omitted.\n */\nexport function resolveYScale(\n data: DataRecord[],\n series: LineSeries[],\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = yExtent(data, series)\n const segments = Math.max(1, Math.round(divisions))\n if (hi === lo) return constantScale(lo, segments)\n const step = niceStep((hi - lo) / segments)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n\n/**\n * The scale for data that never moves. A zero-width scale puts every tick on the\n * same value and the line on the plot's floor, so it is widened to a few steps\n * either side of the value (the step sized to its magnitude, so `5` gets\n * `[3, 7]` and `5000` gets `[3000, 7000]`), or to `[0, 1]` for all zeros, as for\n * no data at all.\n */\nfunction constantScale(value: number, segments: number): Range {\n if (value === 0) return [0, 1]\n const step = niceStep(Math.abs(value) / segments)\n return [value - step, value + step]\n}\n"]}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/line-chart/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { Types, type DataRecord, type Vector2 } from \"@motionscript/sdk\"\n\nimport { niceRange, num, type ChartSeries, type Range } from \"../kit/shared\"\nimport type { XScale } from \"./x-scale\"\n\n/** One polyline of a {@link LineChart}: {@link ChartSeries} under the name for what it draws here. */\nexport type LineSeries = ChartSeries\n\n/**\n * A rectangular slice of the plot in data units, the input to `spotlightRegion`\n * and `zoomTo`. Either axis may be omitted: `{ x: [60, 100] }` is a full-height\n * band. A reversed range reads the same as a forward one.\n */\nexport interface PlotRegion {\n /** Omit to span the full plot width. */\n x?: Range\n /** Omit to span the full plot height. */\n y?: Range\n}\n\n/**\n * A {@link PlotRegion} in the plot's centred pixel space (origin at the centre,\n * y-up), the frame the series points live in. An axis the author left unbounded\n * has its edges pinned to the plot's own and draws no rule.\n */\nexport interface RegionBox {\n left: number\n right: number\n bottom: number\n top: number\n xBounded: boolean\n yBounded: boolean\n}\n\n/** The \"no region\" resting value. */\nexport const EMPTY_REGION_BOX: RegionBox = {\n left: 0,\n right: 0,\n bottom: 0,\n top: 0,\n xBounded: false,\n yBounded: false,\n}\n\n/** Whether `box` is non-degenerate, so there is something to spotlight. */\nexport function hasRegion(box: RegionBox): boolean {\n return box.right > box.left && box.top > box.bottom\n}\n\n/**\n * Resolve a {@link PlotRegion} against the plot rectangle (centred pixel space)\n * and the data ranges its edges span. An omitted axis takes the plot's own\n * edges; a given one is clamped to the plot.\n */\nexport function resolveRegionBox(\n region: PlotRegion | null | undefined,\n plot: { left: number; right: number; bottom: number; top: number },\n xScale: Range,\n yScale: Range\n): RegionBox {\n if (!region || (region.x === undefined && region.y === undefined)) {\n return EMPTY_REGION_BOX\n }\n\n const span = (\n range: Range | undefined,\n [min, max]: Range,\n lo: number,\n hi: number\n ): [number, number, boolean] => {\n if (range === undefined || max <= min) return [lo, hi, false]\n const a = Math.min(range[0], range[1])\n const b = Math.max(range[0], range[1])\n const toPx = (v: number) => lo + ((v - min) / (max - min)) * (hi - lo)\n return [\n Math.max(lo, Math.min(hi, toPx(a))),\n Math.max(lo, Math.min(hi, toPx(b))),\n true,\n ]\n }\n\n const [left, right, xBounded] = span(region.x, xScale, plot.left, plot.right)\n const [bottom, top, yBounded] = span(region.y, yScale, plot.bottom, plot.top)\n return { left, right, bottom, top, xBounded, yBounded }\n}\n\n/**\n * The parts of `points` inside `box`, as contiguous sub-polylines: a series is\n * drawn dimmed in full, then these runs are re-drawn at full strength. Segments\n * are clipped, not filtered by endpoint, so a line entering mid-segment is cut\n * exactly on the boundary.\n */\nexport function clipPolylineToBox(\n points: Vector2[],\n box: RegionBox\n): Vector2[][] {\n if (points.length < 2 || !hasRegion(box)) return []\n\n const runs: Vector2[][] = []\n let run: Vector2[] = []\n\n for (let i = 1; i < points.length; i++) {\n const seg = clipSegment(points[i - 1], points[i], box)\n if (!seg) {\n // Misses the box: close the open run.\n if (run.length >= 2) runs.push(run)\n run = []\n continue\n }\n const [a, b] = seg\n // Continues the open run if it starts where the last ended; otherwise the line left and came back.\n if (run.length > 0 && samePoint(run[run.length - 1], a)) run.push(b)\n else {\n if (run.length >= 2) runs.push(run)\n run = [a, b]\n }\n }\n if (run.length >= 2) runs.push(run)\n return runs\n}\n\n/** Points within half a pixel are the same point, for run-joining purposes. */\nfunction samePoint(a: Vector2, b: Vector2): boolean {\n return Math.abs(a.x - b.x) < 0.5 && Math.abs(a.y - b.y) < 0.5\n}\n\n/** Liang-Barsky clip of one segment against `box`: the surviving `[start, end]`, or `null` when wholly outside. */\nfunction clipSegment(\n a: Vector2,\n b: Vector2,\n box: RegionBox\n): [Vector2, Vector2] | null {\n const dx = b.x - a.x\n const dy = b.y - a.y\n let t0 = 0\n let t1 = 1\n\n // Each edge as (p, q): the segment survives where p*t <= q.\n const edges: [number, number][] = [\n [-dx, a.x - box.left],\n [dx, box.right - a.x],\n [-dy, a.y - box.bottom],\n [dy, box.top - a.y],\n ]\n for (const [p, q] of edges) {\n if (p === 0) {\n // Parallel to this edge: outside it, the whole segment is out.\n if (q < 0) return null\n continue\n }\n const r = q / p\n if (p < 0) {\n if (r > t1) return null\n if (r > t0) t0 = r\n } else {\n if (r < t0) return null\n if (r < t1) t1 = r\n }\n }\n return [\n { x: a.x + t0 * dx, y: a.y + t0 * dy },\n { x: a.x + t1 * dx, y: a.y + t1 * dy },\n ]\n}\n\n/**\n * One series' `[x, y]` points, sorted along x so the polyline traces left to\n * right. A row missing its y contributes 0 rather than a gap, since dropping it\n * would silently bridge the hole.\n */\nexport function seriesPoints(\n data: DataRecord[],\n x: XScale,\n series: LineSeries\n): [x: number, y: number][] {\n return data\n .map((row, i): [number, number] => [x.at(i), num(row, series.field)])\n .sort((a, b) => a[0] - b[0])\n}\n\nexport function yExtent(data: DataRecord[], series: LineSeries[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const row of data)\n for (const s of series) {\n const v = num(row, s.field)\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * The explicit `range`, else the data's y extent nudged out to tidy step bounds.\n * `divisions` (one fewer than the marker count) only shapes the step when\n * `range` is omitted.\n */\nexport function resolveYScale(\n data: DataRecord[],\n series: LineSeries[],\n range: Range | undefined,\n divisions: number\n): Range {\n return range ?? niceRange(yExtent(data, series), divisions)\n}\n\n/** How the series sit on one another: apart, each on the sum of those before it, or as shares of the row's total. */\nexport type LineStack = \"none\" | \"stacked\" | \"percent\"\n\n/** One series as drawn: its `[x, y]` points along x, and the line its area fills down to (`null`: the baseline). */\nexport interface StackedSeries {\n readonly points: [x: number, y: number][]\n readonly base: [x: number, y: number][] | null\n}\n\n/**\n * Every series' points under `stack`, rows in x order. A series contributes its\n * values times its weight (`1` when missing), so a series sinking out of a stack\n * lowers the ones above it. `percent` reads each row as shares of its total\n * magnitude, so the top of a full stack is 100.\n */\nexport function stackSeries(\n data: DataRecord[],\n x: XScale,\n series: readonly LineSeries[],\n stack: LineStack,\n weights: readonly number[] = []\n): StackedSeries[] {\n const order = data.map((_, i) => i).sort((a, b) => x.at(a) - x.at(b))\n if (stack !== \"stacked\" && stack !== \"percent\") {\n return series.map((s) => ({ points: order.map((r): [number, number] => [x.at(r), num(data[r], s.field)]), base: null }))\n }\n const weight = (i: number) => Math.max(0, weights[i] ?? 1)\n const totals = order.map((r) => series.reduce((sum, s, i) => sum + Math.abs(num(data[r], s.field) * weight(i)), 0))\n const running = order.map(() => 0)\n return series.map((s, i) => {\n const base = order.map((r, k): [number, number] => [x.at(r), running[k]])\n const points = order.map((r, k): [number, number] => {\n const value = num(data[r], s.field) * weight(i)\n running[k] += stack === \"percent\" ? (totals[k] > 0 ? (value / totals[k]) * 100 : 0) : value\n return [x.at(r), running[k]]\n })\n return { points, base }\n })\n}\n\n/** The lowest and highest y anything in `stacks` reaches, its bases included. */\nexport function stackExtent(stacks: readonly StackedSeries[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const { points, base } of stacks)\n for (const [, y] of [...points, ...(base ?? [])]) {\n if (y < lo) lo = y\n if (y > hi) hi = y\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/** How a series' line runs between its points. */\nexport type LineCurve = \"smooth\" | \"linear\" | \"step\"\n\n/** Points along a cubic Bezier segment, its start left out. */\nconst SAMPLES = 16\n\n/**\n * The drawn path through `points` (ordered along x) as a polyline: the points\n * themselves for `linear`; for `step`, level runs that change at the midpoint\n * between neighbours; for `smooth`, a monotone cubic (Fritsch-Carlson, as d3's\n * `curveMonotoneX`), which never overshoots the data between two points.\n */\nexport function curvePoints(points: readonly Vector2[], curve: LineCurve): Vector2[] {\n if (curve === \"step\") {\n const out: Vector2[] = points.length > 0 ? [points[0]] : []\n for (let i = 1; i < points.length; i++) {\n const mid = (points[i - 1].x + points[i].x) / 2\n out.push({ x: mid, y: points[i - 1].y }, { x: mid, y: points[i].y }, points[i])\n }\n return out\n }\n if (curve !== \"smooth\" || points.length < 3) return [...points]\n const tangents = monotoneTangents(points)\n const out: Vector2[] = [points[0]]\n for (let i = 1; i < points.length; i++) {\n const a = points[i - 1]\n const b = points[i]\n const dx = (b.x - a.x) / 3\n const c1 = { x: a.x + dx, y: a.y + dx * tangents[i - 1] }\n const c2 = { x: b.x - dx, y: b.y - dx * tangents[i] }\n for (let k = 1; k <= SAMPLES; k++) {\n const t = k / SAMPLES\n const u = 1 - t\n out.push({\n x: u * u * u * a.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * b.x,\n y: u * u * u * a.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * b.y,\n })\n }\n }\n return out\n}\n\nfunction monotoneTangents(points: readonly Vector2[]): number[] {\n const n = points.length\n const slope = (i: number) => {\n const h = points[i + 1].x - points[i].x\n return h === 0 ? 0 : (points[i + 1].y - points[i].y) / h\n }\n const m = new Array<number>(n).fill(0)\n for (let i = 1; i < n - 1; i++) {\n const h0 = points[i].x - points[i - 1].x\n const h1 = points[i + 1].x - points[i].x\n const s0 = slope(i - 1)\n const s1 = slope(i)\n const p = h0 + h1 === 0 ? 0 : (s0 * h1 + s1 * h0) / (h0 + h1)\n m[i] = (Math.sign(s0) + Math.sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0\n }\n m[0] = endTangent(slope(0), m[1])\n m[n - 1] = endTangent(slope(n - 2), m[n - 2])\n return m\n}\n\n/** d3's one-sided end tangent, held to the end segment's own direction and to three times its slope, past which it would overshoot. */\nfunction endTangent(secant: number, inner: number): number {\n const t = (3 * secant - inner) / 2\n if (secant === 0 || Math.sign(t) !== Math.sign(secant)) return 0\n return Math.abs(t) > 3 * Math.abs(secant) ? 3 * secant : t\n}\n\n/** The part of `points` (ordered along x) left of `x`, ending on the point where it crosses `x`. */\nexport function cutAt(points: readonly Vector2[], x: number): Vector2[] {\n if (points.length === 0 || x < points[0].x) return []\n const out: Vector2[] = []\n for (let i = 0; i < points.length; i++) {\n const p = points[i]\n if (p.x <= x) {\n out.push(p)\n continue\n }\n const a = points[i - 1]\n if (a && p.x > a.x) out.push({ x, y: a.y + ((p.y - a.y) * (x - a.x)) / (p.x - a.x) })\n break\n }\n return out\n}\n\n/**\n * `polygon` clipped to `box` (Sutherland-Hodgman against each edge in turn).\n * The box is convex, so the result is the part inside, possibly with seams of\n * no width where a concave outline leaves and re-enters, which fill as nothing.\n */\nexport function clipPolygonToBox(polygon: readonly Vector2[], box: RegionBox): Vector2[] {\n if (polygon.length < 3 || !hasRegion(box)) return []\n const edges: ((p: Vector2) => number)[] = [\n (p) => p.x - box.left,\n (p) => box.right - p.x,\n (p) => p.y - box.bottom,\n (p) => box.top - p.y,\n ]\n let out: Vector2[] = [...polygon]\n for (const inside of edges) {\n const input = out\n out = []\n for (let i = 0; i < input.length; i++) {\n const cur = input[i]\n const prev = input[(i + input.length - 1) % input.length]\n const dc = inside(cur)\n const dp = inside(prev)\n if (dc >= 0) {\n if (dp < 0) out.push(crossing(prev, cur, dp, dc))\n out.push(cur)\n } else if (dp >= 0) out.push(crossing(prev, cur, dp, dc))\n }\n if (out.length === 0) return []\n }\n return out\n}\n\nfunction crossing(a: Vector2, b: Vector2, da: number, db: number): Vector2 {\n const t = da / (da - db)\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t }\n}\n\n/** A line chart's own look: how each series is stroked, dotted and filled. */\nexport const LINE_MARKS = {\n strokeWidth: Types.number(4, { min: 0, max: 24, step: 0.5, unit: \"px\" }),\n curve: Types.oneOf([\"smooth\", \"linear\", \"step\"] as const),\n dots: Types.boolean(false),\n /** Each dot's diameter. */\n dotSize: Types.number(12, { min: 0, max: 48, step: 1, unit: \"px\" }),\n /** Fill under each line: none, the series colour, or the colour fading to nothing at the baseline. */\n area: Types.oneOf([\"none\", \"solid\", \"gradient\"] as const),\n /** The fill's opacity at the line. */\n areaOpacity: Types.number(0.4, { min: 0, max: 1, step: 0.05 }),\n}\n\nexport const LINE_LABELS = {\n /** Read as the chart is built: each point's value printed above or below it, or none. */\n values: Types.oneOf([\"none\", \"above\", \"below\"] as const, { animatable: false }),\n}\n"]}