ggaction 0.0.3 → 0.0.5
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 +58 -3
- package/README.md +117 -83
- package/package.json +27 -11
- package/src/actions/boxPlots/create.js +13 -7
- package/src/actions/boxPlots/edit.js +31 -35
- package/src/actions/boxPlots/materialize.js +3 -0
- package/src/actions/boxPlots/options.js +8 -1
- package/src/actions/boxPlots/resolve.js +10 -2
- package/src/actions/charts/bar.js +50 -0
- package/src/actions/charts/heatmap.js +205 -0
- package/src/actions/charts/histogram.js +58 -0
- package/src/actions/charts/index.js +15 -0
- package/src/actions/charts/line.js +69 -0
- package/src/actions/charts/parallel.js +76 -0
- package/src/actions/charts/scatter.js +56 -0
- package/src/actions/charts/shared.js +121 -0
- package/src/actions/coordinates/parallel.js +50 -0
- package/src/actions/data/bin2d.js +137 -0
- package/src/actions/data/density.js +54 -13
- package/src/actions/data/derived.js +22 -0
- package/src/actions/data/filter.js +5 -0
- package/src/actions/data/gradientProfile.js +65 -0
- package/src/actions/data/horizon.js +71 -0
- package/src/actions/data/index.js +29 -2
- package/src/actions/data/regression.js +1 -1
- package/src/actions/data/window.js +50 -0
- package/src/actions/encodings/appearance.js +1 -1
- package/src/actions/encodings/color/continuous.js +137 -0
- package/src/actions/encodings/color/index.js +167 -0
- package/src/actions/encodings/color/layout.js +99 -0
- package/src/actions/encodings/color/policy.js +88 -0
- package/src/actions/encodings/density/resolve.js +109 -0
- package/src/actions/encodings/density.js +193 -88
- package/src/actions/encodings/horizon/resolve.js +283 -0
- package/src/actions/encodings/horizon.js +364 -0
- package/src/actions/encodings/index.js +8 -2
- package/src/actions/encodings/offset.js +1 -1
- package/src/actions/encodings/parallel.js +115 -0
- package/src/actions/encodings/pathOrder.js +131 -0
- package/src/actions/encodings/position/apply.js +73 -4
- package/src/actions/encodings/{position.js → position/index.js} +21 -8
- package/src/actions/encodings/position/policies/arc.js +27 -4
- package/src/actions/encodings/position/policies/area.js +33 -2
- package/src/actions/encodings/position/policies/index.js +7 -2
- package/src/actions/encodings/position/policies/line.js +18 -2
- package/src/actions/encodings/position/resolve.js +25 -3
- package/src/actions/encodings/ranged.js +5 -3
- package/src/actions/encodings/ruleAppearance.js +88 -10
- package/src/actions/encodings/strokeDash.js +1 -1
- package/src/actions/encodings/text.js +1 -1
- package/src/actions/errorBands/options.js +1 -1
- package/src/actions/errorBars/options.js +1 -1
- package/src/actions/facets/actions.js +2 -3
- package/src/actions/facets/derive.js +35 -5
- package/src/actions/facets/guides.js +1 -1
- package/src/actions/facets/replay.js +7 -36
- package/src/actions/gradientPlots/components.js +177 -0
- package/src/actions/gradientPlots/create.js +96 -0
- package/src/actions/gradientPlots/edit.js +153 -0
- package/src/actions/gradientPlots/index.js +24 -0
- package/src/actions/gradientPlots/materialize.js +287 -0
- package/src/actions/gradientPlots/options.js +120 -0
- package/src/actions/gradientPlots/paint.js +107 -0
- package/src/actions/gradientPlots/rebind.js +51 -0
- package/src/actions/gradientPlots/resolve.js +81 -0
- package/src/actions/guides/applicability.js +44 -17
- package/src/actions/guides/axes/axes.js +37 -3
- package/src/actions/guides/axes/index.js +2 -0
- package/src/actions/guides/axes/labels.js +12 -7
- package/src/actions/guides/axes/parallel/resolve.js +86 -0
- package/src/actions/guides/axes/parallel.js +228 -0
- package/src/actions/guides/axes/ticks.js +1 -1
- package/src/actions/guides/axes/titles.js +12 -9
- package/src/actions/guides/grids/resolve.js +1 -1
- package/src/actions/guides/guides.js +60 -5
- package/src/actions/guides/legends/categorical/actions.js +48 -11
- package/src/actions/guides/legends/categorical/layout.js +4 -2
- package/src/actions/guides/legends/continuous/interval.js +1 -1
- package/src/actions/guides/legends/continuous/opacity.js +1 -1
- package/src/actions/guides/legends/edit.js +7 -20
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/remove.js +6 -31
- package/src/actions/guides/legends/size.js +1 -1
- package/src/actions/guides/legends/strokeWidth.js +170 -0
- package/src/actions/guides/legends/target.js +25 -0
- package/src/actions/guides/polar/{axes.js → axes/index.js} +5 -5
- package/src/actions/guides/polar/index.js +1 -1
- package/src/actions/guides/polar/resolve.js +1 -1
- package/src/actions/guides/tickValues.js +1 -1
- package/src/actions/index.js +6 -0
- package/src/actions/intervals/resolve.js +1 -1
- package/src/actions/marks/arc/actions.js +2 -2
- package/src/actions/marks/area/actions.js +61 -137
- package/src/actions/marks/area/materialize.js +163 -0
- package/src/actions/marks/bar/edit.js +1 -1
- package/src/actions/marks/index.js +5 -1
- package/src/actions/marks/line/actions.js +100 -81
- package/src/actions/marks/line/materialize.js +146 -0
- package/src/actions/marks/point/create.js +76 -0
- package/src/actions/marks/point/edit.js +85 -0
- package/src/actions/marks/point/index.js +11 -1
- package/src/actions/marks/point/jitter.js +72 -0
- package/src/actions/marks/point/{actions.js → materialize.js} +175 -170
- package/src/actions/marks/rect/actions.js +4 -1
- package/src/actions/marks/remove.js +13 -2
- package/src/actions/marks/rule/actions.js +26 -4
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +20 -5
- package/src/actions/marks/text/index.js +7 -1
- package/src/actions/marks/text/layout.js +321 -0
- package/src/actions/primitives/semanticValidation/dataset.js +22 -0
- package/src/actions/primitives/semanticValidation/guide.js +43 -0
- package/src/actions/primitives/semanticValidation/index.js +23 -0
- package/src/actions/primitives/semanticValidation/layer.js +74 -0
- package/src/actions/primitives/semanticValidation/scale.js +74 -0
- package/src/actions/primitives/semanticValidation/shared.js +5 -0
- package/src/actions/primitives/semanticValue.js +1 -204
- package/src/actions/regression/components.js +1 -1
- package/src/actions/regression/create.js +1 -1
- package/src/actions/regression/edit.js +20 -22
- package/src/actions/scales/consumers/common.js +109 -0
- package/src/actions/scales/consumers/families.js +58 -0
- package/src/actions/scales/consumers/index.js +41 -0
- package/src/actions/scales/{consumers.js → consumers/seriesLayout.js} +10 -132
- package/src/actions/scales/create.js +1 -1
- package/src/actions/scales/definitions.js +32 -2
- package/src/actions/scales/edit.js +8 -206
- package/src/actions/scales/editPolicy.js +214 -0
- package/src/actions/scales/materialize.js +3 -3
- package/src/actions/scales/patch.js +26 -0
- package/src/actions/selection/actions.js +28 -3
- package/src/actions/violinPlots/create.js +198 -0
- package/src/actions/violinPlots/index.js +5 -0
- package/src/core/ChartProgram.js +22 -236
- package/src/core/compositionState.js +169 -0
- package/src/core/materializationState.js +65 -0
- package/src/core/programState.js +28 -0
- package/src/core/textMetrics.js +47 -0
- package/src/core/vocabulary.js +7 -2
- package/src/grammar/arcs.js +50 -13
- package/src/grammar/areaSeries.js +42 -6
- package/src/grammar/bars/aggregate.js +1 -1
- package/src/grammar/bin2d.js +333 -0
- package/src/grammar/categoricalDensity.js +192 -0
- package/src/grammar/coordinates.js +1 -1
- package/src/grammar/curveCommands.js +3 -1
- package/src/grammar/density.js +253 -27
- package/src/grammar/facets/dependencies.js +3 -11
- package/src/grammar/{facets.js → facets/index.js} +8 -6
- package/src/grammar/gradientProfile.js +213 -0
- package/src/grammar/horizon.js +455 -0
- package/src/grammar/interval.js +2 -96
- package/src/grammar/jitter.js +197 -0
- package/src/grammar/lineSeries.js +70 -1
- package/src/grammar/paint.js +88 -0
- package/src/grammar/palettes.js +5 -7
- package/src/grammar/parallelCoordinates.js +213 -0
- package/src/grammar/pathOrder.js +35 -0
- package/src/grammar/pointShapes.js +24 -0
- package/src/grammar/polarLineCommands.js +1 -1
- package/src/grammar/regression/derive.js +97 -0
- package/src/grammar/regression/index.js +22 -0
- package/src/grammar/regression/models.js +282 -0
- package/src/grammar/regression/parameters.js +106 -0
- package/src/grammar/rules.js +2 -1
- package/src/grammar/scales/appearance.js +21 -0
- package/src/grammar/scales/color.js +4 -2
- package/src/grammar/schemas/concreteGraphic.js +11 -1
- package/src/grammar/schemas/graphicBounds.js +19 -37
- package/src/grammar/schemas/semanticPath.js +17 -2
- package/src/grammar/statistics/studentT.js +98 -0
- package/src/grammar/transforms.js +101 -12
- package/src/grammar/window.js +339 -0
- package/src/layout/labels.js +270 -0
- package/src/layout/text.js +4 -1
- package/src/layout/title.js +10 -20
- package/src/materialization/bars/aggregate.js +1 -1
- package/src/materialization/bars/grouped.js +1 -1
- package/src/materialization/bars/histogram.js +1 -1
- package/src/materialization/bars/ranged.js +1 -1
- package/src/materialization/dataProvenance.js +56 -2
- package/src/materialization/density.js +1 -1
- package/src/materialization/dependencies.js +15 -101
- package/src/materialization/encodings.js +2 -2
- package/src/materialization/facetGuides/index.js +2 -0
- package/src/materialization/facetGuides/legacyCategorical.js +125 -0
- package/src/materialization/facetGuides/placement.js +126 -0
- package/src/materialization/facetGuides/preparation.js +188 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/guides/resources.js +74 -24
- package/src/materialization/horizon.js +29 -0
- package/src/materialization/layout.js +15 -0
- package/src/materialization/marks/capabilities.js +161 -0
- package/src/materialization/marks/index.js +108 -0
- package/src/materialization/marks/pathOrder.js +71 -0
- package/src/materialization/marks/policies.js +91 -0
- package/src/materialization/rect.js +1 -1
- package/src/materialization/rowEncoding.js +1 -1
- package/src/materialization/scaleGuideDependencies.js +102 -0
- package/src/materialization/scales/map.js +1 -1
- package/src/materialization/scales/policies/series.js +1 -1
- package/src/materialization/scales/resolve.js +3 -1
- package/src/materialization/selection/items/bar.js +1 -1
- package/src/materialization/selection/items/path.js +27 -1
- package/src/materialization/selection/items/point.js +1 -5
- package/src/materialization/selection/items/rect.js +21 -0
- package/src/materialization/selection/styles.js +21 -10
- package/src/materialization/text.js +12 -4
- package/src/renderers/canvas/fill.js +33 -0
- package/src/renderers/canvas/index.js +7 -0
- package/src/renderers/canvas/path.js +7 -4
- package/src/renderers/canvas/rect.js +9 -3
- package/src/renderers/canvas/text.js +14 -1
- package/src/selectors/datasets.js +4 -0
- package/src/theme/defaults.js +4 -0
- package/types/extension.d.ts +10 -1
- package/types/index.d.ts +76 -0
- package/types/program.d.ts +632 -10
- package/src/actions/encodings/color.js +0 -424
- package/src/grammar/regression.js +0 -578
- package/src/grammar/scales.js +0 -1
- package/src/materialization/facetGuides.js +0 -426
- package/src/materialization/marks.js +0 -316
|
@@ -1,204 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { isPlainObject } from "../../core/immutable.js";
|
|
3
|
-
import {
|
|
4
|
-
COLOR_LAYOUTS,
|
|
5
|
-
STACK_MODES,
|
|
6
|
-
CATEGORICAL_LEGEND_CHANNELS,
|
|
7
|
-
MARK_TYPES
|
|
8
|
-
} from "../../core/vocabulary.js";
|
|
9
|
-
import { validateAggregate } from "../../grammar/aggregate.js";
|
|
10
|
-
import { findLayer, findSemanticScale, hasDataset } from "../../selectors/index.js";
|
|
11
|
-
import { validateCoordinateType } from "../../grammar/coordinates.js";
|
|
12
|
-
import {
|
|
13
|
-
normalizeHistogramBin,
|
|
14
|
-
validateHistogramBinBoundaries,
|
|
15
|
-
validateHistogramBinStep
|
|
16
|
-
} from "../../grammar/histogram.js";
|
|
17
|
-
import { validateDatasetTransforms } from "../../grammar/transforms.js";
|
|
18
|
-
import {
|
|
19
|
-
validateSemanticFieldType,
|
|
20
|
-
validateContinuousColorInterpolation,
|
|
21
|
-
validateScalePropertyForType,
|
|
22
|
-
validateSemanticScaleDomain,
|
|
23
|
-
validateSemanticScaleRange,
|
|
24
|
-
validateSemanticScaleType
|
|
25
|
-
} from "../../grammar/scales.js";
|
|
26
|
-
|
|
27
|
-
function nonEmptyString(value, label) {
|
|
28
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
29
|
-
throw new TypeError(`${label} must be a non-empty string.`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function validateLegend(property, value) {
|
|
34
|
-
if (property === "title") {
|
|
35
|
-
nonEmptyString(value, "Legend title");
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
39
|
-
throw new TypeError(`Legend ${property} must be a non-empty array.`);
|
|
40
|
-
}
|
|
41
|
-
if (new Set(value).size !== value.length) {
|
|
42
|
-
throw new Error(`Legend ${property} must not contain duplicates.`);
|
|
43
|
-
}
|
|
44
|
-
if (property === "channels") {
|
|
45
|
-
if (!value.every(channel => CATEGORICAL_LEGEND_CHANNELS.includes(channel))) {
|
|
46
|
-
throw new Error("Legend channels support only color, strokeDash, and shape.");
|
|
47
|
-
}
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
for (const id of value) validateUserId(id, "Legend scale id");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function validateSemanticValue(program, parsed, value) {
|
|
54
|
-
if (parsed.kind === "dataset" && parsed.path[0] === "values") {
|
|
55
|
-
if (!Array.isArray(value) || !value.every(isPlainObject)) {
|
|
56
|
-
throw new TypeError("Dataset values must be an array of plain row objects.");
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (parsed.kind === "dataset" && parsed.path[0] === "source") {
|
|
60
|
-
validateUserId(value, "Dataset source id");
|
|
61
|
-
if (!hasDataset(program, value)) {
|
|
62
|
-
throw new Error(`Unknown source dataset "${value}".`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (parsed.kind === "dataset" && parsed.path[0] === "transform") {
|
|
66
|
-
validateDatasetTransforms(value);
|
|
67
|
-
}
|
|
68
|
-
if (
|
|
69
|
-
parsed.kind === "layer" &&
|
|
70
|
-
parsed.path.join(".") === "mark.type" &&
|
|
71
|
-
!MARK_TYPES.includes(value)
|
|
72
|
-
) {
|
|
73
|
-
throw new Error(`Unknown mark type "${value}".`);
|
|
74
|
-
}
|
|
75
|
-
if (parsed.kind === "layer") {
|
|
76
|
-
const property = parsed.path.join(".");
|
|
77
|
-
if (property === "source") {
|
|
78
|
-
validateUserId(value, "Layer source id");
|
|
79
|
-
if (value === parsed.id) {
|
|
80
|
-
throw new Error("A layer cannot use itself as its source.");
|
|
81
|
-
}
|
|
82
|
-
const source = findLayer(program, value);
|
|
83
|
-
if (source === undefined) {
|
|
84
|
-
throw new Error(`Unknown source layer "${value}".`);
|
|
85
|
-
}
|
|
86
|
-
if (!["point", "bar", "rule", "rect"].includes(source.mark?.type)) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`Layer source "${value}" must be a point, bar, rule, or rect mark.`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (property.endsWith(".title")) {
|
|
93
|
-
nonEmptyString(value, "Encoding title");
|
|
94
|
-
}
|
|
95
|
-
if (property.endsWith(".fieldType")) validateSemanticFieldType(value);
|
|
96
|
-
if (property.endsWith(".aggregate")) validateAggregate(value);
|
|
97
|
-
if (property.endsWith(".bin.maxBins")) normalizeHistogramBin({ maxBins: value });
|
|
98
|
-
if (property.endsWith(".bin.step")) validateHistogramBinStep(value);
|
|
99
|
-
if (property.endsWith(".bin.boundaries")) {
|
|
100
|
-
validateHistogramBinBoundaries(value);
|
|
101
|
-
}
|
|
102
|
-
if (
|
|
103
|
-
property.endsWith(".stack") &&
|
|
104
|
-
value !== null &&
|
|
105
|
-
!STACK_MODES.includes(value)
|
|
106
|
-
) {
|
|
107
|
-
throw new Error(`Unsupported stack "${value}".`);
|
|
108
|
-
}
|
|
109
|
-
if (
|
|
110
|
-
property === "encoding.color.layout" &&
|
|
111
|
-
!COLOR_LAYOUTS.includes(value)
|
|
112
|
-
) {
|
|
113
|
-
throw new Error(`Unsupported color layout "${value}".`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (parsed.kind === "scale") {
|
|
117
|
-
const property = parsed.path.join(".");
|
|
118
|
-
const existing = findSemanticScale(program, parsed.id);
|
|
119
|
-
if (property === "type") {
|
|
120
|
-
validateSemanticScaleType(value);
|
|
121
|
-
for (const owned of [
|
|
122
|
-
"nice", "zero", "clamp", "base", "exponent", "constant",
|
|
123
|
-
"paddingInner", "paddingOuter", "padding", "align"
|
|
124
|
-
]) {
|
|
125
|
-
if (existing?.[owned] !== undefined) {
|
|
126
|
-
validateScalePropertyForType(value, owned);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} else if (property === "domain") validateSemanticScaleDomain(value);
|
|
130
|
-
else if (property === "range") validateSemanticScaleRange(value);
|
|
131
|
-
else if (property === "nice") {
|
|
132
|
-
if (typeof value !== "boolean") throw new TypeError("Scale nice must be a boolean.");
|
|
133
|
-
if (existing?.type !== undefined) {
|
|
134
|
-
validateScalePropertyForType(existing.type, "nice");
|
|
135
|
-
}
|
|
136
|
-
} else if (property === "zero") {
|
|
137
|
-
if (typeof value !== "boolean") throw new TypeError("Scale zero must be a boolean.");
|
|
138
|
-
if (existing?.type !== undefined) {
|
|
139
|
-
validateScalePropertyForType(existing.type, "zero");
|
|
140
|
-
}
|
|
141
|
-
} else if (property === "clamp") {
|
|
142
|
-
if (typeof value !== "boolean") throw new TypeError("Scale clamp must be a boolean.");
|
|
143
|
-
if (existing?.type !== undefined) {
|
|
144
|
-
validateScalePropertyForType(existing.type, "clamp");
|
|
145
|
-
}
|
|
146
|
-
} else if (property === "reverse") {
|
|
147
|
-
if (typeof value !== "boolean") throw new TypeError("Scale reverse must be a boolean.");
|
|
148
|
-
} else if (["base", "exponent", "constant"].includes(property)) {
|
|
149
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
150
|
-
throw new RangeError(`Scale ${property} must be a positive finite number.`);
|
|
151
|
-
}
|
|
152
|
-
if (property === "base" && value === 1) {
|
|
153
|
-
throw new RangeError("Scale base must not equal 1.");
|
|
154
|
-
}
|
|
155
|
-
if (existing?.type !== undefined) {
|
|
156
|
-
validateScalePropertyForType(existing.type, property);
|
|
157
|
-
}
|
|
158
|
-
} else if (property === "interpolate") {
|
|
159
|
-
validateContinuousColorInterpolation(value);
|
|
160
|
-
} else if (property === "paddingInner") {
|
|
161
|
-
if (!Number.isFinite(value) || value < 0 || value >= 1) {
|
|
162
|
-
throw new RangeError(
|
|
163
|
-
"Scale paddingInner must be from 0 (inclusive) to 1 (exclusive)."
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
if (existing?.type !== undefined) {
|
|
167
|
-
validateScalePropertyForType(existing.type, property);
|
|
168
|
-
}
|
|
169
|
-
} else if (property === "paddingOuter" || property === "padding") {
|
|
170
|
-
if (!Number.isFinite(value) || value < 0) {
|
|
171
|
-
throw new RangeError(`Scale ${property} must be a non-negative finite number.`);
|
|
172
|
-
}
|
|
173
|
-
if (existing?.type !== undefined) {
|
|
174
|
-
validateScalePropertyForType(existing.type, property);
|
|
175
|
-
}
|
|
176
|
-
} else if (property === "align") {
|
|
177
|
-
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
178
|
-
throw new RangeError("Scale align must be between 0 and 1.");
|
|
179
|
-
}
|
|
180
|
-
if (existing?.type !== undefined) {
|
|
181
|
-
validateScalePropertyForType(existing.type, property);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (parsed.kind === "coordinate" && parsed.path[0] === "type") {
|
|
186
|
-
validateCoordinateType(value);
|
|
187
|
-
}
|
|
188
|
-
if (parsed.kind === "guide" && parsed.id === "legend.series") {
|
|
189
|
-
validateLegend(parsed.path.at(-1), value);
|
|
190
|
-
}
|
|
191
|
-
if (parsed.kind === "guide" && (
|
|
192
|
-
parsed.id.startsWith("grid.") || parsed.id.startsWith("axis.")
|
|
193
|
-
)) {
|
|
194
|
-
const property = parsed.path.at(-1);
|
|
195
|
-
if (property === "title") nonEmptyString(value, "Axis title");
|
|
196
|
-
else {
|
|
197
|
-
const guideKind = parsed.id.startsWith("grid.") ? "Grid" : "Axis";
|
|
198
|
-
validateUserId(value, `${guideKind} ${property} id`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
if (parsed.kind === "title") {
|
|
202
|
-
nonEmptyString(value, `Chart title ${parsed.path[0]}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
1
|
+
export { validateSemanticValue } from "./semanticValidation/index.js";
|
|
@@ -7,7 +7,7 @@ import { resolveEligibleLayer } from "../../selectors/layers.js";
|
|
|
7
7
|
import {
|
|
8
8
|
REGRESSION_LOWER_FIELD,
|
|
9
9
|
REGRESSION_UPPER_FIELD
|
|
10
|
-
} from "../../grammar/regression.js";
|
|
10
|
+
} from "../../grammar/regression/index.js";
|
|
11
11
|
import { validateAreaCreateOutline } from "../marks/area/index.js";
|
|
12
12
|
|
|
13
13
|
const BAND_OPTIONS = Object.freeze([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { action } from "../../core/action.js";
|
|
2
2
|
import { validateKeys } from "../../core/validation.js";
|
|
3
|
-
import { normalizeRegressionParameters } from "../../grammar/regression.js";
|
|
3
|
+
import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
|
|
4
4
|
import {
|
|
5
5
|
findRegressionPoint,
|
|
6
6
|
inferRegressionGroup,
|
|
@@ -7,9 +7,10 @@ import {
|
|
|
7
7
|
validateNonNegativeFinite,
|
|
8
8
|
validateUnitInterval
|
|
9
9
|
} from "../../core/validation.js";
|
|
10
|
-
import { normalizeRegressionParameters } from "../../grammar/regression.js";
|
|
10
|
+
import { normalizeRegressionParameters } from "../../grammar/regression/index.js";
|
|
11
11
|
import { validateCurveInterpolation } from "../../grammar/curveCommands.js";
|
|
12
|
-
import {
|
|
12
|
+
import { planDerivedDataRevision } from
|
|
13
|
+
"../../materialization/dataProvenance.js";
|
|
13
14
|
import { findLayer } from "../../selectors/layers.js";
|
|
14
15
|
|
|
15
16
|
const OPTIONS = Object.freeze([
|
|
@@ -117,14 +118,6 @@ function resolveParameters(previous, args) {
|
|
|
117
118
|
return normalizeRegressionParameters(raw);
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
function nextRevisionId(program, ownerId) {
|
|
121
|
-
let revision = 1;
|
|
122
|
-
while (hasDataset(program, `${ownerId}RegressionDataRevision${revision}`)) {
|
|
123
|
-
revision += 1;
|
|
124
|
-
}
|
|
125
|
-
return `${ownerId}RegressionDataRevision${revision}`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
121
|
function removeBand(program, id) {
|
|
129
122
|
return program
|
|
130
123
|
.editSemantic({ property: `layer[${id}]`, remove: true })
|
|
@@ -165,9 +158,20 @@ export const editRegression = action(
|
|
|
165
158
|
: hadBand || bandPatch !== undefined || current.parameters.method === "loess");
|
|
166
159
|
let dataId = current.dataId;
|
|
167
160
|
let next = this;
|
|
161
|
+
const revision = changesStatistics
|
|
162
|
+
? planDerivedDataRevision(this, {
|
|
163
|
+
owner: owner.id,
|
|
164
|
+
role: "RegressionData",
|
|
165
|
+
previous: current.dataId,
|
|
166
|
+
consumers: [
|
|
167
|
+
current.lineId,
|
|
168
|
+
...(hadBand ? [current.bandId] : [])
|
|
169
|
+
]
|
|
170
|
+
})
|
|
171
|
+
: undefined;
|
|
168
172
|
|
|
169
173
|
if (changesStatistics) {
|
|
170
|
-
dataId =
|
|
174
|
+
dataId = revision.id;
|
|
171
175
|
next = next.createRegressionData({
|
|
172
176
|
id: dataId,
|
|
173
177
|
source: current.source,
|
|
@@ -176,6 +180,9 @@ export const editRegression = action(
|
|
|
176
180
|
...(current.groupBy === undefined ? {} : { groupBy: current.groupBy }),
|
|
177
181
|
...parameters
|
|
178
182
|
});
|
|
183
|
+
for (const rebind of revision.rebinds) {
|
|
184
|
+
next = next.rebindLayerData(rebind);
|
|
185
|
+
}
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
let bandId = current.bandId;
|
|
@@ -198,21 +205,12 @@ export const editRegression = action(
|
|
|
198
205
|
});
|
|
199
206
|
} else if (hadBand && changesStatistics) {
|
|
200
207
|
const bandConfig = next.markConfigs[bandId];
|
|
201
|
-
next = next
|
|
202
|
-
.editSemantic({ property: `layer[${bandId}].data`, value: dataId })
|
|
203
|
-
._withMarkConfig(bandId, {
|
|
208
|
+
next = next._withMarkConfig(bandId, {
|
|
204
209
|
...bandConfig,
|
|
205
210
|
errorBand: { ...bandConfig.errorBand, data: dataId }
|
|
206
211
|
});
|
|
207
212
|
}
|
|
208
213
|
|
|
209
|
-
if (changesStatistics) {
|
|
210
|
-
next = next.editSemantic({
|
|
211
|
-
property: `layer[${current.lineId}].data`,
|
|
212
|
-
value: dataId
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
214
|
if (bandId !== undefined) {
|
|
217
215
|
if (
|
|
218
216
|
bandPatch !== undefined &&
|
|
@@ -232,7 +230,7 @@ export const editRegression = action(
|
|
|
232
230
|
}
|
|
233
231
|
|
|
234
232
|
if (changesStatistics) {
|
|
235
|
-
next = next.releaseDerivedData(
|
|
233
|
+
next = next.releaseDerivedData(revision.release);
|
|
236
234
|
}
|
|
237
235
|
return next._withMarkConfig(owner.id, {
|
|
238
236
|
...next.markConfigs[owner.id],
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readNominalField,
|
|
3
|
+
readQuantitativeField,
|
|
4
|
+
readScaleField,
|
|
5
|
+
readTemporalField
|
|
6
|
+
} from "../../../grammar/scales/index.js";
|
|
7
|
+
import { findDataset } from "../../../selectors/datasets.js";
|
|
8
|
+
import { requireSemanticScale } from "../../../selectors/scales.js";
|
|
9
|
+
import { SCALED_ENCODING_CHANNELS } from "../../../core/vocabulary.js";
|
|
10
|
+
|
|
11
|
+
export function findScale(program, id) {
|
|
12
|
+
return requireSemanticScale(program, id);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function findScaleConsumers(program, id) {
|
|
16
|
+
const consumers = [];
|
|
17
|
+
for (const layer of program.semanticSpec.layers) {
|
|
18
|
+
for (const channel of SCALED_ENCODING_CHANNELS) {
|
|
19
|
+
const encoding = layer.encoding?.[channel];
|
|
20
|
+
if (encoding?.scale === id) consumers.push({ layer, channel, encoding });
|
|
21
|
+
}
|
|
22
|
+
for (const dimension of layer.encoding?.parallel?.dimensions ?? []) {
|
|
23
|
+
if (dimension.scale === id) {
|
|
24
|
+
consumers.push({
|
|
25
|
+
layer,
|
|
26
|
+
channel: "y",
|
|
27
|
+
encoding: dimension,
|
|
28
|
+
role: "parallelDimension"
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return consumers;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function requireConsumerDataset(program, consumer) {
|
|
37
|
+
const dataset = findDataset(program, consumer.layer.data);
|
|
38
|
+
if (dataset === undefined) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Mark "${consumer.layer.id}" references unknown dataset "${consumer.layer.data}".`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return dataset;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function isDirectCategoricalConsumer(consumer) {
|
|
47
|
+
return ["color", "strokeDash", "xOffset", "yOffset", "shape"].includes(
|
|
48
|
+
consumer.channel
|
|
49
|
+
) && consumer.encoding.fieldType === "nominal";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function readConsumerFieldValues(
|
|
53
|
+
program,
|
|
54
|
+
consumer,
|
|
55
|
+
dataset,
|
|
56
|
+
scale = findScale(program, consumer.encoding.scale)
|
|
57
|
+
) {
|
|
58
|
+
const allowUnknown = Object.hasOwn(scale, "unknown");
|
|
59
|
+
if (consumer.role === "parallelDimension") {
|
|
60
|
+
return readScaleField(
|
|
61
|
+
dataset.values,
|
|
62
|
+
consumer.encoding.field,
|
|
63
|
+
consumer.encoding.fieldType,
|
|
64
|
+
{ allowUnknown: true }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (isDirectCategoricalConsumer(consumer)) {
|
|
68
|
+
return allowUnknown
|
|
69
|
+
? readScaleField(
|
|
70
|
+
dataset.values,
|
|
71
|
+
consumer.encoding.field,
|
|
72
|
+
consumer.encoding.fieldType,
|
|
73
|
+
{ allowUnknown: true }
|
|
74
|
+
)
|
|
75
|
+
: readNominalField(dataset.values, consumer.encoding.field);
|
|
76
|
+
}
|
|
77
|
+
if (consumer.encoding.fieldType === "temporal") {
|
|
78
|
+
return allowUnknown
|
|
79
|
+
? readScaleField(dataset.values, consumer.encoding.field, "temporal", {
|
|
80
|
+
allowUnknown: true
|
|
81
|
+
})
|
|
82
|
+
: readTemporalField(dataset.values, consumer.encoding.field);
|
|
83
|
+
}
|
|
84
|
+
if (["nominal", "ordinal"].includes(consumer.encoding.fieldType)) {
|
|
85
|
+
if (!["ordinal", "band", "point"].includes(scale.type)) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Scale materialization requires a quantitative encoding on mark "${consumer.layer.id}".`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return allowUnknown
|
|
91
|
+
? readScaleField(
|
|
92
|
+
dataset.values,
|
|
93
|
+
consumer.encoding.field,
|
|
94
|
+
consumer.encoding.fieldType,
|
|
95
|
+
{ allowUnknown: true }
|
|
96
|
+
)
|
|
97
|
+
: readNominalField(dataset.values, consumer.encoding.field);
|
|
98
|
+
}
|
|
99
|
+
if (consumer.encoding.fieldType !== "quantitative") {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Scale materialization requires a quantitative encoding on mark "${consumer.layer.id}".`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return allowUnknown
|
|
105
|
+
? readScaleField(dataset.values, consumer.encoding.field, "quantitative", {
|
|
106
|
+
allowUnknown: true
|
|
107
|
+
})
|
|
108
|
+
: readQuantitativeField(dataset.values, consumer.encoding.field);
|
|
109
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { deriveBarAggregates } from "../../../grammar/bars/aggregate.js";
|
|
2
|
+
import {
|
|
3
|
+
BAR_GRAINS,
|
|
4
|
+
resolveBarGrain
|
|
5
|
+
} from "../../../grammar/bars/policy.js";
|
|
6
|
+
import {
|
|
7
|
+
deriveLineSeries,
|
|
8
|
+
deriveLineSeriesFieldValues
|
|
9
|
+
} from "../../../grammar/lineSeries.js";
|
|
10
|
+
import { isAggregate } from "../../../grammar/aggregate.js";
|
|
11
|
+
import {
|
|
12
|
+
resolveRectConsumerValues
|
|
13
|
+
} from "../../../materialization/rect.js";
|
|
14
|
+
|
|
15
|
+
export function resolveMarkFamilyConsumerValues(consumer, dataset) {
|
|
16
|
+
if (consumer.layer.mark?.type === "rect") {
|
|
17
|
+
return {
|
|
18
|
+
matched: true,
|
|
19
|
+
values: resolveRectConsumerValues(
|
|
20
|
+
consumer.layer,
|
|
21
|
+
dataset,
|
|
22
|
+
consumer.channel
|
|
23
|
+
)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (
|
|
27
|
+
consumer.layer.mark?.type === "line" &&
|
|
28
|
+
(isAggregate(consumer.layer.encoding?.y?.aggregate) ||
|
|
29
|
+
consumer.channel === "strokeWidth")
|
|
30
|
+
) {
|
|
31
|
+
const derived = deriveLineSeries(dataset.values, consumer.layer);
|
|
32
|
+
return {
|
|
33
|
+
matched: true,
|
|
34
|
+
values: consumer.channel === "strokeWidth"
|
|
35
|
+
? deriveLineSeriesFieldValues(
|
|
36
|
+
dataset.values,
|
|
37
|
+
consumer.layer,
|
|
38
|
+
derived,
|
|
39
|
+
consumer.encoding.field
|
|
40
|
+
)
|
|
41
|
+
: consumer.channel === "x" ? derived.xValues : derived.yValues
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate) {
|
|
45
|
+
const derived = deriveBarAggregates(dataset.values, consumer.layer);
|
|
46
|
+
if (consumer.channel === "color") {
|
|
47
|
+
return {
|
|
48
|
+
matched: true,
|
|
49
|
+
values: derived.values.map(value => value.color)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
matched: true,
|
|
54
|
+
values: consumer.channel === "x" ? derived.xValues : derived.yValues
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { matched: false };
|
|
58
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { normalizeRuleDatum } from "../../../grammar/rules.js";
|
|
2
|
+
import {
|
|
3
|
+
findScale,
|
|
4
|
+
isDirectCategoricalConsumer,
|
|
5
|
+
readConsumerFieldValues,
|
|
6
|
+
requireConsumerDataset
|
|
7
|
+
} from "./common.js";
|
|
8
|
+
import { resolveMarkFamilyConsumerValues } from "./families.js";
|
|
9
|
+
|
|
10
|
+
export { findScale, findScaleConsumers } from "./common.js";
|
|
11
|
+
export {
|
|
12
|
+
resolveHistogramCountValues,
|
|
13
|
+
resolveSeriesLayoutScaleValues
|
|
14
|
+
} from "./seriesLayout.js";
|
|
15
|
+
|
|
16
|
+
export function resolveConsumerValues(program, consumer) {
|
|
17
|
+
const dataset = requireConsumerDataset(program, consumer);
|
|
18
|
+
if (Object.hasOwn(consumer.encoding, "datum")) {
|
|
19
|
+
return [normalizeRuleDatum(
|
|
20
|
+
consumer.encoding.datum,
|
|
21
|
+
consumer.encoding.fieldType,
|
|
22
|
+
consumer.channel
|
|
23
|
+
)];
|
|
24
|
+
}
|
|
25
|
+
if (
|
|
26
|
+
consumer.channel === "strokeDash" &&
|
|
27
|
+
!["line", "rule"].includes(consumer.layer.mark?.type)
|
|
28
|
+
) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"strokeDash scale materialization requires a line mark or rule mark."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const scale = findScale(program, consumer.encoding.scale);
|
|
34
|
+
if (isDirectCategoricalConsumer(consumer)) {
|
|
35
|
+
return readConsumerFieldValues(program, consumer, dataset, scale);
|
|
36
|
+
}
|
|
37
|
+
const family = resolveMarkFamilyConsumerValues(consumer, dataset);
|
|
38
|
+
return family.matched
|
|
39
|
+
? family.values
|
|
40
|
+
: readConsumerFieldValues(program, consumer, dataset, scale);
|
|
41
|
+
}
|