ggaction 0.0.10 → 0.0.12
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 +97 -0
- package/README.md +5 -3
- 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/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 +18 -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 +41 -45
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/apply.js +30 -0
- package/src/actions/encodings/position/policies/arc.js +23 -2
- 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 +36 -7
- 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 +212 -30
- package/src/actions/errorBars/edit.js +91 -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 +38 -16
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +79 -54
- 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/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/core/vocabulary.js +2 -2
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +170 -32
- 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/positionCompatibility.js +1 -1
- 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/rules.js +2 -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 +139 -60
- 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 +55 -30
- 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 +9 -2
- package/src/materialization/facetGuides/placement.js +49 -19
- package/src/materialization/facetGuides/preparation.js +35 -6
- package/src/materialization/facets.js +42 -28
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +15 -4
- package/src/materialization/marks/index.js +29 -6
- package/src/materialization/rowEncoding.js +57 -1
- package/src/materialization/scales/policies/bar.js +61 -102
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/offset.js +54 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +6 -9
- 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/index.d.ts +3 -0
- package/types/program.d.ts +523 -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
|
@@ -15,7 +15,7 @@ import { planDerivedDataRevision } from
|
|
|
15
15
|
import { validateOptions } from "./shared.js";
|
|
16
16
|
import { normalizeDensityPlacement } from "../../grammar/density.js";
|
|
17
17
|
import { scaleEditPatch } from "../scales/patch.js";
|
|
18
|
-
import {
|
|
18
|
+
import { removeOwnedColorLegends } from "../guides/legends/remove.js";
|
|
19
19
|
import {
|
|
20
20
|
requireDensityField,
|
|
21
21
|
resolveDensityCategoryScaleOptions,
|
|
@@ -215,19 +215,6 @@ function assertGroupingSelectionCompatibility(program, layer, changesGroup) {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
function removeOwnedColorLegends(program, target) {
|
|
219
|
-
const kinds = Object.entries(program.guideConfigs.legend ?? {})
|
|
220
|
-
.filter(([kind, config]) =>
|
|
221
|
-
config?.target === target &&
|
|
222
|
-
(
|
|
223
|
-
config.channels?.includes("color") ||
|
|
224
|
-
["gradient", "interval"].includes(kind)
|
|
225
|
-
)
|
|
226
|
-
)
|
|
227
|
-
.map(([kind]) => kind);
|
|
228
|
-
return kinds.length === 0 ? program : removeLegendKinds(program, kinds);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
218
|
const editDensity = action(
|
|
232
219
|
{
|
|
233
220
|
op: "editDensity",
|
|
@@ -454,7 +441,6 @@ const editDensity = action(
|
|
|
454
441
|
}
|
|
455
442
|
return next;
|
|
456
443
|
};
|
|
457
|
-
applyEdit(this);
|
|
458
444
|
return applyEdit(this);
|
|
459
445
|
}
|
|
460
446
|
);
|
|
@@ -167,7 +167,11 @@ export function resolveHorizonPaletteColors(transform) {
|
|
|
167
167
|
for (const sign of ["negative", "positive"]) {
|
|
168
168
|
const palette = transform.palette[sign];
|
|
169
169
|
const dense = palette.count === undefined && transform.bands > 1
|
|
170
|
-
? resolvePalette(
|
|
170
|
+
? resolvePalette(
|
|
171
|
+
{ ...palette, count: transform.bands * 2 },
|
|
172
|
+
transform.bands * 2,
|
|
173
|
+
transform.bands * 2
|
|
174
|
+
)
|
|
171
175
|
: resolvePalette(palette, transform.bands);
|
|
172
176
|
const colors = palette.count === undefined && transform.bands > 1
|
|
173
177
|
? Array.from({ length: transform.bands }, (_, index) =>
|
|
@@ -181,11 +185,17 @@ export function resolveHorizonPaletteColors(transform) {
|
|
|
181
185
|
return { domain, range };
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
export function resolveFoldedHorizonScaleOptions(target, requested) {
|
|
188
|
+
export function resolveFoldedHorizonScaleOptions(target, requested, existing = {}) {
|
|
185
189
|
if (requested !== undefined && !isPlainObject(requested)) {
|
|
186
190
|
throw new TypeError("Horizon y scale must be a plain object.");
|
|
187
191
|
}
|
|
188
|
-
const
|
|
192
|
+
const patch = requested ?? {};
|
|
193
|
+
validateOptionObject(
|
|
194
|
+
patch,
|
|
195
|
+
["id", "type", "domain", "range", "clamp", "reverse"],
|
|
196
|
+
"Horizon y scale"
|
|
197
|
+
);
|
|
198
|
+
const options = { ...existing, ...patch };
|
|
189
199
|
if (
|
|
190
200
|
options.domain !== undefined &&
|
|
191
201
|
(!Array.isArray(options.domain) ||
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
deriveHorizon,
|
|
6
6
|
validateHorizonTransform
|
|
7
7
|
} from "../../grammar/horizon.js";
|
|
8
|
+
import { numericExtent } from "../../grammar/numeric.js";
|
|
8
9
|
import {
|
|
9
10
|
normalizeTemporalValue,
|
|
10
11
|
readQuantitativeField
|
|
@@ -217,7 +218,7 @@ const editHorizon = action(
|
|
|
217
218
|
id: currentX.id,
|
|
218
219
|
...(emptyXDomain === undefined
|
|
219
220
|
? {}
|
|
220
|
-
: { domain:
|
|
221
|
+
: { domain: numericExtent(emptyXDomain) }),
|
|
221
222
|
...(xTypeChanged && requestedXScale.type === undefined
|
|
222
223
|
? { type: x.fieldType === "temporal" ? "time" : "linear" }
|
|
223
224
|
: {})
|
|
@@ -233,11 +234,11 @@ const editHorizon = action(
|
|
|
233
234
|
if (requestedYScale.id !== undefined && requestedYScale.id !== currentY.id) {
|
|
234
235
|
throw new Error("editHorizon y scale cannot change its id.");
|
|
235
236
|
}
|
|
236
|
-
const nextY = resolveFoldedHorizonScaleOptions(
|
|
237
|
-
|
|
238
|
-
...requestedYScale,
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
const nextY = resolveFoldedHorizonScaleOptions(
|
|
238
|
+
layer.id,
|
|
239
|
+
{ ...requestedYScale, id: currentY.id },
|
|
240
|
+
currentY
|
|
241
|
+
);
|
|
241
242
|
next = applyScaleDefinition(next, currentY, nextY);
|
|
242
243
|
|
|
243
244
|
const colors = resolveHorizonPaletteColors(candidate);
|
|
@@ -314,7 +315,7 @@ const encodeHorizon = action(
|
|
|
314
315
|
: {})
|
|
315
316
|
};
|
|
316
317
|
if (Array.isArray(xScale.domain) && xScale.domain.length > 2) {
|
|
317
|
-
xScale.domain =
|
|
318
|
+
xScale.domain = numericExtent(xScale.domain);
|
|
318
319
|
}
|
|
319
320
|
const yScale = resolveFoldedHorizonScaleOptions(layer.id, y.scale);
|
|
320
321
|
const colors = resolveHorizonPaletteColors(transform);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { registerAppearanceEncodingAction } from "./appearance.js";
|
|
2
|
-
import { registerAngleEncodingAction } from "./angle.js";
|
|
3
|
-
import { registerBarWidthEncodingAction } from "./barWidth.js";
|
|
4
|
-
import { registerColorEncodingAction } from "./color/index.js";
|
|
5
1
|
import { registerDensityEncodingAction } from "./density.js";
|
|
6
|
-
import { registerHistogramEncodingAction } from "./histogram.js";
|
|
7
|
-
import { registerHorizonEncodingAction } from "./horizon.js";
|
|
8
|
-
import { registerOffsetEncodingAction } from "./offset.js";
|
|
9
2
|
import { registerPathOrderEncodingActions } from "./pathOrder.js";
|
|
3
|
+
import { registerHorizonEncodingAction } from "./horizon.js";
|
|
4
|
+
import { registerHistogramEncodingAction } from "./histogram.js";
|
|
10
5
|
import { registerPositionEncodingActions } from "./position/index.js";
|
|
11
6
|
import { registerRangedEncodingActions } from "./ranged.js";
|
|
7
|
+
import { registerAngleEncodingAction } from "./angle.js";
|
|
8
|
+
import { registerOffsetEncodingAction } from "./offset.js";
|
|
9
|
+
import { registerParallelEncodingAction } from "./parallel.js";
|
|
10
|
+
import { registerColorEncodingAction } from "./color/index.js";
|
|
11
|
+
import { registerTextEncodingAction } from "./text.js";
|
|
12
|
+
import { registerAppearanceEncodingAction } from "./appearance.js";
|
|
12
13
|
import { registerStrokeDashEncodingActions } from "./strokeDash.js";
|
|
13
14
|
import { registerRuleAppearanceEncodingActions } from "./ruleAppearance.js";
|
|
14
|
-
import {
|
|
15
|
-
import { registerParallelEncodingAction } from "./parallel.js";
|
|
15
|
+
import { registerBarWidthEncodingAction } from "./barWidth.js";
|
|
16
16
|
import { registerEncodingRemovalAction } from "./remove.js";
|
|
17
17
|
|
|
18
18
|
export function registerEncodingActions(ProgramClass) {
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
import { resolveOffsetScaleDefinition } from "../scales/definitions.js";
|
|
7
7
|
import {
|
|
8
8
|
applyEncodingScale,
|
|
9
|
+
rematerializeEncoding,
|
|
9
10
|
resolveReassignmentScaleOptions,
|
|
10
11
|
resolveTarget,
|
|
12
|
+
setEncodingProperties,
|
|
11
13
|
validateOptions
|
|
12
14
|
} from "./shared.js";
|
|
13
15
|
import {
|
|
@@ -16,8 +18,6 @@ import {
|
|
|
16
18
|
resolveBarGrain
|
|
17
19
|
} from "../../grammar/bars/policy.js";
|
|
18
20
|
import { normalizeOffsetPadding } from "../../grammar/bars/geometry.js";
|
|
19
|
-
import { applyMaterializationPlan } from "../../materialization/dependencies.js";
|
|
20
|
-
import { planEncodingRematerialization } from "../../materialization/encodings.js";
|
|
21
21
|
|
|
22
22
|
const ENCODING_OPTIONS = Object.freeze([
|
|
23
23
|
"field", "target", "fieldType", "scale", "paddingInner", "paddingOuter"
|
|
@@ -36,27 +36,39 @@ function createOffsetEncoding(channel) {
|
|
|
36
36
|
const { id: target, dataset, layer } = resolveTarget(
|
|
37
37
|
this,
|
|
38
38
|
args.target,
|
|
39
|
-
["bar"],
|
|
40
|
-
"
|
|
39
|
+
["bar", "point", "rule"],
|
|
40
|
+
"offset-compatible mark"
|
|
41
41
|
);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
layer
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
43
|
+
if (layer.mark.type === "bar") {
|
|
44
|
+
const channels = resolveBarChannels(layer);
|
|
45
|
+
if (
|
|
46
|
+
resolveBarGrain(layer) === undefined ||
|
|
47
|
+
channels?.category !== parentChannel
|
|
48
|
+
) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`${operation} requires a complete bar with a ${parentChannel} category encoding.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (
|
|
54
|
+
layer.encoding?.color !== undefined &&
|
|
55
|
+
(resolveBarColorLayout(layer) !== "group" ||
|
|
56
|
+
layer.encoding.color.field !== args.field)
|
|
57
|
+
) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`${operation} field must match a grouped bar color field.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
const parent = layer.encoding?.[parentChannel];
|
|
64
|
+
if (
|
|
65
|
+
parent === undefined ||
|
|
66
|
+
!["nominal", "ordinal"].includes(parent.fieldType)
|
|
67
|
+
) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`${operation} requires a ${parentChannel} category encoding (categorical position).`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
readNominalField(dataset.values, args.field);
|
|
@@ -67,7 +79,7 @@ function createOffsetEncoding(channel) {
|
|
|
67
79
|
const scale = resolveOffsetScaleDefinition(this, requestedScale, channel);
|
|
68
80
|
if (Object.hasOwn(scale, "unknown")) {
|
|
69
81
|
throw new Error(
|
|
70
|
-
`${channel} scale unknown is not supported for
|
|
82
|
+
`${channel} scale unknown is not supported for offset positions.`
|
|
71
83
|
);
|
|
72
84
|
}
|
|
73
85
|
const padding = normalizeOffsetPadding(
|
|
@@ -76,39 +88,23 @@ function createOffsetEncoding(channel) {
|
|
|
76
88
|
channel
|
|
77
89
|
);
|
|
78
90
|
|
|
79
|
-
let next = this
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
.editSemantic({
|
|
85
|
-
property: `layer[${target}].encoding.${channel}.fieldType`,
|
|
86
|
-
value: fieldType
|
|
87
|
-
})
|
|
88
|
-
.editSemantic({
|
|
89
|
-
property: `layer[${target}].encoding.${channel}.scale`,
|
|
90
|
-
value: scale.id
|
|
91
|
-
})
|
|
92
|
-
._withMarkConfig(target, {
|
|
91
|
+
let next = setEncodingProperties(this, target, channel, {
|
|
92
|
+
field: args.field,
|
|
93
|
+
fieldType,
|
|
94
|
+
scale: scale.id
|
|
95
|
+
})._withMarkConfig(target, {
|
|
93
96
|
...this.markConfigs[target],
|
|
94
97
|
[channel]: padding
|
|
95
98
|
});
|
|
96
99
|
next = applyEncodingScale(next, scale, requestedScale, {
|
|
97
100
|
reassignment: layer.encoding?.[channel]?.scale === scale.id
|
|
98
101
|
});
|
|
99
|
-
if (layer.encoding?.color === undefined) {
|
|
102
|
+
if (layer.mark.type === "bar" && layer.encoding?.color === undefined) {
|
|
100
103
|
return next
|
|
101
104
|
.rematerializeScale({ id: scale.id })
|
|
102
105
|
.editGraphics({ target, property: "length", value: 0 });
|
|
103
106
|
}
|
|
104
|
-
return
|
|
105
|
-
next,
|
|
106
|
-
planEncodingRematerialization(next, {
|
|
107
|
-
target,
|
|
108
|
-
channel,
|
|
109
|
-
scale: scale.id
|
|
110
|
-
})
|
|
111
|
-
);
|
|
107
|
+
return rematerializeEncoding(next, target, channel, scale.id);
|
|
112
108
|
}
|
|
113
109
|
);
|
|
114
110
|
}
|
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
validateParallelRows
|
|
8
8
|
} from "../../grammar/parallelCoordinates.js";
|
|
9
9
|
import { resolvePositionScaleDefinition } from "../scales/definitions.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
applyEncodingScale,
|
|
12
|
+
resolveTarget,
|
|
13
|
+
setEncodingProperties
|
|
14
|
+
} from "./shared.js";
|
|
11
15
|
import { resolveParallelCoordinate } from "../coordinates/parallel.js";
|
|
12
16
|
|
|
13
17
|
const OPTIONS = Object.freeze([
|
|
@@ -76,15 +80,10 @@ export const encodeParallelCoordinates = action(
|
|
|
76
80
|
value: coordinate.id
|
|
77
81
|
});
|
|
78
82
|
}
|
|
79
|
-
next = next
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
})
|
|
84
|
-
.editSemantic({
|
|
85
|
-
property: `layer[${target}].encoding.parallel.missing`,
|
|
86
|
-
value: missing
|
|
87
|
-
});
|
|
83
|
+
next = setEncodingProperties(next, target, "parallel", {
|
|
84
|
+
dimensions: storedDimensions,
|
|
85
|
+
missing
|
|
86
|
+
});
|
|
88
87
|
if (key === undefined && layer.encoding?.parallel?.key !== undefined) {
|
|
89
88
|
next = next.editSemantic({
|
|
90
89
|
property: `layer[${target}].encoding.parallel.key`,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
canMaterializeLine
|
|
16
16
|
} from "../../materialization/marks/index.js";
|
|
17
17
|
import { findLayer } from "../../selectors/layers.js";
|
|
18
|
+
import { setEncodingProperties } from "./shared.js";
|
|
18
19
|
|
|
19
20
|
const ENCODE_OPTIONS = Object.freeze([
|
|
20
21
|
"target", "field", "fieldType", "order"
|
|
@@ -92,19 +93,11 @@ const encodePathOrder = action(
|
|
|
92
93
|
const dataset = assertPathOrderCompatible(this, layer, "encodePathOrder");
|
|
93
94
|
readQuantitativeField(dataset.values, field);
|
|
94
95
|
|
|
95
|
-
const next = this
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.editSemantic({
|
|
101
|
-
property: `layer[${layer.id}].encoding.pathOrder.fieldType`,
|
|
102
|
-
value: fieldType
|
|
103
|
-
})
|
|
104
|
-
.editSemantic({
|
|
105
|
-
property: `layer[${layer.id}].encoding.pathOrder.order`,
|
|
106
|
-
value: order
|
|
107
|
-
});
|
|
96
|
+
const next = setEncodingProperties(this, layer.id, "pathOrder", {
|
|
97
|
+
field,
|
|
98
|
+
fieldType,
|
|
99
|
+
order
|
|
100
|
+
});
|
|
108
101
|
return rematerializePath(next, findLayer(next, layer.id));
|
|
109
102
|
}
|
|
110
103
|
);
|
|
@@ -80,6 +80,28 @@ function applyArcThetaWeight(program, target, channel, previous, aggregate, weig
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function removeStaleArcThetaAggregate(
|
|
84
|
+
program,
|
|
85
|
+
target,
|
|
86
|
+
channel,
|
|
87
|
+
previous,
|
|
88
|
+
fieldType,
|
|
89
|
+
aggregate
|
|
90
|
+
) {
|
|
91
|
+
if (
|
|
92
|
+
channel !== "theta" ||
|
|
93
|
+
fieldType !== "quantitative" ||
|
|
94
|
+
aggregate !== undefined ||
|
|
95
|
+
previous?.aggregate === undefined
|
|
96
|
+
) {
|
|
97
|
+
return program;
|
|
98
|
+
}
|
|
99
|
+
return program.editSemantic({
|
|
100
|
+
property: `layer[${target}].encoding.theta.aggregate`,
|
|
101
|
+
remove: true
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
83
105
|
export function applyPositionSemantics(program, {
|
|
84
106
|
target,
|
|
85
107
|
channel,
|
|
@@ -124,6 +146,14 @@ export function applyPositionSemantics(program, {
|
|
|
124
146
|
value
|
|
125
147
|
});
|
|
126
148
|
}
|
|
149
|
+
next = removeStaleArcThetaAggregate(
|
|
150
|
+
next,
|
|
151
|
+
target,
|
|
152
|
+
channel,
|
|
153
|
+
previous,
|
|
154
|
+
fieldType,
|
|
155
|
+
aggregate
|
|
156
|
+
);
|
|
127
157
|
return applyArcThetaWeight(
|
|
128
158
|
next,
|
|
129
159
|
target,
|
|
@@ -2,7 +2,7 @@ import { validateAggregate } from "../../../../grammar/aggregate.js";
|
|
|
2
2
|
import { validateNonEmptyString } from "../../../../core/validation.js";
|
|
3
3
|
import { emptyPositionPolicy } from "./common.js";
|
|
4
4
|
|
|
5
|
-
export function resolveArcPositionPolicy({ channel, args, fieldType }) {
|
|
5
|
+
export function resolveArcPositionPolicy({ channel, args, fieldType, layer }) {
|
|
6
6
|
if (args.bin !== undefined) {
|
|
7
7
|
throw new Error(`Arc ${channel} encoding does not support bin.`);
|
|
8
8
|
}
|
|
@@ -10,6 +10,11 @@ export function resolveArcPositionPolicy({ channel, args, fieldType }) {
|
|
|
10
10
|
throw new Error(`Arc ${channel} encoding does not support stack.`);
|
|
11
11
|
}
|
|
12
12
|
if (channel === "radius") {
|
|
13
|
+
if (layer.encoding?.theta?.fieldType === "quantitative") {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Arc radius encoding cannot be combined with quantitative theta."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
13
18
|
if (args.aggregate !== undefined) {
|
|
14
19
|
throw new Error("Arc radius encoding does not support aggregate.");
|
|
15
20
|
}
|
|
@@ -18,8 +23,24 @@ export function resolveArcPositionPolicy({ channel, args, fieldType }) {
|
|
|
18
23
|
}
|
|
19
24
|
return emptyPositionPolicy();
|
|
20
25
|
}
|
|
26
|
+
if (fieldType === "quantitative") {
|
|
27
|
+
if (layer.encoding?.radius !== undefined) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"Quantitative arc theta cannot be combined with radius encoding."
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (args.aggregate !== undefined) {
|
|
33
|
+
throw new Error("Quantitative arc theta does not support aggregate.");
|
|
34
|
+
}
|
|
35
|
+
if (args.weight !== undefined) {
|
|
36
|
+
throw new Error("Quantitative arc theta does not support weight.");
|
|
37
|
+
}
|
|
38
|
+
return emptyPositionPolicy();
|
|
39
|
+
}
|
|
21
40
|
if (!["ordinal", "nominal"].includes(fieldType)) {
|
|
22
|
-
throw new Error(
|
|
41
|
+
throw new Error(
|
|
42
|
+
"Arc theta encoding requires a quantitative, ordinal, or nominal field."
|
|
43
|
+
);
|
|
23
44
|
}
|
|
24
45
|
if (args.aggregate === undefined) {
|
|
25
46
|
if (args.weight !== undefined) {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
"../../../../grammar/positionCompatibility.js";
|
|
1
|
+
import { resolveTickPositionPolicy } from "./tick.js";
|
|
3
2
|
import {
|
|
4
3
|
isCategoricalDensityPosition,
|
|
5
4
|
resolveAreaPositionPolicy
|
|
6
5
|
} from "./area.js";
|
|
7
|
-
import { resolveArcPositionPolicy } from "./arc.js";
|
|
8
6
|
import { resolveBarPositionPolicy } from "./bar.js";
|
|
9
7
|
import { resolveLinePositionPolicy } from "./line.js";
|
|
10
8
|
import { resolvePointPositionPolicy } from "./point.js";
|
|
11
9
|
import { resolveRulePositionPolicy } from "./rule.js";
|
|
12
|
-
import {
|
|
10
|
+
import { resolveArcPositionPolicy } from "./arc.js";
|
|
11
|
+
import { validatePositionFieldCompatibility } from
|
|
12
|
+
"../../../../grammar/positionCompatibility.js";
|
|
13
13
|
|
|
14
14
|
const POSITION_POLICIES = Object.freeze({
|
|
15
15
|
area: resolveAreaPositionPolicy,
|
|
@@ -2,17 +2,20 @@ import {
|
|
|
2
2
|
validateAggregate,
|
|
3
3
|
validateAggregateFieldType
|
|
4
4
|
} from "../../../../grammar/aggregate.js";
|
|
5
|
-
import { emptyPositionPolicy } from "./common.js";
|
|
5
|
+
import { emptyPositionPolicy, resolveBin } from "./common.js";
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
if (args.bin !== undefined) {
|
|
9
|
-
throw new Error(`Line ${channel} encoding does not support bin.`);
|
|
10
|
-
}
|
|
7
|
+
function rejectStack(args, channel) {
|
|
11
8
|
if (args.stack !== undefined) {
|
|
12
9
|
throw new Error(`Line ${channel} encoding does not support stack.`);
|
|
13
10
|
}
|
|
14
11
|
}
|
|
15
12
|
|
|
13
|
+
function rejectBin(args, channel) {
|
|
14
|
+
if (args.bin !== undefined) {
|
|
15
|
+
throw new Error(`Line ${channel} encoding does not support bin.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
16
19
|
export function resolveLinePositionPolicy({
|
|
17
20
|
program,
|
|
18
21
|
layer,
|
|
@@ -27,7 +30,8 @@ export function resolveLinePositionPolicy({
|
|
|
27
30
|
if (config.curve !== undefined && config.curve !== "linear") {
|
|
28
31
|
throw new Error("Polar line position currently requires curve \"linear\".");
|
|
29
32
|
}
|
|
30
|
-
|
|
33
|
+
rejectBin(args, channel);
|
|
34
|
+
rejectStack(args, channel);
|
|
31
35
|
if (args.aggregate !== undefined) {
|
|
32
36
|
throw new Error(`Line ${channel} encoding does not support aggregate.`);
|
|
33
37
|
}
|
|
@@ -44,6 +48,26 @@ export function resolveLinePositionPolicy({
|
|
|
44
48
|
);
|
|
45
49
|
|
|
46
50
|
if (channel === "x") {
|
|
51
|
+
rejectStack(args, "x");
|
|
52
|
+
if (args.aggregate !== undefined) {
|
|
53
|
+
throw new Error("Line x encoding does not support aggregate.");
|
|
54
|
+
}
|
|
55
|
+
if (args.bin !== undefined) {
|
|
56
|
+
if (fieldType !== "quantitative") {
|
|
57
|
+
throw new Error("Binned line x encoding requires a quantitative field.");
|
|
58
|
+
}
|
|
59
|
+
if (
|
|
60
|
+
layer.encoding?.y !== undefined &&
|
|
61
|
+
layer.encoding.y.aggregate === undefined
|
|
62
|
+
) {
|
|
63
|
+
throw new Error("Binned line x encoding requires an aggregate y encoding.");
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
bin: resolveBin(args.bin),
|
|
67
|
+
aggregate: undefined,
|
|
68
|
+
stack: undefined
|
|
69
|
+
};
|
|
70
|
+
}
|
|
47
71
|
const directPair =
|
|
48
72
|
layer.encoding?.y?.aggregate === undefined &&
|
|
49
73
|
["quantitative", "temporal"].includes(layer.encoding?.y?.fieldType) &&
|
|
@@ -61,17 +85,18 @@ export function resolveLinePositionPolicy({
|
|
|
61
85
|
"Line x encoding requires a temporal field or a compatible derived quantitative field."
|
|
62
86
|
);
|
|
63
87
|
}
|
|
64
|
-
if (args.aggregate !== undefined) {
|
|
65
|
-
throw new Error("Line x encoding does not support aggregate.");
|
|
66
|
-
}
|
|
67
|
-
rejectSharedOptions(args, "x");
|
|
68
88
|
return emptyPositionPolicy();
|
|
69
89
|
}
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
rejectBin(args, "y");
|
|
92
|
+
rejectStack(args, "y");
|
|
93
|
+
if (layer.encoding?.x?.bin !== undefined && args.aggregate === undefined) {
|
|
94
|
+
throw new Error("Binned line x encoding requires an aggregate y encoding.");
|
|
95
|
+
}
|
|
72
96
|
const quantitativePair =
|
|
73
97
|
fieldType === "quantitative" &&
|
|
74
|
-
layer.encoding?.x?.fieldType === "quantitative"
|
|
98
|
+
layer.encoding?.x?.fieldType === "quantitative" &&
|
|
99
|
+
layer.encoding.x.bin === undefined;
|
|
75
100
|
const prospectiveDirect =
|
|
76
101
|
args.aggregate === undefined &&
|
|
77
102
|
(fieldType === "temporal" ||
|
|
@@ -98,11 +123,16 @@ export function resolveLinePositionPolicy({
|
|
|
98
123
|
return emptyPositionPolicy();
|
|
99
124
|
}
|
|
100
125
|
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
126
|
+
if (layer.encoding?.x !== undefined) {
|
|
127
|
+
const compatibleAggregateX =
|
|
128
|
+
layer.encoding.x.fieldType === "temporal" ||
|
|
129
|
+
(layer.encoding.x.fieldType === "quantitative" &&
|
|
130
|
+
layer.encoding.x.bin !== undefined);
|
|
131
|
+
if (!compatibleAggregateX) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
"Aggregate line y encoding requires a temporal or binned quantitative x encoding."
|
|
134
|
+
);
|
|
135
|
+
}
|
|
106
136
|
}
|
|
107
137
|
|
|
108
138
|
const aggregate = validateAggregate(args.aggregate);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
2
2
|
import { getPositionCoordinateDefaults } from "../../../grammar/coordinates.js";
|
|
3
3
|
import {
|
|
4
|
+
isNominalValue,
|
|
4
5
|
readNominalField,
|
|
5
6
|
readQuantitativeField,
|
|
6
7
|
readScaleField,
|
|
@@ -21,7 +22,10 @@ import {
|
|
|
21
22
|
validateAggregateFieldValues,
|
|
22
23
|
} from "../../../grammar/aggregate.js";
|
|
23
24
|
import { normalizeRuleDatum } from "../../../grammar/rules.js";
|
|
24
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
readArcThetaValues,
|
|
27
|
+
readArcThetaWeights
|
|
28
|
+
} from "../../../grammar/arcs.js";
|
|
25
29
|
import { resolveMarkPositionPolicy } from "./policies/index.js";
|
|
26
30
|
import {
|
|
27
31
|
getPositionChannelDefinition,
|
|
@@ -33,6 +37,12 @@ const POSITION_ENCODING_OPTIONS = Object.freeze([
|
|
|
33
37
|
"aggregate", "bin", "stack", "weight"
|
|
34
38
|
]);
|
|
35
39
|
|
|
40
|
+
function inferRuleDatumFieldType(datum, operation) {
|
|
41
|
+
if (Number.isFinite(datum)) return "quantitative";
|
|
42
|
+
if (isNominalValue(datum)) return "nominal";
|
|
43
|
+
throw new Error(`${operation} datum requires an explicit fieldType.`);
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
function validateCoordinateFamily(layer, channel, operation) {
|
|
37
47
|
const family = getPositionChannelDefinition(channel).family;
|
|
38
48
|
const incompatible = positionChannelsForFamily(
|
|
@@ -96,7 +106,7 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
96
106
|
if (hasField === hasDatum) {
|
|
97
107
|
throw new Error(`${operation} requires exactly one of field or datum for a rule mark.`);
|
|
98
108
|
}
|
|
99
|
-
if (args.fieldType === undefined) {
|
|
109
|
+
if (hasField && args.fieldType === undefined) {
|
|
100
110
|
throw new Error(`${operation} requires fieldType for a rule mark.`);
|
|
101
111
|
}
|
|
102
112
|
} else if (hasDatum) {
|
|
@@ -104,10 +114,12 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
104
114
|
}
|
|
105
115
|
const previous = layer.encoding?.[channel];
|
|
106
116
|
const requestedFieldType = args.fieldType ?? previous?.fieldType ?? (
|
|
107
|
-
layer.mark.type === "
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
layer.mark.type === "rule" && hasDatum
|
|
118
|
+
? inferRuleDatumFieldType(args.datum, operation)
|
|
119
|
+
: layer.mark.type === "arc" && channel === "theta" &&
|
|
120
|
+
["count", "sum"].includes(args.aggregate)
|
|
121
|
+
? "nominal"
|
|
122
|
+
: "quantitative"
|
|
111
123
|
);
|
|
112
124
|
const fieldType = requestedFieldType === "nominal"
|
|
113
125
|
? "nominal"
|
|
@@ -119,9 +131,14 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
119
131
|
: args.field;
|
|
120
132
|
const datum = args.datum;
|
|
121
133
|
const effectiveArgs = { ...args };
|
|
134
|
+
const directQuantitativeArcTheta =
|
|
135
|
+
layer.mark.type === "arc" &&
|
|
136
|
+
channel === "theta" &&
|
|
137
|
+
fieldType === "quantitative";
|
|
122
138
|
for (const property of ["aggregate", "bin", "stack"]) {
|
|
123
139
|
if (!Object.hasOwn(effectiveArgs, property) && previous !== undefined &&
|
|
124
|
-
Object.hasOwn(previous, property)
|
|
140
|
+
Object.hasOwn(previous, property) &&
|
|
141
|
+
!(directQuantitativeArcTheta && property === "aggregate")) {
|
|
125
142
|
effectiveArgs[property] = previous[property];
|
|
126
143
|
}
|
|
127
144
|
}
|
|
@@ -169,6 +186,8 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
169
186
|
: fieldType === "temporal"
|
|
170
187
|
? { nice: true }
|
|
171
188
|
: { discreteType: "band" }
|
|
189
|
+
: layer.mark.type === "line" && channel === "x" && policy.bin !== undefined
|
|
190
|
+
? { nice: true, zero: false }
|
|
172
191
|
: ["ordinal", "nominal"].includes(fieldType)
|
|
173
192
|
? {
|
|
174
193
|
discreteType: ["arc", "rect"].includes(layer.mark.type)
|
|
@@ -177,6 +196,14 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
177
196
|
}
|
|
178
197
|
: {}
|
|
179
198
|
);
|
|
199
|
+
if (
|
|
200
|
+
layer.mark.type === "bar" &&
|
|
201
|
+
program.markConfigs[target]?.boxPlot === undefined &&
|
|
202
|
+
scale.type === "log" &&
|
|
203
|
+
policy.bin === undefined
|
|
204
|
+
) {
|
|
205
|
+
throw new Error('Bar scale type "log" does not support zero baselines.');
|
|
206
|
+
}
|
|
180
207
|
if (
|
|
181
208
|
layer.mark.type === "arc" &&
|
|
182
209
|
channel === "theta" &&
|
|
@@ -201,6 +228,8 @@ export function resolvePositionEncoding(program, channel, args, operation) {
|
|
|
201
228
|
policy.aggregate === "sum"
|
|
202
229
|
) {
|
|
203
230
|
readArcThetaWeights(dataset.values, policy.weight, layer.id);
|
|
231
|
+
} else if (directQuantitativeArcTheta) {
|
|
232
|
+
readArcThetaValues(dataset.values, field, layer.id);
|
|
204
233
|
} else if (program.markConfigs[target]?.boxPlot !== undefined) {
|
|
205
234
|
for (const [index, row] of dataset.values.entries()) {
|
|
206
235
|
const value = row[field];
|