ggaction 0.0.9 → 0.0.11
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 +84 -0
- package/README.md +5 -4
- package/knowledge/action-card.schema.json +1 -0
- package/knowledge/action-cards.json +1 -10941
- package/knowledge/action-cards.schema.json +1 -0
- package/knowledge/extension-authoring.md +248 -0
- package/knowledge/intent-taxonomy.json +1 -183
- package/knowledge/intent-taxonomy.schema.json +1 -0
- package/knowledge/mcp-resources.json +1 -95
- package/knowledge/mcp-resources.schema.json +1 -0
- package/knowledge/task-packet.schema.json +1 -159
- package/knowledge/task-resolver.js +1 -1230
- package/package.json +19 -2
- package/src/actions/boxPlots/edit.js +45 -298
- package/src/actions/boxPlots/options.js +46 -77
- package/src/actions/boxPlots/resolve.js +1 -1
- package/src/actions/charts/bar.js +3 -0
- package/src/actions/charts/heatmap.js +73 -83
- package/src/actions/charts/index.js +1 -1
- package/src/actions/charts/parallel.js +35 -1
- package/src/actions/charts/shared.js +20 -0
- package/src/actions/composition/actions.js +46 -49
- package/src/actions/data/bin2d.js +105 -2
- package/src/actions/data/box.js +17 -17
- package/src/actions/data/density.js +42 -78
- package/src/actions/data/gradientProfile.js +22 -52
- package/src/actions/data/horizon.js +16 -41
- package/src/actions/data/index.js +10 -10
- package/src/actions/data/interval.js +16 -38
- package/src/actions/data/intervalEdit.js +55 -0
- package/src/actions/data/regression.js +7 -17
- package/src/actions/data/shared.js +47 -0
- package/src/actions/data/timeUnit.js +14 -38
- package/src/actions/data/window.js +14 -38
- package/src/actions/distributions/revision.js +154 -34
- package/src/actions/encodings/angle.js +9 -10
- package/src/actions/encodings/appearance.js +17 -51
- package/src/actions/encodings/density/resolve.js +3 -1
- package/src/actions/encodings/density.js +1 -15
- package/src/actions/encodings/horizon/resolve.js +13 -3
- package/src/actions/encodings/horizon.js +8 -7
- package/src/actions/encodings/index.js +9 -9
- package/src/actions/encodings/offset.js +8 -24
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/policies/index.js +4 -4
- package/src/actions/encodings/position/policies/line.js +47 -17
- package/src/actions/encodings/position/resolve.js +24 -5
- package/src/actions/encodings/ranged.js +18 -23
- package/src/actions/encodings/remove.js +15 -25
- package/src/actions/encodings/ruleAppearance.js +9 -24
- package/src/actions/encodings/shared.js +37 -0
- package/src/actions/encodings/strokeDash.js +8 -23
- package/src/actions/encodings/text.js +9 -10
- package/src/actions/errorBands/create.js +105 -39
- package/src/actions/errorBands/edit.js +89 -93
- package/src/actions/errorBands/index.js +1 -2
- package/src/actions/errorBars/create.js +78 -27
- package/src/actions/errorBars/edit.js +81 -48
- package/src/actions/errorBars/index.js +1 -2
- package/src/actions/facets/actions.js +45 -69
- package/src/actions/facets/derive.js +38 -53
- package/src/actions/facets/replay.js +0 -1
- package/src/actions/gradientPlots/components.js +59 -48
- package/src/actions/gradientPlots/edit.js +35 -216
- package/src/actions/gradientPlots/index.js +11 -10
- package/src/actions/gradientPlots/materialize.js +21 -35
- package/src/actions/gradientPlots/options.js +5 -7
- package/src/actions/gradientPlots/paint.js +9 -6
- package/src/actions/gradientPlots/resolve.js +1 -1
- package/src/actions/guides/axes/index.js +3 -3
- package/src/actions/guides/axes/labels.js +68 -26
- package/src/actions/guides/axes/lines.js +22 -2
- package/src/actions/guides/axes/policy.js +28 -8
- package/src/actions/guides/axes/ticks.js +27 -9
- package/src/actions/guides/axes/titles.js +113 -65
- package/src/actions/guides/grids/resolve.js +14 -0
- package/src/actions/guides/guides.js +2 -4
- package/src/actions/guides/index.js +2 -2
- package/src/actions/guides/legends/categorical/actions.js +44 -68
- package/src/actions/guides/legends/categorical/components.js +6 -1
- package/src/actions/guides/legends/categorical/index.js +2 -2
- package/src/actions/guides/legends/categorical/layout.js +148 -175
- package/src/actions/guides/legends/categorical/recipes.js +5 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -1
- package/src/actions/guides/legends/continuous/common.js +117 -66
- package/src/actions/guides/legends/continuous/gradient.js +38 -37
- package/src/actions/guides/legends/continuous/interval.js +97 -76
- package/src/actions/guides/legends/continuous/opacity.js +28 -38
- package/src/actions/guides/legends/edit.js +14 -1
- package/src/actions/guides/legends/index.js +1 -1
- package/src/actions/guides/legends/lane.js +2 -1
- package/src/actions/guides/legends/remove.js +8 -0
- package/src/actions/guides/legends/size.js +36 -43
- package/src/actions/guides/legends/strokeWidth.js +20 -26
- package/src/actions/guides/polar/axes/labels.js +17 -1
- package/src/actions/guides/polar/axes/shared.js +7 -1
- package/src/actions/guides/polar/axes/titles.js +19 -2
- package/src/actions/guides/polar/grids.js +4 -0
- package/src/actions/guides/polar/resolve.js +6 -0
- package/src/actions/index.js +13 -13
- package/src/actions/intervals/resolve.js +87 -117
- package/src/actions/marks/arc/actions.js +31 -36
- package/src/actions/marks/area/actions.js +23 -31
- package/src/actions/marks/index.js +6 -6
- package/src/actions/marks/line/actions.js +33 -52
- package/src/actions/marks/line/materialize.js +7 -5
- package/src/actions/marks/point/materialize.js +5 -3
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +53 -53
- package/src/actions/marks/shared.js +8 -4
- package/src/actions/marks/text/actions.js +14 -6
- package/src/actions/marks/text/layout.js +21 -45
- package/src/actions/marks/tick/actions.js +22 -21
- package/src/actions/primitives/createGraphics.js +4 -0
- package/src/actions/primitives/editGraphics.js +4 -0
- package/src/actions/primitives/index.js +3 -3
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +2 -2
- package/src/actions/primitives/semanticValidation/layer.js +1 -1
- package/src/actions/regression/edit.js +3 -43
- package/src/actions/scales/consumers/families.js +24 -4
- package/src/actions/scales/consumers/index.js +1 -1
- package/src/actions/scales/consumers/seriesLayout.js +17 -0
- package/src/actions/scales/definitions.js +100 -103
- package/src/actions/scales/materialize.js +5 -0
- package/src/actions/selection/actions.js +79 -121
- package/src/actions/titles/actions.js +48 -94
- package/src/actions/titles/resolve.js +14 -27
- package/src/actions/violinPlots/create.js +37 -44
- package/src/core/action.js +20 -5
- package/src/core/extensionRegistry.js +104 -0
- package/src/core/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/extension.js +8 -1
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +58 -11
- package/src/grammar/areaSeries.js +14 -16
- package/src/grammar/bin2d.js +37 -23
- package/src/grammar/boxPlot.js +14 -13
- package/src/grammar/categoricalDensity.js +14 -14
- package/src/grammar/categoryOrder.js +8 -14
- package/src/grammar/curveCommands.js +87 -20
- package/src/grammar/density.js +162 -141
- package/src/grammar/facets/index.js +14 -0
- package/src/grammar/facets/scales.js +7 -4
- package/src/grammar/gradientProfile.js +12 -15
- package/src/grammar/histogram.js +145 -83
- package/src/grammar/horizon.js +60 -23
- package/src/grammar/interval.js +14 -13
- package/src/grammar/lineSeries.js +91 -61
- package/src/grammar/numeric.js +290 -0
- package/src/grammar/paint.js +3 -4
- package/src/grammar/palettes.js +21 -32
- package/src/grammar/pathCommands.js +6 -0
- package/src/grammar/polarPaths.js +5 -5
- package/src/grammar/regression/derive.js +48 -9
- package/src/grammar/regression/models.js +248 -69
- package/src/grammar/regression/parameters.js +8 -0
- package/src/grammar/scales/color.js +35 -26
- package/src/grammar/scales/continuous.js +71 -24
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/discretized.js +42 -12
- package/src/grammar/scales/fields.js +21 -4
- package/src/grammar/scales/ordinal.js +117 -61
- package/src/grammar/scales/temporal.js +40 -11
- package/src/grammar/scales/transformed.js +199 -52
- package/src/grammar/schemas/graphicBounds.js +270 -79
- package/src/grammar/seriesLayout.js +50 -18
- package/src/grammar/ticks.js +123 -53
- package/src/grammar/timeUnit.js +18 -8
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/window.js +70 -57
- package/src/index.js +1 -1
- package/src/layout/canvas.js +4 -8
- package/src/layout/composition.js +39 -40
- package/src/layout/facets.js +10 -14
- package/src/layout/labels.js +91 -15
- package/src/layout/legend.js +22 -20
- package/src/layout/legendLane.js +54 -40
- package/src/layout/title.js +7 -7
- package/src/materialization/bars/aggregate.js +14 -40
- package/src/materialization/bars/grouped.js +7 -18
- package/src/materialization/bars/histogram.js +2 -19
- package/src/materialization/bars/ranged.js +20 -15
- package/src/materialization/bars/resolve.js +21 -0
- package/src/materialization/facetGuides/legacyCategorical.js +2 -1
- package/src/materialization/facetGuides/placement.js +34 -19
- package/src/materialization/facetGuides/preparation.js +9 -5
- package/src/materialization/facets.js +39 -27
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +5 -2
- package/src/materialization/marks/index.js +24 -4
- package/src/materialization/scales/policies/bar.js +61 -59
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +4 -5
- package/src/materialization/selection/items/arc.js +16 -2
- package/src/materialization/selection/items/bar.js +4 -2
- package/src/materialization/selection/items/common.js +21 -1
- package/src/materialization/selection/items/path.js +19 -2
- package/src/materialization/selection/items/point.js +4 -16
- package/src/materialization/selection/items/tick.js +13 -21
- package/src/materialization/selection/point.js +11 -4
- package/src/materialization/selection/styles.js +19 -27
- package/src/materialization/text.js +4 -1
- package/src/mcp/adapter.js +8 -3
- package/src/renderers/canvas/fill.js +2 -2
- package/src/renderers/canvas/index.js +35 -2
- package/src/renderers/canvas/native.js +160 -0
- package/src/renderers/pdf.js +14 -0
- package/src/renderers/svg.js +37 -4
- package/types/extension.d.ts +19 -0
- package/types/index.d.ts +3 -0
- package/types/program.d.ts +509 -131
- package/types/svg.d.ts +1 -0
- package/src/actions/errorBands/components.js +0 -81
- package/src/actions/errorBands/options.js +0 -39
- package/src/actions/errorBands/resolve.js +0 -28
- package/src/actions/errorBars/components.js +0 -62
- package/src/actions/errorBars/options.js +0 -58
- package/src/actions/errorBars/resolve.js +0 -25
|
@@ -65,6 +65,14 @@ function requireLegendOwner(program, owner) {
|
|
|
65
65
|
return { layer, config };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
function editProperties(program, target, properties) {
|
|
69
|
+
let next = program;
|
|
70
|
+
for (const [property, value] of Object.entries(properties)) {
|
|
71
|
+
next = next.editGraphics({ target, property, value });
|
|
72
|
+
}
|
|
73
|
+
return next;
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
export const rematerializeGradientPlotLegend = action(
|
|
69
77
|
{
|
|
70
78
|
op: "rematerializeGradientPlotLegend",
|
|
@@ -82,35 +90,33 @@ export const rematerializeGradientPlotLegend = action(
|
|
|
82
90
|
const y = bounds.y + 50;
|
|
83
91
|
const width = 22;
|
|
84
92
|
const height = Math.min(170, Math.max(80, bounds.height - 110));
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
.editGraphics({ target: ids.title, property: "textAlign", value: "left" })
|
|
113
|
-
.editGraphics({ target: ids.title, property: "textBaseline", value: "middle" });
|
|
93
|
+
let next = editProperties(this, ids.strip, {
|
|
94
|
+
x, y, width, height,
|
|
95
|
+
fill: createDensityLegendPaint(config.gradient.opacity),
|
|
96
|
+
stroke: "#cbd5e1",
|
|
97
|
+
strokeWidth: 1
|
|
98
|
+
});
|
|
99
|
+
next = editProperties(next, ids.labels, {
|
|
100
|
+
x: x + width + 10,
|
|
101
|
+
y: [y + height, y],
|
|
102
|
+
text: ["Low", "High"],
|
|
103
|
+
fill: "#64748b",
|
|
104
|
+
fontSize: 11,
|
|
105
|
+
fontFamily: "sans-serif",
|
|
106
|
+
textAlign: "left",
|
|
107
|
+
textBaseline: "middle"
|
|
108
|
+
});
|
|
109
|
+
return editProperties(next, ids.title, {
|
|
110
|
+
x,
|
|
111
|
+
y: y - 18,
|
|
112
|
+
text: config.guides.legend.title,
|
|
113
|
+
fill: "#334155",
|
|
114
|
+
fontSize: 12,
|
|
115
|
+
fontFamily: "sans-serif",
|
|
116
|
+
fontWeight: 600,
|
|
117
|
+
textAlign: "left",
|
|
118
|
+
textBaseline: "middle"
|
|
119
|
+
});
|
|
114
120
|
}
|
|
115
121
|
);
|
|
116
122
|
|
|
@@ -130,24 +136,29 @@ export const createGradientPlotLegend = action(
|
|
|
130
136
|
}
|
|
131
137
|
const ids = legendIds(args.owner);
|
|
132
138
|
const densityScale = `${args.owner}Density`;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
.createGraphics({
|
|
150
|
-
|
|
139
|
+
const title = args.title ?? "Relative density";
|
|
140
|
+
let next = this;
|
|
141
|
+
for (const [property, value] of [
|
|
142
|
+
[`scale[${densityScale}].type`, "sequential"],
|
|
143
|
+
[`scale[${densityScale}].domain`, this.markConfigs[args.owner].gradientPlot.intensityDomain],
|
|
144
|
+
[`scale[${densityScale}].range`, { palette: { name: "greys" } }],
|
|
145
|
+
["guide.legend.color.scale", densityScale],
|
|
146
|
+
["guide.legend.color.title", title]
|
|
147
|
+
]) {
|
|
148
|
+
next = next.editSemantic({ property, value });
|
|
149
|
+
}
|
|
150
|
+
for (const [id, type, length] of [
|
|
151
|
+
[ids.strip, "rect"],
|
|
152
|
+
[ids.labels, "text", 2],
|
|
153
|
+
[ids.title, "text"]
|
|
154
|
+
]) {
|
|
155
|
+
next = next.createGraphics({
|
|
156
|
+
id,
|
|
157
|
+
type,
|
|
158
|
+
...(length === undefined ? {} : { length }),
|
|
159
|
+
parent: "canvas"
|
|
160
|
+
});
|
|
161
|
+
}
|
|
151
162
|
next = next._withMarkConfig(args.owner, {
|
|
152
163
|
...next.markConfigs[args.owner],
|
|
153
164
|
gradientPlot: {
|
|
@@ -155,7 +166,7 @@ export const createGradientPlotLegend = action(
|
|
|
155
166
|
guides: {
|
|
156
167
|
...next.markConfigs[args.owner].gradientPlot.guides,
|
|
157
168
|
legend: {
|
|
158
|
-
title
|
|
169
|
+
title,
|
|
159
170
|
position: args.position ?? "right"
|
|
160
171
|
}
|
|
161
172
|
}
|
|
@@ -5,15 +5,13 @@ import { validateKeys } from "../../core/validation.js";
|
|
|
5
5
|
import { planDerivedDataRevision } from "../../materialization/dataProvenance.js";
|
|
6
6
|
import { findDataset } from "../../selectors/datasets.js";
|
|
7
7
|
import { findLayer } from "../../selectors/layers.js";
|
|
8
|
+
import { removeOwnedMark } from "../marks/remove.js";
|
|
8
9
|
import { GRADIENT_PROFILE_FIELDS } from "../../grammar/gradientProfile.js";
|
|
9
10
|
import { removeGradientPlotLegend } from "./components.js";
|
|
10
11
|
import {
|
|
11
|
-
|
|
12
|
-
clearCartesianPositions,
|
|
13
|
-
rebindDistributionGuides,
|
|
14
|
-
resolveDistributionScalePlan,
|
|
12
|
+
resolveDistributionRoles,
|
|
15
13
|
setCartesianPosition,
|
|
16
|
-
|
|
14
|
+
updateDistributionPositions
|
|
17
15
|
} from "../distributions/revision.js";
|
|
18
16
|
import {
|
|
19
17
|
resolveGradientAppearance,
|
|
@@ -39,140 +37,20 @@ function patch(value, label) {
|
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
function removeCenter(program, id) {
|
|
42
|
-
|
|
43
|
-
const selections = Object.entries(
|
|
44
|
-
program.materializationConfigs.selections ?? {}
|
|
45
|
-
).filter(([, config]) => config.target === id).map(([selection]) => selection);
|
|
46
|
-
for (const [highlight, config] of Object.entries(
|
|
47
|
-
program.materializationConfigs.highlights ?? {}
|
|
48
|
-
)) {
|
|
49
|
-
if (config.target === id || selections.includes(config.selection)) {
|
|
50
|
-
next = next._withoutMaterializationConfig(["highlights", highlight]);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
for (const selection of selections) {
|
|
54
|
-
next = next._withoutMaterializationConfig(["selections", selection]);
|
|
55
|
-
}
|
|
56
|
-
if (findLayer(next, id) !== undefined) {
|
|
57
|
-
next = next.editSemantic({ property: `layer[${id}]`, remove: true });
|
|
58
|
-
}
|
|
59
|
-
if (next.graphicSpec.objects[id] !== undefined) {
|
|
60
|
-
next = next.editGraphics({ target: id, remove: true });
|
|
61
|
-
}
|
|
62
|
-
return next
|
|
63
|
-
._withoutMaterializationConfig(["marks", id])
|
|
64
|
-
._withContext({
|
|
65
|
-
...(program.context.currentMark === id ? { currentMark: undefined } : {}),
|
|
66
|
-
...(selections.includes(program.context.currentSelection)
|
|
67
|
-
? { currentSelection: undefined }
|
|
68
|
-
: {})
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function currentGradientPositions(owner, current) {
|
|
73
|
-
const categoryChannel = current.orientation === "vertical" ? "x" : "y";
|
|
74
|
-
const measureChannel = current.orientation === "vertical" ? "y" : "x";
|
|
75
|
-
const categoryEncoding = owner.encoding[categoryChannel];
|
|
76
|
-
const measureEncoding = owner.encoding[measureChannel];
|
|
77
|
-
return {
|
|
78
|
-
x: current.orientation === "vertical"
|
|
79
|
-
? {
|
|
80
|
-
field: current.category,
|
|
81
|
-
fieldType: current.categoryType,
|
|
82
|
-
scale: categoryEncoding.scale
|
|
83
|
-
}
|
|
84
|
-
: {
|
|
85
|
-
field: current.measure,
|
|
86
|
-
fieldType: "quantitative",
|
|
87
|
-
scale: measureEncoding.scale
|
|
88
|
-
},
|
|
89
|
-
y: current.orientation === "vertical"
|
|
90
|
-
? {
|
|
91
|
-
field: current.measure,
|
|
92
|
-
fieldType: "quantitative",
|
|
93
|
-
scale: measureEncoding.scale
|
|
94
|
-
}
|
|
95
|
-
: {
|
|
96
|
-
field: current.category,
|
|
97
|
-
fieldType: current.categoryType,
|
|
98
|
-
scale: categoryEncoding.scale
|
|
99
|
-
},
|
|
100
|
-
categoryScale: categoryEncoding.scale,
|
|
101
|
-
measureScale: measureEncoding.scale
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function requirePositionField(position, label) {
|
|
106
|
-
if (typeof position?.field !== "string" || position.field.length === 0) {
|
|
107
|
-
throw new TypeError(`${label} field must be a non-empty string.`);
|
|
108
|
-
}
|
|
109
|
-
return position;
|
|
40
|
+
return removeOwnedMark(program, id, true);
|
|
110
41
|
}
|
|
111
42
|
|
|
112
43
|
function roleCandidate(program, owner, current, args) {
|
|
113
|
-
const previous = currentGradientPositions(owner, current);
|
|
114
|
-
const positions = normalizeGradientPositionTypes(
|
|
115
|
-
requirePositionField(
|
|
116
|
-
Object.hasOwn(args, "x")
|
|
117
|
-
? resolveGradientPosition(args.x, "x", "editGradientPlot")
|
|
118
|
-
: previous.x,
|
|
119
|
-
"editGradientPlot x"
|
|
120
|
-
),
|
|
121
|
-
requirePositionField(
|
|
122
|
-
Object.hasOwn(args, "y")
|
|
123
|
-
? resolveGradientPosition(args.y, "y", "editGradientPlot")
|
|
124
|
-
: previous.y,
|
|
125
|
-
"editGradientPlot y"
|
|
126
|
-
)
|
|
127
|
-
);
|
|
128
|
-
if (positions.orientation === undefined) {
|
|
129
|
-
throw new Error(
|
|
130
|
-
"editGradientPlot requires one categorical axis and one quantitative axis."
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
const xRoleScale = positions.orientation === "vertical"
|
|
134
|
-
? previous.categoryScale
|
|
135
|
-
: previous.measureScale;
|
|
136
|
-
const yRoleScale = positions.orientation === "vertical"
|
|
137
|
-
? previous.measureScale
|
|
138
|
-
: previous.categoryScale;
|
|
139
|
-
const xScale = resolveDistributionScalePlan(program, {
|
|
140
|
-
channel: "x",
|
|
141
|
-
fieldType: positions.x.fieldType,
|
|
142
|
-
requested: positions.x.scale,
|
|
143
|
-
fallback: xRoleScale,
|
|
144
|
-
defaults: ["ordinal", "nominal"].includes(positions.x.fieldType)
|
|
145
|
-
? { discreteType: "band" }
|
|
146
|
-
: {}
|
|
147
|
-
});
|
|
148
|
-
const yScale = resolveDistributionScalePlan(program, {
|
|
149
|
-
channel: "y",
|
|
150
|
-
fieldType: positions.y.fieldType,
|
|
151
|
-
requested: positions.y.scale,
|
|
152
|
-
fallback: yRoleScale,
|
|
153
|
-
defaults: ["ordinal", "nominal"].includes(positions.y.fieldType)
|
|
154
|
-
? { discreteType: "band" }
|
|
155
|
-
: {}
|
|
156
|
-
});
|
|
157
44
|
return {
|
|
158
45
|
source: Object.hasOwn(args, "data")
|
|
159
46
|
? validateUserId(args.data, "Gradient-plot data id")
|
|
160
47
|
: current.source,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
? positions.x.field
|
|
168
|
-
: positions.y.field,
|
|
169
|
-
categoryType: positions.orientation === "vertical"
|
|
170
|
-
? positions.x.fieldType
|
|
171
|
-
: positions.y.fieldType,
|
|
172
|
-
measure: positions.orientation === "vertical"
|
|
173
|
-
? positions.y.field
|
|
174
|
-
: positions.x.field,
|
|
175
|
-
previous
|
|
48
|
+
...resolveDistributionRoles(program, owner, current, args, {
|
|
49
|
+
operation: "editGradientPlot",
|
|
50
|
+
resolvePosition: resolveGradientPosition,
|
|
51
|
+
normalize: normalizeGradientPositionTypes,
|
|
52
|
+
quantitativeDefaults: {}
|
|
53
|
+
})
|
|
176
54
|
};
|
|
177
55
|
}
|
|
178
56
|
|
|
@@ -190,46 +68,27 @@ function sameRoleCandidate(current, candidate) {
|
|
|
190
68
|
|
|
191
69
|
function updateGradientPositions(program, owner, current, candidate, hasCenter) {
|
|
192
70
|
const owned = [owner.id, ...(hasCenter ? [current.centerId] : [])];
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const category = candidate.orientation === "vertical" ? candidate.x : candidate.y;
|
|
206
|
-
const measure = candidate.orientation === "vertical" ? candidate.y : candidate.x;
|
|
207
|
-
const categoryChannel = candidate.orientation === "vertical" ? "x" : "y";
|
|
208
|
-
const measureChannel = candidate.orientation === "vertical" ? "y" : "x";
|
|
209
|
-
next = setCartesianPosition(next, owner.id, categoryChannel, {
|
|
210
|
-
field: candidate.category,
|
|
211
|
-
fieldType: candidate.categoryType,
|
|
212
|
-
scale: category.scale
|
|
213
|
-
});
|
|
214
|
-
next = setCartesianRange(
|
|
215
|
-
next,
|
|
216
|
-
owner.id,
|
|
217
|
-
measureChannel,
|
|
218
|
-
GRADIENT_PROFILE_FIELDS.lower,
|
|
219
|
-
GRADIENT_PROFILE_FIELDS.upper,
|
|
220
|
-
measure.scale
|
|
221
|
-
);
|
|
222
|
-
if (hasCenter) {
|
|
223
|
-
next = setCartesianPosition(next, current.centerId, categoryChannel, {
|
|
71
|
+
return updateDistributionPositions(
|
|
72
|
+
program,
|
|
73
|
+
owner,
|
|
74
|
+
current,
|
|
75
|
+
candidate,
|
|
76
|
+
{
|
|
77
|
+
owned,
|
|
78
|
+
lower: GRADIENT_PROFILE_FIELDS.lower,
|
|
79
|
+
upper: GRADIENT_PROFILE_FIELDS.upper,
|
|
80
|
+
update(next, { category, measure, categoryChannel, measureChannel }) {
|
|
81
|
+
if (!hasCenter) return next;
|
|
82
|
+
next = setCartesianPosition(next, current.centerId, categoryChannel, {
|
|
224
83
|
field: candidate.category,
|
|
225
84
|
fieldType: candidate.categoryType,
|
|
226
85
|
scale: category.scale
|
|
227
86
|
});
|
|
228
|
-
|
|
87
|
+
return setCartesianPosition(next, current.centerId, measureChannel, {
|
|
229
88
|
field: GRADIENT_PROFILE_FIELDS.center,
|
|
230
89
|
fieldType: "quantitative",
|
|
231
90
|
scale: measure.scale
|
|
232
|
-
|
|
91
|
+
})._withMarkConfig(current.centerId, {
|
|
233
92
|
...next.markConfigs[current.centerId],
|
|
234
93
|
fixedSpan: {
|
|
235
94
|
...next.markConfigs[current.centerId].fixedSpan,
|
|
@@ -237,27 +96,10 @@ function updateGradientPositions(program, owner, current, candidate, hasCenter)
|
|
|
237
96
|
? "horizontal"
|
|
238
97
|
: "vertical"
|
|
239
98
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
next = rebindDistributionGuides(next, {
|
|
246
|
-
oldXScale: candidate.previous.x.scale,
|
|
247
|
-
oldYScale: candidate.previous.y.scale,
|
|
248
|
-
newXScale: candidate.xScale.id,
|
|
249
|
-
newYScale: candidate.yScale.id,
|
|
250
|
-
oldXTitle: candidate.previous.x.field,
|
|
251
|
-
oldYTitle: candidate.previous.y.field,
|
|
252
|
-
newXTitle: candidate.x.field,
|
|
253
|
-
newYTitle: candidate.y.field,
|
|
254
|
-
oldMeasureChannel: current.orientation === "vertical" ? "y" : "x",
|
|
255
|
-
newMeasureChannel: candidate.orientation === "vertical" ? "y" : "x"
|
|
256
|
-
});
|
|
257
|
-
for (const scale of [candidate.xScale, candidate.yScale]) {
|
|
258
|
-
if (scale.edit !== undefined) next = next.editScale(scale.edit);
|
|
259
|
-
}
|
|
260
|
-
return next;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
);
|
|
261
103
|
}
|
|
262
104
|
|
|
263
105
|
export const editGradientPlot = action(
|
|
@@ -301,7 +143,10 @@ export const editGradientPlot = action(
|
|
|
301
143
|
key => Object.hasOwn(args, key)
|
|
302
144
|
);
|
|
303
145
|
const changesRoles = !sameRoleCandidate(current, candidate);
|
|
304
|
-
|
|
146
|
+
const statistical = JSON.stringify(density) !== JSON.stringify(current.density) ||
|
|
147
|
+
center !== false && current.center !== false &&
|
|
148
|
+
center.type !== current.center.type;
|
|
149
|
+
if (changesRoles || statistical) {
|
|
305
150
|
const hadCenter = findLayer(this, current.centerId) !== undefined;
|
|
306
151
|
const revision = planDerivedDataRevision(this, {
|
|
307
152
|
owner: owner.id,
|
|
@@ -404,7 +249,6 @@ export const editGradientPlot = action(
|
|
|
404
249
|
currentData: candidate.source
|
|
405
250
|
});
|
|
406
251
|
};
|
|
407
|
-
applyEdit(this);
|
|
408
252
|
return applyEdit(this);
|
|
409
253
|
}
|
|
410
254
|
if (roleRequested && !OPTIONS.slice(4).some(
|
|
@@ -415,33 +259,8 @@ export const editGradientPlot = action(
|
|
|
415
259
|
currentData: current.source
|
|
416
260
|
});
|
|
417
261
|
}
|
|
418
|
-
const densityChanged = JSON.stringify(density) !== JSON.stringify(current.density);
|
|
419
|
-
const centerTypeChanged = center !== false && current.center !== false &&
|
|
420
|
-
center.type !== current.center.type;
|
|
421
|
-
const statistical = densityChanged || centerTypeChanged;
|
|
422
262
|
let next = this;
|
|
423
|
-
|
|
424
|
-
if (statistical) {
|
|
425
|
-
const consumers = [owner.id, ...(findLayer(next, current.centerId) ? [current.centerId] : [])];
|
|
426
|
-
const revision = planDerivedDataRevision(next, {
|
|
427
|
-
owner: owner.id,
|
|
428
|
-
role: "ProfileData",
|
|
429
|
-
previous: current.profileId,
|
|
430
|
-
consumers
|
|
431
|
-
});
|
|
432
|
-
profileId = revision.id;
|
|
433
|
-
next = next.createGradientProfileData({
|
|
434
|
-
id: profileId,
|
|
435
|
-
source: current.source,
|
|
436
|
-
category: current.category,
|
|
437
|
-
field: current.measure,
|
|
438
|
-
...density,
|
|
439
|
-
center: center === false ? "median" : center.type
|
|
440
|
-
});
|
|
441
|
-
for (const rebind of revision.rebinds) next = next.rebindLayerData(rebind);
|
|
442
|
-
next = next.releaseDerivedData(revision.release);
|
|
443
|
-
}
|
|
444
|
-
const profile = findDataset(next, profileId);
|
|
263
|
+
const profile = findDataset(next, current.profileId);
|
|
445
264
|
next = next._withMarkConfig(owner.id, {
|
|
446
265
|
...next.markConfigs[owner.id],
|
|
447
266
|
gradientPlot: {
|
|
@@ -450,7 +269,7 @@ export const editGradientPlot = action(
|
|
|
450
269
|
width,
|
|
451
270
|
gradient,
|
|
452
271
|
center,
|
|
453
|
-
profileId,
|
|
272
|
+
profileId: current.profileId,
|
|
454
273
|
intensityDomain: profile.transform[0].resolved.intensityDomain
|
|
455
274
|
}
|
|
456
275
|
});
|
|
@@ -463,7 +282,7 @@ export const editGradientPlot = action(
|
|
|
463
282
|
next = next.createGradientPlotCenter({
|
|
464
283
|
id: current.centerId,
|
|
465
284
|
owner: owner.id,
|
|
466
|
-
data: profileId,
|
|
285
|
+
data: current.profileId,
|
|
467
286
|
category: current.category,
|
|
468
287
|
categoryType: current.categoryType,
|
|
469
288
|
coordinate: owner.coordinate,
|
|
@@ -5,20 +5,21 @@ import {
|
|
|
5
5
|
} from "./components.js";
|
|
6
6
|
import { createGradientPlot } from "./create.js";
|
|
7
7
|
import { editGradientPlot } from "./edit.js";
|
|
8
|
+
import { rebindGradientPlotProfile } from "./rebind.js";
|
|
8
9
|
import {
|
|
9
10
|
materializeGradientPlot,
|
|
10
11
|
materializeGradientPlotFill
|
|
11
12
|
} from "./materialize.js";
|
|
12
|
-
import { rebindGradientPlotProfile } from "./rebind.js";
|
|
13
13
|
|
|
14
14
|
export function registerGradientPlotActions(ProgramClass) {
|
|
15
|
-
ProgramClass.prototype
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
rematerializeGradientPlotLegend
|
|
23
|
-
|
|
15
|
+
Object.assign(ProgramClass.prototype, {
|
|
16
|
+
createGradientPlot,
|
|
17
|
+
editGradientPlot,
|
|
18
|
+
materializeGradientPlot,
|
|
19
|
+
materializeGradientPlotFill,
|
|
20
|
+
createGradientPlotCenter,
|
|
21
|
+
createGradientPlotLegend,
|
|
22
|
+
rematerializeGradientPlotLegend,
|
|
23
|
+
rebindGradientPlotProfile
|
|
24
|
+
});
|
|
24
25
|
}
|
|
@@ -23,10 +23,6 @@ function position(values, scale) {
|
|
|
23
23
|
: mapContinuousScaleValues(values, scale);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function profileValue(row, key) {
|
|
27
|
-
return row[GRADIENT_PROFILE_FIELDS[key]];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
function axisWithTitle(option, text) {
|
|
31
27
|
if (option === false) return false;
|
|
32
28
|
const axis = option ?? {};
|
|
@@ -56,20 +52,22 @@ function gradientAxesOptions(config) {
|
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
function bodyItems(program, layer, dataset, config) {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
55
|
+
const vertical = config.orientation === "vertical";
|
|
56
|
+
const categoryChannel = vertical ? "x" : "y";
|
|
57
|
+
const measureChannel = vertical ? "y" : "x";
|
|
61
58
|
const categoryEncoding = layer.encoding[categoryChannel];
|
|
62
59
|
const measureEncoding = layer.encoding[measureChannel];
|
|
63
60
|
const categoryScale = program.resolvedScales[categoryEncoding.scale];
|
|
64
61
|
const measureScale = program.resolvedScales[measureEncoding.scale];
|
|
65
|
-
const
|
|
62
|
+
const rows = dataset.values;
|
|
63
|
+
const categories = rows.map(row => row[config.category]);
|
|
66
64
|
const centers = position(categories, categoryScale);
|
|
67
65
|
const lower = position(
|
|
68
|
-
|
|
66
|
+
rows.map(row => row[GRADIENT_PROFILE_FIELDS.lower]),
|
|
69
67
|
measureScale
|
|
70
68
|
);
|
|
71
69
|
const upper = position(
|
|
72
|
-
|
|
70
|
+
rows.map(row => row[GRADIENT_PROFILE_FIELDS.upper]),
|
|
73
71
|
measureScale
|
|
74
72
|
);
|
|
75
73
|
const categoryColors = resolveGradientPlotCategoryColors(
|
|
@@ -81,10 +79,10 @@ function bodyItems(program, layer, dataset, config) {
|
|
|
81
79
|
if (!Number.isFinite(thickness) || thickness <= 0) {
|
|
82
80
|
throw new Error(`Gradient plot "${layer.id}" requires a positive band width.`);
|
|
83
81
|
}
|
|
84
|
-
return
|
|
82
|
+
return rows.map((row, index) => {
|
|
85
83
|
const a = lower[index];
|
|
86
84
|
const b = upper[index];
|
|
87
|
-
const geometry =
|
|
85
|
+
const geometry = vertical
|
|
88
86
|
? {
|
|
89
87
|
x: centers[index] - thickness / 2,
|
|
90
88
|
y: Math.min(a, b),
|
|
@@ -102,8 +100,8 @@ function bodyItems(program, layer, dataset, config) {
|
|
|
102
100
|
properties: {
|
|
103
101
|
...geometry,
|
|
104
102
|
fill: createGradientPlotPaint({
|
|
105
|
-
values:
|
|
106
|
-
intensities:
|
|
103
|
+
values: row[GRADIENT_PROFILE_FIELDS.values],
|
|
104
|
+
intensities: row[GRADIENT_PROFILE_FIELDS.intensities]
|
|
107
105
|
}, {
|
|
108
106
|
orientation: config.orientation,
|
|
109
107
|
valueScale: measureScale,
|
|
@@ -223,28 +221,16 @@ export const materializeGradientPlot = action(
|
|
|
223
221
|
}
|
|
224
222
|
});
|
|
225
223
|
const measureChannel = orientation === "vertical" ? "y" : "x";
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
.editSemantic({
|
|
237
|
-
property: `layer[${owner}].encoding.${secondaryChannel}.field`,
|
|
238
|
-
value: GRADIENT_PROFILE_FIELDS.upper
|
|
239
|
-
})
|
|
240
|
-
.editSemantic({
|
|
241
|
-
property: `layer[${owner}].encoding.${secondaryChannel}.fieldType`,
|
|
242
|
-
value: "quantitative"
|
|
243
|
-
})
|
|
244
|
-
.editSemantic({
|
|
245
|
-
property: `layer[${owner}].encoding.${secondaryChannel}.scale`,
|
|
246
|
-
value: measure.scale
|
|
247
|
-
});
|
|
224
|
+
const path = `layer[${owner}].encoding.`;
|
|
225
|
+
for (const [property, value] of [
|
|
226
|
+
[`${path}${measureChannel}.field`, GRADIENT_PROFILE_FIELDS.lower],
|
|
227
|
+
[`${path}${measureChannel}.fieldType`, "quantitative"],
|
|
228
|
+
[`${path}${measureChannel}2.field`, GRADIENT_PROFILE_FIELDS.upper],
|
|
229
|
+
[`${path}${measureChannel}2.fieldType`, "quantitative"],
|
|
230
|
+
[`${path}${measureChannel}2.scale`, measure.scale]
|
|
231
|
+
]) {
|
|
232
|
+
next = next.editSemantic({ property, value });
|
|
233
|
+
}
|
|
248
234
|
next = next.materializeGradientPlotFill({ id: owner });
|
|
249
235
|
if (config.center !== false) {
|
|
250
236
|
const categoryScale = next.resolvedScales[category.scale];
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { isPlainObject } from "../../core/immutable.js";
|
|
2
1
|
import {
|
|
3
|
-
validateKeys,
|
|
4
2
|
validateNonEmptyString,
|
|
5
3
|
validateNonNegativeFinite,
|
|
4
|
+
validateOptionObject,
|
|
6
5
|
validateUnitInterval
|
|
7
6
|
} from "../../core/validation.js";
|
|
8
7
|
import { normalizePalette } from "../../grammar/palettes.js";
|
|
@@ -14,11 +13,10 @@ export const GRADIENT_PLOT_OPTIONS = Object.freeze([
|
|
|
14
13
|
|
|
15
14
|
function plain(value, keys, label, operation) {
|
|
16
15
|
if (value === undefined) return {};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return value;
|
|
16
|
+
const context = `${operation} ${label}`;
|
|
17
|
+
return validateOptionObject(value, keys, context, {
|
|
18
|
+
plainObjectMessage: `${context} must be a plain object.`
|
|
19
|
+
});
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
export function resolveGradientPosition(value, label, operation = "createGradientPlot") {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { resolveContinuousPalette } from "../../grammar/palettes.js";
|
|
2
|
+
import { inverseLerp } from "../../grammar/numeric.js";
|
|
2
3
|
import { mapScaleConsumerValues } from "../../materialization/scales/map.js";
|
|
3
4
|
|
|
4
5
|
function parseHex(value) {
|
|
5
6
|
const match = value.match(/^#([0-9a-f]{6})$/i);
|
|
6
7
|
if (!match) throw new Error(`Gradient plot requires a six-digit hex color, received "${value}".`);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
);
|
|
8
|
+
const color = Number.parseInt(match[1], 16);
|
|
9
|
+
return [color >> 16, color >> 8 & 255, color & 255];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function interpolate(left, right, amount) {
|
|
@@ -23,8 +23,11 @@ function paletteColor(colors, amount) {
|
|
|
23
23
|
const bounded = Math.max(0, Math.min(1, amount));
|
|
24
24
|
const scaled = bounded * (colors.length - 1);
|
|
25
25
|
const left = Math.floor(scaled);
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
return interpolate(
|
|
27
|
+
parseHex(colors[left]),
|
|
28
|
+
parseHex(colors[Math.min(colors.length - 1, left + 1)]),
|
|
29
|
+
scaled - left
|
|
30
|
+
);
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
function categoricalRamp(base) {
|
|
@@ -75,7 +78,7 @@ export function createGradientPlotPaint(profile, {
|
|
|
75
78
|
const stops = values.map((_, index) => {
|
|
76
79
|
const fraction = intensities[index] / maximum;
|
|
77
80
|
const offset = Number(
|
|
78
|
-
(
|
|
81
|
+
inverseLerp(mapped[index], startPosition, endPosition).toFixed(12)
|
|
79
82
|
);
|
|
80
83
|
return {
|
|
81
84
|
offset: Object.is(offset, -0) ? 0 : offset,
|