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 @@
|
|
|
1
|
+
export { registerFacetActions } from "./actions.js";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { action } from "../../core/action.js";
|
|
2
|
+
import { isPlainObject } from "../../core/immutable.js";
|
|
3
|
+
import { validateUserId } from "../../core/identifiers.js";
|
|
4
|
+
import { validateKeys } from "../../core/validation.js";
|
|
5
|
+
import { hasDataset } from "../../selectors/datasets.js";
|
|
6
|
+
import { requireLayer } from "../../selectors/layers.js";
|
|
7
|
+
|
|
8
|
+
const REPLAY_OPTIONS = Object.freeze(["id", "source", "transform"]);
|
|
9
|
+
const REBIND_OPTIONS = Object.freeze(["id", "data"]);
|
|
10
|
+
const MATERIALIZERS = Object.freeze({
|
|
11
|
+
filter: "materializeFilteredData",
|
|
12
|
+
regression: "materializeRegressionData",
|
|
13
|
+
density: "materializeDensityData",
|
|
14
|
+
interval: "materializeIntervalData",
|
|
15
|
+
boxSummary: "materializeBoxSummaryData",
|
|
16
|
+
boxOutlier: "materializeBoxOutlierData"
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function requestedTransform(transform) {
|
|
20
|
+
if (!isPlainObject(transform)) {
|
|
21
|
+
throw new TypeError("replayDerivedData transform must be a plain object.");
|
|
22
|
+
}
|
|
23
|
+
const materialize = MATERIALIZERS[transform.type];
|
|
24
|
+
if (materialize === undefined) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`replayDerivedData does not support transform "${transform.type ?? "unknown"}".`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
if (transform.type !== "density") return { transform, materialize };
|
|
30
|
+
const { resolved: _resolved, ...requested } = transform;
|
|
31
|
+
return { transform: requested, materialize };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const replayDerivedData = action(
|
|
35
|
+
{
|
|
36
|
+
op: "replayDerivedData",
|
|
37
|
+
description: "Replay one stored derived-data transform for a facet cell."
|
|
38
|
+
},
|
|
39
|
+
function (args = {}) {
|
|
40
|
+
validateKeys(args, REPLAY_OPTIONS, "replayDerivedData");
|
|
41
|
+
const id = validateUserId(args.id, "Facet replay dataset id");
|
|
42
|
+
const source = validateUserId(args.source, "Facet replay source id");
|
|
43
|
+
const resolved = requestedTransform(args.transform);
|
|
44
|
+
return this
|
|
45
|
+
.createDerivedData({ id, source, transform: [resolved.transform] })
|
|
46
|
+
[resolved.materialize]({ id });
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
export const rebindLayerData = action(
|
|
51
|
+
{
|
|
52
|
+
op: "rebindLayerData",
|
|
53
|
+
description: "Rebind one facet-cell layer to its replayed dataset."
|
|
54
|
+
},
|
|
55
|
+
function (args = {}) {
|
|
56
|
+
validateKeys(args, REBIND_OPTIONS, "rebindLayerData");
|
|
57
|
+
const id = validateUserId(args.id, "Facet layer id");
|
|
58
|
+
const data = validateUserId(args.data, "Facet layer dataset id");
|
|
59
|
+
requireLayer(this, id);
|
|
60
|
+
if (!hasDataset(this, data)) {
|
|
61
|
+
throw new Error(`Facet layer dataset "${data}" does not exist.`);
|
|
62
|
+
}
|
|
63
|
+
return this.editSemantic({
|
|
64
|
+
property: `layer[${id}].data`,
|
|
65
|
+
value: data
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BAR_ORIENTATIONS,
|
|
3
|
+
resolveBarOrientation
|
|
4
|
+
} from "../../grammar/bars/policy.js";
|
|
5
|
+
import {
|
|
6
|
+
CARTESIAN_POSITION_CHANNELS,
|
|
7
|
+
POLAR_POSITION_CHANNELS
|
|
8
|
+
} from "../../core/vocabulary.js";
|
|
9
|
+
import { findSemanticScale } from "../../selectors/scales.js";
|
|
10
|
+
|
|
11
|
+
function hasEncoding(program, channel, { scaled = false, grid = false } = {}) {
|
|
12
|
+
return program.semanticSpec.layers.some(layer => {
|
|
13
|
+
const encoding = layer.encoding?.[channel];
|
|
14
|
+
if (!scaled && !grid) return encoding !== undefined;
|
|
15
|
+
if (encoding?.scale === undefined) return false;
|
|
16
|
+
if (!grid || POLAR_POSITION_CHANNELS.includes(channel)) return true;
|
|
17
|
+
const type = findSemanticScale(program, encoding.scale)?.type;
|
|
18
|
+
return type !== undefined && !["ordinal", "band", "point"].includes(type);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function positionalApplicability(program, channels) {
|
|
23
|
+
return Object.freeze(Object.fromEntries(
|
|
24
|
+
channels.map(channel => [
|
|
25
|
+
channel,
|
|
26
|
+
Object.freeze({
|
|
27
|
+
axis: hasEncoding(program, channel),
|
|
28
|
+
grid: hasEncoding(program, channel, { grid: true })
|
|
29
|
+
})
|
|
30
|
+
])
|
|
31
|
+
));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function hasInferableLegend(program) {
|
|
35
|
+
return program.semanticSpec.layers.some(layer =>
|
|
36
|
+
(layer.mark?.type === "point" &&
|
|
37
|
+
layer.encoding?.opacity?.scale !== undefined) ||
|
|
38
|
+
(layer.mark?.type === "point" &&
|
|
39
|
+
layer.encoding?.size?.scale !== undefined) ||
|
|
40
|
+
(layer.mark?.type === "point" &&
|
|
41
|
+
layer.encoding?.color?.scale !== undefined &&
|
|
42
|
+
(layer.encoding?.shape?.scale !== undefined ||
|
|
43
|
+
["sequential", "quantize", "quantile", "threshold"].includes(
|
|
44
|
+
findSemanticScale(program, layer.encoding.color.scale)?.type
|
|
45
|
+
))) ||
|
|
46
|
+
(layer.mark?.type === "line" &&
|
|
47
|
+
["color", "strokeDash"].some(
|
|
48
|
+
channel => layer.encoding?.[channel]?.scale !== undefined
|
|
49
|
+
)) ||
|
|
50
|
+
(["bar", "area", "arc", "rect"].includes(layer.mark?.type) &&
|
|
51
|
+
layer.encoding?.color?.scale !== undefined)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resolveGuideApplicability(program) {
|
|
56
|
+
const cartesian = positionalApplicability(
|
|
57
|
+
program,
|
|
58
|
+
CARTESIAN_POSITION_CHANNELS
|
|
59
|
+
);
|
|
60
|
+
const polar = positionalApplicability(program, POLAR_POSITION_CHANNELS);
|
|
61
|
+
return Object.freeze({
|
|
62
|
+
axes: Object.freeze({
|
|
63
|
+
cartesian: CARTESIAN_POSITION_CHANNELS.some(
|
|
64
|
+
channel => cartesian[channel].axis
|
|
65
|
+
),
|
|
66
|
+
polar: POLAR_POSITION_CHANNELS.some(channel => polar[channel].axis)
|
|
67
|
+
}),
|
|
68
|
+
grid: Object.freeze({
|
|
69
|
+
cartesian: CARTESIAN_POSITION_CHANNELS.some(
|
|
70
|
+
channel => cartesian[channel].grid
|
|
71
|
+
),
|
|
72
|
+
polar: POLAR_POSITION_CHANNELS.some(channel => polar[channel].grid),
|
|
73
|
+
directions: Object.freeze({
|
|
74
|
+
horizontal: cartesian.y.grid,
|
|
75
|
+
vertical: cartesian.x.grid,
|
|
76
|
+
theta: polar.theta.grid,
|
|
77
|
+
radial: polar.radius.grid
|
|
78
|
+
})
|
|
79
|
+
}),
|
|
80
|
+
legend: hasInferableLegend(program)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function resolveAutomaticGridOptions(program) {
|
|
85
|
+
const applicability = resolveGuideApplicability(program);
|
|
86
|
+
const directions = applicability.grid.directions;
|
|
87
|
+
if (applicability.grid.polar && !applicability.grid.cartesian) {
|
|
88
|
+
return Object.freeze({
|
|
89
|
+
...(directions.theta ? { theta: {} } : {}),
|
|
90
|
+
...(directions.radial ? { radial: {} } : {})
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const barOrientations = program.semanticSpec.layers
|
|
95
|
+
.map(resolveBarOrientation)
|
|
96
|
+
.filter(Boolean);
|
|
97
|
+
const positionedLayers = program.semanticSpec.layers.filter(
|
|
98
|
+
layer => layer.encoding?.x !== undefined && layer.encoding?.y !== undefined
|
|
99
|
+
);
|
|
100
|
+
const horizontalQuantitative = positionedLayers.length > 0 &&
|
|
101
|
+
positionedLayers.every(layer =>
|
|
102
|
+
layer.encoding.x.fieldType === "quantitative" &&
|
|
103
|
+
["nominal", "ordinal", "temporal"].includes(layer.encoding.y.fieldType)
|
|
104
|
+
);
|
|
105
|
+
if (
|
|
106
|
+
horizontalQuantitative ||
|
|
107
|
+
(barOrientations.length > 0 &&
|
|
108
|
+
barOrientations.every(value => value === BAR_ORIENTATIONS.horizontal))
|
|
109
|
+
) {
|
|
110
|
+
return Object.freeze({ horizontal: false, vertical: {} });
|
|
111
|
+
}
|
|
112
|
+
return Object.freeze({ horizontal: {}, vertical: false });
|
|
113
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { action } from "../../../core/action.js";
|
|
2
2
|
import { isPlainObject } from "../../../core/immutable.js";
|
|
3
3
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
4
|
-
import { validateKeys } from "../../../core/validation.js";
|
|
4
|
+
import { validateKeys, validateOptionObject } from "../../../core/validation.js";
|
|
5
5
|
import { findCoordinate } from "../../../selectors/coordinates.js";
|
|
6
|
+
import { resolvePolarGuideResources } from "../polar/resolve.js";
|
|
6
7
|
|
|
7
|
-
const TOP_OPTIONS = Object.freeze([
|
|
8
|
+
const TOP_OPTIONS = Object.freeze([
|
|
9
|
+
"coordinate", "x", "y", "theta", "radius"
|
|
10
|
+
]);
|
|
8
11
|
const COORDINATE_OPTIONS = Object.freeze(["id", "type"]);
|
|
9
12
|
const COORDINATE_API_TYPES = new Set(["auto", "cartesian", "polar"]);
|
|
10
13
|
|
|
@@ -15,13 +18,11 @@ function validateAxisOption(value, channel) {
|
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
function validateArgs(args) {
|
|
18
|
-
|
|
19
|
-
throw new TypeError("createAxes options must be a plain object.");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
validateKeys(args, TOP_OPTIONS, "createAxes");
|
|
21
|
+
validateOptionObject(args, TOP_OPTIONS, "createAxes");
|
|
23
22
|
validateAxisOption(args.x, "x");
|
|
24
23
|
validateAxisOption(args.y, "y");
|
|
24
|
+
validateAxisOption(args.theta, "theta");
|
|
25
|
+
validateAxisOption(args.radius, "radius");
|
|
25
26
|
|
|
26
27
|
const coordinate = args.coordinate ?? {};
|
|
27
28
|
if (!isPlainObject(coordinate)) {
|
|
@@ -60,14 +61,10 @@ function inspectChannels(layers) {
|
|
|
60
61
|
);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
return { cartesianLayers,
|
|
64
|
+
return { cartesianLayers, polarLayers };
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
function resolveCoordinate(program, descriptor, cartesianLayers
|
|
67
|
-
if (hasPolar && cartesianLayers.length === 0) {
|
|
68
|
-
throw new Error("createAxes does not yet support Polar axes.");
|
|
69
|
-
}
|
|
70
|
-
|
|
67
|
+
function resolveCoordinate(program, descriptor, cartesianLayers) {
|
|
71
68
|
if (cartesianLayers.length === 0) {
|
|
72
69
|
throw new Error("createAxes requires an x or y encoding.");
|
|
73
70
|
}
|
|
@@ -124,6 +121,24 @@ function resolveCoordinate(program, descriptor, cartesianLayers, hasPolar) {
|
|
|
124
121
|
return { id, layers };
|
|
125
122
|
}
|
|
126
123
|
|
|
124
|
+
function resolvePolarAxisArgs(program, layers, channel, option, descriptor) {
|
|
125
|
+
const selected = option === false
|
|
126
|
+
? false
|
|
127
|
+
: option !== undefined || hasChannel(layers, channel);
|
|
128
|
+
if (!selected) return undefined;
|
|
129
|
+
const args = option ?? {};
|
|
130
|
+
const kind = channel === "theta" ? "theta" : "radius";
|
|
131
|
+
const resource = resolvePolarGuideResources(program, kind, {
|
|
132
|
+
...args,
|
|
133
|
+
...(descriptor.id === undefined ? {} : { coordinate: descriptor.id })
|
|
134
|
+
}, "createAxes");
|
|
135
|
+
return {
|
|
136
|
+
...args,
|
|
137
|
+
scale: resource.scale,
|
|
138
|
+
coordinate: resource.coordinate
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
127
142
|
function hasChannel(layers, channel) {
|
|
128
143
|
return layers.some(layer => layer.encoding?.[channel] !== undefined);
|
|
129
144
|
}
|
|
@@ -170,14 +185,53 @@ const createAxes = action(
|
|
|
170
185
|
},
|
|
171
186
|
function (args = {}) {
|
|
172
187
|
const coordinateDescriptor = validateArgs(args);
|
|
173
|
-
const { cartesianLayers,
|
|
188
|
+
const { cartesianLayers, polarLayers } = inspectChannels(
|
|
174
189
|
this.semanticSpec.layers
|
|
175
190
|
);
|
|
191
|
+
const requestedCoordinateType = coordinateDescriptor.id === undefined
|
|
192
|
+
? undefined
|
|
193
|
+
: findCoordinate(this, coordinateDescriptor.id)?.type;
|
|
194
|
+
const explicitlyPolar = coordinateDescriptor.type === "polar" ||
|
|
195
|
+
requestedCoordinateType === "polar" ||
|
|
196
|
+
args.theta !== undefined || args.radius !== undefined;
|
|
197
|
+
if (polarLayers.length > 0 &&
|
|
198
|
+
(cartesianLayers.length === 0 || explicitlyPolar)) {
|
|
199
|
+
if (coordinateDescriptor.type === "cartesian") {
|
|
200
|
+
throw new Error("createAxes coordinate type conflicts with Polar encodings.");
|
|
201
|
+
}
|
|
202
|
+
if (args.x !== undefined || args.y !== undefined) {
|
|
203
|
+
throw new Error("createAxes x/y options require Cartesian encodings.");
|
|
204
|
+
}
|
|
205
|
+
const theta = resolvePolarAxisArgs(
|
|
206
|
+
this,
|
|
207
|
+
polarLayers,
|
|
208
|
+
"theta",
|
|
209
|
+
args.theta,
|
|
210
|
+
coordinateDescriptor
|
|
211
|
+
);
|
|
212
|
+
const radius = resolvePolarAxisArgs(
|
|
213
|
+
this,
|
|
214
|
+
polarLayers,
|
|
215
|
+
"radius",
|
|
216
|
+
args.radius,
|
|
217
|
+
coordinateDescriptor
|
|
218
|
+
);
|
|
219
|
+
if (theta === undefined && radius === undefined) {
|
|
220
|
+
throw new Error("createAxes requires at least one selected axis.");
|
|
221
|
+
}
|
|
222
|
+
let polarProgram = this;
|
|
223
|
+
if (theta !== undefined) {
|
|
224
|
+
polarProgram = polarProgram.createThetaAxis(theta);
|
|
225
|
+
}
|
|
226
|
+
if (radius !== undefined) {
|
|
227
|
+
polarProgram = polarProgram.createRadialAxis(radius);
|
|
228
|
+
}
|
|
229
|
+
return polarProgram;
|
|
230
|
+
}
|
|
176
231
|
const coordinate = resolveCoordinate(
|
|
177
232
|
this,
|
|
178
233
|
coordinateDescriptor,
|
|
179
|
-
cartesianLayers
|
|
180
|
-
hasPolar
|
|
234
|
+
cartesianLayers
|
|
181
235
|
);
|
|
182
236
|
const x = resolveAxisArgs(coordinate.layers, "x", args.x);
|
|
183
237
|
const y = resolveAxisArgs(coordinate.layers, "y", args.y);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { isPlainObject } from "../../../core/immutable.js";
|
|
3
|
+
import { validateKeys, validateOptionObject } from "../../../core/validation.js";
|
|
4
|
+
|
|
5
|
+
const OPTIONS = Object.freeze([
|
|
6
|
+
"position", "line", "ticks", "labels", "ticksAndLabels", "title"
|
|
7
|
+
]);
|
|
8
|
+
const LINE_OPTIONS = Object.freeze(["color", "lineWidth"]);
|
|
9
|
+
const TICK_OPTIONS = Object.freeze([
|
|
10
|
+
"count", "values", "length", "color", "lineWidth"
|
|
11
|
+
]);
|
|
12
|
+
const LABEL_OPTIONS = Object.freeze([
|
|
13
|
+
"count", "values", "offset", "format", "color", "fontSize",
|
|
14
|
+
"fontFamily", "fontWeight"
|
|
15
|
+
]);
|
|
16
|
+
const GROUP_OPTIONS = Object.freeze(["count", "values", "ticks", "labels"]);
|
|
17
|
+
const TITLE_OPTIONS = Object.freeze([
|
|
18
|
+
"text", "at", "offset", "rotation", "color", "fontSize",
|
|
19
|
+
"fontFamily", "fontWeight"
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
function validateNested(args, key, options, operation) {
|
|
23
|
+
if (!Object.hasOwn(args, key)) return;
|
|
24
|
+
if (!isPlainObject(args[key])) {
|
|
25
|
+
throw new TypeError(`${operation}.${key} must be a plain object.`);
|
|
26
|
+
}
|
|
27
|
+
validateKeys(args[key], options, `${operation}.${key}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function validateArgs(args, operation) {
|
|
31
|
+
validateOptionObject(args, OPTIONS, operation, {
|
|
32
|
+
allowEmpty: false,
|
|
33
|
+
emptyMessage: `${operation} requires at least one axis change.`,
|
|
34
|
+
emptyError: Error
|
|
35
|
+
});
|
|
36
|
+
validateNested(args, "line", LINE_OPTIONS, operation);
|
|
37
|
+
validateNested(args, "ticks", TICK_OPTIONS, operation);
|
|
38
|
+
validateNested(args, "labels", LABEL_OPTIONS, operation);
|
|
39
|
+
validateNested(args, "ticksAndLabels", GROUP_OPTIONS, operation);
|
|
40
|
+
validateNested(args, "title", TITLE_OPTIONS, operation);
|
|
41
|
+
if (args.ticksAndLabels !== undefined && (
|
|
42
|
+
args.ticks !== undefined || args.labels !== undefined
|
|
43
|
+
)) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`${operation} cannot combine ticksAndLabels with ticks or labels.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (args.ticksAndLabels?.ticks !== undefined) {
|
|
49
|
+
validateNested(args.ticksAndLabels, "ticks", ["length", "color", "lineWidth"], `${operation}.ticksAndLabels`);
|
|
50
|
+
}
|
|
51
|
+
if (args.ticksAndLabels?.labels !== undefined) {
|
|
52
|
+
validateNested(args.ticksAndLabels, "labels", [
|
|
53
|
+
"offset", "format", "color", "fontSize", "fontFamily", "fontWeight"
|
|
54
|
+
], `${operation}.ticksAndLabels`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function names(channel) {
|
|
59
|
+
const prefix = channel === "x" ? "X" : "Y";
|
|
60
|
+
return {
|
|
61
|
+
operation: `edit${prefix}Axis`,
|
|
62
|
+
line: `edit${prefix}AxisLine`,
|
|
63
|
+
ticks: `edit${prefix}AxisTicks`,
|
|
64
|
+
labels: `edit${prefix}AxisLabels`,
|
|
65
|
+
group: `edit${prefix}AxisTicksAndLabels`,
|
|
66
|
+
title: `edit${prefix}AxisTitle`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function makeEditAxis(channel) {
|
|
71
|
+
const operation = names(channel);
|
|
72
|
+
return action(
|
|
73
|
+
{
|
|
74
|
+
op: operation.operation,
|
|
75
|
+
description: `Edit selected existing ${channel}-axis components.`
|
|
76
|
+
},
|
|
77
|
+
function (args = {}) {
|
|
78
|
+
validateArgs(args, operation.operation);
|
|
79
|
+
const shared = Object.hasOwn(args, "position")
|
|
80
|
+
? { position: args.position }
|
|
81
|
+
: {};
|
|
82
|
+
const has = id => this.graphicSpec.objects[id] !== undefined;
|
|
83
|
+
let next = this;
|
|
84
|
+
|
|
85
|
+
if (args.line !== undefined || (args.position !== undefined && has(`${channel}AxisLine`))) {
|
|
86
|
+
next = next[operation.line]({ ...shared, ...(args.line ?? {}) });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (args.ticksAndLabels !== undefined) {
|
|
90
|
+
next = next[operation.group]({
|
|
91
|
+
...shared,
|
|
92
|
+
...args.ticksAndLabels
|
|
93
|
+
});
|
|
94
|
+
} else if (args.ticks !== undefined || args.labels !== undefined) {
|
|
95
|
+
if (args.ticks !== undefined) {
|
|
96
|
+
next = next[operation.ticks]({ ...shared, ...args.ticks });
|
|
97
|
+
}
|
|
98
|
+
if (args.labels !== undefined) {
|
|
99
|
+
next = next[operation.labels]({ ...shared, ...args.labels });
|
|
100
|
+
}
|
|
101
|
+
} else if (args.position !== undefined) {
|
|
102
|
+
const hasTicks = has(`${channel}AxisTicks`);
|
|
103
|
+
const hasLabels = has(`${channel}AxisLabels`);
|
|
104
|
+
if (hasTicks && hasLabels) {
|
|
105
|
+
next = next[operation.group](shared);
|
|
106
|
+
} else {
|
|
107
|
+
if (hasTicks) next = next[operation.ticks](shared);
|
|
108
|
+
if (hasLabels) next = next[operation.labels](shared);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (args.title !== undefined || (args.position !== undefined && has(`${channel}AxisTitle`))) {
|
|
113
|
+
next = next[operation.title]({ ...shared, ...(args.title ?? {}) });
|
|
114
|
+
}
|
|
115
|
+
return next;
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const editXAxis = makeEditAxis("x");
|
|
121
|
+
const editYAxis = makeEditAxis("y");
|
|
122
|
+
|
|
123
|
+
export function registerCompleteAxisEditActions(ProgramClass) {
|
|
124
|
+
ProgramClass.prototype.editXAxis = editXAxis;
|
|
125
|
+
ProgramClass.prototype.editYAxis = editYAxis;
|
|
126
|
+
}
|
|
@@ -5,6 +5,13 @@ import { registerAxisTickGroupActions } from "./tickGroups.js";
|
|
|
5
5
|
import { registerAxisTickActions } from "./ticks.js";
|
|
6
6
|
import { registerAxisTitleActions } from "./titles.js";
|
|
7
7
|
import { registerAxisActions } from "./axis.js";
|
|
8
|
+
import { registerCompleteAxisEditActions } from "./edit.js";
|
|
9
|
+
import {
|
|
10
|
+
removeRadialAxis,
|
|
11
|
+
removeThetaAxis,
|
|
12
|
+
removeXAxis,
|
|
13
|
+
removeYAxis
|
|
14
|
+
} from "./remove.js";
|
|
8
15
|
|
|
9
16
|
export function registerGuideAxisActions(ProgramClass) {
|
|
10
17
|
registerAxisLineActions(ProgramClass);
|
|
@@ -14,4 +21,9 @@ export function registerGuideAxisActions(ProgramClass) {
|
|
|
14
21
|
registerAxisTitleActions(ProgramClass);
|
|
15
22
|
registerAxisActions(ProgramClass);
|
|
16
23
|
registerAxisCollectionActions(ProgramClass);
|
|
24
|
+
registerCompleteAxisEditActions(ProgramClass);
|
|
25
|
+
ProgramClass.prototype.removeXAxis = removeXAxis;
|
|
26
|
+
ProgramClass.prototype.removeYAxis = removeYAxis;
|
|
27
|
+
ProgramClass.prototype.removeThetaAxis = removeThetaAxis;
|
|
28
|
+
ProgramClass.prototype.removeRadialAxis = removeRadialAxis;
|
|
17
29
|
}
|
|
@@ -2,6 +2,7 @@ import { action } from "../../../core/action.js";
|
|
|
2
2
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
3
|
import {
|
|
4
4
|
sameOrderedValues,
|
|
5
|
+
validateOptionObject,
|
|
5
6
|
validateNonEmptyString,
|
|
6
7
|
validateNonNegativeFinite,
|
|
7
8
|
validatePositiveFinite
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
formatTransformedTick,
|
|
14
15
|
mapOrdinalPositionValues
|
|
15
16
|
} from "../../../grammar/scales.js";
|
|
16
|
-
import { formatTimeTick } from "../../../grammar/ticks.js";
|
|
17
|
+
import { formatTimeTick, formatTimeTicks } from "../../../grammar/ticks.js";
|
|
17
18
|
import { valuesFromTickConfig } from "../tickValues.js";
|
|
18
19
|
import { DEFAULT_COLORS, DEFAULT_FONT_FAMILY } from
|
|
19
20
|
"../../../theme/defaults.js";
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
validateAxisFormat,
|
|
25
26
|
validateAxisPosition
|
|
26
27
|
} from "./policy.js";
|
|
27
|
-
import { resolvePlotGraphicPlacement } from
|
|
28
|
+
import { findCanvasGraphic, resolvePlotGraphicPlacement } from
|
|
28
29
|
"../../../materialization/graphicHierarchy.js";
|
|
29
30
|
|
|
30
31
|
const OPTIONS = Object.freeze([
|
|
@@ -41,11 +42,11 @@ const DEFAULTS = Object.freeze({
|
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
function validateOptions(args, operation, create) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
validateOptionObject(
|
|
46
|
+
args,
|
|
47
|
+
create ? OPTIONS : OPTIONS.filter(key => key !== "scale"),
|
|
48
|
+
operation
|
|
49
|
+
);
|
|
49
50
|
if (Object.hasOwn(args, "count") && Object.hasOwn(args, "values")) {
|
|
50
51
|
throw new Error(`${operation} cannot use count and values together.`);
|
|
51
52
|
}
|
|
@@ -97,16 +98,18 @@ function resolve(program, channel, config) {
|
|
|
97
98
|
const positions = discrete
|
|
98
99
|
? mapOrdinalPositionValues(values, scale)
|
|
99
100
|
: mapContinuousScaleValues(values, scale);
|
|
100
|
-
const text =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
const text = scale.type === "time" && config.format === "auto"
|
|
102
|
+
? formatTimeTicks(values, scale.domain)
|
|
103
|
+
: values.map(value => formatAxisValue(
|
|
104
|
+
value,
|
|
105
|
+
scale.type,
|
|
106
|
+
config.format,
|
|
107
|
+
item => scale.type === "time"
|
|
108
|
+
? formatTimeTick(item, scale.domain)
|
|
109
|
+
: isTransformedScaleType(scale.type)
|
|
110
|
+
? formatTransformedTick(scale.type, item)
|
|
111
|
+
: String(item)
|
|
112
|
+
));
|
|
110
113
|
const resolved = {
|
|
111
114
|
values,
|
|
112
115
|
text,
|
|
@@ -118,7 +121,7 @@ function resolve(program, channel, config) {
|
|
|
118
121
|
offset: config.offset
|
|
119
122
|
})
|
|
120
123
|
};
|
|
121
|
-
const canvas = program
|
|
124
|
+
const canvas = findCanvasGraphic(program)?.properties;
|
|
122
125
|
const maximumTextWidth = Math.max(
|
|
123
126
|
0,
|
|
124
127
|
...text.map(value => [...value].length * config.fontSize * 0.6)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { action } from "../../../core/action.js";
|
|
2
|
+
import { validateKeys } from "../../../core/validation.js";
|
|
3
|
+
import { axisGraphicIds } from
|
|
4
|
+
"../../../materialization/guides/resources.js";
|
|
5
|
+
|
|
6
|
+
const OPTIONS = Object.freeze(["coordinate", "scale"]);
|
|
7
|
+
|
|
8
|
+
function makeRemoveAxis(channel) {
|
|
9
|
+
const prefix = channel === "x"
|
|
10
|
+
? "X"
|
|
11
|
+
: channel === "y"
|
|
12
|
+
? "Y"
|
|
13
|
+
: channel === "theta"
|
|
14
|
+
? "Theta"
|
|
15
|
+
: "Radial";
|
|
16
|
+
const operation = `remove${prefix}Axis`;
|
|
17
|
+
const graphicIds = axisGraphicIds(channel);
|
|
18
|
+
return action(
|
|
19
|
+
{
|
|
20
|
+
op: operation,
|
|
21
|
+
description: `Remove the complete ${channel}-axis resource.`
|
|
22
|
+
},
|
|
23
|
+
function (args = {}) {
|
|
24
|
+
validateKeys(args, OPTIONS, operation);
|
|
25
|
+
const semantic = this.semanticSpec.guides.axis?.[channel];
|
|
26
|
+
const config = this.guideConfigs.axis?.[channel];
|
|
27
|
+
const hasGraphic = graphicIds.some(
|
|
28
|
+
id => this.graphicSpec.objects[id] !== undefined
|
|
29
|
+
);
|
|
30
|
+
if (semantic === undefined && config === undefined && !hasGraphic) {
|
|
31
|
+
throw new Error(`${operation} requires an existing ${channel}-axis.`);
|
|
32
|
+
}
|
|
33
|
+
if (args.scale !== undefined && semantic?.scale !== args.scale) {
|
|
34
|
+
throw new Error(`${operation} found no axis for scale "${args.scale}".`);
|
|
35
|
+
}
|
|
36
|
+
if (args.coordinate !== undefined && semantic?.coordinate !== args.coordinate) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`${operation} found no axis for coordinate "${args.coordinate}".`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
let next = semantic === undefined
|
|
42
|
+
? this
|
|
43
|
+
: this.editSemantic({
|
|
44
|
+
property: `guide.axis.${channel}`,
|
|
45
|
+
remove: true
|
|
46
|
+
});
|
|
47
|
+
for (const id of graphicIds) {
|
|
48
|
+
if (next.graphicSpec.objects[id] !== undefined) {
|
|
49
|
+
next = next.editGraphics({ target: id, remove: true });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return next._withoutMaterializationConfig(["guides", "axis", channel]);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const removeXAxis = makeRemoveAxis("x");
|
|
58
|
+
export const removeYAxis = makeRemoveAxis("y");
|
|
59
|
+
export const removeThetaAxis = makeRemoveAxis("theta");
|
|
60
|
+
export const removeRadialAxis = makeRemoveAxis("radius");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { action } from "../../../core/action.js";
|
|
2
2
|
import { isPlainObject } from "../../../core/immutable.js";
|
|
3
|
-
import { validateKeys } from "../../../core/validation.js";
|
|
3
|
+
import { validateKeys, validateOptionObject } from "../../../core/validation.js";
|
|
4
4
|
|
|
5
5
|
const CREATE_OPTIONS = Object.freeze([
|
|
6
6
|
"scale",
|
|
@@ -38,7 +38,12 @@ function validateNested(value, supported, label) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function validateArgs(args, operation, create) {
|
|
41
|
-
|
|
41
|
+
validateOptionObject(
|
|
42
|
+
args,
|
|
43
|
+
create ? CREATE_OPTIONS : EDIT_OPTIONS,
|
|
44
|
+
operation,
|
|
45
|
+
{ allowEmpty: create }
|
|
46
|
+
);
|
|
42
47
|
|
|
43
48
|
if (Object.hasOwn(args, "count") && Object.hasOwn(args, "values")) {
|
|
44
49
|
throw new Error(`${operation} cannot use count and values together.`);
|
|
@@ -52,9 +57,6 @@ function validateArgs(args, operation, create) {
|
|
|
52
57
|
validateNested(args.labels, LABEL_OPTIONS, `${operation}.labels`);
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
if (!create && Object.keys(args).length === 0) {
|
|
56
|
-
throw new TypeError(`${operation} requires at least one option.`);
|
|
57
|
-
}
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
function select(source, keys) {
|
|
@@ -2,7 +2,8 @@ import { action } from "../../../core/action.js";
|
|
|
2
2
|
import { validateUserId } from "../../../core/identifiers.js";
|
|
3
3
|
import {
|
|
4
4
|
validateNonEmptyString,
|
|
5
|
-
validateNonNegativeFinite
|
|
5
|
+
validateNonNegativeFinite,
|
|
6
|
+
validateOptionObject
|
|
6
7
|
} from "../../../core/validation.js";
|
|
7
8
|
import { resolveGraphicBounds } from "../../../layout/canvas.js";
|
|
8
9
|
import {
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
resolveAxisTickGeometry,
|
|
22
23
|
validateAxisPosition
|
|
23
24
|
} from "./policy.js";
|
|
24
|
-
import { resolvePlotGraphicPlacement } from
|
|
25
|
+
import { findCanvasGraphic, resolvePlotGraphicPlacement } from
|
|
25
26
|
"../../../materialization/graphicHierarchy.js";
|
|
26
27
|
|
|
27
28
|
const OPTIONS = Object.freeze(["scale", "position", "count", "values", "length", "color", "lineWidth"]);
|
|
@@ -33,7 +34,11 @@ const DEFAULTS = Object.freeze({
|
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
function validateOptions(args, operation, create) {
|
|
36
|
-
|
|
37
|
+
validateOptionObject(
|
|
38
|
+
args,
|
|
39
|
+
create ? OPTIONS : OPTIONS.filter(key => key !== "scale"),
|
|
40
|
+
operation
|
|
41
|
+
);
|
|
37
42
|
if (Object.hasOwn(args, "count") && Object.hasOwn(args, "values")) throw new Error(`${operation} cannot use count and values together.`);
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -84,7 +89,7 @@ function geometry(program, channel, config) {
|
|
|
84
89
|
length: config.length
|
|
85
90
|
})
|
|
86
91
|
};
|
|
87
|
-
const canvas = program
|
|
92
|
+
const canvas = findCanvasGraphic(program)?.properties;
|
|
88
93
|
const coordinates = channel === "x"
|
|
89
94
|
? [...resolved.x1, ...resolved.x2, resolved.y1, resolved.y2]
|
|
90
95
|
: [resolved.x1, resolved.x2, ...resolved.y1, ...resolved.y2];
|