ggaction 0.0.2 → 0.0.3
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 +28 -0
- package/README.md +12 -1
- package/package.json +6 -1
- 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/encodings/appearance.js +11 -0
- package/src/actions/encodings/barWidth.js +5 -3
- package/src/actions/encodings/color.js +120 -25
- package/src/actions/encodings/index.js +2 -0
- package/src/actions/encodings/offset.js +90 -72
- package/src/actions/encodings/position/apply.js +66 -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 +57 -8
- package/src/actions/encodings/position.js +44 -90
- package/src/actions/encodings/ranged.js +28 -6
- package/src/actions/encodings/shared.js +17 -21
- 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 +21 -18
- 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 +9 -4
- package/src/actions/guides/axes/titles.js +7 -5
- package/src/actions/guides/grids/grid.js +144 -15
- package/src/actions/guides/grids/resolve.js +5 -13
- 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 +3 -1
- 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 +5 -5
- package/src/actions/guides/legends/edit.js +2 -6
- 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 +64 -0
- package/src/actions/guides/legends/size.js +12 -14
- package/src/actions/guides/polar/axes/facade.js +219 -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/axes.js +55 -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/index.js +4 -0
- 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/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 +25 -3
- package/src/actions/regression/components.js +1 -1
- package/src/actions/regression/create.js +18 -1
- 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.js +15 -6
- package/src/actions/scales/definitions.js +13 -4
- package/src/actions/scales/edit.js +42 -19
- package/src/actions/scales/materialize.js +57 -408
- 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 +200 -2
- package/src/core/action.js +10 -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/bars/aggregate.js +2 -2
- 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 +20 -1
- package/src/grammar/facets/dependencies.js +185 -0
- package/src/grammar/facets/guides.js +257 -0
- package/src/grammar/facets/scales.js +254 -0
- package/src/grammar/facets.js +122 -0
- package/src/grammar/lineSeries.js +111 -3
- 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/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/text.js +71 -0
- package/src/grammar/ticks.js +50 -13
- 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 +5 -2
- package/src/materialization/bars/grouped.js +75 -37
- package/src/materialization/composition.js +152 -0
- package/src/materialization/compositionSnapshot.js +114 -0
- package/src/materialization/dependencies.js +71 -16
- package/src/materialization/encodings.js +14 -28
- package/src/materialization/facetGuides.js +426 -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.js +220 -14
- 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/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 +24 -16
- package/src/materialization/text.js +156 -0
- package/src/renderers/canvas/index.js +68 -22
- package/types/extension.d.ts +1 -0
- package/types/index.d.ts +56 -1
- package/types/program.d.ts +664 -27
- package/src/actions/marks/line.js +0 -248
- package/src/materialization/selection/items.js +0 -503
|
@@ -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,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
canDeferScaleConsumerApplication,
|
|
3
|
+
getExistingMarkRematerializationStep,
|
|
4
|
+
getMarkMaterializationStep,
|
|
5
|
+
getSourceDependentMarkSteps
|
|
6
|
+
} from "./marks.js";
|
|
2
7
|
import { requireLayer } from "../selectors/layers.js";
|
|
3
8
|
import {
|
|
4
9
|
applyMaterializationPlan,
|
|
@@ -8,22 +13,34 @@ import {
|
|
|
8
13
|
hasMaterializedLegend,
|
|
9
14
|
materializedLegendUsesScale
|
|
10
15
|
} from "./legends.js";
|
|
16
|
+
import { POSITION_ENCODING_CHANNELS } from "../core/vocabulary.js";
|
|
11
17
|
|
|
12
18
|
function usesPositionalScale(program, id) {
|
|
13
19
|
return program.semanticSpec.layers.some(layer =>
|
|
14
|
-
|
|
20
|
+
POSITION_ENCODING_CHANNELS.some(
|
|
15
21
|
channel => layer.encoding?.[channel]?.scale === id
|
|
16
22
|
)
|
|
17
23
|
);
|
|
18
24
|
}
|
|
19
25
|
|
|
26
|
+
function usesRadialScale(program, id) {
|
|
27
|
+
return program.semanticSpec.layers.some(layer =>
|
|
28
|
+
layer.encoding?.radius?.scale === id
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
function needsCanvasScaleRematerialization(program, scale) {
|
|
21
33
|
return (
|
|
22
34
|
(scale.range === "auto" ||
|
|
35
|
+
usesRadialScale(program, scale.id) ||
|
|
23
36
|
program.semanticSpec.guides.axis?.x?.scale === scale.id ||
|
|
24
37
|
program.semanticSpec.guides.axis?.y?.scale === scale.id ||
|
|
38
|
+
program.semanticSpec.guides.axis?.theta?.scale === scale.id ||
|
|
39
|
+
program.semanticSpec.guides.axis?.radius?.scale === scale.id ||
|
|
25
40
|
program.semanticSpec.guides.grid?.horizontal?.scale === scale.id ||
|
|
26
|
-
program.semanticSpec.guides.grid?.vertical?.scale === scale.id
|
|
41
|
+
program.semanticSpec.guides.grid?.vertical?.scale === scale.id ||
|
|
42
|
+
program.semanticSpec.guides.grid?.theta?.scale === scale.id ||
|
|
43
|
+
program.semanticSpec.guides.grid?.radial?.scale === scale.id) &&
|
|
27
44
|
program.resolvedScales[scale.id] !== undefined &&
|
|
28
45
|
usesPositionalScale(program, scale.id)
|
|
29
46
|
);
|
|
@@ -37,20 +54,36 @@ function hasTitle(program) {
|
|
|
37
54
|
}
|
|
38
55
|
|
|
39
56
|
export function planCanvasRematerialization(program) {
|
|
57
|
+
const marks = [];
|
|
58
|
+
for (const layer of program.semanticSpec.layers) {
|
|
59
|
+
const step = getMarkMaterializationStep(program, layer);
|
|
60
|
+
if (step !== undefined) marks.push(step);
|
|
61
|
+
}
|
|
62
|
+
const deferredMarkIds = new Set(
|
|
63
|
+
marks
|
|
64
|
+
.map(step => step.args.id)
|
|
65
|
+
);
|
|
40
66
|
const scales = [];
|
|
41
67
|
for (const scale of program.semanticSpec.scales) {
|
|
42
68
|
if (needsCanvasScaleRematerialization(program, scale)) {
|
|
69
|
+
const deferredConsumers = program.semanticSpec.layers.filter(layer =>
|
|
70
|
+
canDeferScaleConsumerApplication(layer) &&
|
|
71
|
+
Object.values(layer.encoding ?? {}).some(
|
|
72
|
+
encoding => encoding?.scale === scale.id
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
const canDeferMarks = deferredConsumers.length === 0 ||
|
|
76
|
+
deferredConsumers.every(layer => deferredMarkIds.has(layer.id));
|
|
43
77
|
scales.push({
|
|
44
78
|
op: "rematerializeScale",
|
|
45
|
-
args: {
|
|
79
|
+
args: {
|
|
80
|
+
id: scale.id,
|
|
81
|
+
guides: false,
|
|
82
|
+
...(canDeferMarks ? { marks: false } : {})
|
|
83
|
+
}
|
|
46
84
|
});
|
|
47
85
|
}
|
|
48
86
|
}
|
|
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
87
|
const guides = program.semanticSpec.scales.flatMap(scale =>
|
|
55
88
|
needsCanvasScaleRematerialization(program, scale)
|
|
56
89
|
? planScaleGuideRematerialization(program, scale.id)
|
|
@@ -87,12 +120,27 @@ export function planScaleGuideRematerialization(program, id) {
|
|
|
87
120
|
}
|
|
88
121
|
}
|
|
89
122
|
}
|
|
123
|
+
for (const channel of ["theta", "radius"]) {
|
|
124
|
+
const prefix = channel === "theta" ? "Theta" : "Radial";
|
|
125
|
+
for (const component of ["line", "ticks", "labels", "title"]) {
|
|
126
|
+
if (guideConfigs.axis?.[channel]?.[component]?.scale === id) {
|
|
127
|
+
const suffix = component[0].toUpperCase() + component.slice(1);
|
|
128
|
+
guides.push({ op: `edit${prefix}Axis${suffix}` });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
90
132
|
if (guideConfigs.grid?.horizontal?.scale === id) {
|
|
91
133
|
guides.push({ op: "rematerializeHorizontalGrid" });
|
|
92
134
|
}
|
|
93
135
|
if (guideConfigs.grid?.vertical?.scale === id) {
|
|
94
136
|
guides.push({ op: "rematerializeVerticalGrid" });
|
|
95
137
|
}
|
|
138
|
+
if (guideConfigs.grid?.theta?.scale === id) {
|
|
139
|
+
guides.push({ op: "rematerializeThetaGrid" });
|
|
140
|
+
}
|
|
141
|
+
if (guideConfigs.grid?.radial?.scale === id) {
|
|
142
|
+
guides.push({ op: "rematerializeRadialGrid" });
|
|
143
|
+
}
|
|
96
144
|
if (materializedLegendUsesScale(program, id)) {
|
|
97
145
|
guides.push({ op: "rematerializeLegend" });
|
|
98
146
|
}
|
|
@@ -106,22 +154,29 @@ export function planLayerDataRematerialization(program, id) {
|
|
|
106
154
|
.map(encoding => encoding?.scale)
|
|
107
155
|
.filter(scale => scale !== undefined)
|
|
108
156
|
)];
|
|
157
|
+
const markStep = getMarkMaterializationStep(program, layer);
|
|
109
158
|
const scales = scaleIds.map(scale => ({
|
|
110
159
|
op: "rematerializeScale",
|
|
111
|
-
args: {
|
|
160
|
+
args: {
|
|
161
|
+
id: scale,
|
|
162
|
+
guides: false,
|
|
163
|
+
...(markStep === undefined ? {} : { marks: false })
|
|
164
|
+
}
|
|
112
165
|
}));
|
|
113
|
-
const
|
|
114
|
-
|
|
166
|
+
const marks = [
|
|
167
|
+
...(markStep === undefined ? [] : [markStep]),
|
|
168
|
+
...getSourceDependentMarkSteps(program, id)
|
|
169
|
+
];
|
|
115
170
|
if (scales.length > 0 || marks.length > 0) {
|
|
116
171
|
const guides = scaleIds.flatMap(scale =>
|
|
117
172
|
planScaleGuideRematerialization(program, scale)
|
|
118
173
|
);
|
|
119
174
|
return buildMaterializationPlan({ scales, marks, guides });
|
|
120
175
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
176
|
+
const existingStep = getExistingMarkRematerializationStep(program, layer);
|
|
177
|
+
return buildMaterializationPlan({
|
|
178
|
+
marks: existingStep === undefined ? [] : [existingStep]
|
|
179
|
+
});
|
|
125
180
|
}
|
|
126
181
|
|
|
127
182
|
export function applyLayerDataRematerialization(program, id) {
|
|
@@ -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.js";
|
|
4
6
|
import { buildMaterializationPlan } from "./planner.js";
|
|
7
|
+
import { getSourceDependentMarkSteps } from "./marks.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)) {
|