ggaction 0.0.21 → 0.0.22

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 (253) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +3 -2
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/package.json +6 -2
  7. package/src/BasicChartProgram.js +1 -1
  8. package/src/accessibility.js +1 -0
  9. package/src/actions/boxPlots/components.js +1 -96
  10. package/src/actions/boxPlots/create.js +6 -1
  11. package/src/actions/boxPlots/edit.js +8 -0
  12. package/src/actions/boxPlots/materialize.js +3 -0
  13. package/src/actions/boxPlots/options.js +1 -1
  14. package/src/actions/canvas/actions.js +2 -1
  15. package/src/actions/canvas/index.js +3 -0
  16. package/src/actions/canvas/plotBorder.js +2 -1
  17. package/src/actions/canvas/plotOrder.js +14 -0
  18. package/src/actions/charts/area.js +1 -1
  19. package/src/actions/charts/bar.js +4 -4
  20. package/src/actions/charts/density.js +1 -1
  21. package/src/actions/charts/ecdf.js +1 -1
  22. package/src/actions/charts/endpoints.js +54 -5
  23. package/src/actions/charts/heatmap.js +1 -1
  24. package/src/actions/charts/histogram.js +1 -1
  25. package/src/actions/charts/horizon.js +1 -1
  26. package/src/actions/charts/index.js +2 -1
  27. package/src/actions/charts/interval-regression.js +1 -1
  28. package/src/actions/charts/line.js +1 -1
  29. package/src/actions/charts/radar.js +63 -3
  30. package/src/actions/charts/raincloud.js +37 -6
  31. package/src/actions/charts/rug-strip.js +2 -2
  32. package/src/actions/charts/scatter.js +3 -3
  33. package/src/actions/charts/text.js +1 -1
  34. package/src/actions/composition/actions.js +10 -3
  35. package/src/actions/coordinates/actions.js +2 -0
  36. package/src/actions/data/basic.js +6 -0
  37. package/src/actions/data/derived.js +6 -2
  38. package/src/actions/data/edit.js +21 -35
  39. package/src/actions/data/filter.js +1 -376
  40. package/src/actions/data/flatten.js +19 -0
  41. package/src/actions/data/index.js +6 -0
  42. package/src/actions/data/lookup.js +50 -0
  43. package/src/actions/data/raincloudFilter.js +56 -0
  44. package/src/actions/data/shared.js +10 -0
  45. package/src/actions/distributions/revision.js +3 -1
  46. package/src/actions/encodings/angle.js +2 -1
  47. package/src/actions/encodings/appearance.js +38 -13
  48. package/src/actions/encodings/channels.js +14 -3
  49. package/src/actions/encodings/color/continuous.js +26 -10
  50. package/src/actions/encodings/color/index.js +50 -20
  51. package/src/actions/encodings/color/policy.js +57 -4
  52. package/src/actions/encodings/parallel.js +4 -0
  53. package/src/actions/encodings/position/policies/bar.js +10 -6
  54. package/src/actions/encodings/position/policies/line.js +1 -152
  55. package/src/actions/encodings/position/resolve.js +4 -1
  56. package/src/actions/encodings/ranged.js +46 -0
  57. package/src/actions/encodings/remove.js +1 -1
  58. package/src/actions/encodings/ruleAppearance.js +7 -6
  59. package/src/actions/encodings/shared.js +14 -2
  60. package/src/actions/encodings/stroke.js +20 -14
  61. package/src/actions/encodings/strokeDash.js +9 -11
  62. package/src/actions/encodings/text.js +30 -2
  63. package/src/actions/errorBars/edit.js +24 -4
  64. package/src/actions/facets/actions.js +1 -793
  65. package/src/actions/facets/derive.js +34 -5
  66. package/src/actions/facets/replay.js +1 -38
  67. package/src/actions/gradientPlots/components.js +21 -9
  68. package/src/actions/gradientPlots/create.js +4 -2
  69. package/src/actions/gradientPlots/edit.js +18 -1
  70. package/src/actions/gradientPlots/materialize.js +35 -36
  71. package/src/actions/gradientPlots/options.js +1 -1
  72. package/src/actions/gradientPlots/paint.js +13 -7
  73. package/src/actions/guides/applicability.js +2 -0
  74. package/src/actions/guides/axes/axes.js +39 -0
  75. package/src/actions/guides/axes/axis.js +9 -5
  76. package/src/actions/guides/axes/edit.js +11 -8
  77. package/src/actions/guides/axes/labels.js +24 -8
  78. package/src/actions/guides/axes/parallel/policy.js +3 -5
  79. package/src/actions/guides/axes/parallel/resolve.js +4 -3
  80. package/src/actions/guides/axes/policy.js +22 -0
  81. package/src/actions/guides/axes/tickGroups.js +4 -2
  82. package/src/actions/guides/axes/ticks.js +22 -4
  83. package/src/actions/guides/axes/titles.js +19 -6
  84. package/src/actions/guides/facadeAxes.js +3 -0
  85. package/src/actions/guides/grids/grid.js +3 -1
  86. package/src/actions/guides/grids/resolve.js +7 -11
  87. package/src/actions/guides/guides.js +21 -1
  88. package/src/actions/guides/legends/categorical/actions.js +1 -515
  89. package/src/actions/guides/legends/categorical/layout.js +5 -2
  90. package/src/actions/guides/legends/categorical/options.js +4 -0
  91. package/src/actions/guides/legends/categorical/resolve.js +8 -3
  92. package/src/actions/guides/legends/categorical/symbols.js +3 -3
  93. package/src/actions/guides/legends/colorSize.js +2 -0
  94. package/src/actions/guides/legends/continuous/common.js +1 -1
  95. package/src/actions/guides/legends/continuous/gradient.js +1 -360
  96. package/src/actions/guides/legends/continuous/interval.js +1 -1
  97. package/src/actions/guides/legends/continuous/opacity.js +15 -1
  98. package/src/actions/guides/legends/edit.js +1 -1
  99. package/src/actions/guides/legends/focused.js +1 -1
  100. package/src/actions/guides/legends/lane.js +1 -1
  101. package/src/actions/guides/legends/sampling.js +1 -140
  102. package/src/actions/guides/legends/strokeWidth.js +3 -2
  103. package/src/actions/guides/legends/transition.js +19 -2
  104. package/src/actions/guides/polar/axes/facade.js +5 -4
  105. package/src/actions/guides/polar/axes/labels.js +5 -4
  106. package/src/actions/guides/polar/axes/titles.js +5 -4
  107. package/src/actions/guides/polar/resolve.js +2 -1
  108. package/src/actions/guides/tickValues.js +7 -7
  109. package/src/actions/marks/arc/actions.js +5 -7
  110. package/src/actions/marks/area/actions.js +9 -3
  111. package/src/actions/marks/bar/create.js +4 -2
  112. package/src/actions/marks/bar/edit.js +9 -6
  113. package/src/actions/marks/bar/materialize.js +5 -3
  114. package/src/actions/marks/dependentReferences.js +87 -0
  115. package/src/actions/marks/index.js +2 -0
  116. package/src/actions/marks/lifecycle.js +2 -1
  117. package/src/actions/marks/line/actions.js +24 -6
  118. package/src/actions/marks/line/materialize.js +7 -3
  119. package/src/actions/marks/point/create.js +5 -4
  120. package/src/actions/marks/point/edit.js +6 -3
  121. package/src/actions/marks/point/jitter.js +6 -6
  122. package/src/actions/marks/point/materialize.js +36 -39
  123. package/src/actions/marks/rect/actions.js +16 -4
  124. package/src/actions/marks/references.js +183 -35
  125. package/src/actions/marks/rule/actions.js +20 -9
  126. package/src/actions/marks/shared.js +2 -1
  127. package/src/actions/marks/text/actions.js +10 -3
  128. package/src/actions/marks/tick/actions.js +11 -2
  129. package/src/actions/primitives/editGraphics.js +28 -3
  130. package/src/actions/primitives/index.js +9 -0
  131. package/src/actions/primitives/semanticAction.js +13 -7
  132. package/src/actions/primitives/semanticValidation/layer.js +20 -0
  133. package/src/actions/primitives/semanticValidation/scale.js +8 -1
  134. package/src/actions/scales/channels.js +1 -1
  135. package/src/actions/scales/consumers/common.js +19 -1
  136. package/src/actions/scales/consumers/families.js +2 -2
  137. package/src/actions/scales/consumers/seriesLayout.js +4 -3
  138. package/src/actions/scales/edit.js +11 -0
  139. package/src/actions/scales/editPolicy.js +5 -3
  140. package/src/actions/scales/preview.js +2 -1
  141. package/src/actions/scales/quantitativeColor.js +2 -4
  142. package/src/actions/selection/actions.js +10 -1
  143. package/src/actions/theme/reconcile.js +37 -9
  144. package/src/core/compositionState.js +21 -4
  145. package/src/core/datasetReferences.js +17 -0
  146. package/src/core/immutable.js +1 -0
  147. package/src/core/resourceReferences.js +5 -3
  148. package/src/core/validation.js +4 -0
  149. package/src/core/vocabulary.js +24 -2
  150. package/src/grammar/aggregate.js +1 -0
  151. package/src/grammar/arcs.js +1 -1
  152. package/src/grammar/areaSeries.js +1 -1
  153. package/src/grammar/axisTextPlacement.js +1 -1
  154. package/src/grammar/bars/policy.js +2 -2
  155. package/src/grammar/datasetDependencies.js +97 -0
  156. package/src/grammar/datasetSchema.js +31 -1
  157. package/src/grammar/facetHeaders.js +1 -0
  158. package/src/grammar/facets/dependencies.js +6 -2
  159. package/src/grammar/facets/legendIntent.js +6 -2
  160. package/src/grammar/facets/scales.js +27 -7
  161. package/src/grammar/flatten.js +85 -0
  162. package/src/grammar/histogram.js +13 -1
  163. package/src/grammar/horizon.js +1 -494
  164. package/src/grammar/jitter.js +2 -2
  165. package/src/grammar/legendSpacing.js +45 -0
  166. package/src/grammar/lineMissing.js +90 -0
  167. package/src/grammar/lineSeries.js +1 -376
  168. package/src/grammar/lookup.js +97 -0
  169. package/src/grammar/markFilter.js +4 -2
  170. package/src/grammar/markLabels.js +8 -3
  171. package/src/grammar/numeric.js +1 -1
  172. package/src/grammar/pathCommands.js +1 -1
  173. package/src/grammar/pathSeries.js +1 -109
  174. package/src/grammar/pointPolygon.js +81 -0
  175. package/src/grammar/pointShapes.js +15 -15
  176. package/src/grammar/rects.js +1 -1
  177. package/src/grammar/regression/derive.js +1 -195
  178. package/src/grammar/scales/appearance.js +1 -1
  179. package/src/grammar/scales/color.js +1 -1
  180. package/src/grammar/scales/colorConsumers.js +1 -63
  181. package/src/grammar/scales/continuous.js +58 -1
  182. package/src/grammar/scales/definition.js +1 -1
  183. package/src/grammar/scales/ordinal.js +5 -2
  184. package/src/grammar/scales/policies.js +3 -3
  185. package/src/grammar/schemas/graphicBounds.js +40 -12
  186. package/src/grammar/schemas/graphicClip.js +23 -0
  187. package/src/grammar/schemas/graphicTree.js +16 -0
  188. package/src/grammar/statisticalReference.js +22 -0
  189. package/src/grammar/text.js +65 -1
  190. package/src/grammar/ticks.js +92 -1
  191. package/src/grammar/transformTopology.js +3 -1
  192. package/src/grammar/transforms.js +24 -0
  193. package/src/inspection.js +1 -495
  194. package/src/inspectionDescriptors.js +1 -1
  195. package/src/layout/canvas.js +1 -1
  196. package/src/layout/facetContentFlow.js +101 -0
  197. package/src/layout/facetHeaderTitles.js +1 -0
  198. package/src/layout/facetTopLane.js +29 -0
  199. package/src/layout/facets.js +1 -1
  200. package/src/layout/legendGroup.js +1 -1
  201. package/src/layout/legendItems.js +26 -10
  202. package/src/layout/legendSpacing.js +66 -0
  203. package/src/layout/plotDrawOrder.js +17 -0
  204. package/src/materialization/bars/histogram.js +8 -2
  205. package/src/materialization/bars/resolve.js +1 -1
  206. package/src/materialization/composition.js +1 -0
  207. package/src/materialization/compositionSnapshot.js +22 -2
  208. package/src/materialization/dataProvenance.js +1 -1
  209. package/src/materialization/facetGuides/layout.js +1 -1
  210. package/src/materialization/facetGuides/placement.js +23 -11
  211. package/src/materialization/facetGuides/preparation.js +43 -24
  212. package/src/materialization/facetHeaders.js +1 -1
  213. package/src/materialization/facetTopLane.js +25 -0
  214. package/src/materialization/facets.js +1 -1
  215. package/src/materialization/graphicHierarchy.js +5 -0
  216. package/src/materialization/graphicTranslation.js +2 -0
  217. package/src/materialization/guides/layout.js +1 -1
  218. package/src/materialization/guides/resources.js +1 -1
  219. package/src/materialization/legends.js +1 -1
  220. package/src/materialization/markClip.js +26 -0
  221. package/src/materialization/marks/capabilities.js +3 -2
  222. package/src/materialization/marks/grouping.js +7 -5
  223. package/src/materialization/marks/index.js +4 -2
  224. package/src/materialization/marks/policies.js +4 -0
  225. package/src/materialization/plotDrawOrder.js +44 -0
  226. package/src/materialization/rect.js +11 -3
  227. package/src/materialization/scaleGuideDependencies.js +13 -5
  228. package/src/materialization/selection/items/common.js +1 -1
  229. package/src/materialization/selection/items/path.js +1 -0
  230. package/src/materialization/selection/policies/index.js +2 -1
  231. package/src/materialization/text.js +10 -5
  232. package/src/mcp/cli.js +0 -0
  233. package/src/persistence/validation.js +108 -18
  234. package/src/persistence.js +4 -2
  235. package/src/renderers/canvas/index.js +16 -0
  236. package/src/renderers/canvas/isolated.js +11 -1
  237. package/src/renderers/canvas/native.js +7 -0
  238. package/src/renderers/canvas/scene.js +66 -0
  239. package/src/renderers/canvas/text.js +3 -2
  240. package/src/renderers/svg.js +1 -617
  241. package/src/renderers/svgSerialization.js +643 -0
  242. package/src/renderers/transitionScene.js +67 -0
  243. package/src/scene.js +89 -0
  244. package/src/selectors/axes.js +1 -1
  245. package/src/selectors/datasets.js +1 -0
  246. package/src/selectors/grids.js +9 -0
  247. package/src/selectors/markClip.js +28 -0
  248. package/src/selectors/raincloud.js +7 -0
  249. package/src/version.js +1 -1
  250. package/types/basic.d.ts +7 -0
  251. package/types/index.d.ts +8 -0
  252. package/types/program.d.ts +270 -60
  253. package/types/scene.d.ts +35 -0
