ggaction 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/README.md +5 -4
- package/knowledge/action-card.schema.json +1 -0
- package/knowledge/action-cards.json +1 -10941
- package/knowledge/action-cards.schema.json +1 -0
- package/knowledge/extension-authoring.md +248 -0
- package/knowledge/intent-taxonomy.json +1 -183
- package/knowledge/intent-taxonomy.schema.json +1 -0
- package/knowledge/mcp-resources.json +1 -95
- package/knowledge/mcp-resources.schema.json +1 -0
- package/knowledge/task-packet.schema.json +1 -159
- package/knowledge/task-resolver.js +1 -1230
- package/package.json +19 -2
- package/src/actions/boxPlots/edit.js +45 -298
- package/src/actions/boxPlots/options.js +46 -77
- package/src/actions/boxPlots/resolve.js +1 -1
- package/src/actions/charts/bar.js +3 -0
- package/src/actions/charts/heatmap.js +73 -83
- package/src/actions/charts/index.js +1 -1
- package/src/actions/charts/parallel.js +35 -1
- package/src/actions/charts/shared.js +20 -0
- package/src/actions/composition/actions.js +46 -49
- package/src/actions/data/bin2d.js +105 -2
- package/src/actions/data/box.js +17 -17
- package/src/actions/data/density.js +42 -78
- package/src/actions/data/gradientProfile.js +22 -52
- package/src/actions/data/horizon.js +16 -41
- package/src/actions/data/index.js +10 -10
- package/src/actions/data/interval.js +16 -38
- package/src/actions/data/intervalEdit.js +55 -0
- package/src/actions/data/regression.js +7 -17
- package/src/actions/data/shared.js +47 -0
- package/src/actions/data/timeUnit.js +14 -38
- package/src/actions/data/window.js +14 -38
- package/src/actions/distributions/revision.js +154 -34
- package/src/actions/encodings/angle.js +9 -10
- package/src/actions/encodings/appearance.js +17 -51
- package/src/actions/encodings/density/resolve.js +3 -1
- package/src/actions/encodings/density.js +1 -15
- package/src/actions/encodings/horizon/resolve.js +13 -3
- package/src/actions/encodings/horizon.js +8 -7
- package/src/actions/encodings/index.js +9 -9
- package/src/actions/encodings/offset.js +8 -24
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/policies/index.js +4 -4
- package/src/actions/encodings/position/policies/line.js +47 -17
- package/src/actions/encodings/position/resolve.js +24 -5
- package/src/actions/encodings/ranged.js +18 -23
- package/src/actions/encodings/remove.js +15 -25
- package/src/actions/encodings/ruleAppearance.js +9 -24
- package/src/actions/encodings/shared.js +37 -0
- package/src/actions/encodings/strokeDash.js +8 -23
- package/src/actions/encodings/text.js +9 -10
- package/src/actions/errorBands/create.js +105 -39
- package/src/actions/errorBands/edit.js +89 -93
- package/src/actions/errorBands/index.js +1 -2
- package/src/actions/errorBars/create.js +78 -27
- package/src/actions/errorBars/edit.js +81 -48
- package/src/actions/errorBars/index.js +1 -2
- package/src/actions/facets/actions.js +45 -69
- package/src/actions/facets/derive.js +38 -53
- package/src/actions/facets/replay.js +0 -1
- package/src/actions/gradientPlots/components.js +59 -48
- package/src/actions/gradientPlots/edit.js +35 -216
- package/src/actions/gradientPlots/index.js +11 -10
- package/src/actions/gradientPlots/materialize.js +21 -35
- package/src/actions/gradientPlots/options.js +5 -7
- package/src/actions/gradientPlots/paint.js +9 -6
- package/src/actions/gradientPlots/resolve.js +1 -1
- package/src/actions/guides/axes/index.js +3 -3
- package/src/actions/guides/axes/labels.js +68 -26
- package/src/actions/guides/axes/lines.js +22 -2
- package/src/actions/guides/axes/policy.js +28 -8
- package/src/actions/guides/axes/ticks.js +27 -9
- package/src/actions/guides/axes/titles.js +113 -65
- package/src/actions/guides/grids/resolve.js +14 -0
- package/src/actions/guides/guides.js +2 -4
- package/src/actions/guides/index.js +2 -2
- package/src/actions/guides/legends/categorical/actions.js +44 -68
- package/src/actions/guides/legends/categorical/components.js +6 -1
- package/src/actions/guides/legends/categorical/index.js +2 -2
- package/src/actions/guides/legends/categorical/layout.js +148 -175
- package/src/actions/guides/legends/categorical/recipes.js +5 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -1
- package/src/actions/guides/legends/continuous/common.js +117 -66
- package/src/actions/guides/legends/continuous/gradient.js +38 -37
- package/src/actions/guides/legends/continuous/interval.js +97 -76
- package/src/actions/guides/legends/continuous/opacity.js +28 -38
- package/src/actions/guides/legends/edit.js +14 -1
- package/src/actions/guides/legends/index.js +1 -1
- package/src/actions/guides/legends/lane.js +2 -1
- package/src/actions/guides/legends/remove.js +8 -0
- package/src/actions/guides/legends/size.js +36 -43
- package/src/actions/guides/legends/strokeWidth.js +20 -26
- package/src/actions/guides/polar/axes/labels.js +17 -1
- package/src/actions/guides/polar/axes/shared.js +7 -1
- package/src/actions/guides/polar/axes/titles.js +19 -2
- package/src/actions/guides/polar/grids.js +4 -0
- package/src/actions/guides/polar/resolve.js +6 -0
- package/src/actions/index.js +13 -13
- package/src/actions/intervals/resolve.js +87 -117
- package/src/actions/marks/arc/actions.js +31 -36
- package/src/actions/marks/area/actions.js +23 -31
- package/src/actions/marks/index.js +6 -6
- package/src/actions/marks/line/actions.js +33 -52
- package/src/actions/marks/line/materialize.js +7 -5
- package/src/actions/marks/point/materialize.js +5 -3
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +53 -53
- package/src/actions/marks/shared.js +8 -4
- package/src/actions/marks/text/actions.js +14 -6
- package/src/actions/marks/text/layout.js +21 -45
- package/src/actions/marks/tick/actions.js +22 -21
- package/src/actions/primitives/createGraphics.js +4 -0
- package/src/actions/primitives/editGraphics.js +4 -0
- package/src/actions/primitives/index.js +3 -3
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +2 -2
- package/src/actions/primitives/semanticValidation/layer.js +1 -1
- package/src/actions/regression/edit.js +3 -43
- package/src/actions/scales/consumers/families.js +24 -4
- package/src/actions/scales/consumers/index.js +1 -1
- package/src/actions/scales/consumers/seriesLayout.js +17 -0
- package/src/actions/scales/definitions.js +100 -103
- package/src/actions/scales/materialize.js +5 -0
- package/src/actions/selection/actions.js +79 -121
- package/src/actions/titles/actions.js +48 -94
- package/src/actions/titles/resolve.js +14 -27
- package/src/actions/violinPlots/create.js +37 -44
- package/src/core/action.js +20 -5
- package/src/core/extensionRegistry.js +104 -0
- package/src/core/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/extension.js +8 -1
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +58 -11
- package/src/grammar/areaSeries.js +14 -16
- package/src/grammar/bin2d.js +37 -23
- package/src/grammar/boxPlot.js +14 -13
- package/src/grammar/categoricalDensity.js +14 -14
- package/src/grammar/categoryOrder.js +8 -14
- package/src/grammar/curveCommands.js +87 -20
- package/src/grammar/density.js +162 -141
- package/src/grammar/facets/index.js +14 -0
- package/src/grammar/facets/scales.js +7 -4
- package/src/grammar/gradientProfile.js +12 -15
- package/src/grammar/histogram.js +145 -83
- package/src/grammar/horizon.js +60 -23
- package/src/grammar/interval.js +14 -13
- package/src/grammar/lineSeries.js +91 -61
- package/src/grammar/numeric.js +290 -0
- package/src/grammar/paint.js +3 -4
- package/src/grammar/palettes.js +21 -32
- package/src/grammar/pathCommands.js +6 -0
- package/src/grammar/polarPaths.js +5 -5
- package/src/grammar/regression/derive.js +48 -9
- package/src/grammar/regression/models.js +248 -69
- package/src/grammar/regression/parameters.js +8 -0
- package/src/grammar/scales/color.js +35 -26
- package/src/grammar/scales/continuous.js +71 -24
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/discretized.js +42 -12
- package/src/grammar/scales/fields.js +21 -4
- package/src/grammar/scales/ordinal.js +117 -61
- package/src/grammar/scales/temporal.js +40 -11
- package/src/grammar/scales/transformed.js +199 -52
- package/src/grammar/schemas/graphicBounds.js +270 -79
- package/src/grammar/seriesLayout.js +50 -18
- package/src/grammar/ticks.js +123 -53
- package/src/grammar/timeUnit.js +18 -8
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/window.js +70 -57
- package/src/index.js +1 -1
- package/src/layout/canvas.js +4 -8
- package/src/layout/composition.js +39 -40
- package/src/layout/facets.js +10 -14
- package/src/layout/labels.js +91 -15
- package/src/layout/legend.js +22 -20
- package/src/layout/legendLane.js +54 -40
- package/src/layout/title.js +7 -7
- package/src/materialization/bars/aggregate.js +14 -40
- package/src/materialization/bars/grouped.js +7 -18
- package/src/materialization/bars/histogram.js +2 -19
- package/src/materialization/bars/ranged.js +20 -15
- package/src/materialization/bars/resolve.js +21 -0
- package/src/materialization/facetGuides/legacyCategorical.js +2 -1
- package/src/materialization/facetGuides/placement.js +34 -19
- package/src/materialization/facetGuides/preparation.js +9 -5
- package/src/materialization/facets.js +39 -27
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +5 -2
- package/src/materialization/marks/index.js +24 -4
- package/src/materialization/scales/policies/bar.js +61 -59
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +4 -5
- package/src/materialization/selection/items/arc.js +16 -2
- package/src/materialization/selection/items/bar.js +4 -2
- package/src/materialization/selection/items/common.js +21 -1
- package/src/materialization/selection/items/path.js +19 -2
- package/src/materialization/selection/items/point.js +4 -16
- package/src/materialization/selection/items/tick.js +13 -21
- package/src/materialization/selection/point.js +11 -4
- package/src/materialization/selection/styles.js +19 -27
- package/src/materialization/text.js +4 -1
- package/src/mcp/adapter.js +8 -3
- package/src/renderers/canvas/fill.js +2 -2
- package/src/renderers/canvas/index.js +35 -2
- package/src/renderers/canvas/native.js +160 -0
- package/src/renderers/pdf.js +14 -0
- package/src/renderers/svg.js +37 -4
- package/types/extension.d.ts +19 -0
- package/types/index.d.ts +3 -0
- package/types/program.d.ts +509 -131
- package/types/svg.d.ts +1 -0
- package/src/actions/errorBands/components.js +0 -81
- package/src/actions/errorBands/options.js +0 -39
- package/src/actions/errorBands/resolve.js +0 -28
- package/src/actions/errorBars/components.js +0 -62
- package/src/actions/errorBars/options.js +0 -58
- package/src/actions/errorBars/resolve.js +0 -25
|
@@ -8,11 +8,16 @@ import {
|
|
|
8
8
|
aggregateRows,
|
|
9
9
|
isAggregate,
|
|
10
10
|
validateAggregateFieldType,
|
|
11
|
-
validateAggregateFieldValues
|
|
11
|
+
validateAggregateFieldValues
|
|
12
12
|
} from "./aggregate.js";
|
|
13
13
|
import { stableOrderPathValues } from "./pathOrder.js";
|
|
14
|
+
import { finiteMidpoint } from "./numeric.js";
|
|
15
|
+
import {
|
|
16
|
+
findHistogramBinIndex,
|
|
17
|
+
resolveHistogramBins
|
|
18
|
+
} from "./histogram.js";
|
|
14
19
|
|
|
15
|
-
const SERIES_CHANNELS =
|
|
20
|
+
const SERIES_CHANNELS = ["group", "color", "strokeDash"];
|
|
16
21
|
|
|
17
22
|
function requireLineEncoding(layer) {
|
|
18
23
|
if (layer?.mark?.type !== "line") {
|
|
@@ -26,10 +31,16 @@ function requireLineEncoding(layer) {
|
|
|
26
31
|
throw new Error(`Line mark "${layer.id}" requires a temporal x encoding.`);
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
const binnedAggregate =
|
|
35
|
+
x?.fieldType === "quantitative" &&
|
|
36
|
+
x.bin !== undefined &&
|
|
37
|
+
isAggregate(y?.aggregate);
|
|
29
38
|
const aggregateMode =
|
|
30
|
-
x?.fieldType === "temporal" && isAggregate(y?.aggregate)
|
|
39
|
+
(x?.fieldType === "temporal" && isAggregate(y?.aggregate)) ||
|
|
40
|
+
binnedAggregate;
|
|
31
41
|
const directQuantitative =
|
|
32
42
|
x?.fieldType === "quantitative" &&
|
|
43
|
+
x.bin === undefined &&
|
|
33
44
|
y?.fieldType === "quantitative" &&
|
|
34
45
|
y.aggregate === undefined;
|
|
35
46
|
const directTemporal =
|
|
@@ -53,6 +64,8 @@ function requireLineEncoding(layer) {
|
|
|
53
64
|
x,
|
|
54
65
|
y,
|
|
55
66
|
isAggregate: aggregateMode,
|
|
67
|
+
binnedAggregate,
|
|
68
|
+
directRows: directTemporal || directQuantitative,
|
|
56
69
|
directTemporal
|
|
57
70
|
};
|
|
58
71
|
}
|
|
@@ -87,8 +100,50 @@ function groupKey(values) {
|
|
|
87
100
|
return JSON.stringify(values);
|
|
88
101
|
}
|
|
89
102
|
|
|
90
|
-
function
|
|
91
|
-
const
|
|
103
|
+
function groupedSeries(groups, fields, dimensions) {
|
|
104
|
+
const id = groupKey(dimensions);
|
|
105
|
+
let series = groups.get(id);
|
|
106
|
+
if (series === undefined) {
|
|
107
|
+
series = {
|
|
108
|
+
key: Object.fromEntries(
|
|
109
|
+
fields.map((field, index) => [field, dimensions[index]])
|
|
110
|
+
),
|
|
111
|
+
values: []
|
|
112
|
+
};
|
|
113
|
+
groups.set(id, series);
|
|
114
|
+
}
|
|
115
|
+
return series;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function freezeCartesianSeries(series) {
|
|
119
|
+
return cloneAndFreeze({
|
|
120
|
+
xValues: series.flatMap(item => item.values.map(value => value.x)),
|
|
121
|
+
yValues: series.flatMap(item => item.values.map(value => value.y)),
|
|
122
|
+
series
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function resolveLineBins(rows, layer, scale = {}) {
|
|
127
|
+
const x = layer?.encoding?.x;
|
|
128
|
+
if (
|
|
129
|
+
layer?.mark?.type !== "line" ||
|
|
130
|
+
x?.fieldType !== "quantitative" ||
|
|
131
|
+
x.bin === undefined
|
|
132
|
+
) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
return resolveHistogramBins({
|
|
136
|
+
values: readQuantitativeField(rows, x.field),
|
|
137
|
+
bin: x.bin,
|
|
138
|
+
domain: scale.domain ?? "auto",
|
|
139
|
+
nice: scale.nice ?? true,
|
|
140
|
+
zero: scale.zero ?? false
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function deriveCartesianLineSeries(rows, layer, options = {}) {
|
|
145
|
+
const { x, y, isAggregate, binnedAggregate, directRows, directTemporal } =
|
|
146
|
+
requireLineEncoding(layer);
|
|
92
147
|
if (isAggregate) {
|
|
93
148
|
validateAggregateFieldValues(rows, y.field, y.fieldType);
|
|
94
149
|
}
|
|
@@ -101,6 +156,9 @@ function deriveCartesianLineSeries(rows, layer) {
|
|
|
101
156
|
? readTemporalField(rows, y.field)
|
|
102
157
|
: readQuantitativeField(rows, y.field);
|
|
103
158
|
const seriesFields = readSeriesFields(rows, layer);
|
|
159
|
+
const binBoundaries = binnedAggregate
|
|
160
|
+
? options.xBinBoundaries ?? resolveLineBins(rows, layer).boundaries
|
|
161
|
+
: undefined;
|
|
104
162
|
|
|
105
163
|
const pathOrder = layer.encoding?.pathOrder;
|
|
106
164
|
if (pathOrder !== undefined) {
|
|
@@ -115,17 +173,10 @@ function deriveCartesianLineSeries(rows, layer) {
|
|
|
115
173
|
const dimensions = seriesFields.fields.map(
|
|
116
174
|
field => seriesFields.values.get(field)[index]
|
|
117
175
|
);
|
|
118
|
-
const
|
|
119
|
-
const series = groups.get(key) ?? {
|
|
120
|
-
key: Object.fromEntries(
|
|
121
|
-
seriesFields.fields.map((field, item) => [field, dimensions[item]])
|
|
122
|
-
),
|
|
123
|
-
values: [],
|
|
124
|
-
orderValues: []
|
|
125
|
-
};
|
|
176
|
+
const series = groupedSeries(groups, seriesFields.fields, dimensions);
|
|
126
177
|
series.values.push({ x: xValues[index], y: yValues[index] });
|
|
178
|
+
series.orderValues ??= [];
|
|
127
179
|
series.orderValues.push(orderValues[index]);
|
|
128
|
-
groups.set(key, series);
|
|
129
180
|
}
|
|
130
181
|
const series = [...groups.values()].flatMap(item => {
|
|
131
182
|
const values = stableOrderPathValues(
|
|
@@ -140,30 +191,19 @@ function deriveCartesianLineSeries(rows, layer) {
|
|
|
140
191
|
`Line series on mark "${layer.id}" requires at least two ordered points.`
|
|
141
192
|
);
|
|
142
193
|
}
|
|
143
|
-
return
|
|
144
|
-
xValues: series.flatMap(item => item.values.map(value => value.x)),
|
|
145
|
-
yValues: series.flatMap(item => item.values.map(value => value.y)),
|
|
146
|
-
series
|
|
147
|
-
});
|
|
194
|
+
return freezeCartesianSeries(series);
|
|
148
195
|
}
|
|
149
196
|
|
|
150
|
-
if (
|
|
197
|
+
if (directRows) {
|
|
151
198
|
const groups = new Map();
|
|
152
199
|
for (let index = 0; index < rows.length; index += 1) {
|
|
153
200
|
const dimensions = seriesFields.fields.map(
|
|
154
201
|
field => seriesFields.values.get(field)[index]
|
|
155
202
|
);
|
|
156
|
-
const
|
|
157
|
-
const series = groups.get(key) ?? {
|
|
158
|
-
key: Object.fromEntries(
|
|
159
|
-
seriesFields.fields.map((field, item) => [field, dimensions[item]])
|
|
160
|
-
),
|
|
161
|
-
values: []
|
|
162
|
-
};
|
|
203
|
+
const series = groupedSeries(groups, seriesFields.fields, dimensions);
|
|
163
204
|
series.values.push({ x: xValues[index], y: yValues[index] });
|
|
164
|
-
groups.set(key, series);
|
|
165
205
|
}
|
|
166
|
-
const orderBy =
|
|
206
|
+
const orderBy = directTemporal && y.fieldType === "temporal" ? "y" : "x";
|
|
167
207
|
const series = [...groups.values()].flatMap(item => {
|
|
168
208
|
const values = item.values.sort(
|
|
169
209
|
(left, right) => left[orderBy] - right[orderBy]
|
|
@@ -175,11 +215,7 @@ function deriveCartesianLineSeries(rows, layer) {
|
|
|
175
215
|
`Line series on mark "${layer.id}" requires at least two direct points.`
|
|
176
216
|
);
|
|
177
217
|
}
|
|
178
|
-
return
|
|
179
|
-
xValues: series.flatMap(item => item.values.map(value => value.x)),
|
|
180
|
-
yValues: series.flatMap(item => item.values.map(value => value.y)),
|
|
181
|
-
series
|
|
182
|
-
});
|
|
218
|
+
return freezeCartesianSeries(series);
|
|
183
219
|
}
|
|
184
220
|
const aggregateGroups = new Map();
|
|
185
221
|
|
|
@@ -187,9 +223,19 @@ function deriveCartesianLineSeries(rows, layer) {
|
|
|
187
223
|
const dimensions = seriesFields.fields.map(
|
|
188
224
|
field => seriesFields.values.get(field)[index]
|
|
189
225
|
);
|
|
190
|
-
const
|
|
226
|
+
const binIndex = binnedAggregate
|
|
227
|
+
? findHistogramBinIndex(xValues[index], binBoundaries)
|
|
228
|
+
: undefined;
|
|
229
|
+
if (binIndex === -1) continue;
|
|
230
|
+
const position = binnedAggregate
|
|
231
|
+
? finiteMidpoint(
|
|
232
|
+
binBoundaries[binIndex],
|
|
233
|
+
binBoundaries[binIndex + 1]
|
|
234
|
+
)
|
|
235
|
+
: xValues[index];
|
|
236
|
+
const key = groupKey([binnedAggregate ? binIndex : position, ...dimensions]);
|
|
191
237
|
const group = aggregateGroups.get(key) ?? {
|
|
192
|
-
x:
|
|
238
|
+
x: position,
|
|
193
239
|
dimensions,
|
|
194
240
|
rows: []
|
|
195
241
|
};
|
|
@@ -205,23 +251,18 @@ function deriveCartesianLineSeries(rows, layer) {
|
|
|
205
251
|
const seriesGroups = new Map();
|
|
206
252
|
|
|
207
253
|
for (const group of aggregateGroups.values()) {
|
|
208
|
-
const value =
|
|
209
|
-
? aggregateRows(group.rows, y.field, y.aggregate)
|
|
210
|
-
: group.rows.reduce((sum, row) => sum + row[y.field], 0);
|
|
254
|
+
const value = aggregateRows(group.rows, y.field, y.aggregate);
|
|
211
255
|
if (value === undefined) continue;
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
values: []
|
|
218
|
-
};
|
|
256
|
+
const series = groupedSeries(
|
|
257
|
+
seriesGroups,
|
|
258
|
+
seriesFields.fields,
|
|
259
|
+
group.dimensions
|
|
260
|
+
);
|
|
219
261
|
|
|
220
262
|
series.values.push({
|
|
221
263
|
x: group.x,
|
|
222
264
|
y: value
|
|
223
265
|
});
|
|
224
|
-
seriesGroups.set(key, series);
|
|
225
266
|
}
|
|
226
267
|
|
|
227
268
|
const series = [...seriesGroups.values()].flatMap(item => {
|
|
@@ -240,11 +281,7 @@ function deriveCartesianLineSeries(rows, layer) {
|
|
|
240
281
|
);
|
|
241
282
|
}
|
|
242
283
|
|
|
243
|
-
return
|
|
244
|
-
xValues: series.flatMap(item => item.values.map(value => value.x)),
|
|
245
|
-
yValues: series.flatMap(item => item.values.map(value => value.y)),
|
|
246
|
-
series
|
|
247
|
-
});
|
|
284
|
+
return freezeCartesianSeries(series);
|
|
248
285
|
}
|
|
249
286
|
|
|
250
287
|
function requirePolarLineEncoding(layer) {
|
|
@@ -311,20 +348,13 @@ export function derivePolarLineSeries(rows, layer, { thetaDomain } = {}) {
|
|
|
311
348
|
const dimensions = seriesFields.fields.map(
|
|
312
349
|
field => seriesFields.values.get(field)[index]
|
|
313
350
|
);
|
|
314
|
-
const
|
|
315
|
-
const series = groups.get(key) ?? {
|
|
316
|
-
key: Object.fromEntries(
|
|
317
|
-
seriesFields.fields.map((field, item) => [field, dimensions[item]])
|
|
318
|
-
),
|
|
319
|
-
values: []
|
|
320
|
-
};
|
|
351
|
+
const series = groupedSeries(groups, seriesFields.fields, dimensions);
|
|
321
352
|
series.values.push({
|
|
322
353
|
theta: thetaValues[index],
|
|
323
354
|
radius: radiusValues[index],
|
|
324
355
|
order: sortValues[index],
|
|
325
356
|
sourceIndex: index
|
|
326
357
|
});
|
|
327
|
-
groups.set(key, series);
|
|
328
358
|
}
|
|
329
359
|
|
|
330
360
|
const series = [...groups.values()].flatMap(item => {
|
|
@@ -352,7 +382,7 @@ export function deriveLineSeries(rows, layer, options) {
|
|
|
352
382
|
layer?.encoding?.radius !== undefined;
|
|
353
383
|
return polar
|
|
354
384
|
? derivePolarLineSeries(rows, layer, options)
|
|
355
|
-
: deriveCartesianLineSeries(rows, layer);
|
|
385
|
+
: deriveCartesianLineSeries(rows, layer, options);
|
|
356
386
|
}
|
|
357
387
|
|
|
358
388
|
export function deriveLineSeriesFieldValues(rows, layer, derived, field) {
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
export const NICE_FACTORS = Object.freeze([1, 2, 3, 5]);
|
|
2
|
+
|
|
3
|
+
function finiteArray(values, label) {
|
|
4
|
+
if (!Array.isArray(values) || !values.every(Number.isFinite)) {
|
|
5
|
+
throw new TypeError(`${label} must contain finite numbers.`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function rangeError(label) {
|
|
10
|
+
return new RangeError(`${label} is outside the finite numeric range.`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function compensatedSum(values, scale = 1) {
|
|
14
|
+
let total = 0;
|
|
15
|
+
let correction = 0;
|
|
16
|
+
for (const source of values) {
|
|
17
|
+
const value = source / scale;
|
|
18
|
+
const next = total + value;
|
|
19
|
+
correction += Math.abs(total) >= Math.abs(value)
|
|
20
|
+
? total - next + value
|
|
21
|
+
: value - next + total;
|
|
22
|
+
total = next;
|
|
23
|
+
}
|
|
24
|
+
return total + correction;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function maximumMagnitude(values) {
|
|
28
|
+
let maximum = 0;
|
|
29
|
+
for (const value of values) maximum = Math.max(maximum, Math.abs(value));
|
|
30
|
+
return maximum;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function normalizedFiniteSum(values, label = "Numeric values") {
|
|
34
|
+
finiteArray(values, label);
|
|
35
|
+
const scale = maximumMagnitude(values) || 1;
|
|
36
|
+
return { scale, total: compensatedSum(values, scale) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function requireFiniteResult(value, label = "Numeric result") {
|
|
40
|
+
if (!Number.isFinite(value)) throw rangeError(label);
|
|
41
|
+
return value === 0 ? 0 : value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function restoreFiniteScale(value, scale, label = "Numeric result") {
|
|
45
|
+
if (!Number.isFinite(value) || !Number.isFinite(scale) || scale < 0) {
|
|
46
|
+
throw rangeError(label);
|
|
47
|
+
}
|
|
48
|
+
return requireFiniteResult(value * scale, label);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function stableReducedSum(values, label, divisor = 1) {
|
|
52
|
+
finiteArray(values, label);
|
|
53
|
+
const ordinary = values.reduce((sum, value) => sum + value, 0);
|
|
54
|
+
if (Number.isFinite(ordinary)) {
|
|
55
|
+
return ordinary === 0 ? 0 : ordinary / divisor;
|
|
56
|
+
}
|
|
57
|
+
const scale = maximumMagnitude(values) || 1;
|
|
58
|
+
return restoreFiniteScale(
|
|
59
|
+
compensatedSum(values, scale) / divisor, scale, label
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function stableFiniteSum(values, label = "Numeric sum") {
|
|
64
|
+
return stableReducedSum(values, label);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function stableFinitePrefixSums(values, label = "Numeric sum") {
|
|
68
|
+
const normalized = normalizedFiniteSum(values, label);
|
|
69
|
+
let ordinary = 0;
|
|
70
|
+
let sum = 0;
|
|
71
|
+
let correction = 0;
|
|
72
|
+
return values.map(value => {
|
|
73
|
+
ordinary += value;
|
|
74
|
+
const scaled = value / normalized.scale;
|
|
75
|
+
const next = sum + scaled;
|
|
76
|
+
correction += Math.abs(sum) >= Math.abs(scaled)
|
|
77
|
+
? sum - next + scaled
|
|
78
|
+
: scaled - next + sum;
|
|
79
|
+
sum = next;
|
|
80
|
+
return Number.isFinite(ordinary)
|
|
81
|
+
? ordinary === 0 ? 0 : ordinary
|
|
82
|
+
: restoreFiniteScale(sum + correction, normalized.scale, label);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function stableFiniteMean(values, label = "Numeric mean") {
|
|
87
|
+
finiteArray(values, label);
|
|
88
|
+
if (values.length === 0) {
|
|
89
|
+
throw new RangeError(`${label} requires at least one value.`);
|
|
90
|
+
}
|
|
91
|
+
return stableReducedSum(values, label, values.length);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function stableFiniteSquareSum(values, label = "Numeric square sum") {
|
|
95
|
+
finiteArray(values, label);
|
|
96
|
+
const ordinary = values.reduce((sum, value) => sum + value ** 2, 0);
|
|
97
|
+
if (Number.isFinite(ordinary)) return ordinary;
|
|
98
|
+
const scale = maximumMagnitude(values);
|
|
99
|
+
if (scale === 0) return 0;
|
|
100
|
+
const normalized = values.reduce(
|
|
101
|
+
(sum, value) => sum + (value / scale) ** 2,
|
|
102
|
+
0
|
|
103
|
+
);
|
|
104
|
+
const magnitude = restoreFiniteScale(Math.sqrt(normalized), scale, label);
|
|
105
|
+
return requireFiniteResult(magnitude ** 2, label);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function stableFiniteDeviation(values, {
|
|
109
|
+
sample = false,
|
|
110
|
+
divisor = 1,
|
|
111
|
+
label = "Numeric deviation"
|
|
112
|
+
} = {}) {
|
|
113
|
+
if (!Number.isFinite(divisor) || divisor <= 0) {
|
|
114
|
+
throw new RangeError(`${label} divisor must be positive and finite.`);
|
|
115
|
+
}
|
|
116
|
+
const denominator = values.length - (sample ? 1 : 0);
|
|
117
|
+
if (denominator <= 0) {
|
|
118
|
+
throw new RangeError(`${label} requires more values.`);
|
|
119
|
+
}
|
|
120
|
+
const mean = stableFiniteMean(values, `${label} mean`);
|
|
121
|
+
const differences = values.map(value => value - mean);
|
|
122
|
+
const squared = differences.reduce(
|
|
123
|
+
(sum, difference) => sum + difference ** 2,
|
|
124
|
+
0
|
|
125
|
+
);
|
|
126
|
+
if (Number.isFinite(squared)) {
|
|
127
|
+
return {
|
|
128
|
+
mean,
|
|
129
|
+
deviation: Math.sqrt(squared / denominator) / divisor,
|
|
130
|
+
squared
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const finiteDifferences = differences.every(Number.isFinite);
|
|
134
|
+
const scale = finiteDifferences
|
|
135
|
+
? maximumMagnitude(differences)
|
|
136
|
+
: Math.max(Math.abs(mean), maximumMagnitude(values));
|
|
137
|
+
if (scale === 0) return { mean, deviation: 0 };
|
|
138
|
+
const normalizedSquares = values.reduce((sum, value, index) => {
|
|
139
|
+
const difference = finiteDifferences
|
|
140
|
+
? differences[index] / scale
|
|
141
|
+
: value / scale - mean / scale;
|
|
142
|
+
return sum + difference ** 2;
|
|
143
|
+
}, 0);
|
|
144
|
+
const deviation = restoreFiniteScale(
|
|
145
|
+
Math.sqrt(normalizedSquares / denominator) / divisor,
|
|
146
|
+
scale,
|
|
147
|
+
label
|
|
148
|
+
);
|
|
149
|
+
return { mean, deviation };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function numericExtent(values) {
|
|
153
|
+
let minimum = Infinity;
|
|
154
|
+
let maximum = -Infinity;
|
|
155
|
+
for (const value of values) {
|
|
156
|
+
minimum = Math.min(minimum, value);
|
|
157
|
+
maximum = Math.max(maximum, value);
|
|
158
|
+
}
|
|
159
|
+
return [minimum, maximum];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function normalizeNumericRange(start, end) {
|
|
163
|
+
const magnitude = Math.max(Math.abs(start), Math.abs(end));
|
|
164
|
+
if (magnitude === 0) {
|
|
165
|
+
return { scale: 1, start: 0, end: 0, span: 0 };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const decimalScale = 10 ** Math.floor(Math.log10(magnitude));
|
|
169
|
+
const scale = Number.isFinite(decimalScale) && decimalScale > 0
|
|
170
|
+
? decimalScale
|
|
171
|
+
: magnitude;
|
|
172
|
+
start /= scale;
|
|
173
|
+
end /= scale;
|
|
174
|
+
return {
|
|
175
|
+
scale,
|
|
176
|
+
start,
|
|
177
|
+
end,
|
|
178
|
+
span: end - start
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function niceNumericStep(
|
|
183
|
+
span,
|
|
184
|
+
count = 5,
|
|
185
|
+
factors = NICE_FACTORS
|
|
186
|
+
) {
|
|
187
|
+
if (!Number.isFinite(span) || span <= 0) {
|
|
188
|
+
throw new RangeError("Nice numeric span must be positive and finite.");
|
|
189
|
+
}
|
|
190
|
+
const rough = span / Math.max(1, count);
|
|
191
|
+
const power = 10 ** Math.floor(Math.log10(rough));
|
|
192
|
+
const fraction = rough / power;
|
|
193
|
+
const factor = factors.find(candidate => candidate >= fraction) ?? 10;
|
|
194
|
+
return factor * power;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function alignNumericStep(value, step, direction) {
|
|
198
|
+
let quotient = value / step;
|
|
199
|
+
const nearest = Math.round(quotient);
|
|
200
|
+
if (Math.abs(quotient - nearest) <= 1e-10) quotient = nearest;
|
|
201
|
+
return (direction === "floor" ? Math.floor(quotient) : Math.ceil(quotient)) * step;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function cleanNumericValue(value, resolution) {
|
|
205
|
+
if (!Number.isFinite(value) || value === 0) return value === 0 ? 0 : value;
|
|
206
|
+
const rounded = Number(value.toPrecision(15));
|
|
207
|
+
return Number.isFinite(resolution) && resolution > 0 &&
|
|
208
|
+
Math.abs(rounded - value) <= resolution * 1e-10
|
|
209
|
+
? rounded
|
|
210
|
+
: value;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function inverseLerp(value, start, end) {
|
|
214
|
+
if (value === start) return 0;
|
|
215
|
+
if (value === end) return 1;
|
|
216
|
+
|
|
217
|
+
const span = end - start;
|
|
218
|
+
const offset = value - start;
|
|
219
|
+
if (Number.isFinite(span) && span !== 0 && Number.isFinite(offset)) {
|
|
220
|
+
return offset / span;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const scale = Math.max(Math.abs(value), Math.abs(start), Math.abs(end));
|
|
224
|
+
return (value / scale - start / scale) / (end / scale - start / scale);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function interpolateNumber(start, end, proportion) {
|
|
228
|
+
let result = proportion === 0 ? start : proportion === 1 ? end : undefined;
|
|
229
|
+
if (result === undefined) {
|
|
230
|
+
const span = end - start;
|
|
231
|
+
if (Number.isFinite(span)) {
|
|
232
|
+
result = start + proportion * span;
|
|
233
|
+
} else {
|
|
234
|
+
const scale = Math.max(Math.abs(start), Math.abs(end));
|
|
235
|
+
result = scale * (
|
|
236
|
+
start / scale * (1 - proportion) + end / scale * proportion
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return requireFiniteResult(result, "Numeric interpolation");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function finiteMidpoint(start, end) {
|
|
244
|
+
const sum = start + end;
|
|
245
|
+
const result = Number.isFinite(sum) ? sum / 2 : start / 2 + end / 2;
|
|
246
|
+
return requireFiniteResult(result, "Numeric midpoint");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function sampleNumericRange(start, end, count, label = "Numeric range") {
|
|
250
|
+
const resolution = Math.abs((end - start) / (count - 1));
|
|
251
|
+
const values = [];
|
|
252
|
+
const direction = Math.sign(end - start);
|
|
253
|
+
for (let index = 0; index < count; index += 1) {
|
|
254
|
+
const value = cleanNumericValue(
|
|
255
|
+
interpolateNumber(start, end, index / (count - 1)),
|
|
256
|
+
resolution
|
|
257
|
+
);
|
|
258
|
+
if (
|
|
259
|
+
!Number.isFinite(value) ||
|
|
260
|
+
(direction !== 0 && index > 0 &&
|
|
261
|
+
direction * (value - values[index - 1]) <= 0)
|
|
262
|
+
) {
|
|
263
|
+
throw new RangeError(`${label} cannot represent ${count} distinct samples.`);
|
|
264
|
+
}
|
|
265
|
+
values.push(value);
|
|
266
|
+
}
|
|
267
|
+
return values;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function formatDistinctNumericSamples(values, initialFormat) {
|
|
271
|
+
let labels = values.map(initialFormat ?? (value =>
|
|
272
|
+
String(+value.toPrecision(3))
|
|
273
|
+
));
|
|
274
|
+
const distinct = () => labels.every((label, index) =>
|
|
275
|
+
index === 0 || values[index] === values[index - 1] ||
|
|
276
|
+
label !== labels[index - 1]
|
|
277
|
+
);
|
|
278
|
+
for (let precision = 4; !distinct() && precision <= 17; precision += 1) {
|
|
279
|
+
labels = values.map(value => String(+value.toPrecision(precision)));
|
|
280
|
+
}
|
|
281
|
+
return distinct() ? labels : values.map(String);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function stableDecimal(value, places = 12) {
|
|
285
|
+
if (value === 0) return 0;
|
|
286
|
+
const threshold = 10 ** -places;
|
|
287
|
+
return Math.abs(value) < threshold
|
|
288
|
+
? Number(value.toPrecision(places))
|
|
289
|
+
: Number(value.toFixed(places));
|
|
290
|
+
}
|
package/src/grammar/paint.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isPlainObject } from "../core/immutable.js";
|
|
2
|
+
import { interpolateNumber } from "./numeric.js";
|
|
2
3
|
|
|
3
4
|
const PAINT_KEYS = Object.freeze(["type", "from", "to", "stops"]);
|
|
4
5
|
const POINT_KEYS = Object.freeze(["x", "y"]);
|
|
@@ -78,11 +79,9 @@ export function resolveLinearGradientCoordinates(paint, bounds) {
|
|
|
78
79
|
) {
|
|
79
80
|
throw new TypeError("Linear gradient bounds must be finite and ordered.");
|
|
80
81
|
}
|
|
81
|
-
const width = bounds.right - bounds.left;
|
|
82
|
-
const height = bounds.bottom - bounds.top;
|
|
83
82
|
const point = value => Object.freeze({
|
|
84
|
-
x: bounds.left
|
|
85
|
-
y: bounds.top
|
|
83
|
+
x: interpolateNumber(bounds.left, bounds.right, value.x),
|
|
84
|
+
y: interpolateNumber(bounds.top, bounds.bottom, value.y)
|
|
86
85
|
});
|
|
87
86
|
return Object.freeze({ from: point(paint.from), to: point(paint.to) });
|
|
88
87
|
}
|
package/src/grammar/palettes.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { cloneAndFreeze, isPlainObject } from "../core/immutable.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
validateGeneratedItemLimit,
|
|
4
|
+
validateKeys
|
|
5
|
+
} from "../core/validation.js";
|
|
3
6
|
|
|
4
7
|
function colors(encoded) {
|
|
5
8
|
return cloneAndFreeze(
|
|
6
|
-
encoded.match(/.{6}/g).map(value => `#${value
|
|
9
|
+
encoded.match(/.{6}/g).map(value => `#${value}`)
|
|
7
10
|
);
|
|
8
11
|
}
|
|
9
12
|
|
|
@@ -82,38 +85,21 @@ const CONTINUOUS = Object.freeze({
|
|
|
82
85
|
});
|
|
83
86
|
|
|
84
87
|
export const PALETTE_NAMES = cloneAndFreeze([
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"observable10",
|
|
88
|
-
"dark2", "paired", "pastel1", "pastel2",
|
|
89
|
-
"set1", "set2", "set3",
|
|
90
|
-
"tableau10", "tableau20",
|
|
91
|
-
"blues", "tealblues", "teals", "greens", "browns",
|
|
92
|
-
"oranges", "reds", "purples", "warmgreys", "greys",
|
|
93
|
-
"viridis", "magma", "inferno", "plasma", "cividis", "turbo",
|
|
94
|
-
"bluegreen", "bluepurple",
|
|
95
|
-
"goldgreen", "goldorange", "goldred",
|
|
96
|
-
"greenblue", "orangered",
|
|
97
|
-
"purplebluegreen", "purpleblue", "purplered", "redpurple",
|
|
98
|
-
"yellowgreenblue", "yellowgreen", "yelloworangebrown", "yelloworangered",
|
|
99
|
-
"darkblue", "darkgold", "darkgreen", "darkmulti", "darkred",
|
|
100
|
-
"lightgreyred", "lightgreyteal", "lightmulti", "lightorange", "lighttealblue",
|
|
101
|
-
"blueorange", "brownbluegreen", "purplegreen", "pinkyellowgreen",
|
|
102
|
-
"purpleorange", "redblue", "redgrey",
|
|
103
|
-
"redyellowblue", "redyellowgreen", "spectral",
|
|
104
|
-
"rainbow", "sinebow"
|
|
88
|
+
...Object.keys(DISCRETE),
|
|
89
|
+
...Object.keys(CONTINUOUS)
|
|
105
90
|
]);
|
|
106
91
|
|
|
107
|
-
const PALETTE_SET = new Set(PALETTE_NAMES);
|
|
108
|
-
|
|
109
92
|
export function validatePaletteName(name) {
|
|
110
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
typeof name !== "string" ||
|
|
95
|
+
!Object.hasOwn(DISCRETE, name) && !Object.hasOwn(CONTINUOUS, name)
|
|
96
|
+
) {
|
|
111
97
|
throw new Error(`Unknown palette "${name}".`);
|
|
112
98
|
}
|
|
113
99
|
return name;
|
|
114
100
|
}
|
|
115
101
|
|
|
116
|
-
export function normalizePalette(value) {
|
|
102
|
+
export function normalizePalette(value, maximum) {
|
|
117
103
|
if (typeof value === "string") return { name: validatePaletteName(value) };
|
|
118
104
|
if (!isPlainObject(value)) {
|
|
119
105
|
throw new TypeError("Palette must be a name or a plain object.");
|
|
@@ -125,6 +111,7 @@ export function normalizePalette(value) {
|
|
|
125
111
|
if (!Number.isInteger(value.count) || value.count <= 0) {
|
|
126
112
|
throw new RangeError("Palette count must be a positive integer.");
|
|
127
113
|
}
|
|
114
|
+
validateGeneratedItemLimit(value.count, "Palette count", maximum);
|
|
128
115
|
normalized.count = value.count;
|
|
129
116
|
}
|
|
130
117
|
if (value.extent !== undefined) {
|
|
@@ -161,15 +148,14 @@ function interpolateColor(left, right, amount) {
|
|
|
161
148
|
}
|
|
162
149
|
|
|
163
150
|
function sample(colorsValue, position) {
|
|
164
|
-
const
|
|
165
|
-
const scaled = bounded * (colorsValue.length - 1);
|
|
151
|
+
const scaled = position * (colorsValue.length - 1);
|
|
166
152
|
const left = Math.floor(scaled);
|
|
167
153
|
const right = Math.min(colorsValue.length - 1, left + 1);
|
|
168
154
|
return interpolateColor(colorsValue[left], colorsValue[right], scaled - left);
|
|
169
155
|
}
|
|
170
156
|
|
|
171
|
-
export function resolvePalette(value, domainCount) {
|
|
172
|
-
const palette = normalizePalette(value);
|
|
157
|
+
export function resolvePalette(value, domainCount, maximum) {
|
|
158
|
+
const palette = normalizePalette(value, maximum);
|
|
173
159
|
const discrete = DISCRETE[palette.name];
|
|
174
160
|
if (discrete !== undefined) {
|
|
175
161
|
const count = palette.count ?? discrete.length;
|
|
@@ -182,6 +168,7 @@ export function resolvePalette(value, domainCount) {
|
|
|
182
168
|
}
|
|
183
169
|
const source = CONTINUOUS[palette.name];
|
|
184
170
|
const count = palette.count ?? domainCount;
|
|
171
|
+
validateGeneratedItemLimit(count, "Palette count", maximum);
|
|
185
172
|
const extent = palette.extent ?? [0, 1];
|
|
186
173
|
return cloneAndFreeze(Array.from({ length: count }, (_, index) => {
|
|
187
174
|
const unit = count === 1 ? 0.5 : index / (count - 1);
|
|
@@ -195,12 +182,14 @@ export function resolveContinuousPalette(value, count = 31) {
|
|
|
195
182
|
if (!Number.isInteger(resolvedCount) || resolvedCount < 2) {
|
|
196
183
|
throw new RangeError("Continuous palette count must be an integer of at least 2.");
|
|
197
184
|
}
|
|
185
|
+
validateGeneratedItemLimit(resolvedCount, "Continuous palette count");
|
|
198
186
|
const discrete = DISCRETE[palette.name];
|
|
199
187
|
if (discrete !== undefined && palette.count === undefined) return discrete;
|
|
200
188
|
return resolvePalette({ ...palette, count: resolvedCount }, resolvedCount);
|
|
201
189
|
}
|
|
202
190
|
|
|
203
191
|
export function paletteFamily(name) {
|
|
204
|
-
|
|
205
|
-
|
|
192
|
+
return DISCRETE[validatePaletteName(name)] === undefined
|
|
193
|
+
? "continuous"
|
|
194
|
+
: "categorical";
|
|
206
195
|
}
|