ggaction 0.0.2 → 0.0.4
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 +55 -2
- package/README.md +95 -72
- package/package.json +15 -5
- package/src/actions/boxPlots/components.js +3 -3
- package/src/actions/boxPlots/edit.js +277 -0
- package/src/actions/boxPlots/index.js +2 -0
- package/src/actions/boxPlots/materialize.js +12 -1
- package/src/actions/boxPlots/options.js +25 -14
- package/src/actions/canvas/actions.js +21 -17
- package/src/actions/composition/actions.js +247 -0
- package/src/actions/composition/index.js +1 -0
- package/src/actions/coordinates/actions.js +2 -5
- package/src/actions/data/density.js +7 -1
- package/src/actions/data/filter.js +3 -1
- package/src/actions/data/regression.js +1 -1
- package/src/actions/encodings/appearance.js +12 -1
- package/src/actions/encodings/barWidth.js +5 -3
- package/src/actions/encodings/color/continuous.js +137 -0
- package/src/actions/encodings/color/index.js +151 -0
- package/src/actions/encodings/color/layout.js +91 -0
- package/src/actions/encodings/color/policy.js +88 -0
- package/src/actions/encodings/index.js +4 -2
- package/src/actions/encodings/offset.js +91 -73
- package/src/actions/encodings/position/apply.js +66 -0
- package/src/actions/encodings/position/index.js +130 -0
- package/src/actions/encodings/position/policies/arc.js +26 -0
- package/src/actions/encodings/position/policies/bar.js +9 -5
- package/src/actions/encodings/position/policies/index.js +5 -1
- package/src/actions/encodings/position/policies/line.js +20 -3
- package/src/actions/encodings/position/resolve.js +58 -9
- package/src/actions/encodings/ranged.js +30 -8
- package/src/actions/encodings/shared.js +17 -21
- package/src/actions/encodings/strokeDash.js +1 -1
- package/src/actions/encodings/text.js +73 -0
- package/src/actions/errorBands/create.js +37 -29
- package/src/actions/errorBands/edit.js +206 -0
- package/src/actions/errorBands/index.js +9 -0
- package/src/actions/errorBands/options.js +39 -0
- package/src/actions/errorBars/create.js +50 -43
- package/src/actions/errorBars/edit.js +130 -0
- package/src/actions/errorBars/index.js +3 -0
- package/src/actions/errorBars/options.js +52 -0
- package/src/actions/facets/actions.js +162 -0
- package/src/actions/facets/derive.js +200 -0
- package/src/actions/facets/guides.js +16 -0
- package/src/actions/facets/index.js +1 -0
- package/src/actions/facets/replay.js +69 -0
- package/src/actions/guides/applicability.js +113 -0
- package/src/actions/guides/axes/axes.js +70 -16
- package/src/actions/guides/axes/edit.js +126 -0
- package/src/actions/guides/axes/index.js +12 -0
- package/src/actions/guides/axes/labels.js +22 -19
- package/src/actions/guides/axes/remove.js +60 -0
- package/src/actions/guides/axes/tickGroups.js +7 -5
- package/src/actions/guides/axes/ticks.js +10 -5
- package/src/actions/guides/axes/titles.js +8 -6
- package/src/actions/guides/grids/grid.js +144 -15
- package/src/actions/guides/grids/resolve.js +6 -14
- package/src/actions/guides/guides.js +17 -75
- package/src/actions/guides/index.js +6 -0
- package/src/actions/guides/legends/categorical/actions.js +48 -9
- package/src/actions/guides/legends/categorical/components.js +16 -16
- package/src/actions/guides/legends/categorical/index.js +14 -14
- package/src/actions/guides/legends/categorical/layout.js +7 -3
- package/src/actions/guides/legends/categorical/options.js +2 -5
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +14 -14
- package/src/actions/guides/legends/continuous/common.js +7 -7
- package/src/actions/guides/legends/continuous/interval.js +6 -6
- package/src/actions/guides/legends/continuous/opacity.js +1 -1
- package/src/actions/guides/legends/edit.js +4 -26
- package/src/actions/guides/legends/focused.js +63 -0
- package/src/actions/guides/legends/index.js +4 -0
- package/src/actions/guides/legends/remove.js +46 -0
- package/src/actions/guides/legends/size.js +13 -15
- package/src/actions/guides/legends/target.js +25 -0
- package/src/actions/guides/polar/axes/facade.js +219 -0
- package/src/actions/guides/polar/axes/index.js +55 -0
- package/src/actions/guides/polar/axes/labels.js +188 -0
- package/src/actions/guides/polar/axes/lines.js +113 -0
- package/src/actions/guides/polar/axes/shared.js +112 -0
- package/src/actions/guides/polar/axes/ticks.js +164 -0
- package/src/actions/guides/polar/axes/titles.js +165 -0
- package/src/actions/guides/polar/grids.js +252 -0
- package/src/actions/guides/polar/index.js +2 -0
- package/src/actions/guides/polar/resolve.js +269 -0
- package/src/actions/guides/tickValues.js +1 -1
- package/src/actions/index.js +4 -0
- package/src/actions/intervals/resolve.js +1 -1
- package/src/actions/marks/arc/actions.js +239 -0
- package/src/actions/marks/arc/index.js +1 -0
- package/src/actions/marks/{area.js → area/actions.js} +51 -38
- package/src/actions/marks/area/index.js +4 -0
- package/src/actions/marks/bar/create.js +26 -5
- package/src/actions/marks/bar/edit.js +1 -1
- package/src/actions/marks/bar/materialize.js +25 -31
- package/src/actions/marks/index.js +12 -4
- package/src/actions/marks/lifecycle.js +27 -0
- package/src/actions/marks/line/actions.js +373 -0
- package/src/actions/marks/line/index.js +1 -0
- package/src/actions/marks/{point.js → point/actions.js} +67 -108
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/rect/actions.js +165 -0
- package/src/actions/marks/rect/index.js +1 -0
- package/src/actions/marks/remove.js +214 -0
- package/src/actions/marks/{rule.js → rule/actions.js} +34 -28
- package/src/actions/marks/rule/index.js +1 -0
- package/src/actions/marks/shared.js +156 -25
- package/src/actions/marks/text/actions.js +192 -0
- package/src/actions/marks/text/index.js +1 -0
- package/src/actions/primitives/createGraphics.js +13 -6
- package/src/actions/primitives/editGraphics.js +10 -3
- package/src/actions/primitives/semantic.js +26 -3
- package/src/actions/primitives/semanticValue.js +26 -4
- package/src/actions/regression/components.js +2 -2
- package/src/actions/regression/create.js +19 -2
- package/src/actions/regression/edit.js +247 -0
- package/src/actions/regression/index.js +3 -0
- package/src/actions/regression/resolve.js +3 -1
- package/src/actions/scales/consumers/common.js +91 -0
- package/src/actions/scales/consumers/families.js +47 -0
- package/src/actions/scales/consumers/index.js +41 -0
- package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +14 -127
- package/src/actions/scales/create.js +1 -1
- package/src/actions/scales/definitions.js +15 -6
- package/src/actions/scales/edit.js +45 -22
- package/src/actions/scales/materialize.js +58 -409
- package/src/actions/selection/actions.js +38 -24
- package/src/actions/selection/index.js +2 -0
- package/src/actions/titles/actions.js +63 -2
- package/src/actions/titles/index.js +2 -0
- package/src/actions/titles/resolve.js +12 -17
- package/src/composition.js +10 -0
- package/src/core/ChartProgram.js +49 -65
- package/src/core/action.js +10 -0
- package/src/core/compositionState.js +169 -0
- package/src/core/materializationState.js +65 -0
- package/src/core/programState.js +28 -0
- package/src/core/validation.js +16 -0
- package/src/core/vocabulary.js +154 -4
- package/src/grammar/arcs.js +154 -0
- package/src/grammar/areaSeries.js +1 -1
- package/src/grammar/bars/aggregate.js +3 -3
- package/src/grammar/bars/geometry.js +3 -3
- package/src/grammar/bars/policy.js +7 -1
- package/src/grammar/coordinates.js +3 -7
- package/src/grammar/density.js +21 -2
- package/src/grammar/facets/dependencies.js +185 -0
- package/src/grammar/facets/guides.js +257 -0
- package/src/grammar/facets/index.js +122 -0
- package/src/grammar/facets/scales.js +254 -0
- package/src/grammar/interval.js +2 -96
- package/src/grammar/lineSeries.js +112 -4
- package/src/grammar/palettes.js +5 -7
- package/src/grammar/polar.js +102 -0
- package/src/grammar/polarGuides.js +243 -0
- package/src/grammar/polarLineCommands.js +43 -0
- package/src/grammar/polarPaths.js +161 -0
- package/src/grammar/positionCompatibility.js +18 -2
- package/src/grammar/rects.js +26 -0
- package/src/grammar/regression/derive.js +97 -0
- package/src/grammar/regression/index.js +22 -0
- package/src/grammar/regression/models.js +282 -0
- package/src/grammar/regression/parameters.js +106 -0
- package/src/grammar/rules.js +1 -1
- package/src/grammar/scales/color.js +4 -2
- package/src/grammar/scales/continuous.js +6 -1
- package/src/grammar/scales/fields.js +7 -0
- package/src/grammar/scales/ordinal.js +5 -2
- package/src/grammar/scales/policies.js +1 -1
- package/src/grammar/schemas/graphic.js +1 -1
- package/src/grammar/schemas/graphicBounds.js +64 -9
- package/src/grammar/schemas/graphicTree.js +44 -10
- package/src/grammar/schemas/semanticPath.js +31 -9
- package/src/grammar/seriesLayout.js +13 -2
- package/src/grammar/statistics/studentT.js +98 -0
- package/src/grammar/text.js +71 -0
- package/src/grammar/ticks.js +50 -13
- package/src/grammar/transforms.js +1 -1
- package/src/index.js +1 -0
- package/src/layout/composition.js +310 -0
- package/src/layout/facets.js +131 -0
- package/src/layout/title.js +6 -0
- package/src/materialization/bars/aggregate.js +6 -3
- package/src/materialization/bars/grouped.js +75 -37
- package/src/materialization/bars/histogram.js +1 -1
- package/src/materialization/bars/ranged.js +1 -1
- package/src/materialization/composition.js +152 -0
- package/src/materialization/compositionSnapshot.js +114 -0
- package/src/materialization/density.js +1 -1
- package/src/materialization/dependencies.js +47 -73
- package/src/materialization/encodings.js +14 -28
- package/src/materialization/facetGuides/index.js +2 -0
- package/src/materialization/facetGuides/legacyCategorical.js +125 -0
- package/src/materialization/facetGuides/placement.js +126 -0
- package/src/materialization/facetGuides/preparation.js +188 -0
- package/src/materialization/facets.js +214 -0
- package/src/materialization/graphicHierarchy.js +15 -5
- package/src/materialization/guides/resources.js +46 -0
- package/src/materialization/marks/capabilities.js +141 -0
- package/src/materialization/marks/index.js +105 -0
- package/src/materialization/marks/policies.js +87 -0
- package/src/materialization/planner.js +105 -12
- package/src/materialization/rect.js +138 -0
- package/src/materialization/rectConfig.js +38 -0
- package/src/materialization/rowEncoding.js +33 -0
- package/src/materialization/scaleGuideDependencies.js +94 -0
- package/src/materialization/scales/map.js +40 -0
- package/src/materialization/scales/policies/arc.js +37 -0
- package/src/materialization/scales/policies/bar.js +132 -0
- package/src/materialization/scales/policies/series.js +76 -0
- package/src/materialization/scales/resolve.js +326 -0
- package/src/materialization/selection/items/arc.js +29 -0
- package/src/materialization/selection/items/bar.js +255 -0
- package/src/materialization/selection/items/common.js +131 -0
- package/src/materialization/selection/items/index.js +6 -0
- package/src/materialization/selection/items/path.js +59 -0
- package/src/materialization/selection/items/point.js +55 -0
- package/src/materialization/selection/items/rect.js +23 -0
- package/src/materialization/selection/items/rule.js +25 -0
- package/src/materialization/selection/policies/arc.js +10 -0
- package/src/materialization/selection/policies/area.js +1 -1
- package/src/materialization/selection/policies/bar.js +1 -1
- package/src/materialization/selection/policies/index.js +5 -1
- package/src/materialization/selection/policies/line.js +1 -1
- package/src/materialization/selection/policies/point.js +1 -1
- package/src/materialization/selection/policies/rect.js +10 -0
- package/src/materialization/selection/policies/rule.js +1 -1
- package/src/materialization/selection/styles.js +25 -17
- package/src/materialization/text.js +156 -0
- package/src/renderers/canvas/index.js +68 -22
- package/src/renderers/canvas/text.js +14 -1
- package/types/extension.d.ts +5 -1
- package/types/index.d.ts +56 -1
- package/types/program.d.ts +664 -27
- package/src/actions/encodings/color.js +0 -329
- package/src/actions/encodings/position.js +0 -176
- package/src/actions/marks/line.js +0 -248
- package/src/grammar/regression.js +0 -578
- package/src/grammar/scales.js +0 -1
- package/src/materialization/marks.js +0 -110
- package/src/materialization/selection/items.js +0 -503
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import { deriveBarAggregates } from "../../grammar/bars/aggregate.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
mapContinuousScaleValues,
|
|
4
|
+
mapOrdinalPositionValues
|
|
5
|
+
} from "../../grammar/scales/index.js";
|
|
3
6
|
import { sameOrderedValues } from "../../core/validation.js";
|
|
4
7
|
import {
|
|
5
8
|
DEFAULT_BAR_STROKE,
|
|
6
9
|
DEFAULT_BAR_STROKE_WIDTH
|
|
7
10
|
} from "./resolve.js";
|
|
8
11
|
import { resolveBarWidth } from "../../grammar/bars/geometry.js";
|
|
12
|
+
import { DEFAULT_SERIES_BASELINE } from "../../grammar/seriesLayout.js";
|
|
13
|
+
import {
|
|
14
|
+
BAR_ORIENTATIONS,
|
|
15
|
+
resolveBarChannels,
|
|
16
|
+
resolveBarOffsetChannel
|
|
17
|
+
} from "../../grammar/bars/policy.js";
|
|
9
18
|
|
|
10
19
|
export function deriveGroupedRectangles(required, resolved, widthConfig) {
|
|
11
20
|
const { dataset, layer } = required;
|
|
12
21
|
const xScale = resolved.resolvedScales[required.xEncoding.scale];
|
|
13
22
|
const yScale = resolved.resolvedScales[required.yEncoding.scale];
|
|
23
|
+
const channels = resolveBarChannels(layer);
|
|
24
|
+
const vertical = channels.orientation === BAR_ORIENTATIONS.vertical;
|
|
25
|
+
const categoryScale = vertical ? xScale : yScale;
|
|
26
|
+
const measureScale = vertical ? yScale : xScale;
|
|
27
|
+
const offsetChannel = resolveBarOffsetChannel(layer);
|
|
14
28
|
const colorEncoding = layer.encoding?.color;
|
|
15
|
-
const offsetEncoding = layer.encoding?.
|
|
29
|
+
const offsetEncoding = layer.encoding?.[offsetChannel];
|
|
16
30
|
const colorScale = resolved.resolvedScales[colorEncoding?.scale];
|
|
17
31
|
const offsetScale = resolved.resolvedScales[offsetEncoding?.scale];
|
|
18
32
|
|
|
@@ -23,16 +37,18 @@ export function deriveGroupedRectangles(required, resolved, widthConfig) {
|
|
|
23
37
|
offsetScale === undefined
|
|
24
38
|
) {
|
|
25
39
|
throw new Error(
|
|
26
|
-
`Grouped bar mark "${layer.id}" requires matching color and
|
|
40
|
+
`Grouped bar mark "${layer.id}" requires matching color and ${offsetChannel} scales.`
|
|
27
41
|
);
|
|
28
42
|
}
|
|
29
43
|
if (!sameOrderedValues(colorScale.domain, offsetScale.domain)) {
|
|
30
44
|
throw new Error(
|
|
31
|
-
`Grouped bar mark "${layer.id}" requires matching color and
|
|
45
|
+
`Grouped bar mark "${layer.id}" requires matching color and ${offsetChannel} domains.`
|
|
32
46
|
);
|
|
33
47
|
}
|
|
34
48
|
|
|
35
|
-
const
|
|
49
|
+
const categoryIndex = new Map(
|
|
50
|
+
categoryScale.domain.map((value, index) => [value, index])
|
|
51
|
+
);
|
|
36
52
|
const offsetIndex = new Map(
|
|
37
53
|
offsetScale.domain.map((value, index) => [value, index])
|
|
38
54
|
);
|
|
@@ -43,18 +59,21 @@ export function deriveGroupedRectangles(required, resolved, widthConfig) {
|
|
|
43
59
|
])
|
|
44
60
|
);
|
|
45
61
|
const offsetMidpoint = (offsetScale.range[0] + offsetScale.range[1]) / 2;
|
|
46
|
-
const
|
|
62
|
+
const categoryDirection = Math.sign(
|
|
63
|
+
categoryScale.step ?? categoryScale.range[1] - categoryScale.range[0]
|
|
64
|
+
) || 1;
|
|
47
65
|
const offsetDirection = Math.sign(offsetScale.step) || 1;
|
|
48
66
|
const width = resolveBarWidth(widthConfig, offsetScale.bandwidth);
|
|
49
67
|
const baseline = mapContinuousScaleValues(
|
|
50
|
-
[
|
|
51
|
-
|
|
68
|
+
[DEFAULT_SERIES_BASELINE],
|
|
69
|
+
measureScale
|
|
52
70
|
)[0];
|
|
53
71
|
const cells = [...deriveBarAggregates(dataset.values, layer).values].sort(
|
|
54
72
|
(left, right) =>
|
|
55
|
-
(
|
|
56
|
-
? left.
|
|
57
|
-
:
|
|
73
|
+
(categoryScale.type === "time"
|
|
74
|
+
? left[channels.category] - right[channels.category]
|
|
75
|
+
: categoryIndex.get(left[channels.category]) -
|
|
76
|
+
categoryIndex.get(right[channels.category])) ||
|
|
58
77
|
offsetIndex.get(left.color) - offsetIndex.get(right.color)
|
|
59
78
|
);
|
|
60
79
|
const existing = resolved.graphicSpec.objects[layer.id].items;
|
|
@@ -62,41 +81,60 @@ export function deriveGroupedRectangles(required, resolved, widthConfig) {
|
|
|
62
81
|
const appearance = config.barAppearance ?? {};
|
|
63
82
|
|
|
64
83
|
return cells.map((cell, index) => {
|
|
65
|
-
const
|
|
84
|
+
const categoryValue = cell[channels.category];
|
|
85
|
+
const category = categoryIndex.get(categoryValue);
|
|
66
86
|
const offset = offsetIndex.get(cell.color);
|
|
67
|
-
if ((
|
|
87
|
+
if ((categoryScale.type !== "time" && category === undefined) || offset === undefined) {
|
|
68
88
|
throw new Error("Grouped bar value is outside a resolved ordinal domain.");
|
|
69
89
|
}
|
|
70
90
|
|
|
71
|
-
const temporal = xScale.type === "time";
|
|
72
|
-
const categoryStart = temporal
|
|
73
|
-
? mapContinuousScaleValues(
|
|
74
|
-
[cell.x],
|
|
75
|
-
xScale
|
|
76
|
-
)[0] - xScale.bandwidth / 2
|
|
77
|
-
: (xScale.start ?? xScale.range[0]) + category * xScale.step;
|
|
78
91
|
const offsetCenter = offsetScale.start + offset * offsetScale.step +
|
|
79
92
|
offsetDirection * offsetScale.bandwidth / 2;
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
? categoryStart + offsetCenter
|
|
84
|
-
: categoryStart + xScale.step / 2 +
|
|
85
|
-
direction * (offsetCenter - offsetMidpoint);
|
|
86
|
-
const x = temporal || (xScale.step > 0 && offsetScale.step > 0)
|
|
87
|
-
? categoryStart + offsetScale.start + offset * offsetScale.step +
|
|
88
|
-
(offsetScale.bandwidth - width) / 2
|
|
89
|
-
: center - width / 2;
|
|
90
|
-
const valueY = mapContinuousScaleValues(
|
|
91
|
-
[cell.y],
|
|
92
|
-
yScale
|
|
93
|
+
const valuePosition = mapContinuousScaleValues(
|
|
94
|
+
[cell[channels.measure]],
|
|
95
|
+
measureScale
|
|
93
96
|
)[0];
|
|
97
|
+
let geometry;
|
|
98
|
+
if (vertical) {
|
|
99
|
+
// Preserve the established vertical arithmetic order exactly. Primitive
|
|
100
|
+
// baselines intentionally compare concrete floating-point coordinates.
|
|
101
|
+
const temporal = xScale.type === "time";
|
|
102
|
+
const categoryStart = temporal
|
|
103
|
+
? mapContinuousScaleValues([cell.x], xScale)[0] - xScale.bandwidth / 2
|
|
104
|
+
: (xScale.start ?? xScale.range[0]) + category * xScale.step;
|
|
105
|
+
const center = temporal
|
|
106
|
+
? categoryStart + offsetCenter
|
|
107
|
+
: xScale.step > 0 && offsetScale.step > 0
|
|
108
|
+
? categoryStart + offsetCenter
|
|
109
|
+
: categoryStart + xScale.step / 2 +
|
|
110
|
+
categoryDirection * (offsetCenter - offsetMidpoint);
|
|
111
|
+
const x = temporal || (xScale.step > 0 && offsetScale.step > 0)
|
|
112
|
+
? categoryStart + offsetScale.start + offset * offsetScale.step +
|
|
113
|
+
(offsetScale.bandwidth - width) / 2
|
|
114
|
+
: center - width / 2;
|
|
115
|
+
geometry = {
|
|
116
|
+
x,
|
|
117
|
+
y: Math.min(valuePosition, baseline),
|
|
118
|
+
width,
|
|
119
|
+
height: Math.abs(baseline - valuePosition)
|
|
120
|
+
};
|
|
121
|
+
} else {
|
|
122
|
+
const categoryCenter = mapOrdinalPositionValues(
|
|
123
|
+
[categoryValue],
|
|
124
|
+
categoryScale
|
|
125
|
+
)[0];
|
|
126
|
+
const center = categoryCenter + categoryDirection *
|
|
127
|
+
(offsetCenter - offsetMidpoint);
|
|
128
|
+
geometry = {
|
|
129
|
+
x: Math.min(valuePosition, baseline),
|
|
130
|
+
y: center - width / 2,
|
|
131
|
+
width: Math.abs(baseline - valuePosition),
|
|
132
|
+
height: width
|
|
133
|
+
};
|
|
134
|
+
}
|
|
94
135
|
|
|
95
136
|
return {
|
|
96
|
-
|
|
97
|
-
y: Math.min(valueY, baseline),
|
|
98
|
-
width,
|
|
99
|
-
height: Math.abs(baseline - valueY),
|
|
137
|
+
...geometry,
|
|
100
138
|
fill: colors.get(cell.color),
|
|
101
139
|
stroke: appearance.stroke === false
|
|
102
140
|
? "transparent"
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
mapOrdinalValues,
|
|
9
9
|
readNominalField,
|
|
10
10
|
readQuantitativeField
|
|
11
|
-
} from "../../grammar/scales.js";
|
|
11
|
+
} from "../../grammar/scales/index.js";
|
|
12
12
|
import { resolveBarColorLayout } from "../../grammar/bars/policy.js";
|
|
13
13
|
import { layoutSeriesPartition } from "../../grammar/seriesLayout.js";
|
|
14
14
|
import {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mapContinuousScaleValues, mapOrdinalPositionValues, mapOrdinalValues } from "../../grammar/scales.js";
|
|
1
|
+
import { mapContinuousScaleValues, mapOrdinalPositionValues, mapOrdinalValues } from "../../grammar/scales/index.js";
|
|
2
2
|
import { resolveBarWidth } from "../../grammar/bars/geometry.js";
|
|
3
3
|
import { DEFAULT_BAR_FILL, DEFAULT_BAR_STROKE, DEFAULT_BAR_STROKE_WIDTH } from "./resolve.js";
|
|
4
4
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { freezeOwned } from "../core/immutable.js";
|
|
2
|
+
import {
|
|
3
|
+
requireSingleOrderedGraphicByType
|
|
4
|
+
} from "../grammar/schemas/graphicTree.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveCompositionLayout,
|
|
7
|
+
resolveCompositionSnapshotPlacement
|
|
8
|
+
} from "../layout/composition.js";
|
|
9
|
+
import { namespaceGraphicSnapshot } from "./compositionSnapshot.js";
|
|
10
|
+
|
|
11
|
+
const ZERO_MARGIN = freezeOwned({ top: 0, right: 0, bottom: 0, left: 0 });
|
|
12
|
+
|
|
13
|
+
export function requireChildCanvas(program, id) {
|
|
14
|
+
const { object } = requireSingleOrderedGraphicByType(program.graphicSpec, "canvas");
|
|
15
|
+
const { width, height } = object.properties ?? {};
|
|
16
|
+
if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
|
|
17
|
+
throw new Error(`Composition child "${id}" requires a complete positive Canvas size.`);
|
|
18
|
+
}
|
|
19
|
+
return object;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function compositionChildDescriptor(id, program) {
|
|
23
|
+
const canvas = requireChildCanvas(program, id);
|
|
24
|
+
const size = program.materializationConfigs.canvas?.size ?? {
|
|
25
|
+
width: "explicit",
|
|
26
|
+
height: "explicit"
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
id,
|
|
30
|
+
width: canvas.properties.width,
|
|
31
|
+
height: canvas.properties.height,
|
|
32
|
+
widthMode: size.width,
|
|
33
|
+
heightMode: size.height
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveCompositionProgramLayout(program) {
|
|
38
|
+
program._assertCompositionProgram("resolveCompositionProgramLayout");
|
|
39
|
+
const spec = program.compositionSpec;
|
|
40
|
+
return resolveCompositionLayout({
|
|
41
|
+
direction: spec.direction,
|
|
42
|
+
children: spec.children.map(id => compositionChildDescriptor(id, program.children[id])),
|
|
43
|
+
gap: spec.gap,
|
|
44
|
+
align: spec.align,
|
|
45
|
+
padding: spec.padding
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resizeChildForSnapshot(program, placement) {
|
|
50
|
+
const canvas = requireSingleOrderedGraphicByType(program.graphicSpec, "canvas");
|
|
51
|
+
const widthChanged = canvas.object.properties.width !== placement.width;
|
|
52
|
+
const heightChanged = canvas.object.properties.height !== placement.height;
|
|
53
|
+
if (!widthChanged && !heightChanged) return program;
|
|
54
|
+
|
|
55
|
+
if (program.compositionSpec !== undefined) {
|
|
56
|
+
return program;
|
|
57
|
+
}
|
|
58
|
+
if (typeof program.editCanvas !== "function") {
|
|
59
|
+
throw new Error("Automatic composition sizing requires child editCanvas support.");
|
|
60
|
+
}
|
|
61
|
+
return program.editCanvas({
|
|
62
|
+
...(widthChanged ? { width: placement.width } : {}),
|
|
63
|
+
...(heightChanged ? { height: placement.height } : {})
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolveSnapshotPlacement(program, placement, layout) {
|
|
68
|
+
if (program.compositionSpec === undefined) return placement;
|
|
69
|
+
const canvas = requireSingleOrderedGraphicByType(program.graphicSpec, "canvas");
|
|
70
|
+
return resolveCompositionSnapshotPlacement({
|
|
71
|
+
direction: layout.direction,
|
|
72
|
+
align: layout.align,
|
|
73
|
+
placement,
|
|
74
|
+
width: canvas.object.properties.width,
|
|
75
|
+
height: canvas.object.properties.height
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function itemDefinitions(object) {
|
|
80
|
+
return object.items.map(item => ({
|
|
81
|
+
type: item.type ?? object.type,
|
|
82
|
+
properties: item.properties
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function attachSnapshotObject(program, snapshot, id, parent) {
|
|
87
|
+
const object = snapshot.objects[id];
|
|
88
|
+
let next = program.createGraphics({
|
|
89
|
+
id,
|
|
90
|
+
type: object.type,
|
|
91
|
+
parent,
|
|
92
|
+
...(object.items !== undefined && object.type !== "collection"
|
|
93
|
+
? { length: object.items.length }
|
|
94
|
+
: {})
|
|
95
|
+
});
|
|
96
|
+
for (const [property, value] of Object.entries(object.properties ?? {})) {
|
|
97
|
+
next = next.editGraphics({ target: id, property, value });
|
|
98
|
+
}
|
|
99
|
+
if (object.items !== undefined) {
|
|
100
|
+
next = next.editGraphics({
|
|
101
|
+
target: id,
|
|
102
|
+
property: "items",
|
|
103
|
+
value: itemDefinitions(object)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
for (const childId of object.children ?? []) {
|
|
107
|
+
next = attachSnapshotObject(next, snapshot, childId, id);
|
|
108
|
+
}
|
|
109
|
+
return next;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function clearCompositionChildren(program) {
|
|
113
|
+
const canvas = program.graphicSpec.objects.canvas;
|
|
114
|
+
let next = program;
|
|
115
|
+
for (const id of canvas?.children ?? []) {
|
|
116
|
+
next = next.editGraphics({ target: id, remove: true });
|
|
117
|
+
}
|
|
118
|
+
return next;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function materializeCompositionGraphics(program) {
|
|
122
|
+
program._assertCompositionProgram("materializeCompositionGraphics");
|
|
123
|
+
const layout = resolveCompositionProgramLayout(program);
|
|
124
|
+
let next = clearCompositionChildren(program);
|
|
125
|
+
if (next.graphicSpec.objects.canvas === undefined) {
|
|
126
|
+
next = next.createGraphics({ id: "canvas", type: "canvas" });
|
|
127
|
+
}
|
|
128
|
+
for (const [property, value] of Object.entries({
|
|
129
|
+
width: layout.width,
|
|
130
|
+
height: layout.height,
|
|
131
|
+
background: "white"
|
|
132
|
+
})) {
|
|
133
|
+
next = next.editGraphics({ target: "canvas", property, value });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const placement of layout.children) {
|
|
137
|
+
const source = program.children[placement.id];
|
|
138
|
+
const child = resizeChildForSnapshot(source, placement);
|
|
139
|
+
const snapshotPlacement = resolveSnapshotPlacement(child, placement, layout);
|
|
140
|
+
const snapshot = namespaceGraphicSnapshot(child.graphicSpec, {
|
|
141
|
+
namespace: `${program.compositionSpec.id}-${placement.id}`,
|
|
142
|
+
x: snapshotPlacement.x,
|
|
143
|
+
y: snapshotPlacement.y
|
|
144
|
+
});
|
|
145
|
+
next = attachSnapshotObject(next, snapshot, snapshot.order[0], "canvas");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return next._withCanvasConfig({
|
|
149
|
+
margin: ZERO_MARGIN,
|
|
150
|
+
size: { width: "auto", height: "auto" }
|
|
151
|
+
});
|
|
152
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { cloneAndFreeze, freezeOwned, isPlainObject } from "../core/immutable.js";
|
|
2
|
+
import {
|
|
3
|
+
requireSingleOrderedGraphicByType,
|
|
4
|
+
walkGraphicTreeEvents
|
|
5
|
+
} from "../grammar/schemas/graphicTree.js";
|
|
6
|
+
|
|
7
|
+
function validateNamespace(namespace) {
|
|
8
|
+
if (typeof namespace !== "string" || namespace.length === 0) {
|
|
9
|
+
throw new TypeError("Graphic snapshot namespace must be a non-empty string.");
|
|
10
|
+
}
|
|
11
|
+
return namespace;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function validatePlacement(value, property) {
|
|
15
|
+
if (!Number.isFinite(value)) {
|
|
16
|
+
throw new TypeError(`Graphic snapshot ${property} must be a finite number.`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function validateCanvas(canvas, id) {
|
|
22
|
+
const { width, height } = canvas.properties ?? {};
|
|
23
|
+
if (!Number.isFinite(width) || width <= 0) {
|
|
24
|
+
throw new RangeError(`Child Canvas "${id}" width must be a positive finite number.`);
|
|
25
|
+
}
|
|
26
|
+
if (!Number.isFinite(height) || height <= 0) {
|
|
27
|
+
throw new RangeError(`Child Canvas "${id}" height must be a positive finite number.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function namespaceGraphicId(namespace, id) {
|
|
32
|
+
const encode = value => Array.from(value, character =>
|
|
33
|
+
character.codePointAt(0).toString(16).padStart(6, "0")
|
|
34
|
+
).join("");
|
|
35
|
+
return `g${encode(namespace)}_${encode(id)}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function rewriteItem(item, namespace) {
|
|
39
|
+
if (!isPlainObject(item) || typeof item.id !== "string" || item.id.length === 0) {
|
|
40
|
+
throw new TypeError("Graphic snapshot items require non-empty string ids.");
|
|
41
|
+
}
|
|
42
|
+
return freezeOwned({
|
|
43
|
+
...cloneAndFreeze(item),
|
|
44
|
+
id: namespaceGraphicId(namespace, item.id)
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function rewriteObject(object, namespace, rootCanvasId, placement, extraRoots) {
|
|
49
|
+
const children = object.children ?? [];
|
|
50
|
+
const rewritten = {
|
|
51
|
+
...cloneAndFreeze(object),
|
|
52
|
+
...(object.items === undefined
|
|
53
|
+
? {}
|
|
54
|
+
: { items: freezeOwned(object.items.map(item => rewriteItem(item, namespace))) }),
|
|
55
|
+
...(children.length === 0 && rootCanvasId === undefined
|
|
56
|
+
? {}
|
|
57
|
+
: {
|
|
58
|
+
children: freezeOwned([
|
|
59
|
+
...children.map(id => namespaceGraphicId(namespace, id)),
|
|
60
|
+
...(rootCanvasId === undefined
|
|
61
|
+
? []
|
|
62
|
+
: extraRoots.map(id => namespaceGraphicId(namespace, id)))
|
|
63
|
+
])
|
|
64
|
+
})
|
|
65
|
+
};
|
|
66
|
+
if (rootCanvasId !== undefined) {
|
|
67
|
+
rewritten.properties = freezeOwned({
|
|
68
|
+
...rewritten.properties,
|
|
69
|
+
x: placement.x,
|
|
70
|
+
y: placement.y
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return freezeOwned(rewritten);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function namespaceGraphicSnapshot(
|
|
77
|
+
graphicSpec,
|
|
78
|
+
{ namespace, x = 0, y = 0 } = {}
|
|
79
|
+
) {
|
|
80
|
+
const resolvedNamespace = validateNamespace(namespace);
|
|
81
|
+
const placement = {
|
|
82
|
+
x: validatePlacement(x, "x"),
|
|
83
|
+
y: validatePlacement(y, "y")
|
|
84
|
+
};
|
|
85
|
+
if (
|
|
86
|
+
!isPlainObject(graphicSpec) ||
|
|
87
|
+
!isPlainObject(graphicSpec.objects) ||
|
|
88
|
+
!Array.isArray(graphicSpec.order)
|
|
89
|
+
) {
|
|
90
|
+
throw new TypeError("Graphic snapshot requires objects and order.");
|
|
91
|
+
}
|
|
92
|
+
walkGraphicTreeEvents(graphicSpec, {});
|
|
93
|
+
const { id: canvasId, object: canvas } =
|
|
94
|
+
requireSingleOrderedGraphicByType(graphicSpec, "canvas");
|
|
95
|
+
validateCanvas(canvas, canvasId);
|
|
96
|
+
const extraRoots = graphicSpec.order.filter(id => id !== canvasId);
|
|
97
|
+
const objects = Object.fromEntries(Object.entries(graphicSpec.objects).map(
|
|
98
|
+
([id, object]) => [
|
|
99
|
+
namespaceGraphicId(resolvedNamespace, id),
|
|
100
|
+
rewriteObject(
|
|
101
|
+
object,
|
|
102
|
+
resolvedNamespace,
|
|
103
|
+
id === canvasId ? canvasId : undefined,
|
|
104
|
+
placement,
|
|
105
|
+
id === canvasId ? extraRoots : []
|
|
106
|
+
)
|
|
107
|
+
]
|
|
108
|
+
));
|
|
109
|
+
const rootId = namespaceGraphicId(resolvedNamespace, canvasId);
|
|
110
|
+
return freezeOwned({
|
|
111
|
+
objects: freezeOwned(objects),
|
|
112
|
+
order: freezeOwned([rootId])
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -1,33 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
canDeferScaleConsumerApplication,
|
|
3
|
+
getExistingMarkRematerializationStep,
|
|
4
|
+
getMarkMaterializationStep,
|
|
5
|
+
getSourceDependentMarkSteps
|
|
6
|
+
} from "./marks/index.js";
|
|
2
7
|
import { requireLayer } from "../selectors/layers.js";
|
|
3
8
|
import {
|
|
4
9
|
applyMaterializationPlan,
|
|
5
10
|
buildMaterializationPlan
|
|
6
11
|
} from "./planner.js";
|
|
12
|
+
import { hasMaterializedLegend } from "./legends.js";
|
|
7
13
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "./
|
|
11
|
-
|
|
12
|
-
function usesPositionalScale(program, id) {
|
|
13
|
-
return program.semanticSpec.layers.some(layer =>
|
|
14
|
-
["x", "y", "x2", "y2", "xOffset"].some(
|
|
15
|
-
channel => layer.encoding?.[channel]?.scale === id
|
|
16
|
-
)
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function needsCanvasScaleRematerialization(program, scale) {
|
|
21
|
-
return (
|
|
22
|
-
(scale.range === "auto" ||
|
|
23
|
-
program.semanticSpec.guides.axis?.x?.scale === scale.id ||
|
|
24
|
-
program.semanticSpec.guides.axis?.y?.scale === scale.id ||
|
|
25
|
-
program.semanticSpec.guides.grid?.horizontal?.scale === scale.id ||
|
|
26
|
-
program.semanticSpec.guides.grid?.vertical?.scale === scale.id) &&
|
|
27
|
-
program.resolvedScales[scale.id] !== undefined &&
|
|
28
|
-
usesPositionalScale(program, scale.id)
|
|
29
|
-
);
|
|
30
|
-
}
|
|
14
|
+
needsCanvasScaleRematerialization,
|
|
15
|
+
planScaleGuideRematerialization
|
|
16
|
+
} from "./scaleGuideDependencies.js";
|
|
31
17
|
|
|
32
18
|
function hasTitle(program) {
|
|
33
19
|
return (
|
|
@@ -37,20 +23,36 @@ function hasTitle(program) {
|
|
|
37
23
|
}
|
|
38
24
|
|
|
39
25
|
export function planCanvasRematerialization(program) {
|
|
26
|
+
const marks = [];
|
|
27
|
+
for (const layer of program.semanticSpec.layers) {
|
|
28
|
+
const step = getMarkMaterializationStep(program, layer);
|
|
29
|
+
if (step !== undefined) marks.push(step);
|
|
30
|
+
}
|
|
31
|
+
const deferredMarkIds = new Set(
|
|
32
|
+
marks
|
|
33
|
+
.map(step => step.args.id)
|
|
34
|
+
);
|
|
40
35
|
const scales = [];
|
|
41
36
|
for (const scale of program.semanticSpec.scales) {
|
|
42
37
|
if (needsCanvasScaleRematerialization(program, scale)) {
|
|
38
|
+
const deferredConsumers = program.semanticSpec.layers.filter(layer =>
|
|
39
|
+
canDeferScaleConsumerApplication(layer) &&
|
|
40
|
+
Object.values(layer.encoding ?? {}).some(
|
|
41
|
+
encoding => encoding?.scale === scale.id
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
const canDeferMarks = deferredConsumers.length === 0 ||
|
|
45
|
+
deferredConsumers.every(layer => deferredMarkIds.has(layer.id));
|
|
43
46
|
scales.push({
|
|
44
47
|
op: "rematerializeScale",
|
|
45
|
-
args: {
|
|
48
|
+
args: {
|
|
49
|
+
id: scale.id,
|
|
50
|
+
guides: false,
|
|
51
|
+
...(canDeferMarks ? { marks: false } : {})
|
|
52
|
+
}
|
|
46
53
|
});
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
|
-
const marks = [];
|
|
50
|
-
for (const layer of program.semanticSpec.layers) {
|
|
51
|
-
const step = getMarkMaterializationStep(program, layer);
|
|
52
|
-
if (step !== undefined) marks.push(step);
|
|
53
|
-
}
|
|
54
56
|
const guides = program.semanticSpec.scales.flatMap(scale =>
|
|
55
57
|
needsCanvasScaleRematerialization(program, scale)
|
|
56
58
|
? planScaleGuideRematerialization(program, scale.id)
|
|
@@ -63,42 +65,6 @@ export function planCanvasRematerialization(program) {
|
|
|
63
65
|
return buildMaterializationPlan({ scales, marks, guides, layout });
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
export function planScaleGuideRematerialization(program, id) {
|
|
67
|
-
const guides = [];
|
|
68
|
-
const objects = program.graphicSpec?.objects ?? {};
|
|
69
|
-
const guideConfigs = program.guideConfigs ?? {};
|
|
70
|
-
if (
|
|
71
|
-
objects.xAxisLine &&
|
|
72
|
-
program.semanticSpec.guides.axis?.x?.scale === id
|
|
73
|
-
) {
|
|
74
|
-
guides.push({ op: "editXAxisLine" });
|
|
75
|
-
}
|
|
76
|
-
if (
|
|
77
|
-
objects.yAxisLine &&
|
|
78
|
-
program.semanticSpec.guides.axis?.y?.scale === id
|
|
79
|
-
) {
|
|
80
|
-
guides.push({ op: "editYAxisLine" });
|
|
81
|
-
}
|
|
82
|
-
for (const channel of ["x", "y"]) {
|
|
83
|
-
for (const component of ["ticks", "labels", "title"]) {
|
|
84
|
-
if (guideConfigs.axis?.[channel]?.[component]?.scale === id) {
|
|
85
|
-
const suffix = component[0].toUpperCase() + component.slice(1);
|
|
86
|
-
guides.push({ op: `edit${channel.toUpperCase()}Axis${suffix}` });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (guideConfigs.grid?.horizontal?.scale === id) {
|
|
91
|
-
guides.push({ op: "rematerializeHorizontalGrid" });
|
|
92
|
-
}
|
|
93
|
-
if (guideConfigs.grid?.vertical?.scale === id) {
|
|
94
|
-
guides.push({ op: "rematerializeVerticalGrid" });
|
|
95
|
-
}
|
|
96
|
-
if (materializedLegendUsesScale(program, id)) {
|
|
97
|
-
guides.push({ op: "rematerializeLegend" });
|
|
98
|
-
}
|
|
99
|
-
return buildMaterializationPlan({ guides });
|
|
100
|
-
}
|
|
101
|
-
|
|
102
68
|
export function planLayerDataRematerialization(program, id) {
|
|
103
69
|
const layer = requireLayer(program, id);
|
|
104
70
|
const scaleIds = [...new Set(
|
|
@@ -106,22 +72,29 @@ export function planLayerDataRematerialization(program, id) {
|
|
|
106
72
|
.map(encoding => encoding?.scale)
|
|
107
73
|
.filter(scale => scale !== undefined)
|
|
108
74
|
)];
|
|
75
|
+
const markStep = getMarkMaterializationStep(program, layer);
|
|
109
76
|
const scales = scaleIds.map(scale => ({
|
|
110
77
|
op: "rematerializeScale",
|
|
111
|
-
args: {
|
|
78
|
+
args: {
|
|
79
|
+
id: scale,
|
|
80
|
+
guides: false,
|
|
81
|
+
...(markStep === undefined ? {} : { marks: false })
|
|
82
|
+
}
|
|
112
83
|
}));
|
|
113
|
-
const
|
|
114
|
-
|
|
84
|
+
const marks = [
|
|
85
|
+
...(markStep === undefined ? [] : [markStep]),
|
|
86
|
+
...getSourceDependentMarkSteps(program, id)
|
|
87
|
+
];
|
|
115
88
|
if (scales.length > 0 || marks.length > 0) {
|
|
116
89
|
const guides = scaleIds.flatMap(scale =>
|
|
117
90
|
planScaleGuideRematerialization(program, scale)
|
|
118
91
|
);
|
|
119
92
|
return buildMaterializationPlan({ scales, marks, guides });
|
|
120
93
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
94
|
+
const existingStep = getExistingMarkRematerializationStep(program, layer);
|
|
95
|
+
return buildMaterializationPlan({
|
|
96
|
+
marks: existingStep === undefined ? [] : [existingStep]
|
|
97
|
+
});
|
|
125
98
|
}
|
|
126
99
|
|
|
127
100
|
export function applyLayerDataRematerialization(program, id) {
|
|
@@ -132,3 +105,4 @@ export function applyLayerDataRematerialization(program, id) {
|
|
|
132
105
|
}
|
|
133
106
|
|
|
134
107
|
export { applyMaterializationPlan } from "./planner.js";
|
|
108
|
+
export { planScaleGuideRematerialization };
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { hasMaterializedLegend } from "./legends.js";
|
|
2
2
|
import { requireLayer } from "../selectors/layers.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getEncodingMaterializationStages
|
|
5
|
+
} from "./marks/index.js";
|
|
4
6
|
import { buildMaterializationPlan } from "./planner.js";
|
|
7
|
+
import { getSourceDependentMarkSteps } from "./marks/index.js";
|
|
5
8
|
|
|
6
9
|
export function planEncodingRematerialization(program, {
|
|
7
10
|
target,
|
|
@@ -14,33 +17,16 @@ export function planEncodingRematerialization(program, {
|
|
|
14
17
|
`Unknown encoding materialization target "${target}"`
|
|
15
18
|
);
|
|
16
19
|
|
|
17
|
-
const scales =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
layer.mark?.type === "area" &&
|
|
28
|
-
channel === "color" &&
|
|
29
|
-
scale !== undefined
|
|
30
|
-
) {
|
|
31
|
-
for (const candidate of program.semanticSpec.layers) {
|
|
32
|
-
if (
|
|
33
|
-
candidate.mark?.type === "area" &&
|
|
34
|
-
candidate.encoding?.color?.scale === scale
|
|
35
|
-
) {
|
|
36
|
-
const step = getMarkRematerializationStep(candidate);
|
|
37
|
-
if (step !== undefined) marks.push(step);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
const step = getMarkRematerializationStep(layer);
|
|
42
|
-
if (step !== undefined) marks.push(step);
|
|
43
|
-
}
|
|
20
|
+
const { scales, marks: directMarks } = getEncodingMaterializationStages(
|
|
21
|
+
program,
|
|
22
|
+
layer,
|
|
23
|
+
channel,
|
|
24
|
+
scale
|
|
25
|
+
);
|
|
26
|
+
const marks = [
|
|
27
|
+
...directMarks,
|
|
28
|
+
...getSourceDependentMarkSteps(program, target)
|
|
29
|
+
];
|
|
44
30
|
|
|
45
31
|
const guides = [];
|
|
46
32
|
if (hasMaterializedLegend(program)) {
|