@@ -1,376 +1 @@
1
- import { cloneAndFreeze } from "../core/immutable.js";
2
- import {
3
- readNominalField,
4
- readQuantitativeField,
5
- readTemporalField
6
- } from "./scales/index.js";
7
- import {
8
- aggregateRows,
9
- isAggregate,
10
- validateAggregateFieldType,
11
- validateAggregateFieldValues
12
- } from "./aggregate.js";
13
- import { stableOrderPathValues } from "./pathOrder.js";
14
- import { finiteMidpoint } from "./numeric.js";
15
- import {
16
- findHistogramBinIndex,
17
- resolveHistogramBins
18
- } from "./histogram.js";
19
- import {
20
- validatePathSeriesAppearance
21
- } from "./pathSeries.js";
22
-
23
- function requireLineEncoding(layer) {
24
- if (layer?.mark?.type !== "line") {
25
- throw new Error("Line series derivation requires a semantic line mark.");
26
- }
27
-
28
- const x = layer.encoding?.x;
29
- const y = layer.encoding?.y;
30
-
31
- if (x === undefined) {
32
- throw new Error(`Line mark "${layer.id}" requires an x encoding.`);
33
- }
34
-
35
- const binnedAggregate =
36
- x?.fieldType === "quantitative" &&
37
- x.bin !== undefined &&
38
- isAggregate(y?.aggregate);
39
- const aggregateMode =
40
- (["temporal", "nominal", "ordinal"].includes(x?.fieldType) && isAggregate(y?.aggregate)) ||
41
- binnedAggregate;
42
- const directNumericY =
43
- ["quantitative", "nominal", "ordinal"].includes(x?.fieldType) &&
44
- x.bin === undefined &&
45
- y?.fieldType === "quantitative" &&
46
- y.aggregate === undefined;
47
- const directTemporal =
48
- y?.aggregate === undefined &&
49
- ((x?.fieldType === "temporal" && y?.fieldType === "quantitative") ||
50
- (x?.fieldType === "quantitative" && y?.fieldType === "temporal"));
51
- if (!aggregateMode && !directNumericY && !directTemporal) {
52
- if (x?.fieldType === "temporal") {
53
- throw new Error(
54
- `Line mark "${layer.id}" requires a supported aggregate y encoding.`
55
- );
56
- }
57
- throw new Error(
58
- `Line mark "${layer.id}" requires categorical/temporal aggregate or compatible direct x/y encodings.`
59
- );
60
- }
61
- if (aggregateMode) {
62
- validateAggregateFieldType(y.aggregate, y.fieldType);
63
- }
64
- return {
65
- x,
66
- y,
67
- isAggregate: aggregateMode,
68
- binnedAggregate,
69
- directRows: directTemporal || directNumericY,
70
- directTemporal
71
- };
72
- }
73
-
74
- function groupKey(values) {
75
- return JSON.stringify(values);
76
- }
77
-
78
- function groupedSeries(groups, fields, dimensions) {
79
- const id = groupKey(dimensions);
80
- let series = groups.get(id);
81
- if (series === undefined) {
82
- series = {
83
- key: Object.fromEntries(
84
- fields.map((field, index) => [field, dimensions[index]])
85
- ),
86
- values: []
87
- };
88
- groups.set(id, series);
89
- }
90
- return series;
91
- }
92
-
93
- function freezeCartesianSeries(series, categoryOrder) {
94
- const observedX = series.flatMap(item => item.values.map(value => value.x));
95
- const observedSet = new Set(observedX);
96
- if (categoryOrder !== undefined) {
97
- const domain = new Set(categoryOrder);
98
- if (observedX.some(value => !domain.has(value))) {
99
- throw new Error("Line categorical x domain must contain every observed vertex.");
100
- }
101
- }
102
- return cloneAndFreeze({
103
- xValues: categoryOrder === undefined ? observedX : categoryOrder.filter(value => observedSet.has(value)),
104
- yValues: series.flatMap(item => item.values.map(value => value.y)),
105
- series
106
- });
107
- }
108
-
109
- export function resolveLineBins(rows, layer, scale = {}) {
110
- const x = layer?.encoding?.x;
111
- if (
112
- layer?.mark?.type !== "line" ||
113
- x?.fieldType !== "quantitative" ||
114
- x.bin === undefined
115
- ) {
116
- return undefined;
117
- }
118
- return resolveHistogramBins({
119
- values: readQuantitativeField(rows, x.field),
120
- bin: x.bin,
121
- domain: scale.domain ?? "auto",
122
- nice: scale.nice ?? true,
123
- zero: scale.zero ?? false
124
- });
125
- }
126
-
127
- function deriveCartesianLineSeries(rows, layer, options = {}) {
128
- const { x, y, isAggregate, binnedAggregate, directRows, directTemporal } =
129
- requireLineEncoding(layer);
130
- if (isAggregate) {
131
- validateAggregateFieldValues(rows, y.field, y.fieldType);
132
- }
133
- const categorical = ["nominal", "ordinal"].includes(x.fieldType);
134
- const xValues = categorical ? readNominalField(rows, x.field)
135
- : x.fieldType === "temporal"
136
- ? readTemporalField(rows, x.field, x.temporalUnit)
137
- : readQuantitativeField(rows, x.field);
138
- const yValues = isAggregate
139
- ? undefined
140
- : y.fieldType === "temporal"
141
- ? readTemporalField(rows, y.field, y.temporalUnit)
142
- : readQuantitativeField(rows, y.field);
143
- const categoryOrder = categorical ? options.xDomain ?? [...new Set(xValues)] : undefined;
144
- const ranks = new Map(categoryOrder?.map((value, index) => [value, index]));
145
- const compareX = categorical
146
- ? (left, right) => ranks.get(left.x) - ranks.get(right.x)
147
- : (left, right) => left.x - right.x;
148
- const seriesFields = validatePathSeriesAppearance(rows, layer);
149
- const binBoundaries = binnedAggregate
150
- ? options.xBinBoundaries ?? resolveLineBins(rows, layer).boundaries
151
- : undefined;
152
-
153
- const pathOrder = layer.encoding?.pathOrder;
154
- if (pathOrder !== undefined) {
155
- if (isAggregate) {
156
- throw new Error(
157
- `Line mark "${layer.id}" cannot combine aggregate y and path order.`
158
- );
159
- }
160
- const orderValues = readQuantitativeField(rows, pathOrder.field);
161
- const groups = new Map();
162
- for (let index = 0; index < rows.length; index += 1) {
163
- const dimensions = seriesFields.map(
164
- field => rows[index][field]
165
- );
166
- const series = groupedSeries(groups, seriesFields, dimensions);
167
- series.values.push({ x: xValues[index], y: yValues[index] });
168
- series.orderValues ??= [];
169
- series.orderValues.push(orderValues[index]);
170
- }
171
- const series = [...groups.values()].flatMap(item => {
172
- const values = stableOrderPathValues(
173
- item.values,
174
- item.orderValues,
175
- pathOrder.order
176
- );
177
- return [{ key: item.key, values }];
178
- });
179
- if (series.length === 0) {
180
- throw new Error(
181
- `Line series on mark "${layer.id}" requires at least two ordered points.`
182
- );
183
- }
184
- return freezeCartesianSeries(series, categoryOrder);
185
- }
186
-
187
- if (directRows) {
188
- const groups = new Map();
189
- for (let index = 0; index < rows.length; index += 1) {
190
- const dimensions = seriesFields.map(
191
- field => rows[index][field]
192
- );
193
- const series = groupedSeries(groups, seriesFields, dimensions);
194
- series.values.push({ x: xValues[index], y: yValues[index] });
195
- }
196
- const orderBy = directTemporal && y.fieldType === "temporal" ? "y" : "x";
197
- const series = [...groups.values()].flatMap(item => {
198
- const values = item.values.sort(
199
- orderBy === "x" ? compareX : (left, right) => left.y - right.y
200
- );
201
- return [{ key: item.key, values }];
202
- });
203
- if (series.length === 0) {
204
- throw new Error(
205
- `Line series on mark "${layer.id}" requires at least two direct points.`
206
- );
207
- }
208
- return freezeCartesianSeries(series, categoryOrder);
209
- }
210
- const aggregateGroups = new Map();
211
-
212
- for (let index = 0; index < rows.length; index += 1) {
213
- const dimensions = seriesFields.map(
214
- field => rows[index][field]
215
- );
216
- const binIndex = binnedAggregate
217
- ? findHistogramBinIndex(xValues[index], binBoundaries)
218
- : undefined;
219
- if (binIndex === -1) continue;
220
- const position = binnedAggregate
221
- ? finiteMidpoint(
222
- binBoundaries[binIndex],
223
- binBoundaries[binIndex + 1]
224
- )
225
- : xValues[index];
226
- const key = groupKey([binnedAggregate ? binIndex : position, ...dimensions]);
227
- const group = aggregateGroups.get(key) ?? {
228
- x: position,
229
- dimensions,
230
- rows: []
231
- };
232
-
233
- group.rows.push(rows[index]);
234
- aggregateGroups.set(key, group);
235
- }
236
-
237
- if (aggregateGroups.size === 0) {
238
- throw new Error(`Line mark "${layer.id}" has no values to aggregate.`);
239
- }
240
-
241
- const seriesGroups = new Map();
242
-
243
- for (const group of aggregateGroups.values()) {
244
- const value = aggregateRows(group.rows, y.field, y.aggregate);
245
- if (value === undefined) continue;
246
- const series = groupedSeries(
247
- seriesGroups,
248
- seriesFields,
249
- group.dimensions
250
- );
251
-
252
- series.values.push({
253
- x: group.x,
254
- y: value
255
- });
256
- }
257
-
258
- const series = [...seriesGroups.values()].flatMap(item => {
259
- const values = item.values.sort(compareX);
260
-
261
- if (values.length < 2) {
262
- return [];
263
- }
264
-
265
- return [{ key: item.key, values }];
266
- });
267
-
268
- if (series.length === 0) {
269
- throw new Error(
270
- `Line series on mark "${layer.id}" requires at least two aggregate points.`
271
- );
272
- }
273
-
274
- return freezeCartesianSeries(series, categoryOrder);
275
- }
276
-
277
- function requirePolarLineEncoding(layer) {
278
- if (layer?.mark?.type !== "line") {
279
- throw new Error("Polar line series derivation requires a semantic line mark.");
280
- }
281
- const theta = layer.encoding?.theta;
282
- const radius = layer.encoding?.radius;
283
- if (theta === undefined || radius === undefined) {
284
- throw new Error(
285
- `Polar line mark "${layer.id}" requires theta and radius encodings.`
286
- );
287
- }
288
- if (!["quantitative", "temporal", "ordinal", "nominal"].includes(
289
- theta.fieldType
290
- )) {
291
- throw new Error(
292
- `Polar line mark "${layer.id}" has unsupported theta field type.`
293
- );
294
- }
295
- if (radius.fieldType !== "quantitative") {
296
- throw new Error(
297
- `Polar line mark "${layer.id}" requires a quantitative radius encoding.`
298
- );
299
- }
300
- return { theta, radius };
301
- }
302
-
303
- function readThetaValues(rows, encoding) {
304
- if (["nominal", "ordinal"].includes(encoding.fieldType)) {
305
- return readNominalField(rows, encoding.field);
306
- }
307
- return encoding.fieldType === "temporal"
308
- ? readTemporalField(rows, encoding.field, encoding.temporalUnit)
309
- : readQuantitativeField(rows, encoding.field);
310
- }
311
-
312
- function thetaOrder(values, fieldType, domain) {
313
- if (!["nominal", "ordinal"].includes(fieldType)) return values;
314
- const order = domain ?? [...new Set(values)];
315
- const indices = new Map(order.map((value, index) => [value, index]));
316
- for (const value of values) {
317
- if (!indices.has(value)) {
318
- throw new Error(`Polar line theta domain does not contain "${value}".`);
319
- }
320
- }
321
- return values.map(value => indices.get(value));
322
- }
323
-
324
- export function derivePolarLineSeries(rows, layer, { thetaDomain } = {}) {
325
- if (layer.encoding?.pathOrder !== undefined) {
326
- throw new Error(
327
- `Polar line mark "${layer.id}" does not support path order.`
328
- );
329
- }
330
- const { theta, radius } = requirePolarLineEncoding(layer);
331
- const thetaValues = readThetaValues(rows, theta);
332
- const radiusValues = readQuantitativeField(rows, radius.field);
333
- const sortValues = thetaOrder(thetaValues, theta.fieldType, thetaDomain);
334
- const seriesFields = validatePathSeriesAppearance(rows, layer);
335
- const groups = new Map();
336
-
337
- for (let index = 0; index < rows.length; index += 1) {
338
- const dimensions = seriesFields.map(
339
- field => rows[index][field]
340
- );
341
- const series = groupedSeries(groups, seriesFields, dimensions);
342
- series.values.push({
343
- theta: thetaValues[index],
344
- radius: radiusValues[index],
345
- order: sortValues[index],
346
- sourceIndex: index
347
- });
348
- }
349
-
350
- const series = [...groups.values()].flatMap(item => {
351
- const values = item.values.sort(
352
- (left, right) => left.order - right.order ||
353
- left.sourceIndex - right.sourceIndex
354
- );
355
- return values.length < 2 ? [] : [{ key: item.key, values }];
356
- });
357
- if (series.length === 0) {
358
- throw new Error(
359
- `Polar line series on mark "${layer.id}" requires at least two points.`
360
- );
361
- }
362
- return cloneAndFreeze({
363
- thetaFieldType: theta.fieldType,
364
- thetaValues: series.flatMap(item => item.values.map(value => value.theta)),
365
- radiusValues: series.flatMap(item => item.values.map(value => value.radius)),
366
- series
367
- });
368
- }
369
-
370
- export function deriveLineSeries(rows, layer, options) {
371
- const polar = layer?.encoding?.theta !== undefined ||
372
- layer?.encoding?.radius !== undefined;
373
- return polar
374
- ? derivePolarLineSeries(rows, layer, options)
375
- : deriveCartesianLineSeries(rows, layer, options);
376
- }
1
+ import{cloneAndFreeze as $}from"../core/immutable.js";import{readLinePosition as R,validateLineMissingScope as N,orderLineGapValues as X,splitLineGapValues as j}from"./lineMissing.js";import{readNominalField as U,readQuantitativeField as k,readTemporalField as P}from"./scales/index.js";import{aggregateRows as D,isAggregate as C,validateAggregateFieldType as Q,validateAggregateFieldValues as Y}from"./aggregate.js";import{stableOrderPathValues as _}from"./pathOrder.js";import{finiteMidpoint as J}from"./numeric.js";import{findHistogramBinIndex as W,resolveHistogramBins as Z}from"./histogram.js";import{validatePathSeriesAppearance as I,pathSeriesKey as H}from"./pathSeries.js";function ee(e){if(e?.mark?.type!=="line")throw new Error("Line series derivation requires a semantic line mark.");const n=e.encoding?.x,i=e.encoding?.y;if(n===void 0)throw new Error(`Line mark "${e.id}" requires an x encoding.`);const a=n?.fieldType==="quantitative"&&n.bin!==void 0&&C(i?.aggregate),r=["temporal","nominal","ordinal"].includes(n?.fieldType)&&C(i?.aggregate)||a,s=["quantitative","nominal","ordinal"].includes(n?.fieldType)&&n.bin===void 0&&i?.fieldType==="quantitative"&&i.aggregate===void 0,c=i?.aggregate===void 0&&(n?.fieldType==="temporal"&&i?.fieldType==="quantitative"||n?.fieldType==="quantitative"&&i?.fieldType==="temporal");if(!r&&!s&&!c)throw n?.fieldType==="temporal"?new Error(`Line mark "${e.id}" requires a supported aggregate y encoding.`):new Error(`Line mark "${e.id}" requires categorical/temporal aggregate or compatible direct x/y encodings.`);return r&&Q(i.aggregate,i.fieldType),{x:n,y:i,isAggregate:r,binnedAggregate:a,directRows:c||s,directTemporal:c}}function q(e,n,i){const a=H(i);let r=e.get(a);return r===void 0&&(r={key:Object.fromEntries(n.map((s,c)=>[s,i[c]])),values:[]},e.set(a,r)),r}function A(e,n){const i=e.flatMap(r=>r.values.map(s=>s.x)),a=new Set(i);if(n!==void 0){const r=new Set(n);if(i.some(s=>!r.has(s)))throw new Error("Line categorical x domain must contain every observed vertex.")}return $({xValues:n===void 0?i:n.filter(r=>a.has(r)),yValues:e.flatMap(r=>r.values.map(s=>s.y)),series:e})}function ne(e,n,i={}){const a=n?.encoding?.x;if(!(n?.mark?.type!=="line"||a?.fieldType!=="quantitative"||a.bin===void 0))return Z({values:k(e,a.field),bin:a.bin,domain:i.domain??"auto",nice:i.nice??!0,zero:i.zero??!1})}function ie(e,n,i={}){const{x:a,y:r,isAggregate:s,binnedAggregate:c,directRows:M,directTemporal:V}=ee(n);if(n.mark.missing==="break"){const t=R(e,a),d=R(e,r),f=I(e,n,i.scales),o=n.encoding?.pathOrder,g=o===void 0?V&&r.fieldType==="temporal"?d:t:k(e,o.field),m=new Map;for(let l=0;l<e.length;l+=1){const L=q(m,f,f.map(O=>e[l][O]));L.values.push({x:t[l],y:d[l],sourceIndex:l}),(L.orderValues??=[]).push(g[l])}const v=[...m.values()].flatMap(l=>{const L=X(l.values,l.orderValues,o?.order);return j(L).map(z=>({key:l.key,values:z.map(({sourceIndex:G,...K})=>K),sourceIndices:z.map(G=>G.sourceIndex)}))});return $({xValues:t.filter(l=>l!==null),yValues:d.filter(l=>l!==null),series:v})}s&&Y(e,r.field,r.fieldType);const y=["nominal","ordinal"].includes(a.fieldType),h=y?U(e,a.field):a.fieldType==="temporal"?P(e,a.field,a.temporalUnit):k(e,a.field),u=s?void 0:r.fieldType==="temporal"?P(e,r.field,r.temporalUnit):k(e,r.field),p=y?i.xDomain??[...new Set(h)]:void 0,w=new Map(p?.map((t,d)=>[t,d])),x=y?(t,d)=>w.get(t.x)-w.get(d.x):(t,d)=>t.x-d.x,T=I(e,n,i.scales),b=c?i.xBinBoundaries??ne(e,n).boundaries:void 0,S=n.encoding?.pathOrder;if(S!==void 0){if(s)throw new Error(`Line mark "${n.id}" cannot combine aggregate y and path order.`);const t=k(e,S.field),d=new Map;for(let o=0;o<e.length;o+=1){const g=T.map(v=>e[o][v]),m=q(d,T,g);m.values.push({x:h[o],y:u[o]}),m.orderValues??=[],m.orderValues.push(t[o])}const f=[...d.values()].flatMap(o=>{const g=_(o.values,o.orderValues,S.order);return[{key:o.key,values:g}]});if(f.length===0)throw new Error(`Line series on mark "${n.id}" requires at least two ordered points.`);return A(f,p)}if(M){const t=new Map;for(let o=0;o<e.length;o+=1){const g=T.map(v=>e[o][v]);q(t,T,g).values.push({x:h[o],y:u[o]})}const d=V&&r.fieldType==="temporal"?"y":"x",f=[...t.values()].flatMap(o=>{const g=o.values.sort(d==="x"?x:(m,v)=>m.y-v.y);return[{key:o.key,values:g}]});if(f.length===0)throw new Error(`Line series on mark "${n.id}" requires at least two direct points.`);return A(f,p)}const E=new Map;for(let t=0;t<e.length;t+=1){const d=T.map(v=>e[t][v]),f=c?W(h[t],b):void 0;if(f===-1)continue;const o=c?J(b[f],b[f+1]):h[t],g=H([c?f:o,...d]),m=E.get(g)??{x:o,dimensions:d,rows:[]};m.rows.push(e[t]),E.set(g,m)}if(E.size===0)throw new Error(`Line mark "${n.id}" has no values to aggregate.`);const F=new Map;for(const t of E.values()){const d=D(t.rows,r.field,r.aggregate);if(d===void 0)continue;q(F,T,t.dimensions).values.push({x:t.x,y:d})}const B=[...F.values()].flatMap(t=>{const d=t.values.sort(x);return d.length<(i.allowSingleton===!0?1:2)?[]:[{key:t.key,values:d}]});if(B.length===0)throw new Error(`Line series on mark "${n.id}" requires at least two aggregate points.`);return A(B,p)}function re(e){if(e?.mark?.type!=="line")throw new Error("Polar line series derivation requires a semantic line mark.");const n=e.encoding?.theta,i=e.encoding?.radius;if(n===void 0||i===void 0)throw new Error(`Polar line mark "${e.id}" requires theta and radius encodings.`);if(!["quantitative","temporal","ordinal","nominal"].includes(n.fieldType))throw new Error(`Polar line mark "${e.id}" has unsupported theta field type.`);if(i.fieldType!=="quantitative")throw new Error(`Polar line mark "${e.id}" requires a quantitative radius encoding.`);return{theta:n,radius:i}}function te(e,n){return["nominal","ordinal"].includes(n.fieldType)?U(e,n.field):n.fieldType==="temporal"?P(e,n.field,n.temporalUnit):k(e,n.field)}function ae(e,n,i){if(!["nominal","ordinal"].includes(n))return e;const a=i??[...new Set(e)],r=new Map(a.map((s,c)=>[s,c]));for(const s of e)if(!r.has(s))throw new Error(`Polar line theta domain does not contain "${s}".`);return e.map(s=>r.get(s))}function oe(e,n,{thetaDomain:i}={}){if(n.encoding?.pathOrder!==void 0)throw new Error(`Polar line mark "${n.id}" does not support path order.`);const{theta:a,radius:r}=re(n),s=te(e,a),c=k(e,r.field),M=ae(s,a.fieldType,i),V=I(e,n),y=new Map;for(let u=0;u<e.length;u+=1){const p=V.map(x=>e[u][x]);q(y,V,p).values.push({theta:s[u],radius:c[u],order:M[u],sourceIndex:u})}const h=[...y.values()].flatMap(u=>{const p=u.values.sort((w,x)=>w.order-x.order||w.sourceIndex-x.sourceIndex);return p.length<2?[]:[{key:u.key,values:p}]});if(h.length===0)throw new Error(`Polar line series on mark "${n.id}" requires at least two points.`);return $({thetaFieldType:a.fieldType,thetaValues:h.flatMap(u=>u.values.map(p=>p.theta)),radiusValues:h.flatMap(u=>u.values.map(p=>p.radius)),series:h})}function me(e,n,i){return N(n),n?.encoding?.theta!==void 0||n?.encoding?.radius!==void 0?oe(e,n,i):ie(e,n,i)}export{me as deriveLineSeries,oe as derivePolarLineSeries,ne as resolveLineBins};
@@ -0,0 +1,97 @@
1
+ import {cloneAndFreeze, isPlainObject} from "../core/immutable.js";
2
+ import {validateUserId} from "../core/identifiers.js";
3
+ import {
4
+ rejectUnknownProperties, requireStringValue, validateDataRows, validateWorkLimit
5
+ } from "../core/validation.js";
6
+
7
+ export function normalizeLookupTransform({field, lookup} = {}) {
8
+ if (!isPlainObject(lookup)) throw new TypeError("Lookup requires a lookup object.");
9
+ const definition = {
10
+ type: "lookup", field,
11
+ lookup: {
12
+ ...lookup,
13
+ fields: Array.isArray(lookup.fields) ? [...lookup.fields] : lookup.fields,
14
+ as: Array.isArray(lookup.as === undefined ? lookup.fields : lookup.as) ? [...(lookup.as === undefined ? lookup.fields : lookup.as)] : lookup.as
15
+ }
16
+ };
17
+ validateLookupTransform(definition);
18
+ return cloneAndFreeze(definition);
19
+ }
20
+
21
+ export function validateLookupTransform(transform) {
22
+ if (!isPlainObject(transform)) throw new TypeError("Lookup transform must be a plain object.");
23
+ rejectUnknownProperties(transform, ["type", "field", "lookup"], "lookup transform");
24
+ if (transform.type !== "lookup") throw new Error(`Unsupported lookup transform "${transform.type}".`);
25
+ requireStringValue(transform.field, "Lookup field");
26
+ const lookup = transform.lookup;
27
+ if (!isPlainObject(lookup)) throw new TypeError("Lookup requires a lookup object.");
28
+ rejectUnknownProperties(lookup, ["data", "key", "fields", "as"], "lookup definition");
29
+ validateUserId(lookup.data, "Lookup dataset id");
30
+ requireStringValue(lookup.key, "Lookup key");
31
+ if (!Array.isArray(lookup.fields) || lookup.fields.length === 0) {
32
+ throw new TypeError("Lookup fields must be a non-empty array.");
33
+ }
34
+ if (!Array.isArray(lookup.as) || lookup.as.length !== lookup.fields.length) {
35
+ throw new TypeError("Lookup as requires one alias per field.");
36
+ }
37
+ for (const key of ["fields", "as"]) {
38
+ for (let i = 0; i < lookup[key].length; i += 1) {
39
+ if (!Object.hasOwn(lookup[key], i)) throw new TypeError(`Lookup ${key} must be dense.`);
40
+ requireStringValue(lookup[key][i], `Lookup ${key}[${i}]`);
41
+ }
42
+ if (new Set(lookup[key]).size !== lookup[key].length) {
43
+ throw new Error(`Lookup ${key} must be unique.`);
44
+ }
45
+ }
46
+ return transform;
47
+ }
48
+
49
+ function keyValue(row, field) {
50
+ const key = Object.hasOwn(row, field) ? row[field] : undefined;
51
+ if (key === undefined || key === null) return undefined;
52
+ if (typeof key !== "string" && typeof key !== "boolean" && !Number.isFinite(key)) {
53
+ throw new TypeError(`Lookup key field "${field}" requires a string, finite number or boolean.`);
54
+ }
55
+ return key;
56
+ }
57
+
58
+ export function deriveLookupRows(rows, lookupRows, transform) {
59
+ validateLookupTransform(transform);
60
+ validateDataRows(rows, "Lookup primary data");
61
+ validateDataRows(lookupRows, "Lookup reference data");
62
+ const lookup = transform.lookup;
63
+ validateWorkLimit(lookupRows.length + rows.length * (lookup.fields.length + 1), "Lookup work");
64
+ const existing = new Set(rows.flatMap(row => Object.keys(row)));
65
+ for (const name of lookup.as) {
66
+ if (existing.has(name)) throw new Error(`Lookup output field "${name}" already exists in primary data.`);
67
+ }
68
+ const index = new Map();
69
+ for (const row of lookupRows) {
70
+ const key = keyValue(row, lookup.key);
71
+ if (key === undefined) continue;
72
+ if (index.has(key)) throw new Error(`Lookup key field "${lookup.key}" contains duplicate keys.`);
73
+ index.set(key, row);
74
+ }
75
+ return cloneAndFreeze(rows.map(row => {
76
+ const key = keyValue(row, transform.field);
77
+ const match = key === undefined ? undefined : index.get(key);
78
+ const output = {...row};
79
+ for (let i = 0; i < lookup.fields.length; i += 1) {
80
+ const field = lookup.fields[i];
81
+ Object.defineProperty(output, lookup.as[i], {
82
+ value: match !== undefined && Object.hasOwn(match, field) ? match[field] ?? null : null,
83
+ enumerable: true, configurable: true, writable: true
84
+ });
85
+ }
86
+ return output;
87
+ }));
88
+ }
89
+
90
+ export function sameLookupValue(left, right) {
91
+ if (Object.is(left, right)) return true;
92
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
93
+ if (Array.isArray(left) !== Array.isArray(right)) return false;
94
+ const keys = Object.keys(left), other = Object.keys(right);
95
+ return keys.length === other.length && keys.every((key, index) =>
96
+ key === other[index] && sameLookupValue(left[key], right[key]));
97
+ }
@@ -1,5 +1,6 @@
1
1
  import { cloneAndFreeze, isPlainObject } from "../core/immutable.js";
