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
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { POSITION_CHANNELS } from "../core/vocabulary.js";
|
|
2
1
|
import {
|
|
2
|
+
OFFSET_POSITION_CHANNELS,
|
|
3
|
+
POSITION_CHANNELS
|
|
4
|
+
} from "../core/vocabulary.js";
|
|
5
|
+
import {
|
|
6
|
+
mapOrdinalOffsetValues,
|
|
3
7
|
mapOrdinalPositionValues,
|
|
4
8
|
readNominalField,
|
|
5
9
|
readQuantitativeField,
|
|
6
10
|
readScaleField,
|
|
7
11
|
readTemporalField
|
|
8
12
|
} from "../grammar/scales/index.js";
|
|
13
|
+
import {
|
|
14
|
+
finiteMidpoint,
|
|
15
|
+
requireFiniteResult
|
|
16
|
+
} from "../grammar/numeric.js";
|
|
9
17
|
import { mapScaleConsumerValues } from "./scales/map.js";
|
|
10
18
|
|
|
11
19
|
export function resolveRowEncodingValues(program, layer, dataset, channel) {
|
|
@@ -27,7 +35,55 @@ export function resolveRowEncodingValues(program, layer, dataset, channel) {
|
|
|
27
35
|
: encoding.fieldType === "temporal"
|
|
28
36
|
? readTemporalField(dataset.values, encoding.field)
|
|
29
37
|
: readQuantitativeField(dataset.values, encoding.field);
|
|
38
|
+
if (categorical && OFFSET_POSITION_CHANNELS.includes(channel)) {
|
|
39
|
+
return mapOrdinalOffsetValues(values, scale);
|
|
40
|
+
}
|
|
30
41
|
return categorical && POSITION_CHANNELS.includes(channel)
|
|
31
42
|
? mapOrdinalPositionValues(values, scale)
|
|
32
43
|
: mapScaleConsumerValues(values, scale, channel);
|
|
33
44
|
}
|
|
45
|
+
|
|
46
|
+
export function applyOffsetPositionValues({
|
|
47
|
+
base,
|
|
48
|
+
offset,
|
|
49
|
+
parentScale,
|
|
50
|
+
offsetScale,
|
|
51
|
+
channel
|
|
52
|
+
}) {
|
|
53
|
+
if (base.length !== offset.length) {
|
|
54
|
+
throw new Error(`${channel} offset values must match the base position grain.`);
|
|
55
|
+
}
|
|
56
|
+
const midpoint = finiteMidpoint(offsetScale.range[0], offsetScale.range[1]);
|
|
57
|
+
const direction = Math.sign(parentScale.step) ||
|
|
58
|
+
Math.sign(parentScale.range[1] - parentScale.range[0]) || 1;
|
|
59
|
+
return Object.freeze(base.map((value, index) => {
|
|
60
|
+
if (!Number.isFinite(value) || !Number.isFinite(offset[index])) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return requireFiniteResult(
|
|
64
|
+
value + direction * (offset[index] - midpoint),
|
|
65
|
+
`${channel} offset position`
|
|
66
|
+
);
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function resolveRowPositionValues(program, layer, dataset, channel) {
|
|
71
|
+
const base = resolveRowEncodingValues(program, layer, dataset, channel);
|
|
72
|
+
if (base === undefined) return undefined;
|
|
73
|
+
const offsetChannel = channel === "x" ? "xOffset" : "yOffset";
|
|
74
|
+
const offsetEncoding = layer.encoding?.[offsetChannel];
|
|
75
|
+
if (offsetEncoding === undefined) return base;
|
|
76
|
+
const offset = resolveRowEncodingValues(
|
|
77
|
+
program,
|
|
78
|
+
layer,
|
|
79
|
+
dataset,
|
|
80
|
+
offsetChannel
|
|
81
|
+
);
|
|
82
|
+
return applyOffsetPositionValues({
|
|
83
|
+
base,
|
|
84
|
+
offset,
|
|
85
|
+
parentScale: program.resolvedScales[layer.encoding[channel].scale],
|
|
86
|
+
offsetScale: program.resolvedScales[offsetEncoding.scale],
|
|
87
|
+
channel
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -1,88 +1,14 @@
|
|
|
1
|
-
import { normalizeOffsetPadding } from "../../../grammar/bars/geometry.js";
|
|
2
1
|
import {
|
|
3
2
|
BAR_GRAINS,
|
|
4
3
|
resolveBarChannels,
|
|
5
4
|
resolveBarGrain
|
|
6
5
|
} from "../../../grammar/bars/policy.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
id,
|
|
14
|
-
allValues
|
|
15
|
-
}) {
|
|
16
|
-
const binnedBars = valuesByConsumer.filter(
|
|
17
|
-
({ consumer }) =>
|
|
18
|
-
consumer.layer.mark?.type === "bar" &&
|
|
19
|
-
consumer.encoding.bin !== undefined
|
|
20
|
-
);
|
|
21
|
-
if (binnedBars.length === 0) return undefined;
|
|
22
|
-
if (channel !== "x" || binnedBars.length !== valuesByConsumer.length) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
`Binned scale "${id}" cannot be shared with an unbinned consumer.`
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
const binDefinitions = new Set(
|
|
28
|
-
binnedBars.map(({ consumer }) => JSON.stringify(consumer.encoding.bin))
|
|
29
|
-
);
|
|
30
|
-
if (binDefinitions.size !== 1) {
|
|
31
|
-
throw new Error(`Binned scale "${id}" requires one shared bin definition.`);
|
|
32
|
-
}
|
|
33
|
-
return resolveHistogramBins({
|
|
34
|
-
values: allValues,
|
|
35
|
-
bin: binnedBars[0].consumer.encoding.bin,
|
|
36
|
-
domain: scale.domain,
|
|
37
|
-
nice: scale.nice ?? true,
|
|
38
|
-
zero: scale.zero ?? false
|
|
39
|
-
}).domain;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function resolveOffsetScalePolicy({
|
|
43
|
-
consumers,
|
|
44
|
-
resolvedScales,
|
|
45
|
-
markConfigs,
|
|
46
|
-
id,
|
|
47
|
-
channel
|
|
48
|
-
}) {
|
|
49
|
-
const parentChannel = channel === "xOffset" ? "x" : "y";
|
|
50
|
-
const bandwidths = consumers.map(consumer => {
|
|
51
|
-
if (parentChannel === "x" && consumer.layer.encoding?.x?.bin !== undefined) {
|
|
52
|
-
return 1;
|
|
53
|
-
}
|
|
54
|
-
const parentScaleId = consumer.layer.encoding?.[parentChannel]?.scale;
|
|
55
|
-
return resolvedScales[parentScaleId]?.bandwidth;
|
|
56
|
-
});
|
|
57
|
-
if (
|
|
58
|
-
bandwidths.some(value => !Number.isFinite(value)) ||
|
|
59
|
-
new Set(bandwidths).size !== 1
|
|
60
|
-
) {
|
|
61
|
-
throw new Error(
|
|
62
|
-
`${channel} scale "${id}" requires one shared resolved ${parentChannel} bandwidth.`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
const paddings = consumers.map(consumer => normalizeOffsetPadding(
|
|
66
|
-
markConfigs[consumer.layer.id]?.[channel],
|
|
67
|
-
undefined,
|
|
68
|
-
channel
|
|
69
|
-
));
|
|
70
|
-
const signatures = new Set(
|
|
71
|
-
paddings.map(padding => JSON.stringify(padding))
|
|
72
|
-
);
|
|
73
|
-
if (signatures.size !== 1) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`${channel} scale "${id}" requires one shared padding policy.`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
return {
|
|
79
|
-
parentBandwidth: bandwidths[0],
|
|
80
|
-
...paddings[0]
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function resolveTemporalBarBand(consumers, domain, range, values) {
|
|
85
|
-
const temporalBars = consumers.filter(consumer => {
|
|
6
|
+
import {
|
|
7
|
+
interpolateNumber,
|
|
8
|
+
inverseLerp
|
|
9
|
+
} from "../../../grammar/numeric.js";
|
|
10
|
+
export function resolveTemporalBarBand(valuesByConsumer, domain, range) {
|
|
11
|
+
const temporalBars = valuesByConsumer.filter(({ consumer }) => {
|
|
86
12
|
const channels = resolveBarChannels(consumer.layer);
|
|
87
13
|
return resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate &&
|
|
88
14
|
channels?.category === consumer.channel &&
|
|
@@ -90,43 +16,76 @@ export function resolveTemporalBarBand(consumers, domain, range, values) {
|
|
|
90
16
|
});
|
|
91
17
|
if (temporalBars.length === 0) return undefined;
|
|
92
18
|
const temporalFields = new Set(
|
|
93
|
-
temporalBars.map(consumer => consumer.encoding.field)
|
|
19
|
+
temporalBars.map(({ consumer }) => consumer.encoding.field)
|
|
94
20
|
);
|
|
95
|
-
const
|
|
96
|
-
consumer.layer.mark?.type
|
|
97
|
-
|
|
98
|
-
|
|
21
|
+
const compatibleConsumers = valuesByConsumer.filter(({ consumer }) =>
|
|
22
|
+
["line", "rule", "text"].includes(consumer.layer.mark?.type) &&
|
|
23
|
+
consumer.encoding.fieldType === "temporal" &&
|
|
24
|
+
temporalFields.has(consumer.encoding.field)
|
|
99
25
|
);
|
|
100
|
-
if (
|
|
26
|
+
if (
|
|
27
|
+
temporalBars.length + compatibleConsumers.length !==
|
|
28
|
+
valuesByConsumer.length
|
|
29
|
+
) {
|
|
101
30
|
throw new Error(
|
|
102
|
-
"A temporal bar position scale requires compatible bar or
|
|
31
|
+
"A temporal bar position scale requires compatible bar, line, rule, or text consumers of one field."
|
|
103
32
|
);
|
|
104
33
|
}
|
|
34
|
+
const values = temporalBars.flatMap(item => item.values);
|
|
105
35
|
const ordered = [...new Set(values)].sort((left, right) => left - right);
|
|
106
36
|
if (ordered.length < 2) {
|
|
107
37
|
throw new Error("Temporal bar position requires at least two distinct values.");
|
|
108
38
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
39
|
+
let minimumGap = Infinity;
|
|
40
|
+
for (let index = 1; index < ordered.length; index += 1) {
|
|
41
|
+
minimumGap = Math.min(minimumGap, ordered[index] - ordered[index - 1]);
|
|
42
|
+
}
|
|
112
43
|
const domainSpan = Math.abs(domain[1] - domain[0]);
|
|
113
44
|
if (!(minimumGap > 0) || !(domainSpan > 0)) {
|
|
114
45
|
throw new Error("Temporal bar position requires an increasing time domain.");
|
|
115
46
|
}
|
|
116
47
|
const direction = Math.sign(range[1] - range[0]) || 1;
|
|
117
|
-
const
|
|
48
|
+
const bandwidthFraction = minimumGap / (domainSpan + minimumGap);
|
|
49
|
+
const directBandwidth = Math.abs(range[1] - range[0]) * minimumGap /
|
|
118
50
|
(domainSpan + minimumGap);
|
|
119
|
-
const
|
|
120
|
-
range[0] + direction *
|
|
121
|
-
range[1] - direction *
|
|
51
|
+
const directRange = [
|
|
52
|
+
range[0] + direction * directBandwidth / 2,
|
|
53
|
+
range[1] - direction * directBandwidth / 2
|
|
122
54
|
];
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
55
|
+
const stable = !Number.isFinite(directBandwidth) ||
|
|
56
|
+
!directRange.every(Number.isFinite);
|
|
57
|
+
const estimatedBandwidth = stable
|
|
58
|
+
? Math.abs(
|
|
59
|
+
interpolateNumber(range[0], range[1], bandwidthFraction) - range[0]
|
|
60
|
+
)
|
|
61
|
+
: directBandwidth;
|
|
62
|
+
const resolvedRange = stable
|
|
63
|
+
? [
|
|
64
|
+
interpolateNumber(range[0], range[1], bandwidthFraction / 2),
|
|
65
|
+
interpolateNumber(range[0], range[1], 1 - bandwidthFraction / 2)
|
|
66
|
+
]
|
|
67
|
+
: directRange;
|
|
68
|
+
const resolvedSpan = resolvedRange[1] - resolvedRange[0];
|
|
69
|
+
const positions = ordered.map(value => {
|
|
70
|
+
const direct = resolvedRange[0] +
|
|
71
|
+
(value - domain[0]) / (domain[1] - domain[0]) * resolvedSpan;
|
|
72
|
+
return Number.isFinite(direct)
|
|
73
|
+
? direct
|
|
74
|
+
: interpolateNumber(
|
|
75
|
+
resolvedRange[0],
|
|
76
|
+
resolvedRange[1],
|
|
77
|
+
inverseLerp(value, domain[0], domain[1])
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
let bandwidth = Infinity;
|
|
81
|
+
for (let index = 1; index < positions.length; index += 1) {
|
|
82
|
+
bandwidth = Math.min(
|
|
83
|
+
bandwidth,
|
|
84
|
+
Math.abs(positions[index] - positions[index - 1])
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (![estimatedBandwidth, bandwidth, ...resolvedRange].every(Number.isFinite)) {
|
|
88
|
+
throw new RangeError("Temporal bar position exceeds the finite numeric range.");
|
|
89
|
+
}
|
|
131
90
|
return { bandwidth, range: resolvedRange };
|
|
132
91
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolveHistogramBins } from "../../../grammar/histogram.js";
|
|
2
|
+
|
|
3
|
+
export function resolveBinnedPositionDomain({
|
|
4
|
+
valuesByConsumer,
|
|
5
|
+
channel,
|
|
6
|
+
scale,
|
|
7
|
+
id,
|
|
8
|
+
allValues
|
|
9
|
+
}) {
|
|
10
|
+
const binnedPositions = valuesByConsumer.filter(
|
|
11
|
+
({ consumer }) =>
|
|
12
|
+
["bar", "line"].includes(consumer.layer.mark?.type) &&
|
|
13
|
+
consumer.encoding.bin !== undefined
|
|
14
|
+
);
|
|
15
|
+
if (binnedPositions.length === 0) return undefined;
|
|
16
|
+
if (channel !== "x" || binnedPositions.length !== valuesByConsumer.length) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`Binned scale "${id}" cannot be shared with an unbinned consumer.`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const binDefinitions = new Set(
|
|
22
|
+
binnedPositions.map(({ consumer }) => JSON.stringify(consumer.encoding.bin))
|
|
23
|
+
);
|
|
24
|
+
if (binDefinitions.size !== 1) {
|
|
25
|
+
throw new Error(`Binned scale "${id}" requires one shared bin definition.`);
|
|
26
|
+
}
|
|
27
|
+
return resolveHistogramBins({
|
|
28
|
+
values: allValues,
|
|
29
|
+
bin: binnedPositions[0].consumer.encoding.bin,
|
|
30
|
+
domain: scale.domain,
|
|
31
|
+
nice: scale.nice ?? true,
|
|
32
|
+
zero: scale.zero ?? false
|
|
33
|
+
}).domain;
|
|
34
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { normalizeOffsetPadding } from "../../../grammar/bars/geometry.js";
|
|
2
|
+
|
|
3
|
+
function resolveParentSlot(consumer, resolvedScales, parentChannel) {
|
|
4
|
+
if (parentChannel === "x" && consumer.layer.encoding?.x?.bin !== undefined) {
|
|
5
|
+
return 1;
|
|
6
|
+
}
|
|
7
|
+
const parentScaleId = consumer.layer.encoding?.[parentChannel]?.scale;
|
|
8
|
+
const parentScale = resolvedScales[parentScaleId];
|
|
9
|
+
if (Number.isFinite(parentScale?.bandwidth) && parentScale.bandwidth > 0) {
|
|
10
|
+
return parentScale.bandwidth;
|
|
11
|
+
}
|
|
12
|
+
if (parentScale?.type === "point" && Number.isFinite(parentScale.step)) {
|
|
13
|
+
return Math.abs(parentScale.step);
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveOffsetScalePolicy({
|
|
19
|
+
consumers,
|
|
20
|
+
resolvedScales,
|
|
21
|
+
markConfigs,
|
|
22
|
+
id,
|
|
23
|
+
channel
|
|
24
|
+
}) {
|
|
25
|
+
const parentChannel = channel === "xOffset" ? "x" : "y";
|
|
26
|
+
const slots = consumers.map(consumer =>
|
|
27
|
+
resolveParentSlot(consumer, resolvedScales, parentChannel)
|
|
28
|
+
);
|
|
29
|
+
if (
|
|
30
|
+
slots.some(value => !Number.isFinite(value) || value <= 0) ||
|
|
31
|
+
new Set(slots).size !== 1
|
|
32
|
+
) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`${channel} scale "${id}" requires one shared resolved ${parentChannel} categorical slot.`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const paddings = consumers.map(consumer => normalizeOffsetPadding(
|
|
38
|
+
markConfigs[consumer.layer.id]?.[channel],
|
|
39
|
+
undefined,
|
|
40
|
+
channel
|
|
41
|
+
));
|
|
42
|
+
const signatures = new Set(
|
|
43
|
+
paddings.map(padding => JSON.stringify(padding))
|
|
44
|
+
);
|
|
45
|
+
if (signatures.size !== 1) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`${channel} scale "${id}" requires one shared padding policy.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
parentBandwidth: slots[0],
|
|
52
|
+
...paddings[0]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
resolveContinuousDomain,
|
|
4
4
|
resolveTransformedDomain
|
|
5
5
|
} from "../../../grammar/scales/index.js";
|
|
6
|
+
import { numericExtent } from "../../../grammar/numeric.js";
|
|
6
7
|
|
|
7
8
|
export function resolveSeriesLayoutDomain({
|
|
8
9
|
id,
|
|
@@ -25,9 +26,14 @@ export function resolveSeriesLayoutDomain({
|
|
|
25
26
|
const compatibleDirect = directConsumers.every(({ consumer }) =>
|
|
26
27
|
layoutConsumers.every(({ consumer: layoutConsumer }) => {
|
|
27
28
|
const aggregate = layoutConsumer.encoding.aggregate;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const directEncoding = consumer.encoding;
|
|
30
|
+
return directEncoding.fieldType === "quantitative" && (
|
|
31
|
+
Object.hasOwn(directEncoding, "datum") ||
|
|
32
|
+
(
|
|
33
|
+
aggregate !== "count" &&
|
|
34
|
+
directEncoding.field === layoutConsumer.encoding.field
|
|
35
|
+
)
|
|
36
|
+
);
|
|
31
37
|
})
|
|
32
38
|
);
|
|
33
39
|
if (!compatibleDirect) {
|
|
@@ -41,9 +47,9 @@ export function resolveSeriesLayoutDomain({
|
|
|
41
47
|
...directConsumers.flatMap(item => item.values)
|
|
42
48
|
];
|
|
43
49
|
if (layout === "center" && scale.domain !== "auto") {
|
|
44
|
-
const minimum =
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
50
|
+
const [minimum, maximum] = numericExtent(values);
|
|
51
|
+
const [domainMinimum, domainMaximum] = numericExtent(scale.domain);
|
|
52
|
+
if (domainMinimum > minimum || domainMaximum < maximum) {
|
|
47
53
|
throw new Error(
|
|
48
54
|
`Center layout scale "${id}" explicit domain must contain every centered bound.`
|
|
49
55
|
);
|
|
@@ -52,7 +58,7 @@ export function resolveSeriesLayoutDomain({
|
|
|
52
58
|
if (
|
|
53
59
|
["group", "overlay"].includes(layout) &&
|
|
54
60
|
scale.domain !== "auto" &&
|
|
55
|
-
(
|
|
61
|
+
(numericExtent(scale.domain)[0] > 0 || numericExtent(scale.domain)[1] < 0)
|
|
56
62
|
) {
|
|
57
63
|
throw new Error(
|
|
58
64
|
`Series layout scale "${id}" requires an explicit domain containing zero.`
|
|
@@ -27,11 +27,9 @@ import {
|
|
|
27
27
|
validateTimeScaleType
|
|
28
28
|
} from "../../grammar/scales/index.js";
|
|
29
29
|
import { resolveArcAutoPositionRange } from "./policies/arc.js";
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
resolveTemporalBarBand
|
|
34
|
-
} from "./policies/bar.js";
|
|
30
|
+
import { resolveTemporalBarBand } from "./policies/bar.js";
|
|
31
|
+
import { resolveOffsetScalePolicy } from "./policies/offset.js";
|
|
32
|
+
import { resolveBinnedPositionDomain } from "./policies/binnedPosition.js";
|
|
35
33
|
import { resolveSeriesLayoutDomain } from "./policies/series.js";
|
|
36
34
|
import { OFFSET_POSITION_CHANNELS } from "../../core/vocabulary.js";
|
|
37
35
|
|
|
@@ -223,7 +221,7 @@ export function resolveScaleMaterialization({
|
|
|
223
221
|
}
|
|
224
222
|
const binnedDomain = isDiscretizedColor
|
|
225
223
|
? undefined
|
|
226
|
-
:
|
|
224
|
+
: resolveBinnedPositionDomain({
|
|
227
225
|
valuesByConsumer,
|
|
228
226
|
channel,
|
|
229
227
|
scale,
|
|
@@ -331,10 +329,9 @@ export function resolveScaleMaterialization({
|
|
|
331
329
|
}
|
|
332
330
|
if (scale.type === "time" && ["x", "y"].includes(channel)) {
|
|
333
331
|
const temporalBarBand = resolveTemporalBarBand(
|
|
334
|
-
|
|
332
|
+
valuesByConsumer,
|
|
335
333
|
domain,
|
|
336
|
-
range
|
|
337
|
-
allValues
|
|
334
|
+
range
|
|
338
335
|
);
|
|
339
336
|
if (temporalBarBand !== undefined) {
|
|
340
337
|
resolvedScale = {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { deriveArcSectors } from "../../../grammar/arcs.js";
|
|
2
|
+
import { resolvePolarPoint } from "../../../grammar/polarPaths.js";
|
|
2
3
|
import { resolvePolarFrame } from "../../../grammar/polar.js";
|
|
3
4
|
import { resolveGraphicBounds } from "../../../layout/canvas.js";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
concreteProperties,
|
|
7
|
+
finalizeItems,
|
|
8
|
+
uniqueFields
|
|
9
|
+
} from "./common.js";
|
|
5
10
|
|
|
6
11
|
export function resolveArcItems(program, layer, dataset) {
|
|
7
12
|
const thetaScale = program.resolvedScales[layer.encoding?.theta?.scale];
|
|
@@ -13,7 +18,8 @@ export function resolveArcItems(program, layer, dataset) {
|
|
|
13
18
|
frame,
|
|
14
19
|
innerRadiusRatio: program.markConfigs[layer.id]?.innerRadius ?? 0
|
|
15
20
|
});
|
|
16
|
-
const
|
|
21
|
+
const graphic = program.graphicSpec.objects[layer.id];
|
|
22
|
+
const definitions = derived.sectors.map((sector, index) => {
|
|
17
23
|
const members = sector.sourceIndices.map(index => dataset.values[index]);
|
|
18
24
|
return {
|
|
19
25
|
fields: uniqueFields(members),
|
|
@@ -22,6 +28,14 @@ export function resolveArcItems(program, layer, dataset) {
|
|
|
22
28
|
...(sector.radius === undefined ? {} : { radius: sector.radius }),
|
|
23
29
|
...(sector.color === undefined ? {} : { color: sector.color })
|
|
24
30
|
},
|
|
31
|
+
properties: {
|
|
32
|
+
...concreteProperties(graphic.items[index]?.properties),
|
|
33
|
+
...resolvePolarPoint(
|
|
34
|
+
frame,
|
|
35
|
+
(sector.startTheta + sector.endTheta) / 2,
|
|
36
|
+
(sector.innerRadius + sector.outerRadius) / 2
|
|
37
|
+
)
|
|
38
|
+
},
|
|
25
39
|
members
|
|
26
40
|
};
|
|
27
41
|
});
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
resolveBarOffsetChannel,
|
|
7
7
|
resolveBarGrain
|
|
8
8
|
} from "../../../grammar/bars/policy.js";
|
|
9
|
+
import { numericExtent } from "../../../grammar/numeric.js";
|
|
9
10
|
import { layoutSeriesPartition } from "../../../grammar/seriesLayout.js";
|
|
10
11
|
import {
|
|
11
12
|
readNominalField,
|
|
@@ -215,14 +216,15 @@ function barStackDefinitions(layer, barGrain, definitions) {
|
|
|
215
216
|
`Bar mark "${layer.id}" stack ${index} has no resolved semantic endpoints.`
|
|
216
217
|
);
|
|
217
218
|
}
|
|
219
|
+
const [minimum, maximum] = numericExtent(endpoints);
|
|
218
220
|
const members = grouped.flatMap(definition => definition.members);
|
|
219
221
|
return {
|
|
220
222
|
key: itemKey(layer, "stack", index),
|
|
221
223
|
fields: uniqueFields(members),
|
|
222
224
|
channels: {
|
|
223
225
|
...sharedChannels(grouped),
|
|
224
|
-
[measure]:
|
|
225
|
-
[secondary]:
|
|
226
|
+
[measure]: minimum,
|
|
227
|
+
[secondary]: maximum
|
|
226
228
|
},
|
|
227
229
|
members,
|
|
228
230
|
graphicIndices: entries.map(entry => entry.index)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { cloneAndFreeze, isPlainObject } from "../../../core/immutable.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
MARK_GRAPHIC_PROPERTIES,
|
|
4
|
+
selectMarkItemKeys
|
|
5
|
+
} from "../../../grammar/markSelection.js";
|
|
3
6
|
import { unionConcreteGraphicBounds } from
|
|
4
7
|
"../../../grammar/schemas/graphicBounds.js";
|
|
5
8
|
|
|
@@ -129,3 +132,20 @@ export function finalizeItems(program, layer, grain, definitions, graphicType) {
|
|
|
129
132
|
});
|
|
130
133
|
}));
|
|
131
134
|
}
|
|
135
|
+
|
|
136
|
+
export function reorderHighlightedItems(program, layer, definitions) {
|
|
137
|
+
let ordered = definitions;
|
|
138
|
+
for (const config of Object.values(
|
|
139
|
+
program.materializationConfigs.highlights ?? {}
|
|
140
|
+
)) {
|
|
141
|
+
if (config.target !== layer.id || config.bringToFront !== true) continue;
|
|
142
|
+
const selection = program.materializationConfigs.selections?.[config.selection];
|
|
143
|
+
if (selection?.target !== layer.id) continue;
|
|
144
|
+
const selected = new Set(selectMarkItemKeys(ordered, selection.selector));
|
|
145
|
+
ordered = [
|
|
146
|
+
...ordered.filter(definition => !selected.has(definition.key)),
|
|
147
|
+
...ordered.filter(definition => selected.has(definition.key))
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
return ordered;
|
|
151
|
+
}
|
|
@@ -3,8 +3,12 @@ import {
|
|
|
3
3
|
deriveAreaSeries,
|
|
4
4
|
deriveDensityAreaSeries
|
|
5
5
|
} from "../../../grammar/areaSeries.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
deriveLineSeries,
|
|
8
|
+
resolveLineBins
|
|
9
|
+
} from "../../../grammar/lineSeries.js";
|
|
7
10
|
import { findUpstreamTransform } from "../../dataProvenance.js";
|
|
11
|
+
import { requireSemanticScale } from "../../../selectors/scales.js";
|
|
8
12
|
import {
|
|
9
13
|
channelMapFromRow,
|
|
10
14
|
finalizeItems,
|
|
@@ -61,7 +65,20 @@ export function resolveLineItems(program, layer, dataset) {
|
|
|
61
65
|
});
|
|
62
66
|
return finalizeItems(program, layer, "row", definitions, "path");
|
|
63
67
|
}
|
|
64
|
-
const
|
|
68
|
+
const x = layer.encoding?.x;
|
|
69
|
+
const derived = deriveLineSeries(
|
|
70
|
+
dataset.values,
|
|
71
|
+
layer,
|
|
72
|
+
x?.bin === undefined
|
|
73
|
+
? undefined
|
|
74
|
+
: {
|
|
75
|
+
xBinBoundaries: resolveLineBins(
|
|
76
|
+
dataset.values,
|
|
77
|
+
layer,
|
|
78
|
+
requireSemanticScale(program, x.scale)
|
|
79
|
+
).boundaries
|
|
80
|
+
}
|
|
81
|
+
);
|
|
65
82
|
return finalizeItems(
|
|
66
83
|
program,
|
|
67
84
|
layer,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { selectMarkItemKeys } from "../../../grammar/markSelection.js";
|
|
2
1
|
import {
|
|
3
2
|
channelMapFromRow,
|
|
4
3
|
concreteProperties,
|
|
5
4
|
finalizeItems,
|
|
6
5
|
itemKey,
|
|
7
|
-
ownFields
|
|
6
|
+
ownFields,
|
|
7
|
+
reorderHighlightedItems
|
|
8
8
|
} from "./common.js";
|
|
9
9
|
|
|
10
10
|
export function resolvePointItems(program, layer, dataset) {
|
|
@@ -22,30 +22,18 @@ export function resolvePointItems(program, layer, dataset) {
|
|
|
22
22
|
) {
|
|
23
23
|
throw new Error(`Point mark "${layer.id}" is incomplete for selection.`);
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
const definitions = dataset.values.map((row, index) => ({
|
|
26
26
|
key: itemKey(layer, "point", index),
|
|
27
27
|
fields: ownFields(row),
|
|
28
28
|
channels: channelMapFromRow(row, layer),
|
|
29
29
|
properties: concreteProperties(graphic.items[index]?.properties),
|
|
30
30
|
members: [row]
|
|
31
31
|
}));
|
|
32
|
-
for (const config of Object.values(
|
|
33
|
-
program.materializationConfigs.highlights ?? {}
|
|
34
|
-
)) {
|
|
35
|
-
if (config.target !== layer.id || config.bringToFront !== true) continue;
|
|
36
|
-
const selection = program.materializationConfigs.selections?.[config.selection];
|
|
37
|
-
if (selection?.target !== layer.id) continue;
|
|
38
|
-
const selected = new Set(selectMarkItemKeys(definitions, selection.selector));
|
|
39
|
-
definitions = [
|
|
40
|
-
...definitions.filter(definition => !selected.has(definition.key)),
|
|
41
|
-
...definitions.filter(definition => selected.has(definition.key))
|
|
42
|
-
];
|
|
43
|
-
}
|
|
44
32
|
return finalizeItems(
|
|
45
33
|
program,
|
|
46
34
|
layer,
|
|
47
35
|
"point",
|
|
48
|
-
definitions,
|
|
36
|
+
reorderHighlightedItems(program, layer, definitions),
|
|
49
37
|
program.graphicSpec.objects[layer.id]?.type
|
|
50
38
|
);
|
|
51
39
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { selectMarkItemKeys } from "../../../grammar/markSelection.js";
|
|
2
1
|
import {
|
|
3
2
|
channelMapFromRow,
|
|
4
3
|
concreteProperties,
|
|
5
4
|
finalizeItems,
|
|
6
5
|
itemKey,
|
|
7
|
-
ownFields
|
|
6
|
+
ownFields,
|
|
7
|
+
reorderHighlightedItems
|
|
8
8
|
} from "./common.js";
|
|
9
9
|
|
|
10
10
|
export function resolveTickItems(program, layer, dataset) {
|
|
@@ -15,25 +15,17 @@ export function resolveTickItems(program, layer, dataset) {
|
|
|
15
15
|
if (!Array.isArray(graphic?.items) || !completePosition) {
|
|
16
16
|
throw new Error(`Tick mark "${layer.id}" is incomplete for selection.`);
|
|
17
17
|
}
|
|
18
|
-
let definitions =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const selection = program.materializationConfigs.selections?.[config.selection];
|
|
30
|
-
if (selection?.target !== layer.id) continue;
|
|
31
|
-
const selected = new Set(selectMarkItemKeys(definitions, selection.selector));
|
|
32
|
-
definitions = [
|
|
33
|
-
...definitions.filter(definition => !selected.has(definition.key)),
|
|
34
|
-
...definitions.filter(definition => selected.has(definition.key))
|
|
35
|
-
];
|
|
36
|
-
}
|
|
18
|
+
let definitions = reorderHighlightedItems(
|
|
19
|
+
program,
|
|
20
|
+
layer,
|
|
21
|
+
dataset.values.map((row, index) => ({
|
|
22
|
+
key: itemKey(layer, "tick", index),
|
|
23
|
+
fields: ownFields(row),
|
|
24
|
+
channels: channelMapFromRow(row, layer),
|
|
25
|
+
properties: concreteProperties(graphic.items[index]?.properties),
|
|
26
|
+
members: [row]
|
|
27
|
+
}))
|
|
28
|
+
);
|
|
37
29
|
definitions = definitions.map((definition, index) => ({
|
|
38
30
|
...definition,
|
|
39
31
|
properties: concreteProperties(graphic.items[index]?.properties)
|