ggaction 0.0.11 → 0.0.13
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 +80 -0
- package/README.md +3 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/action-cards.schema.json +1 -1
- package/knowledge/extension-authoring.md +2 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +4 -2
- package/src/actions/basic.js +2 -0
- package/src/actions/boxPlots/materialize.js +2 -1
- package/src/actions/canvas/actions.js +3 -1
- package/src/actions/canvas/fitting.js +211 -0
- package/src/actions/canvas/index.js +2 -0
- package/src/actions/categoryOrder/index.js +3 -2
- package/src/actions/charts/area.js +47 -0
- package/src/actions/charts/bar.js +11 -5
- package/src/actions/charts/beeswarm.js +70 -0
- package/src/actions/charts/density.js +44 -0
- package/src/actions/charts/ecdf.js +183 -0
- package/src/actions/charts/endpoints.js +451 -0
- package/src/actions/charts/heatmap.js +2 -2
- package/src/actions/charts/histogram.js +1 -1
- package/src/actions/charts/horizon.js +71 -0
- package/src/actions/charts/index.js +37 -0
- package/src/actions/charts/interval-regression.js +169 -0
- package/src/actions/charts/line.js +5 -5
- package/src/actions/charts/parallel.js +1 -1
- package/src/actions/charts/pie.js +44 -0
- package/src/actions/charts/polar.js +123 -0
- package/src/actions/charts/radar.js +299 -0
- package/src/actions/charts/radial.js +42 -0
- package/src/actions/charts/raincloud.js +422 -0
- package/src/actions/charts/rug-strip.js +279 -0
- package/src/actions/charts/scatter.js +9 -3
- package/src/actions/charts/shared.js +69 -10
- package/src/actions/composition/actions.js +118 -1
- package/src/actions/data/bin.js +29 -0
- package/src/actions/data/computed.js +24 -0
- package/src/actions/data/derived.js +66 -0
- package/src/actions/data/ecdf.js +38 -0
- package/src/actions/data/filter.js +212 -28
- package/src/actions/data/fold.js +24 -0
- package/src/actions/data/index.js +24 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/intervalEdit.js +86 -4
- package/src/actions/data/regression.js +5 -2
- package/src/actions/data/stack.js +26 -0
- package/src/actions/data/summary.js +26 -0
- package/src/actions/data/timeUnit.js +1 -1
- package/src/actions/encodings/appearance.js +32 -13
- package/src/actions/encodings/barWidth.js +8 -0
- package/src/actions/encodings/basic.js +2 -0
- package/src/actions/encodings/color/continuous.js +19 -59
- package/src/actions/encodings/color/index.js +28 -22
- package/src/actions/encodings/color/layout.js +3 -97
- package/src/actions/encodings/color/policy.js +5 -8
- package/src/actions/encodings/density/resolve.js +37 -5
- package/src/actions/encodings/density.js +120 -33
- package/src/actions/encodings/horizon/resolve.js +8 -4
- package/src/actions/encodings/horizon.js +18 -8
- package/src/actions/encodings/index.js +2 -0
- package/src/actions/encodings/offset.js +33 -27
- package/src/actions/encodings/parallel.js +2 -8
- package/src/actions/encodings/position/apply.js +40 -4
- package/src/actions/encodings/position/index.js +43 -5
- package/src/actions/encodings/position/policies/arc.js +37 -2
- package/src/actions/encodings/position/policies/area.js +4 -42
- package/src/actions/encodings/position/policies/bar.js +48 -13
- package/src/actions/encodings/position/resolve.js +79 -24
- package/src/actions/encodings/ranged.js +123 -97
- package/src/actions/encodings/remove.js +37 -32
- package/src/actions/encodings/ruleAppearance.js +23 -9
- package/src/actions/encodings/seriesLayout.js +104 -0
- package/src/actions/encodings/shared.js +25 -23
- package/src/actions/encodings/strokeDash.js +9 -6
- package/src/actions/encodings/temporal.js +7 -0
- package/src/actions/encodings/text.js +30 -15
- package/src/actions/errorBands/create.js +18 -5
- package/src/actions/errorBands/edit.js +341 -21
- package/src/actions/errorBars/create.js +153 -11
- package/src/actions/errorBars/edit.js +365 -19
- package/src/actions/facets/actions.js +410 -12
- package/src/actions/facets/derive.js +57 -8
- package/src/actions/gradientPlots/components.js +33 -11
- package/src/actions/gradientPlots/materialize.js +5 -7
- package/src/actions/guides/applicability.js +20 -17
- package/src/actions/guides/axes/axes.js +4 -3
- package/src/actions/guides/axes/axis.js +18 -18
- package/src/actions/guides/axes/components.js +43 -0
- package/src/actions/guides/axes/edit.js +16 -51
- package/src/actions/guides/axes/labels.js +129 -19
- package/src/actions/guides/axes/lines.js +7 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
- package/src/actions/guides/axes/parallel/policy.js +137 -0
- package/src/actions/guides/axes/parallel/resolve.js +44 -23
- package/src/actions/guides/axes/parallel.js +93 -217
- package/src/actions/guides/axes/policy.js +21 -67
- package/src/actions/guides/axes/tickGroups.js +14 -8
- package/src/actions/guides/axes/ticks.js +5 -4
- package/src/actions/guides/axes/titles.js +21 -8
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facade.js +106 -0
- package/src/actions/guides/facadeAxes.js +200 -0
- package/src/actions/guides/facadeLegend.js +63 -0
- package/src/actions/guides/grids/grid.js +37 -33
- package/src/actions/guides/grids/resolve.js +10 -2
- package/src/actions/guides/guides.js +35 -28
- package/src/actions/guides/legends/categorical/actions.js +193 -249
- package/src/actions/guides/legends/categorical/components.js +4 -5
- package/src/actions/guides/legends/categorical/layout.js +36 -349
- package/src/actions/guides/legends/categorical/options.js +30 -14
- package/src/actions/guides/legends/categorical/recipes.js +14 -0
- package/src/actions/guides/legends/categorical/resolve.js +21 -10
- package/src/actions/guides/legends/categorical/symbols.js +2 -3
- package/src/actions/guides/legends/continuous/common.js +69 -14
- package/src/actions/guides/legends/continuous/gradient.js +111 -83
- package/src/actions/guides/legends/continuous/index.js +6 -2
- package/src/actions/guides/legends/continuous/interval.js +71 -92
- package/src/actions/guides/legends/continuous/opacity.js +105 -84
- package/src/actions/guides/legends/creation.js +27 -0
- package/src/actions/guides/legends/edit.js +198 -66
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/lane.js +98 -86
- package/src/actions/guides/legends/lifecycle.js +132 -0
- package/src/actions/guides/legends/remove.js +31 -76
- package/src/actions/guides/legends/size.js +149 -105
- package/src/actions/guides/legends/strokeWidth.js +106 -64
- package/src/actions/guides/legends/target.js +12 -0
- package/src/actions/guides/legends/transition.js +43 -0
- package/src/actions/guides/polar/axes/facade.js +77 -62
- package/src/actions/guides/polar/axes/labels.js +5 -4
- package/src/actions/guides/polar/axes/lines.js +2 -1
- package/src/actions/guides/polar/axes/shared.js +6 -0
- package/src/actions/guides/polar/axes/ticks.js +5 -4
- package/src/actions/guides/polar/axes/titles.js +5 -4
- package/src/actions/guides/polar/grids.js +7 -7
- package/src/actions/guides/reuse.js +44 -0
- package/src/actions/guides/tickValues.js +19 -21
- package/src/actions/index.js +2 -0
- package/src/actions/intervals/resolve.js +35 -15
- package/src/actions/marks/arc/actions.js +1 -1
- package/src/actions/marks/area/actions.js +18 -4
- package/src/actions/marks/area/materialize.js +5 -2
- package/src/actions/marks/bar/materialize.js +11 -7
- package/src/actions/marks/index.js +5 -0
- package/src/actions/marks/line/actions.js +43 -108
- package/src/actions/marks/line/materialize.js +34 -52
- package/src/actions/marks/point/edit.js +11 -15
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/point/jitter.js +3 -0
- package/src/actions/marks/point/materialize.js +138 -14
- package/src/actions/marks/point/packing.js +68 -0
- package/src/actions/marks/references.js +87 -0
- package/src/actions/marks/remove.js +17 -4
- package/src/actions/marks/rule/actions.js +60 -4
- package/src/actions/marks/rule/appearance.js +26 -0
- package/src/actions/marks/shared.js +12 -12
- package/src/actions/marks/text/actions.js +224 -6
- package/src/actions/marks/text/layout.js +2 -2
- package/src/actions/primitives/createGraphics.js +3 -1
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticAction.js +39 -2
- package/src/actions/primitives/semanticValidation/guide.js +10 -0
- package/src/actions/primitives/semanticValidation/layer.js +18 -1
- package/src/actions/primitives/semanticValidation/scale.js +10 -1
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +19 -6
- package/src/actions/regression/resolve.js +1 -1
- package/src/actions/scales/consumers/common.js +20 -53
- package/src/actions/scales/consumers/families.js +27 -14
- package/src/actions/scales/consumers/index.js +23 -2
- package/src/actions/scales/consumers/seriesLayout.js +33 -2
- package/src/actions/scales/create.js +4 -4
- package/src/actions/scales/definitions.js +19 -3
- package/src/actions/scales/edit.js +46 -34
- package/src/actions/scales/editPolicy.js +21 -31
- package/src/actions/scales/materialize.js +10 -46
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +39 -0
- package/src/actions/scales/quantitativeColor.js +9 -5
- package/src/actions/theme/actions.js +43 -0
- package/src/actions/theme/index.js +9 -0
- package/src/actions/theme/reconcile.js +882 -0
- package/src/actions/titles/actions.js +11 -10
- package/src/actions/titles/resolve.js +1 -40
- package/src/actions/violinPlots/create.js +86 -42
- package/src/actions/violinPlots/edit.js +191 -0
- package/src/actions/violinPlots/index.js +2 -0
- package/src/core/ChartProgram.js +7 -0
- package/src/core/action.js +22 -2
- package/src/core/compositionState.js +136 -8
- package/src/core/vocabulary.js +2 -2
- package/src/grammar/aggregate.js +66 -23
- package/src/grammar/arcs.js +213 -65
- package/src/grammar/areaEndpoints.js +46 -0
- package/src/grammar/areaSeries.js +87 -41
- package/src/grammar/bars/aggregate.js +33 -12
- package/src/grammar/bars/policy.js +9 -7
- package/src/grammar/bin.js +181 -0
- package/src/grammar/categoryOrder.js +34 -0
- package/src/grammar/computed.js +150 -0
- package/src/grammar/ecdf.js +198 -0
- package/src/grammar/facets/index.js +117 -6
- package/src/grammar/fold.js +123 -0
- package/src/grammar/horizon.js +6 -4
- package/src/grammar/interval.js +37 -20
- package/src/grammar/lineSeries.js +20 -63
- package/src/grammar/markFilter.js +22 -4
- package/src/grammar/markLabels.js +93 -0
- package/src/grammar/parallelAxisTitles.js +16 -0
- package/src/grammar/pathSeries.js +91 -0
- package/src/grammar/pointPacking.js +183 -0
- package/src/grammar/positionCompatibility.js +1 -1
- package/src/grammar/positionDatum.js +12 -0
- package/src/grammar/rects.js +9 -1
- package/src/grammar/regression/derive.js +4 -0
- package/src/grammar/regression/index.js +7 -2
- package/src/grammar/regression/models.js +22 -7
- package/src/grammar/regression/parameters.js +40 -13
- package/src/grammar/rotation.js +29 -0
- package/src/grammar/rules.js +9 -21
- package/src/grammar/scales/color.js +23 -1
- package/src/grammar/scales/colorConsumers.js +38 -0
- package/src/grammar/scales/definition.js +15 -1
- package/src/grammar/scales/discretized.js +11 -5
- package/src/grammar/scales/fields.js +38 -8
- package/src/grammar/scales/mapping.js +2 -0
- package/src/grammar/scales/ordinal.js +23 -0
- package/src/grammar/scales/radial.js +55 -0
- package/src/grammar/scales/types.js +1 -0
- package/src/grammar/schemas/semanticPath.js +17 -3
- package/src/grammar/stack.js +170 -0
- package/src/grammar/statistics/confidenceInterval.js +101 -0
- package/src/grammar/summary.js +183 -0
- package/src/grammar/text.js +22 -16
- package/src/grammar/timeUnit.js +21 -11
- package/src/grammar/transformTopology.js +6 -0
- package/src/grammar/transforms.js +36 -0
- package/src/grammar/valueFormat.js +112 -0
- package/src/grammar/window.js +13 -7
- package/src/layout/facets.js +86 -23
- package/src/layout/guideCollisions.js +12 -0
- package/src/layout/legend.js +2 -10
- package/src/layout/legendItems.js +102 -0
- package/src/layout/legendLane.js +100 -11
- package/src/materialization/bars/aggregate.js +38 -152
- package/src/materialization/bars/histogram.js +24 -0
- package/src/materialization/categorySlotOffset.js +40 -0
- package/src/materialization/compositionSnapshot.js +1 -1
- package/src/materialization/dependencies.js +57 -16
- package/src/materialization/encodings.js +1 -1
- package/src/materialization/facetGuides/legacyCategorical.js +7 -1
- package/src/materialization/facetGuides/placement.js +16 -1
- package/src/materialization/facetGuides/preparation.js +26 -1
- package/src/materialization/facets.js +11 -2
- package/src/materialization/guides/layout.js +57 -0
- package/src/materialization/guides/resources.js +2 -2
- package/src/materialization/legends.js +10 -1
- package/src/materialization/marks/capabilities.js +30 -7
- package/src/materialization/marks/grouping.js +52 -0
- package/src/materialization/marks/index.js +63 -25
- package/src/materialization/marks/policies.js +2 -0
- package/src/materialization/rect.js +33 -33
- package/src/materialization/rowEncoding.js +70 -14
- package/src/materialization/scaleGuideDependencies.js +8 -5
- package/src/materialization/scales/map.js +1 -0
- package/src/materialization/scales/policies/arc.js +42 -0
- package/src/materialization/scales/policies/bar.js +0 -43
- package/src/materialization/scales/policies/binnedPosition.js +7 -4
- package/src/materialization/scales/policies/offset.js +54 -0
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +48 -18
- package/src/materialization/selection/compatibility.js +11 -0
- package/src/materialization/selection/items/bar.js +12 -68
- package/src/materialization/selection/items/common.js +8 -2
- package/src/materialization/selection/items/path.js +3 -13
- package/src/materialization/selection/items/rect.js +7 -4
- package/src/materialization/text.js +64 -5
- package/src/mcp/adapter.js +2 -2
- package/src/selectors/datasets.js +13 -0
- package/src/selectors/layers.js +3 -2
- package/src/selectors/scales.js +6 -0
- package/src/theme/defaults.js +39 -0
- package/types/basic.d.ts +11 -2
- package/types/index.d.ts +126 -0
- package/types/program.d.ts +1327 -124
- package/src/materialization/bars/grouped.js +0 -144
|
@@ -1,11 +1,17 @@
|
|
|
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
|
-
|
|
5
|
-
readQuantitativeField,
|
|
6
|
-
readScaleField,
|
|
7
|
-
readTemporalField
|
|
8
|
+
readScaleField
|
|
8
9
|
} from "../grammar/scales/index.js";
|
|
10
|
+
import {
|
|
11
|
+
finiteMidpoint,
|
|
12
|
+
requireFiniteResult
|
|
13
|
+
} from "../grammar/numeric.js";
|
|
14
|
+
import { normalizePositionDatum } from "../grammar/positionDatum.js";
|
|
9
15
|
import { mapScaleConsumerValues } from "./scales/map.js";
|
|
10
16
|
|
|
11
17
|
export function resolveRowEncodingValues(program, layer, dataset, channel) {
|
|
@@ -18,16 +24,66 @@ export function resolveRowEncodingValues(program, layer, dataset, channel) {
|
|
|
18
24
|
);
|
|
19
25
|
}
|
|
20
26
|
const categorical = ["nominal", "ordinal"].includes(encoding.fieldType);
|
|
21
|
-
const values = Object.hasOwn(
|
|
22
|
-
?
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const values = Object.hasOwn(encoding, "datum")
|
|
28
|
+
? Array(dataset.values.length).fill(normalizePositionDatum(
|
|
29
|
+
encoding.datum,
|
|
30
|
+
encoding.fieldType,
|
|
31
|
+
channel,
|
|
32
|
+
encoding.temporalUnit,
|
|
33
|
+
layer.mark.type[0].toUpperCase() + layer.mark.type.slice(1)
|
|
34
|
+
))
|
|
35
|
+
: readScaleField(dataset.values, encoding.field, encoding.fieldType, {
|
|
36
|
+
allowUnknown: Object.hasOwn(scale, "unknown"), temporalUnit: encoding.temporalUnit
|
|
37
|
+
});
|
|
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,3 +1,4 @@
|
|
|
1
|
+
import { isSourceOwnedText } from "../grammar/text.js";
|
|
1
2
|
import { POSITION_ENCODING_CHANNELS } from "../core/vocabulary.js";
|
|
2
3
|
import { materializedLegendUsesScale } from "./legends.js";
|
|
3
4
|
import { buildMaterializationPlan } from "./planner.js";
|
|
@@ -19,10 +20,12 @@ const GRIDS = Object.freeze([
|
|
|
19
20
|
|
|
20
21
|
function usesPositionalScale(program, id) {
|
|
21
22
|
return program.semanticSpec.layers.some(layer =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
!isSourceOwnedText(layer) && (
|
|
24
|
+
POSITION_ENCODING_CHANNELS.some(
|
|
25
|
+
channel => layer.encoding?.[channel]?.scale === id
|
|
26
|
+
) || (layer.encoding?.parallel?.dimensions ?? []).some(
|
|
27
|
+
dimension => dimension.scale === id
|
|
28
|
+
)
|
|
26
29
|
)
|
|
27
30
|
);
|
|
28
31
|
}
|
|
@@ -50,7 +53,7 @@ export function needsCanvasScaleRematerialization(program, scale) {
|
|
|
50
53
|
(scale.range === "auto" ||
|
|
51
54
|
usesRadialScale(program, scale.id) ||
|
|
52
55
|
semanticGuideUsesScale(program, scale.id)) &&
|
|
53
|
-
program.resolvedScales[scale.id] !== undefined &&
|
|
56
|
+
(program.resolvedScales[scale.id] !== undefined || scale.radialMapping !== undefined) &&
|
|
54
57
|
usesPositionalScale(program, scale.id)
|
|
55
58
|
);
|
|
56
59
|
}
|
|
@@ -19,6 +19,7 @@ export function mapScaleConsumerValues(values, resolvedScale, channel) {
|
|
|
19
19
|
resolvedScale.range,
|
|
20
20
|
{
|
|
21
21
|
interpolation: resolvedScale.interpolate,
|
|
22
|
+
midpoint: resolvedScale.midpoint,
|
|
22
23
|
clamp: resolvedScale.clamp ?? false,
|
|
23
24
|
...(Object.hasOwn(resolvedScale, "unknown")
|
|
24
25
|
? { unknown: resolvedScale.unknown }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateMeasuredRadiusScale } from "../../../grammar/scales/radial.js";
|
|
1
2
|
export function resolveArcAutoPositionRange({
|
|
2
3
|
consumers,
|
|
3
4
|
scale,
|
|
@@ -35,3 +36,44 @@ export function resolveArcAutoPositionRange({
|
|
|
35
36
|
}
|
|
36
37
|
return range;
|
|
37
38
|
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
export function resolveMeasuredRadiusDomain({ scale, channel, allValues }) {
|
|
42
|
+
if (scale.radialMapping === undefined) return undefined;
|
|
43
|
+
if (channel !== "radius") throw new Error("Measured radius scales support only radius consumers.");
|
|
44
|
+
const maximum = allValues.reduce((maximum, value) => Math.max(maximum, value), -Infinity);
|
|
45
|
+
if (!Number.isFinite(maximum) || maximum <= 0 || allValues.some(value => value < 0)) {
|
|
46
|
+
throw new Error("Measured radius requires finite non-negative aggregates and a positive maximum.");
|
|
47
|
+
}
|
|
48
|
+
const domain = scale.domain === "auto" ? [0, maximum] : scale.domain;
|
|
49
|
+
if (Array.isArray(domain) && domain[1] < maximum) {
|
|
50
|
+
throw new RangeError("Measured radius domain maximum must cover every category aggregate.");
|
|
51
|
+
}
|
|
52
|
+
return domain;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function validateMeasuredRadiusConsumers({ scale, domain, range, consumers, markConfigs, thetaScales }) {
|
|
56
|
+
if (scale.radialMapping === undefined) return;
|
|
57
|
+
validateMeasuredRadiusScale({ ...scale, domain, range }, { allowAuto: true });
|
|
58
|
+
for (const consumer of consumers) {
|
|
59
|
+
const { layer, encoding } = consumer;
|
|
60
|
+
const theta = layer.encoding?.theta;
|
|
61
|
+
const thetaScale = thetaScales[layer.id];
|
|
62
|
+
if (!isPendingMeasuredRadiusConsumer(consumer) && (layer.mark?.type !== "arc" || !["count", "sum"].includes(encoding.aggregate) ||
|
|
63
|
+
!["nominal", "ordinal"].includes(theta?.fieldType) || theta.aggregate !== undefined ||
|
|
64
|
+
thetaScale?.type !== "band" || (thetaScale.paddingInner ?? 0) !== 0 || (thetaScale.paddingOuter ?? 0) !== 0)) {
|
|
65
|
+
throw new Error("Measured radius requires only equal-angle categorical Arc consumers.");
|
|
66
|
+
}
|
|
67
|
+
const config = markConfigs[layer.id] ?? {};
|
|
68
|
+
if ((config.padAngle ?? 0) !== 0) throw new Error("Measured Arc requires padAngle 0.");
|
|
69
|
+
if (scale.range !== "auto" && config.innerRadiusExplicit === true &&
|
|
70
|
+
Math.abs(config.innerRadius - range[0] / range[1]) > Number.EPSILON * 8) {
|
|
71
|
+
throw new Error("Measured Arc innerRadius must agree with its explicit radius range.");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isPendingMeasuredRadiusConsumer({ layer, channel, encoding }) {
|
|
77
|
+
return layer.mark?.type === "arc" && channel === "radius" &&
|
|
78
|
+
["count", "sum"].includes(encoding.aggregate) && layer.encoding?.theta === undefined;
|
|
79
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { normalizeOffsetPadding } from "../../../grammar/bars/geometry.js";
|
|
2
1
|
import {
|
|
3
2
|
BAR_GRAINS,
|
|
4
3
|
resolveBarChannels,
|
|
@@ -8,48 +7,6 @@ import {
|
|
|
8
7
|
interpolateNumber,
|
|
9
8
|
inverseLerp
|
|
10
9
|
} from "../../../grammar/numeric.js";
|
|
11
|
-
export function resolveOffsetScalePolicy({
|
|
12
|
-
consumers,
|
|
13
|
-
resolvedScales,
|
|
14
|
-
markConfigs,
|
|
15
|
-
id,
|
|
16
|
-
channel
|
|
17
|
-
}) {
|
|
18
|
-
const parentChannel = channel === "xOffset" ? "x" : "y";
|
|
19
|
-
const bandwidths = consumers.map(consumer => {
|
|
20
|
-
if (parentChannel === "x" && consumer.layer.encoding?.x?.bin !== undefined) {
|
|
21
|
-
return 1;
|
|
22
|
-
}
|
|
23
|
-
const parentScaleId = consumer.layer.encoding?.[parentChannel]?.scale;
|
|
24
|
-
return resolvedScales[parentScaleId]?.bandwidth;
|
|
25
|
-
});
|
|
26
|
-
if (
|
|
27
|
-
bandwidths.some(value => !Number.isFinite(value)) ||
|
|
28
|
-
new Set(bandwidths).size !== 1
|
|
29
|
-
) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`${channel} scale "${id}" requires one shared resolved ${parentChannel} bandwidth.`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
const paddings = consumers.map(consumer => normalizeOffsetPadding(
|
|
35
|
-
markConfigs[consumer.layer.id]?.[channel],
|
|
36
|
-
undefined,
|
|
37
|
-
channel
|
|
38
|
-
));
|
|
39
|
-
const signatures = new Set(
|
|
40
|
-
paddings.map(padding => JSON.stringify(padding))
|
|
41
|
-
);
|
|
42
|
-
if (signatures.size !== 1) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`${channel} scale "${id}" requires one shared padding policy.`
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
parentBandwidth: bandwidths[0],
|
|
49
|
-
...paddings[0]
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
10
|
export function resolveTemporalBarBand(valuesByConsumer, domain, range) {
|
|
54
11
|
const temporalBars = valuesByConsumer.filter(({ consumer }) => {
|
|
55
12
|
const channels = resolveBarChannels(consumer.layer);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
1
2
|
import { resolveHistogramBins } from "../../../grammar/histogram.js";
|
|
2
3
|
|
|
3
4
|
export function resolveBinnedPositionDomain({
|
|
4
5
|
valuesByConsumer,
|
|
5
6
|
channel,
|
|
6
7
|
scale,
|
|
7
|
-
id
|
|
8
|
-
allValues
|
|
8
|
+
id
|
|
9
9
|
}) {
|
|
10
10
|
const binnedPositions = valuesByConsumer.filter(
|
|
11
11
|
({ consumer }) =>
|
|
@@ -13,7 +13,10 @@ export function resolveBinnedPositionDomain({
|
|
|
13
13
|
consumer.encoding.bin !== undefined
|
|
14
14
|
);
|
|
15
15
|
if (binnedPositions.length === 0) return undefined;
|
|
16
|
-
|
|
16
|
+
const independentConsumers = valuesByConsumer.filter(({ consumer }) =>
|
|
17
|
+
!isSourceOwnedText(consumer.layer)
|
|
18
|
+
);
|
|
19
|
+
if (channel !== "x" || binnedPositions.length !== independentConsumers.length) {
|
|
17
20
|
throw new Error(
|
|
18
21
|
`Binned scale "${id}" cannot be shared with an unbinned consumer.`
|
|
19
22
|
);
|
|
@@ -25,7 +28,7 @@ export function resolveBinnedPositionDomain({
|
|
|
25
28
|
throw new Error(`Binned scale "${id}" requires one shared bin definition.`);
|
|
26
29
|
}
|
|
27
30
|
return resolveHistogramBins({
|
|
28
|
-
values:
|
|
31
|
+
values: binnedPositions.flatMap(item => item.values).filter(value => value !== undefined),
|
|
29
32
|
bin: binnedPositions[0].consumer.encoding.bin,
|
|
30
33
|
domain: scale.domain,
|
|
31
34
|
nice: scale.nice ?? true,
|
|
@@ -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
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
1
2
|
import {
|
|
2
3
|
isTransformedScaleType,
|
|
3
4
|
resolveContinuousDomain,
|
|
@@ -21,7 +22,7 @@ export function resolveSeriesLayoutDomain({
|
|
|
21
22
|
(_, index) => seriesLayouts[index] !== undefined
|
|
22
23
|
);
|
|
23
24
|
const directConsumers = valuesByConsumer.filter(
|
|
24
|
-
(
|
|
25
|
+
({ consumer }, index) => seriesLayouts[index] === undefined && !isSourceOwnedText(consumer.layer)
|
|
25
26
|
);
|
|
26
27
|
const compatibleDirect = directConsumers.every(({ consumer }) =>
|
|
27
28
|
layoutConsumers.every(({ consumer: layoutConsumer }) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateContinuousColorConsumer } from "../../grammar/scales/colorConsumers.js";
|
|
1
2
|
import {
|
|
2
3
|
isContinuousColorScaleType,
|
|
3
4
|
isDiscretePositionScaleType,
|
|
@@ -21,16 +22,15 @@ import {
|
|
|
21
22
|
resolveStrokeWidthRange,
|
|
22
23
|
resolveTransformedDomain,
|
|
23
24
|
SCALE_ROLES,
|
|
25
|
+
validateSequentialMidpoint,
|
|
24
26
|
validateLinearScaleType,
|
|
25
27
|
validateOrdinalScaleType,
|
|
26
28
|
validateScaleTypeForRole,
|
|
27
29
|
validateTimeScaleType
|
|
28
30
|
} from "../../grammar/scales/index.js";
|
|
29
|
-
import { resolveArcAutoPositionRange } from "./policies/arc.js";
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
resolveTemporalBarBand
|
|
33
|
-
} from "./policies/bar.js";
|
|
31
|
+
import { resolveArcAutoPositionRange, resolveMeasuredRadiusDomain, validateMeasuredRadiusConsumers } from "./policies/arc.js";
|
|
32
|
+
import { resolveTemporalBarBand } from "./policies/bar.js";
|
|
33
|
+
import { resolveOffsetScalePolicy } from "./policies/offset.js";
|
|
34
34
|
import { resolveBinnedPositionDomain } from "./policies/binnedPosition.js";
|
|
35
35
|
import { resolveSeriesLayoutDomain } from "./policies/series.js";
|
|
36
36
|
import { OFFSET_POSITION_CHANNELS } from "../../core/vocabulary.js";
|
|
@@ -141,7 +141,9 @@ function resolveContinuousScale({
|
|
|
141
141
|
: validateLinearScaleType(scale.type),
|
|
142
142
|
domain,
|
|
143
143
|
range,
|
|
144
|
+
...(scale.midpoint === undefined ? {} : { midpoint: scale.midpoint }),
|
|
144
145
|
...(scale.clamp === undefined ? {} : { clamp: scale.clamp }),
|
|
146
|
+
...(scale.radialMapping === undefined ? {} : { radialMapping: scale.radialMapping }),
|
|
145
147
|
...(scale.type === "log"
|
|
146
148
|
? {
|
|
147
149
|
base: normalizeTransformParameters("log", {
|
|
@@ -184,19 +186,38 @@ function reverseResolvedScale(scale) {
|
|
|
184
186
|
};
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
export function resolveScaleMaterialization({
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
189
|
+
export function resolveScaleMaterialization(options) {
|
|
190
|
+
const {
|
|
191
|
+
id,
|
|
192
|
+
scale,
|
|
193
|
+
channel,
|
|
194
|
+
consumers,
|
|
195
|
+
valuesByConsumer,
|
|
196
|
+
bounds,
|
|
197
|
+
resolvedScales,
|
|
198
|
+
markConfigs,
|
|
199
|
+
thetaScales
|
|
200
|
+
} = options;
|
|
197
201
|
const allValues = valuesByConsumer
|
|
198
202
|
.flatMap(item => item.values)
|
|
199
203
|
.filter(value => value !== undefined);
|
|
204
|
+
const preserved = resolvedScales[id]?.domain;
|
|
205
|
+
if (
|
|
206
|
+
scale.domain === "auto" &&
|
|
207
|
+
Array.isArray(preserved) &&
|
|
208
|
+
consumers.some(({ layer }) =>
|
|
209
|
+
markConfigs?.[layer.id]?.markFilter?.empty === true
|
|
210
|
+
)
|
|
211
|
+
) {
|
|
212
|
+
return resolveScaleMaterialization({
|
|
213
|
+
...options,
|
|
214
|
+
scale: { ...scale, domain: preserved },
|
|
215
|
+
valuesByConsumer: valuesByConsumer.map(item => ({
|
|
216
|
+
...item,
|
|
217
|
+
categoryOrder: undefined
|
|
218
|
+
}))
|
|
219
|
+
});
|
|
220
|
+
}
|
|
200
221
|
const isSequentialColor = channel === "color" &&
|
|
201
222
|
isContinuousColorScaleType(scale.type);
|
|
202
223
|
const isDiscretizedColor = channel === "color" &&
|
|
@@ -212,6 +233,9 @@ export function resolveScaleMaterialization({
|
|
|
212
233
|
!isOrdinalOffset &&
|
|
213
234
|
isDiscretePositionScaleType(scale.type);
|
|
214
235
|
|
|
236
|
+
if (isSequentialColor || isDiscretizedColor) {
|
|
237
|
+
for (const consumer of consumers) validateContinuousColorConsumer(consumer.layer, consumer.encoding, scale);
|
|
238
|
+
}
|
|
215
239
|
let discretizedScale;
|
|
216
240
|
if (isDiscretizedColor) {
|
|
217
241
|
discretizedScale = resolveDiscretizedColorScale({
|
|
@@ -227,8 +251,7 @@ export function resolveScaleMaterialization({
|
|
|
227
251
|
valuesByConsumer,
|
|
228
252
|
channel,
|
|
229
253
|
scale,
|
|
230
|
-
id
|
|
231
|
-
allValues
|
|
254
|
+
id
|
|
232
255
|
});
|
|
233
256
|
const seriesLayouts = valuesByConsumer.map(item => item.seriesLayout);
|
|
234
257
|
const seriesDomain = isDiscretizedColor || binnedDomain !== undefined
|
|
@@ -249,7 +272,7 @@ export function resolveScaleMaterialization({
|
|
|
249
272
|
}
|
|
250
273
|
const domain = isDiscretizedColor
|
|
251
274
|
? discretizedScale.domain
|
|
252
|
-
: categoryOrderDomain ?? binnedDomain ?? seriesDomain ?? resolveDefaultDomain({
|
|
275
|
+
: resolveMeasuredRadiusDomain({ scale, channel, allValues }) ?? categoryOrderDomain ?? binnedDomain ?? seriesDomain ?? resolveDefaultDomain({
|
|
253
276
|
scale,
|
|
254
277
|
allValues,
|
|
255
278
|
isOrdinalAppearance,
|
|
@@ -269,6 +292,13 @@ export function resolveScaleMaterialization({
|
|
|
269
292
|
isOrdinalOffset,
|
|
270
293
|
discretizedScale
|
|
271
294
|
});
|
|
295
|
+
validateSequentialMidpoint(scale.midpoint, scale.type, domain);
|
|
296
|
+
if (scale.midpoint !== undefined && (channel !== "color" || consumers.some(
|
|
297
|
+
consumer => consumer.encoding.fieldType !== "quantitative"
|
|
298
|
+
))) {
|
|
299
|
+
throw new Error("Scale midpoint requires quantitative color consumers.");
|
|
300
|
+
}
|
|
301
|
+
validateMeasuredRadiusConsumers({ scale, domain, range, consumers, markConfigs, thetaScales });
|
|
272
302
|
if (channel === "shape" && domain.length > range.length) {
|
|
273
303
|
throw new Error(
|
|
274
304
|
`Shape scale "${id}" requires at least one distinct shape per domain value.`
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function assertEncodingSelectionCompatibility(program, target, channels) {
|
|
2
|
+
for (const [id, selection] of Object.entries(
|
|
3
|
+
program.materializationConfigs.selections ?? {}
|
|
4
|
+
)) {
|
|
5
|
+
if (selection.target === target && channels.includes(selection.selector?.channel)) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
`Cannot remove ${selection.selector.channel} encoding while selection "${id}" references that channel.`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { deriveSeriesBarCells } from "../../../grammar/bars/aggregate.js";
|
|
2
|
+
import { readSeriesIdentity } from "../../../grammar/pathSeries.js";
|
|
2
3
|
import {
|
|
3
4
|
BAR_GRAINS,
|
|
4
5
|
resolveBarChannels,
|
|
@@ -7,7 +8,6 @@ import {
|
|
|
7
8
|
resolveBarGrain
|
|
8
9
|
} from "../../../grammar/bars/policy.js";
|
|
9
10
|
import { numericExtent } from "../../../grammar/numeric.js";
|
|
10
|
-
import { layoutSeriesPartition } from "../../../grammar/seriesLayout.js";
|
|
11
11
|
import {
|
|
12
12
|
readNominalField,
|
|
13
13
|
readTemporalField
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
|
|
25
25
|
function categoryValues(rows, encoding) {
|
|
26
26
|
return encoding.fieldType === "temporal"
|
|
27
|
-
? readTemporalField(rows, encoding.field)
|
|
27
|
+
? readTemporalField(rows, encoding.field, encoding.temporalUnit)
|
|
28
28
|
: readNominalField(rows, encoding.field);
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -36,20 +36,17 @@ function aggregateMembers(rows, layer, cell, channels) {
|
|
|
36
36
|
const colors = !["nominal", "ordinal"].includes(color?.fieldType)
|
|
37
37
|
? undefined
|
|
38
38
|
: readNominalField(rows, color.field);
|
|
39
|
+
const identity = layer.encoding?.group === undefined ? undefined : readSeriesIdentity(rows, layer);
|
|
39
40
|
return rows.filter((_, index) =>
|
|
40
41
|
categories[index] === categoryValue &&
|
|
41
|
-
(colors === undefined || colors[index] === cell.color)
|
|
42
|
+
(identity === undefined ? colors === undefined || colors[index] === cell.color : identity.values[index] === cell.series)
|
|
42
43
|
);
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
function aggregateCellDefinitions(program, layer, dataset) {
|
|
46
47
|
const channels = resolveBarChannels(layer);
|
|
47
|
-
const derived = deriveBarAggregates(dataset.values, layer).values;
|
|
48
48
|
const categoryEncoding = layer.encoding[channels.category];
|
|
49
49
|
const categoryScale = program.resolvedScales[categoryEncoding.scale];
|
|
50
|
-
const measureScale = program.resolvedScales[
|
|
51
|
-
layer.encoding[channels.measure].scale
|
|
52
|
-
];
|
|
53
50
|
const colorEncoding = layer.encoding?.color;
|
|
54
51
|
const colorScale = program.resolvedScales[colorEncoding?.scale];
|
|
55
52
|
const offsetChannel = resolveBarOffsetChannel(layer);
|
|
@@ -67,70 +64,17 @@ function aggregateCellDefinitions(program, layer, dataset) {
|
|
|
67
64
|
...(cell.color === undefined ? {} : { color: cell.color }),
|
|
68
65
|
...(layer.encoding?.[offsetChannel] === undefined
|
|
69
66
|
? {}
|
|
70
|
-
: { [offsetChannel]: cell.color })
|
|
67
|
+
: { [offsetChannel]: cell.series ?? cell.color })
|
|
71
68
|
},
|
|
72
69
|
members
|
|
73
70
|
};
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
|
|
82
|
-
? categoryScale.domain
|
|
83
|
-
: [...new Set(derived.map(cell => cell[channels.category]))]
|
|
84
|
-
.sort((left, right) => left - right);
|
|
85
|
-
return categories.flatMap(category => {
|
|
86
|
-
const cell = cells.get(category);
|
|
87
|
-
return cell === undefined
|
|
88
|
-
? []
|
|
89
|
-
: [definition(cell, measureScale.domain[0], cell[channels.measure])];
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (layout === "group" && offsetScale !== undefined) {
|
|
94
|
-
const categoryIndex = new Map(
|
|
95
|
-
categoryScale.domain.map((value, index) => [value, index])
|
|
96
|
-
);
|
|
97
|
-
const offsetIndex = new Map(
|
|
98
|
-
offsetScale.domain.map((value, index) => [value, index])
|
|
99
|
-
);
|
|
100
|
-
const cells = [...derived].sort((left, right) =>
|
|
101
|
-
categoryIndex.get(left[channels.category]) -
|
|
102
|
-
categoryIndex.get(right[channels.category]) ||
|
|
103
|
-
offsetIndex.get(left.color) - offsetIndex.get(right.color)
|
|
104
|
-
);
|
|
105
|
-
return cells.map(cell => definition(
|
|
106
|
-
cell,
|
|
107
|
-
measureScale.domain[0],
|
|
108
|
-
cell[channels.measure]
|
|
109
|
-
));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
|
|
113
|
-
? categoryScale.domain
|
|
114
|
-
: [...new Set(derived.map(cell => cell[channels.category]))]
|
|
115
|
-
.sort((left, right) => left - right);
|
|
116
|
-
const series = colorScale?.domain ?? [undefined];
|
|
117
|
-
const lookup = new Map(derived.map(cell => [
|
|
118
|
-
JSON.stringify([cell[channels.category], cell.color]),
|
|
119
|
-
cell
|
|
120
|
-
]));
|
|
121
|
-
const baseline = layout === "overlay" ? measureScale.domain[0] : 0;
|
|
122
|
-
return categories.flatMap(category => {
|
|
123
|
-
const cells = series.map(color =>
|
|
124
|
-
lookup.get(JSON.stringify([category, color]))
|
|
125
|
-
);
|
|
126
|
-
const values = cells.map(cell => cell?.[channels.measure] ?? 0);
|
|
127
|
-
return layoutSeriesPartition(values, layout, { baseline }).flatMap(segment => {
|
|
128
|
-
const cell = cells[segment.index];
|
|
129
|
-
return cell === undefined
|
|
130
|
-
? []
|
|
131
|
-
: [definition(cell, segment.start, segment.end)];
|
|
132
|
-
});
|
|
133
|
-
});
|
|
73
|
+
const categories = categoryScale.type === "time" ? undefined : categoryScale.domain;
|
|
74
|
+
const series = layout === "group" ? offsetScale?.domain
|
|
75
|
+
: layer.encoding.group === undefined && colorEncoding?.fieldType !== "quantitative" ? colorScale?.domain : undefined;
|
|
76
|
+
return deriveSeriesBarCells(dataset.values, layer, categories, series)
|
|
77
|
+
.map(({ cell, start, end }) => definition(cell, start, end));
|
|
134
78
|
}
|
|
135
79
|
|
|
136
80
|
function histogramDefinitions(program, layer, dataset) {
|
|
@@ -145,7 +89,7 @@ function histogramDefinitions(program, layer, dataset) {
|
|
|
145
89
|
});
|
|
146
90
|
const colorScale = program.resolvedScales[colorEncoding?.scale];
|
|
147
91
|
return segments.map(segment => {
|
|
148
|
-
const colorValue = colorScale?.domain[segment.category];
|
|
92
|
+
const colorValue = segment.colorValue ?? colorScale?.domain[segment.category];
|
|
149
93
|
return {
|
|
150
94
|
fields: uniqueFields(segment.members),
|
|
151
95
|
channels: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeTemporalValue } from "../../../grammar/scales/fields.js";
|
|
1
2
|
import { cloneAndFreeze, isPlainObject } from "../../../core/immutable.js";
|
|
2
3
|
import {
|
|
3
4
|
MARK_GRAPHIC_PROPERTIES,
|
|
@@ -16,8 +17,13 @@ function graphicId(layer, index) {
|
|
|
16
17
|
|
|
17
18
|
function encodingValue(row, encoding) {
|
|
18
19
|
if (encoding === undefined) return undefined;
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const value = Object.hasOwn(encoding, "field") ? row?.[encoding.field] : encoding.datum;
|
|
21
|
+
if (encoding.fieldType !== "temporal") return value;
|
|
22
|
+
try {
|
|
23
|
+
return normalizeTemporalValue(value, encoding.field, 0, encoding.temporalUnit);
|
|
24
|
+
} catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export function channelMapFromRow(row, layer) {
|
|
@@ -25,20 +25,10 @@ function rowsForSeries(rows, key) {
|
|
|
25
25
|
|
|
26
26
|
function seriesDefinitions(layer, rows, series) {
|
|
27
27
|
return series.map(item => {
|
|
28
|
-
const members = rowsForSeries(rows, item.key)
|
|
28
|
+
const members = item.sourceIndices === undefined ? rowsForSeries(rows, item.key)
|
|
29
|
+
: item.sourceIndices.map(index => rows[index]);
|
|
29
30
|
const fields = { ...uniqueFields(members), ...item.key };
|
|
30
|
-
const channels =
|
|
31
|
-
Object.entries(layer.encoding ?? {}).flatMap(([channel, encoding]) => {
|
|
32
|
-
if (encoding.field === undefined) {
|
|
33
|
-
return Object.hasOwn(encoding, "datum")
|
|
34
|
-
? [[channel, encoding.datum]]
|
|
35
|
-
: [];
|
|
36
|
-
}
|
|
37
|
-
return Object.hasOwn(fields, encoding.field)
|
|
38
|
-
? [[channel, fields[encoding.field]]]
|
|
39
|
-
: [];
|
|
40
|
-
})
|
|
41
|
-
);
|
|
31
|
+
const channels = channelMapFromRow(fields, layer);
|
|
42
32
|
return { fields, channels, members };
|
|
43
33
|
});
|
|
44
34
|
}
|