2
2
  import { normalizeMarkSelector } from "./markSelection.js";
3
+ import { retainLineGapBoundaries } from "./lineMissing.js";
3
4
 
4
5
  const TRANSFORM_KEYS = Object.freeze([
5
6
  "type", "target", "selector", "selectors"
@@ -50,7 +51,7 @@ export function normalizeMarkFilterTransform(target, selectors) {
50
51
  return cloneAndFreeze(transform);
51
52
  }
52
53
 
53
- export function deriveMarkFilteredRows(sourceRows, items, selectedKeys) {
54
+ export function deriveMarkFilteredRows(sourceRows, items, selectedKeys, layer) {
54
55
  if (!Array.isArray(sourceRows)) {
55
56
  throw new TypeError("Mark filter source values must be an array.");
56
57
  }
@@ -60,5 +61,6 @@ export function deriveMarkFilteredRows(sourceRows, items, selectedKeys) {
60
61
  .filter(item => selected.has(item.key))
61
62
  .flatMap(item => item.members)
62
63
  );
63
- return cloneAndFreeze(sourceRows.filter(row => members.has(row)));
64
+ const retained = retainLineGapBoundaries(sourceRows, members, layer);
65
+ return cloneAndFreeze(sourceRows.filter(row => retained.has(row)));
64
66
  }
@@ -39,9 +39,6 @@ export function normalizeMarkLabelContent(source, { content, normalizeBy }) {
39
39
  source.encoding[`${resolveBarChannels(source).measure}2`] !== undefined) {
40
40
  throw new Error("Ranged Bar text requires an explicit field or value.");
41
41
  }
42
- if (content === "category" && grain === BAR_GRAINS.histogram) {
43
- throw new Error("Histogram categories are intervals; use an explicit text field or value.");
44
- }
45
42
  } else if (content === "category" && source.encoding?.theta?.fieldType === "quantitative") {
46
43
  throw new Error("Category text requires categorical Arc theta.");
47
44
  }
@@ -51,6 +48,8 @@ export function normalizeMarkLabelContent(source, { content, normalizeBy }) {
51
48
  function itemValue(source, item) {
52
49
  if (source.mark.type === "bar") {
53
50
  if (resolveBarGrain(source) === BAR_GRAINS.histogram) {
51
+ const measure = source.encoding.y;
52
+ if (measure.aggregate !== "count") return aggregateRows(item.members, measure.field, measure.aggregate);
54
53
  const weight = source.encoding.x.weight;
55
54
  if (weight === undefined || item.members.length === 0) return item.members.length;
56
55
  const weighted = readStatisticalWeights(item.members, weight, "Histogram label");
@@ -77,6 +76,12 @@ function itemValue(source, item) {
77
76
  }
78
77
 
79
78
  function categoryValue(source, item) {
79
+ if (source.mark.type === "bar" && resolveBarGrain(source) === BAR_GRAINS.histogram) {
80
+ // Describe the semantic bin, independently of stacking/reversal and its
81
+ // concrete pixel width. Suppress binary floating-point boundary artifacts.
82
+ const boundary = value => String(Number(value.toPrecision(12)));
83
+ return `${boundary(item.channels.x)}–${boundary(item.channels.x2)}`;
84
+ }
80
85
  return item.channels[source.mark.type === "arc" ? "theta" : resolveBarChannels(source).category];
81
86
  }
82
87
 
@@ -1 +1 @@
1
- const q=Object.freeze([1,2,3,5]);function h(t,e){if(!Array.isArray(t)||!t.every(Number.isFinite))throw new TypeError(`${e} must contain finite numbers.`)}function p(t){return new RangeError(`${t} is outside the finite numeric range.`)}function x(t,e=1){let n=0,r=0;for(const i of t){const o=i/e,c=n+o;r+=Math.abs(n)>=Math.abs(o)?n-c+o:o-c+n,n=c}return n+r}function f(t){let e=0;for(const n of t)e=Math.max(e,Math.abs(n));return e}function y(t,e="Numeric values"){h(t,e);const n=f(t)||1;return{scale:n,total:x(t,n)}}function b(t,e="Numeric result"){if(!Number.isFinite(t))throw p(e);return t===0?0:t}function N(t,e,n="Numeric result"){if(!Number.isFinite(t)||!Number.isFinite(e)||e<0)throw p(n);return b(t*e,n)}function d(t,e,n=1){h(t,e);const r=t.reduce((o,c)=>o+c,0);if(Number.isFinite(r))return r===0?0:r/n;const i=f(t)||1;return N(x(t,i)/n,i,e)}function $(t,e="Numeric sum"){return d(t,e)}function z(t,e="Numeric sum"){const n=y(t,e);let r=0,i=0,o=0;return t.map(c=>{r+=c;const u=c/n.scale,s=i+u;return o+=Math.abs(i)>=Math.abs(u)?i-s+u:u-s+i,i=s,Number.isFinite(r)?r===0?0:r:N(i+o,n.scale,e)})}function R(t,e="Numeric mean"){if(h(t,e),t.length===0)throw new RangeError(`${e} requires at least one value.`);return d(t,e,t.length)}function P(t,e="Numeric square sum"){h(t,e);const n=t.reduce((c,u)=>c+u**2,0);if(Number.isFinite(n))return n;const r=f(t);if(r===0)return 0;const i=t.reduce((c,u)=>c+(u/r)**2,0),o=N(Math.sqrt(i),r,e);return b(o**2,e)}function A(t,{sample:e=!1,divisor:n=1,label:r="Numeric deviation"}={}){if(!Number.isFinite(n)||n<=0)throw new RangeError(`${r} divisor must be positive and finite.`);const i=t.length-(e?1:0);if(i<=0)throw new RangeError(`${r} requires more values.`);const o=R(t,`${r} mean`),c=t.map(a=>a-o),u=c.reduce((a,M)=>a+M**2,0);if(Number.isFinite(u))return{mean:o,deviation:Math.sqrt(u/i)/n,squared:u};const s=c.every(Number.isFinite),m=s?f(c):Math.max(Math.abs(o),f(t));if(m===0)return{mean:o,deviation:0};const g=t.reduce((a,M,S)=>{const w=s?c[S]/m:M/m-o/m;return a+w**2},0),l=N(Math.sqrt(g/i)/n,m,r);return{mean:o,deviation:l}}function D(t,e){const n=(t.length-1)*e,r=Math.floor(n),i=Math.ceil(n);return F(t[r],t[i],n-r)}function I(t){let e=1/0,n=-1/0;for(const r of t)e=Math.min(e,r),n=Math.max(n,r);return[e,n]}function C(t,e){const n=Math.max(Math.abs(t),Math.abs(e));if(n===0)return{scale:1,start:0,end:0,span:0};const r=10**Math.floor(Math.log10(n)),i=Number.isFinite(r)&&r>0?r:n;return t/=i,e/=i,{scale:i,start:t,end:e,span:e-t}}function O(t,e=5,n=q){if(!Number.isFinite(t)||t<=0)throw new RangeError("Nice numeric span must be positive and finite.");const r=t/Math.max(1,e),i=10**Math.floor(Math.log10(r)),o=r/i;return(n.find(u=>u>=o)??10)*i}function T(t,e,n){let r=t/e;const i=Math.round(r);return Math.abs(r-i)<=1e-10&&(r=i),(n==="floor"?Math.floor(r):Math.ceil(r))*e}function E(t,e){if(!Number.isFinite(t)||t===0)return t===0?0:t;const n=Number(t.toPrecision(15));return Number.isFinite(e)&&e>0&&Math.abs(n-t)<=e*1e-10?n:t}function j(t,e,n){if(t===e)return 0;if(t===n)return 1;const r=n-e,i=t-e;if(Number.isFinite(r)&&r!==0&&Number.isFinite(i))return i/r;const o=Math.max(Math.abs(t),Math.abs(e),Math.abs(n));return(t/o-e/o)/(n/o-e/o)}function F(t,e,n){let r=n===0?t:n===1?e:void 0;if(r===void 0){const i=e-t;if(Number.isFinite(i))r=t+n*i;else{const o=Math.max(Math.abs(t),Math.abs(e));r=o*(t/o*(1-n)+e/o*n)}}return b(r,"Numeric interpolation")}function L(t,e){const n=t+e,r=Number.isFinite(n)?n/2:t/2+e/2;return b(r,"Numeric midpoint")}function V(t,e,n,r="Numeric range"){const i=Math.abs((e-t)/(n-1)),o=[],c=Math.sign(e-t);for(let u=0;u<n;u+=1){const s=E(F(t,e,u/(n-1)),i);if(!Number.isFinite(s)||c!==0&&u>0&&c*(s-o[u-1])<=0)throw new RangeError(`${r} cannot represent ${n} distinct samples.`);o.push(s)}return o}function _(t,e){let n=t.map(e??(i=>String(+i.toPrecision(3))));const r=()=>n.every((i,o)=>o===0||t[o]===t[o-1]||i!==n[o-1]);for(let i=4;!r()&&i<=17;i+=1)n=t.map(o=>String(+o.toPrecision(i)));return r()?n:t.map(String)}function k(t,e=12){if(t===0)return 0;const n=10**-e;return Math.abs(t)<n?Number(t.toPrecision(e)):Number(t.toFixed(e))}export{q as NICE_FACTORS,T as alignNumericStep,E as cleanNumericValue,L as finiteMidpoint,_ as formatDistinctNumericSamples,F as interpolateNumber,j as inverseLerp,f as maximumMagnitude,O as niceNumericStep,C as normalizeNumericRange,y as normalizedFiniteSum,I as numericExtent,D as quantileSorted,b as requireFiniteResult,N as restoreFiniteScale,V as sampleNumericRange,k as stableDecimal,A as stableFiniteDeviation,R as stableFiniteMean,z as stableFinitePrefixSums,P as stableFiniteSquareSum,$ as stableFiniteSum};
1
+ const q=Object.freeze([1,2,3,5]);function h(t,e){if(!Array.isArray(t)||!t.every(Number.isFinite))throw new TypeError(`${e} must contain finite numbers.`)}function p(t){return new RangeError(`${t} is outside the finite numeric range.`)}function x(t,e=1){let n=0,r=0;for(const i of t){const o=i/e,c=n+o;r+=Math.abs(n)>=Math.abs(o)?n-c+o:o-c+n,n=c}return n+r}function f(t){let e=0;for(const n of t)e=Math.max(e,Math.abs(n));return e}function y(t,e="Numeric values"){h(t,e);const n=f(t)||1;return{scale:n,total:x(t,n)}}function M(t,e="Numeric result"){if(!Number.isFinite(t))throw p(e);return t===0?0:t}function N(t,e,n="Numeric result"){if(!Number.isFinite(t)||!Number.isFinite(e)||e<0)throw p(n);return M(t*e,n)}function F(t,e,n=1){h(t,e);const r=t.reduce((o,c)=>o+c,0);if(Number.isFinite(r))return r===0?0:r/n;const i=f(t)||1;return N(x(t,i)/n,i,e)}function $(t,e="Numeric sum"){return F(t,e)}function z(t,e="Numeric sum"){const n=y(t,e);let r=0,i=0,o=0;return t.map(c=>{r+=c;const s=c/n.scale,u=i+s;return o+=Math.abs(i)>=Math.abs(s)?i-u+s:s-u+i,i=u,Number.isFinite(r)?r===0?0:r:N(i+o,n.scale,e)})}function R(t,e="Numeric mean"){if(h(t,e),t.length===0)throw new RangeError(`${e} requires at least one value.`);return F(t,e,t.length)}function P(t,e="Numeric square sum"){h(t,e);const n=t.reduce((c,s)=>c+s**2,0);if(Number.isFinite(n))return n;const r=f(t);if(r===0)return 0;const i=t.reduce((c,s)=>c+(s/r)**2,0),o=N(Math.sqrt(i),r,e);return M(o**2,e)}function A(t,{sample:e=!1,divisor:n=1,label:r="Numeric deviation"}={}){if(!Number.isFinite(n)||n<=0)throw new RangeError(`${r} divisor must be positive and finite.`);const i=t.length-(e?1:0);if(i<=0)throw new RangeError(`${r} requires more values.`);const o=R(t,`${r} mean`),c=t.map(m=>m-o),s=c.reduce((m,b)=>m+b**2,0);if(Number.isFinite(s))return{mean:o,deviation:Math.sqrt(s/i)/n,squared:s};const u=c.every(Number.isFinite),a=u?f(c):Math.max(Math.abs(o),f(t));if(a===0)return{mean:o,deviation:0};const g=t.reduce((m,b,w)=>{const S=u?c[w]/a:b/a-o/a;return m+S**2},0),l=N(Math.sqrt(g/i)/n,a,r);return{mean:o,deviation:l}}function D(t,e){const n=(t.length-1)*e,r=Math.floor(n),i=Math.ceil(n);return d(t[r],t[i],n-r)}function I(t){let e=1/0,n=-1/0;for(const r of t)e=Math.min(e,r),n=Math.max(n,r);return[e,n]}function C(t,e){const n=Math.max(Math.abs(t),Math.abs(e));if(n===0)return{scale:1,start:0,end:0,span:0};const r=10**Math.floor(Math.log10(n)),i=Number.isFinite(r)&&r>0?r:n;return t/=i,e/=i,{scale:i,start:t,end:e,span:e-t}}function O(t,e){const n=t/e,r=Math.floor(Math.log10(n)),i=10**r;if(!Number.isFinite(i)||i===0)return null;const o=n/i,c=o>=Math.sqrt(50)?10:o>=Math.sqrt(10)?5:o>=Math.sqrt(2)?2:1,s=r<0?-(10**-r)/c:c*i;return Number.isFinite(s)&&s!==0?s:null}function T(t,e=5,n=q){if(!Number.isFinite(t)||t<=0)throw new RangeError("Nice numeric span must be positive and finite.");const r=t/Math.max(1,e),i=10**Math.floor(Math.log10(r)),o=r/i;return(n.find(s=>s>=o)??10)*i}function j(t,e,n){let r=t/e;const i=Math.round(r);return Math.abs(r-i)<=1e-10&&(r=i),(n==="floor"?Math.floor(r):Math.ceil(r))*e}function E(t,e){if(!Number.isFinite(t)||t===0)return t===0?0:t;const n=Number(t.toPrecision(15));return Number.isFinite(e)&&e>0&&Math.abs(n-t)<=e*1e-10?n:t}function L(t,e,n){if(t===e)return 0;if(t===n)return 1;const r=n-e,i=t-e;if(Number.isFinite(r)&&r!==0&&Number.isFinite(i))return i/r;const o=Math.max(Math.abs(t),Math.abs(e),Math.abs(n));return(t/o-e/o)/(n/o-e/o)}function d(t,e,n){let r=n===0?t:n===1?e:void 0;if(r===void 0){const i=e-t;if(Number.isFinite(i))r=t+n*i;else{const o=Math.max(Math.abs(t),Math.abs(e));r=o*(t/o*(1-n)+e/o*n)}}return M(r,"Numeric interpolation")}function V(t,e){const n=t+e,r=Number.isFinite(n)?n/2:t/2+e/2;return M(r,"Numeric midpoint")}function _(t,e,n,r="Numeric range"){const i=Math.abs((e-t)/(n-1)),o=[],c=Math.sign(e-t);for(let s=0;s<n;s+=1){const u=E(d(t,e,s/(n-1)),i);if(!Number.isFinite(u)||c!==0&&s>0&&c*(u-o[s-1])<=0)throw new RangeError(`${r} cannot represent ${n} distinct samples.`);o.push(u)}return o}function k(t,e){let n=t.map(e??(i=>String(+i.toPrecision(3))));const r=()=>n.every((i,o)=>o===0||t[o]===t[o-1]||i!==n[o-1]);for(let i=4;!r()&&i<=17;i+=1)n=t.map(o=>String(+o.toPrecision(i)));return r()?n:t.map(String)}function B(t,e=12){if(t===0)return 0;const n=10**-e;return Math.abs(t)<n?Number(t.toPrecision(e)):Number(t.toFixed(e))}export{q as NICE_FACTORS,j as alignNumericStep,E as cleanNumericValue,V as finiteMidpoint,k as formatDistinctNumericSamples,d as interpolateNumber,L as inverseLerp,f as maximumMagnitude,O as nearestNumericIncrement,T as niceNumericStep,C as normalizeNumericRange,y as normalizedFiniteSum,I as numericExtent,D as quantileSorted,M as requireFiniteResult,N as restoreFiniteScale,_ as sampleNumericRange,B as stableDecimal,A as stableFiniteDeviation,R as stableFiniteMean,z as stableFinitePrefixSums,P as stableFiniteSquareSum,$ as stableFiniteSum};
@@ -1,7 +1,7 @@
1
1
  import { freezeOwned, isPlainObject } from "../core/immutable.js";
2
2
  import { validateGeneratedItemLimit } from "../core/validation.js";
3
3
 
4
- export const MAX_PATH_COMMANDS = 20_000;
4
+ export const MAX_PATH_COMMANDS = 30_000;
5
5
 
6
6
  export function validatePathCommandCount(count, label) {
7
7
  return validateGeneratedItemLimit(count, label, MAX_PATH_COMMANDS);