ggaction 0.0.21 → 0.0.22
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 +73 -0
- package/README.md +3 -2
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +6 -2
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +1 -0
- package/src/actions/boxPlots/components.js +1 -96
- package/src/actions/boxPlots/create.js +6 -1
- package/src/actions/boxPlots/edit.js +8 -0
- package/src/actions/boxPlots/materialize.js +3 -0
- package/src/actions/boxPlots/options.js +1 -1
- package/src/actions/canvas/actions.js +2 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +2 -1
- package/src/actions/canvas/plotOrder.js +14 -0
- package/src/actions/charts/area.js +1 -1
- package/src/actions/charts/bar.js +4 -4
- package/src/actions/charts/density.js +1 -1
- package/src/actions/charts/ecdf.js +1 -1
- package/src/actions/charts/endpoints.js +54 -5
- package/src/actions/charts/heatmap.js +1 -1
- package/src/actions/charts/histogram.js +1 -1
- package/src/actions/charts/horizon.js +1 -1
- package/src/actions/charts/index.js +2 -1
- package/src/actions/charts/interval-regression.js +1 -1
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/radar.js +63 -3
- package/src/actions/charts/raincloud.js +37 -6
- package/src/actions/charts/rug-strip.js +2 -2
- package/src/actions/charts/scatter.js +3 -3
- package/src/actions/charts/text.js +1 -1
- package/src/actions/composition/actions.js +10 -3
- package/src/actions/coordinates/actions.js +2 -0
- package/src/actions/data/basic.js +6 -0
- package/src/actions/data/derived.js +6 -2
- package/src/actions/data/edit.js +21 -35
- package/src/actions/data/filter.js +1 -376
- package/src/actions/data/flatten.js +19 -0
- package/src/actions/data/index.js +6 -0
- package/src/actions/data/lookup.js +50 -0
- package/src/actions/data/raincloudFilter.js +56 -0
- package/src/actions/data/shared.js +10 -0
- package/src/actions/distributions/revision.js +3 -1
- package/src/actions/encodings/angle.js +2 -1
- package/src/actions/encodings/appearance.js +38 -13
- package/src/actions/encodings/channels.js +14 -3
- package/src/actions/encodings/color/continuous.js +26 -10
- package/src/actions/encodings/color/index.js +50 -20
- package/src/actions/encodings/color/policy.js +57 -4
- package/src/actions/encodings/parallel.js +4 -0
- package/src/actions/encodings/position/policies/bar.js +10 -6
- package/src/actions/encodings/position/policies/line.js +1 -152
- package/src/actions/encodings/position/resolve.js +4 -1
- package/src/actions/encodings/ranged.js +46 -0
- package/src/actions/encodings/remove.js +1 -1
- package/src/actions/encodings/ruleAppearance.js +7 -6
- package/src/actions/encodings/shared.js +14 -2
- package/src/actions/encodings/stroke.js +20 -14
- package/src/actions/encodings/strokeDash.js +9 -11
- package/src/actions/encodings/text.js +30 -2
- package/src/actions/errorBars/edit.js +24 -4
- package/src/actions/facets/actions.js +1 -793
- package/src/actions/facets/derive.js +34 -5
- package/src/actions/facets/replay.js +1 -38
- package/src/actions/gradientPlots/components.js +21 -9
- package/src/actions/gradientPlots/create.js +4 -2
- package/src/actions/gradientPlots/edit.js +18 -1
- package/src/actions/gradientPlots/materialize.js +35 -36
- package/src/actions/gradientPlots/options.js +1 -1
- package/src/actions/gradientPlots/paint.js +13 -7
- package/src/actions/guides/applicability.js +2 -0
- package/src/actions/guides/axes/axes.js +39 -0
- package/src/actions/guides/axes/axis.js +9 -5
- package/src/actions/guides/axes/edit.js +11 -8
- package/src/actions/guides/axes/labels.js +24 -8
- package/src/actions/guides/axes/parallel/policy.js +3 -5
- package/src/actions/guides/axes/parallel/resolve.js +4 -3
- package/src/actions/guides/axes/policy.js +22 -0
- package/src/actions/guides/axes/tickGroups.js +4 -2
- package/src/actions/guides/axes/ticks.js +22 -4
- package/src/actions/guides/axes/titles.js +19 -6
- package/src/actions/guides/facadeAxes.js +3 -0
- package/src/actions/guides/grids/grid.js +3 -1
- package/src/actions/guides/grids/resolve.js +7 -11
- package/src/actions/guides/guides.js +21 -1
- package/src/actions/guides/legends/categorical/actions.js +1 -515
- package/src/actions/guides/legends/categorical/layout.js +5 -2
- package/src/actions/guides/legends/categorical/options.js +4 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -3
- package/src/actions/guides/legends/categorical/symbols.js +3 -3
- package/src/actions/guides/legends/colorSize.js +2 -0
- package/src/actions/guides/legends/continuous/common.js +1 -1
- package/src/actions/guides/legends/continuous/gradient.js +1 -360
- package/src/actions/guides/legends/continuous/interval.js +1 -1
- package/src/actions/guides/legends/continuous/opacity.js +15 -1
- package/src/actions/guides/legends/edit.js +1 -1
- package/src/actions/guides/legends/focused.js +1 -1
- package/src/actions/guides/legends/lane.js +1 -1
- package/src/actions/guides/legends/sampling.js +1 -140
- package/src/actions/guides/legends/strokeWidth.js +3 -2
- package/src/actions/guides/legends/transition.js +19 -2
- package/src/actions/guides/polar/axes/facade.js +5 -4
- package/src/actions/guides/polar/axes/labels.js +5 -4
- package/src/actions/guides/polar/axes/titles.js +5 -4
- package/src/actions/guides/polar/resolve.js +2 -1
- package/src/actions/guides/tickValues.js +7 -7
- package/src/actions/marks/arc/actions.js +5 -7
- package/src/actions/marks/area/actions.js +9 -3
- package/src/actions/marks/bar/create.js +4 -2
- package/src/actions/marks/bar/edit.js +9 -6
- package/src/actions/marks/bar/materialize.js +5 -3
- package/src/actions/marks/dependentReferences.js +87 -0
- package/src/actions/marks/index.js +2 -0
- package/src/actions/marks/lifecycle.js +2 -1
- package/src/actions/marks/line/actions.js +24 -6
- package/src/actions/marks/line/materialize.js +7 -3
- package/src/actions/marks/point/create.js +5 -4
- package/src/actions/marks/point/edit.js +6 -3
- package/src/actions/marks/point/jitter.js +6 -6
- package/src/actions/marks/point/materialize.js +36 -39
- package/src/actions/marks/rect/actions.js +16 -4
- package/src/actions/marks/references.js +183 -35
- package/src/actions/marks/rule/actions.js +20 -9
- package/src/actions/marks/shared.js +2 -1
- package/src/actions/marks/text/actions.js +10 -3
- package/src/actions/marks/tick/actions.js +11 -2
- package/src/actions/primitives/editGraphics.js +28 -3
- package/src/actions/primitives/index.js +9 -0
- package/src/actions/primitives/semanticAction.js +13 -7
- package/src/actions/primitives/semanticValidation/layer.js +20 -0
- package/src/actions/primitives/semanticValidation/scale.js +8 -1
- package/src/actions/scales/channels.js +1 -1
- package/src/actions/scales/consumers/common.js +19 -1
- package/src/actions/scales/consumers/families.js +2 -2
- package/src/actions/scales/consumers/seriesLayout.js +4 -3
- package/src/actions/scales/edit.js +11 -0
- package/src/actions/scales/editPolicy.js +5 -3
- package/src/actions/scales/preview.js +2 -1
- package/src/actions/scales/quantitativeColor.js +2 -4
- package/src/actions/selection/actions.js +10 -1
- package/src/actions/theme/reconcile.js +37 -9
- package/src/core/compositionState.js +21 -4
- package/src/core/datasetReferences.js +17 -0
- package/src/core/immutable.js +1 -0
- package/src/core/resourceReferences.js +5 -3
- package/src/core/validation.js +4 -0
- package/src/core/vocabulary.js +24 -2
- package/src/grammar/aggregate.js +1 -0
- package/src/grammar/arcs.js +1 -1
- package/src/grammar/areaSeries.js +1 -1
- package/src/grammar/axisTextPlacement.js +1 -1
- package/src/grammar/bars/policy.js +2 -2
- package/src/grammar/datasetDependencies.js +97 -0
- package/src/grammar/datasetSchema.js +31 -1
- package/src/grammar/facetHeaders.js +1 -0
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/legendIntent.js +6 -2
- package/src/grammar/facets/scales.js +27 -7
- package/src/grammar/flatten.js +85 -0
- package/src/grammar/histogram.js +13 -1
- package/src/grammar/horizon.js +1 -494
- package/src/grammar/jitter.js +2 -2
- package/src/grammar/legendSpacing.js +45 -0
- package/src/grammar/lineMissing.js +90 -0
- package/src/grammar/lineSeries.js +1 -376
- package/src/grammar/lookup.js +97 -0
- package/src/grammar/markFilter.js +4 -2
- package/src/grammar/markLabels.js +8 -3
- package/src/grammar/numeric.js +1 -1
- package/src/grammar/pathCommands.js +1 -1
- package/src/grammar/pathSeries.js +1 -109
- package/src/grammar/pointPolygon.js +81 -0
- package/src/grammar/pointShapes.js +15 -15
- package/src/grammar/rects.js +1 -1
- package/src/grammar/regression/derive.js +1 -195
- package/src/grammar/scales/appearance.js +1 -1
- package/src/grammar/scales/color.js +1 -1
- package/src/grammar/scales/colorConsumers.js +1 -63
- package/src/grammar/scales/continuous.js +58 -1
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/ordinal.js +5 -2
- package/src/grammar/scales/policies.js +3 -3
- package/src/grammar/schemas/graphicBounds.js +40 -12
- package/src/grammar/schemas/graphicClip.js +23 -0
- package/src/grammar/schemas/graphicTree.js +16 -0
- package/src/grammar/statisticalReference.js +22 -0
- package/src/grammar/text.js +65 -1
- package/src/grammar/ticks.js +92 -1
- package/src/grammar/transformTopology.js +3 -1
- package/src/grammar/transforms.js +24 -0
- package/src/inspection.js +1 -495
- package/src/inspectionDescriptors.js +1 -1
- package/src/layout/canvas.js +1 -1
- package/src/layout/facetContentFlow.js +101 -0
- package/src/layout/facetHeaderTitles.js +1 -0
- package/src/layout/facetTopLane.js +29 -0
- package/src/layout/facets.js +1 -1
- package/src/layout/legendGroup.js +1 -1
- package/src/layout/legendItems.js +26 -10
- package/src/layout/legendSpacing.js +66 -0
- package/src/layout/plotDrawOrder.js +17 -0
- package/src/materialization/bars/histogram.js +8 -2
- package/src/materialization/bars/resolve.js +1 -1
- package/src/materialization/composition.js +1 -0
- package/src/materialization/compositionSnapshot.js +22 -2
- package/src/materialization/dataProvenance.js +1 -1
- package/src/materialization/facetGuides/layout.js +1 -1
- package/src/materialization/facetGuides/placement.js +23 -11
- package/src/materialization/facetGuides/preparation.js +43 -24
- package/src/materialization/facetHeaders.js +1 -1
- package/src/materialization/facetTopLane.js +25 -0
- package/src/materialization/facets.js +1 -1
- package/src/materialization/graphicHierarchy.js +5 -0
- package/src/materialization/graphicTranslation.js +2 -0
- package/src/materialization/guides/layout.js +1 -1
- package/src/materialization/guides/resources.js +1 -1
- package/src/materialization/legends.js +1 -1
- package/src/materialization/markClip.js +26 -0
- package/src/materialization/marks/capabilities.js +3 -2
- package/src/materialization/marks/grouping.js +7 -5
- package/src/materialization/marks/index.js +4 -2
- package/src/materialization/marks/policies.js +4 -0
- package/src/materialization/plotDrawOrder.js +44 -0
- package/src/materialization/rect.js +11 -3
- package/src/materialization/scaleGuideDependencies.js +13 -5
- package/src/materialization/selection/items/common.js +1 -1
- package/src/materialization/selection/items/path.js +1 -0
- package/src/materialization/selection/policies/index.js +2 -1
- package/src/materialization/text.js +10 -5
- package/src/mcp/cli.js +0 -0
- package/src/persistence/validation.js +108 -18
- package/src/persistence.js +4 -2
- package/src/renderers/canvas/index.js +16 -0
- package/src/renderers/canvas/isolated.js +11 -1
- package/src/renderers/canvas/native.js +7 -0
- package/src/renderers/canvas/scene.js +66 -0
- package/src/renderers/canvas/text.js +3 -2
- package/src/renderers/svg.js +1 -617
- package/src/renderers/svgSerialization.js +643 -0
- package/src/renderers/transitionScene.js +67 -0
- package/src/scene.js +89 -0
- package/src/selectors/axes.js +1 -1
- package/src/selectors/datasets.js +1 -0
- package/src/selectors/grids.js +9 -0
- package/src/selectors/markClip.js +28 -0
- package/src/selectors/raincloud.js +7 -0
- package/src/version.js +1 -1
- package/types/basic.d.ts +7 -0
- package/types/index.d.ts +8 -0
- package/types/program.d.ts +270 -60
- package/types/scene.d.ts +35 -0
|
@@ -1,109 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
readNominalField,
|
|
4
|
-
readQuantitativeField,
|
|
5
|
-
readScaleField
|
|
6
|
-
} from "./scales/fields.js";
|
|
7
|
-
|
|
8
|
-
export function normalizeGroupFields(value) {
|
|
9
|
-
const fields = Array.isArray(value) ? [...value] : [value];
|
|
10
|
-
for (const field of fields) validateNonEmptyString(field, "Path group field");
|
|
11
|
-
if (fields.length === 0 || new Set(fields).size !== fields.length) {
|
|
12
|
-
throw new Error("Path group fields must be non-empty and unique.");
|
|
13
|
-
}
|
|
14
|
-
return fields;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function pathGroupFields(layer) {
|
|
18
|
-
const group = layer.encoding?.group;
|
|
19
|
-
if (group !== undefined) {
|
|
20
|
-
if (group.fieldType !== "nominal") {
|
|
21
|
-
throw new Error(`Path group on mark "${layer.id}" must be nominal.`);
|
|
22
|
-
}
|
|
23
|
-
if (Object.hasOwn(group, "field") && Object.hasOwn(group, "fields")) {
|
|
24
|
-
throw new Error("Path group requires exactly one of field or fields.");
|
|
25
|
-
}
|
|
26
|
-
return normalizeGroupFields(group.fields ?? group.field);
|
|
27
|
-
}
|
|
28
|
-
if (layer.mark?.type !== "line") return [];
|
|
29
|
-
const color = layer.encoding?.color?.field;
|
|
30
|
-
const dash = layer.encoding?.strokeDash?.field;
|
|
31
|
-
if (color !== undefined && dash !== undefined && color !== dash) {
|
|
32
|
-
throw new Error("Line color field must match strokeDash field unless encodeGroup defines the series.");
|
|
33
|
-
}
|
|
34
|
-
return color === undefined && dash === undefined ? [] : [color ?? dash];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function readPathSeriesFields(rows, layer) {
|
|
38
|
-
const fields = pathGroupFields(layer);
|
|
39
|
-
for (const field of fields) readNominalField(rows, field);
|
|
40
|
-
return fields;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function seriesValueIndex(rows, fields, field, channel, encoding = {}) {
|
|
44
|
-
const quantitative = channel === "strokeWidth" || channel === "opacity";
|
|
45
|
-
const values = channel === "stroke" || (channel === "color" && encoding.fieldType === "quantitative")
|
|
46
|
-
? readScaleField(rows, field, encoding.fieldType ?? "nominal", {
|
|
47
|
-
temporalUnit: encoding.temporalUnit
|
|
48
|
-
})
|
|
49
|
-
: quantitative
|
|
50
|
-
? readQuantitativeField(rows, field)
|
|
51
|
-
: readNominalField(rows, field);
|
|
52
|
-
const result = new Map();
|
|
53
|
-
rows.forEach((row, index) => {
|
|
54
|
-
const key = JSON.stringify(fields.map(field => row[field]));
|
|
55
|
-
const value = values[index];
|
|
56
|
-
if (channel === "strokeWidth" && value < 0) {
|
|
57
|
-
throw new RangeError(`Line strokeWidth field "${field}" cannot contain negative values.`);
|
|
58
|
-
}
|
|
59
|
-
if (result.has(key) && result.get(key) !== value) {
|
|
60
|
-
throw new Error(`Path ${channel} field "${field}" must have one value within each series.`);
|
|
61
|
-
}
|
|
62
|
-
result.set(key, value);
|
|
63
|
-
});
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function derivePathSeriesFieldValues(
|
|
68
|
-
rows,
|
|
69
|
-
series,
|
|
70
|
-
field,
|
|
71
|
-
channel,
|
|
72
|
-
encoding
|
|
73
|
-
) {
|
|
74
|
-
const fields = Object.keys(series[0]?.key ?? {});
|
|
75
|
-
const values = seriesValueIndex(rows, fields, field, channel, encoding);
|
|
76
|
-
return series.map(item => values.get(JSON.stringify(Object.values(item.key))));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function validatePathSeriesAppearance(rows, layer) {
|
|
80
|
-
if (!["line", "area"].includes(layer.mark?.type) ||
|
|
81
|
-
layer.encoding?.parallel !== undefined) return;
|
|
82
|
-
const grouping = readPathSeriesFields(rows, layer);
|
|
83
|
-
const channels = layer.mark.type === "line"
|
|
84
|
-
? ["color", "stroke", "strokeDash", "strokeWidth", "opacity"]
|
|
85
|
-
: ["color", "stroke"];
|
|
86
|
-
for (const channel of channels) {
|
|
87
|
-
const encoding = layer.encoding?.[channel];
|
|
88
|
-
if (encoding?.field === undefined) continue;
|
|
89
|
-
const allowed = ["color", "stroke"].includes(channel)
|
|
90
|
-
? channel === "color"
|
|
91
|
-
? layer.mark.type === "area" ? ["nominal", "ordinal", "quantitative"] : ["nominal", "ordinal"]
|
|
92
|
-
: ["nominal", "ordinal", "quantitative", "temporal"]
|
|
93
|
-
: channel === "strokeDash" ? ["nominal"] : ["quantitative"];
|
|
94
|
-
if (!allowed.includes(encoding.fieldType)) {
|
|
95
|
-
throw new Error(`Path ${channel} encoding has an unsupported field type.`);
|
|
96
|
-
}
|
|
97
|
-
seriesValueIndex(rows, grouping, encoding.field, channel, encoding);
|
|
98
|
-
}
|
|
99
|
-
return grouping;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function readSeriesIdentity(rows, layer) {
|
|
103
|
-
const group = layer.encoding?.group;
|
|
104
|
-
const fields = group === undefined ? [] : normalizeGroupFields(group.fields ?? group.field);
|
|
105
|
-
for (const field of fields) readNominalField(rows, field);
|
|
106
|
-
const values = rows.map(row => fields.length === 0 ? undefined : fields.length === 1
|
|
107
|
-
? row[fields[0]] : JSON.stringify(fields.map(field => row[field])));
|
|
108
|
-
return { fields, values, domain: [...new Set(values)] };
|
|
109
|
-
}
|
|
1
|
+
import{validateNonEmptyString as S}from"../core/validation.js";import{readCategoricalLineColor as p,validateCategoricalColorConsumer as y}from"./scales/colorConsumers.js";import{readNominalField as f,readQuantitativeField as O,readScaleField as x}from"./scales/fields.js";function m(n){const e=Array.isArray(n)?[...n]:[n];for(const i of e)S(i,"Path group field");if(e.length===0||new Set(e).size!==e.length)throw new Error("Path group fields must be non-empty and unique.");return e}function E(n){const e=n.encoding?.group;if(e!==void 0){if(e.fieldType!=="nominal")throw new Error(`Path group on mark "${n.id}" must be nominal.`);if(Object.hasOwn(e,"field")&&Object.hasOwn(e,"fields"))throw new Error("Path group requires exactly one of field or fields.");return m(e.fields??e.field)}if(n.mark?.type!=="line")return[];const i=["nominal","ordinal"].includes(n.encoding?.color?.fieldType)?n.encoding.color.field:void 0,o=n.encoding?.strokeDash?.field;if(i!==void 0&&o!==void 0&&i!==o)throw new Error("Line color field must match strokeDash field unless encodeGroup defines the series.");return i===void 0&&o===void 0?[]:[i??o]}function h(n){return JSON.stringify(n.map(e=>e===void 0?{}:e))}function g(n,e){const i=n.encoding?.color,o=Array.isArray(e)?e.find(t=>t.id===i?.scale):e?.[i?.scale];return o!==void 0&&y(n,i,o)?o:void 0}function P(n,e,i){const o=E(e),t=g(e,i);for(const r of o)t&&e.encoding?.group===void 0&&r===e.encoding.color.field?p(n,r):f(n,r);return o}function k(n,e,i,o,t={},r){const l=o==="strokeWidth"||o==="opacity",d=o==="color"&&r!==void 0?p(n,i):o==="stroke"||o==="color"&&["quantitative","temporal"].includes(t.fieldType)?x(n,i,t.fieldType??"nominal",{temporalUnit:t.temporalUnit}):l?O(n,i):f(n,i),a=new Map;return n.forEach((c,v)=>{const s=h(e.map(w=>c[w])),u=d[v];if(o==="strokeWidth"&&u<0)throw new RangeError(`Line strokeWidth field "${i}" cannot contain negative values.`);if(a.has(s)&&a.get(s)!==u)throw new Error(`Path ${o} field "${i}" must have one value within each series.`);a.set(s,u)}),a}function F(n,e,i,o,t,r){if(e.length===0)return[];const l=Object.keys(e[0]?.key??{}),d=k(n,l,i,o,t,Object.hasOwn(r??{},"unknown")?r:void 0);return e.map(a=>d.get(h(Object.values(a.key))))}function j(n,e,i,o){if(!["line","area"].includes(e.mark?.type)||e.encoding?.parallel!==void 0)return;const t=o??P(n,e,i);if(o!==void 0)for(const d of t)f(n,d);const r=g(e,i),l=e.mark.type==="line"?["color","stroke","strokeDash","strokeWidth","opacity"]:["color","stroke"];for(const d of l){const a=e.encoding?.[d];if(a?.field===void 0)continue;if(!(["color","stroke"].includes(d)?d==="color"?e.mark.type==="area"?["nominal","ordinal","quantitative"]:["nominal","ordinal","quantitative","temporal"]:["nominal","ordinal","quantitative","temporal"]:d==="strokeDash"?["nominal","ordinal"]:["quantitative"]).includes(a.fieldType))throw new Error(`Path ${d} encoding has an unsupported field type.`);k(n,t,a.field,d,a,d==="color"?r:void 0)}return t}function A(n,e){const i=e.encoding?.group,o=i===void 0?[]:m(i.fields??i.field);for(const r of o)f(n,r);const t=n.map(r=>o.length===0?void 0:o.length===1?r[o[0]]:JSON.stringify(o.map(l=>r[l])));return{fields:o,values:t,domain:[...new Set(t)]}}export{F as derivePathSeriesFieldValues,m as normalizeGroupFields,E as pathGroupFields,h as pathSeriesKey,P as readPathSeriesFields,A as readSeriesIdentity,j as validatePathSeriesAppearance};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { cloneAndFreeze, isOwned, isPlainObject } from "../core/immutable.js";
|
|
2
|
+
|
|
3
|
+
const normalizedShapes = new WeakMap();
|
|
4
|
+
const MAX_VERTICES = 1024;
|
|
5
|
+
const MAX_COORDINATE = 1_000_000;
|
|
6
|
+
|
|
7
|
+
export function pointPolygonArea(points) {
|
|
8
|
+
let twiceArea = 0;
|
|
9
|
+
for (let index = 0; index < points.length; index += 1) {
|
|
10
|
+
const current = points[index], next = points[(index + 1) % points.length];
|
|
11
|
+
twiceArea += current.x * next.y - next.x * current.y;
|
|
12
|
+
}
|
|
13
|
+
return Math.abs(twiceArea) / 2;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function samePoint(a, b) {
|
|
17
|
+
return a.x === b.x && a.y === b.y;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function turn(a, b, c) {
|
|
21
|
+
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function onSegment(a, b, p) {
|
|
25
|
+
return p.x >= Math.min(a.x, b.x) && p.x <= Math.max(a.x, b.x) &&
|
|
26
|
+
p.y >= Math.min(a.y, b.y) && p.y <= Math.max(a.y, b.y);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function intersects(a, b, c, d) {
|
|
30
|
+
const abC = turn(a, b, c), abD = turn(a, b, d);
|
|
31
|
+
const cdA = turn(c, d, a), cdB = turn(c, d, b);
|
|
32
|
+
return (Math.sign(abC) * Math.sign(abD) < 0 && Math.sign(cdA) * Math.sign(cdB) < 0) ||
|
|
33
|
+
(abC === 0 && onSegment(a, b, c)) || (abD === 0 && onSegment(a, b, d)) ||
|
|
34
|
+
(cdA === 0 && onSegment(c, d, a)) || (cdB === 0 && onSegment(c, d, b));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function normalizePointPolygon(shape) {
|
|
38
|
+
if (isOwned(shape) && normalizedShapes.has(shape)) return normalizedShapes.get(shape);
|
|
39
|
+
if (!isPlainObject(shape) || !Object.hasOwn(shape, "polygon") ||
|
|
40
|
+
Object.keys(shape).some(key => !["polygon", "normalization"].includes(key)) ||
|
|
41
|
+
!Array.isArray(shape.polygon) || shape.polygon.length < 3 || shape.polygon.length > MAX_VERTICES + 1) {
|
|
42
|
+
throw new TypeError("Point polygon requires a polygon array of 3 to 1024 vertices and optional normalization.");
|
|
43
|
+
}
|
|
44
|
+
if (Object.hasOwn(shape, "normalization") && !["area", "radius"].includes(shape.normalization)) {
|
|
45
|
+
throw new Error('Point polygon normalization must be "area" or "radius".');
|
|
46
|
+
}
|
|
47
|
+
for (const point of shape.polygon) {
|
|
48
|
+
if (!isPlainObject(point) || Object.keys(point).length !== 2 ||
|
|
49
|
+
!Object.hasOwn(point, "x") || !Object.hasOwn(point, "y") ||
|
|
50
|
+
![point.x, point.y].every(value => Number.isFinite(value) && Math.abs(value) <= MAX_COORDINATE)) {
|
|
51
|
+
throw new TypeError("Point polygon vertices require finite x/y coordinates with magnitude at most 1000000.");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const points = samePoint(shape.polygon[0], shape.polygon.at(-1))
|
|
55
|
+
? shape.polygon.slice(0, -1) : shape.polygon;
|
|
56
|
+
if (points.length < 3 || points.length > MAX_VERTICES) {
|
|
57
|
+
throw new RangeError("Point polygon requires 3 to 1024 vertices after closing-point normalization.");
|
|
58
|
+
}
|
|
59
|
+
const area = pointPolygonArea(points);
|
|
60
|
+
if (!Number.isFinite(area) || area <= 0) {
|
|
61
|
+
throw new RangeError("Point polygon must have positive finite area.");
|
|
62
|
+
}
|
|
63
|
+
for (let i = 0; i < points.length; i += 1) {
|
|
64
|
+
const a = points[i], b = points[(i + 1) % points.length], c = points[(i + 2) % points.length];
|
|
65
|
+
if (samePoint(a, b) || (turn(a, b, c) === 0 &&
|
|
66
|
+
(b.x - a.x) * (c.x - b.x) + (b.y - a.y) * (c.y - b.y) < 0)) {
|
|
67
|
+
throw new Error("Point polygon edges must not be empty or backtrack.");
|
|
68
|
+
}
|
|
69
|
+
for (let j = i + 1; j < points.length; j += 1) {
|
|
70
|
+
if (j === i + 1 || (i === 0 && j === points.length - 1)) continue;
|
|
71
|
+
if (intersects(a, b, points[j], points[(j + 1) % points.length])) {
|
|
72
|
+
throw new Error("Point polygon must be simple, without crossing or touching nonadjacent edges.");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const normalized = cloneAndFreeze({ polygon: points,
|
|
77
|
+
...(Object.hasOwn(shape, "normalization") ? { normalization: shape.normalization } : {}) });
|
|
78
|
+
normalizedShapes.set(normalized, normalized);
|
|
79
|
+
if (isOwned(shape)) normalizedShapes.set(shape, normalized);
|
|
80
|
+
return normalized;
|
|
81
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cloneAndFreeze } from "../core/immutable.js";
|
|
2
2
|
import { buildLinearPathCommands } from "./pathCommands.js";
|
|
3
3
|
import { requestedStrokeDetails } from "./strokeStyle.js";
|
|
4
|
+
import { normalizePointPolygon, pointPolygonArea } from "./pointPolygon.js";
|
|
4
5
|
|
|
5
6
|
export const POINT_SHAPES = cloneAndFreeze([
|
|
6
7
|
"circle",
|
|
@@ -22,6 +23,7 @@ export const POINT_SHAPES = cloneAndFreeze([
|
|
|
22
23
|
const SHAPE_SET = new Set(POINT_SHAPES);
|
|
23
24
|
|
|
24
25
|
export function validatePointShape(shape) {
|
|
26
|
+
if (shape !== null && typeof shape === "object") return normalizePointPolygon(shape);
|
|
25
27
|
if (typeof shape !== "string" || !SHAPE_SET.has(shape)) {
|
|
26
28
|
throw new Error(`Unsupported point shape "${shape}".`);
|
|
27
29
|
}
|
|
@@ -35,16 +37,6 @@ export function getPointGraphicType(shape) {
|
|
|
35
37
|
return "path";
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
function polygonArea(points) {
|
|
39
|
-
let twiceArea = 0;
|
|
40
|
-
for (let index = 0; index < points.length; index += 1) {
|
|
41
|
-
const current = points[index];
|
|
42
|
-
const next = points[(index + 1) % points.length];
|
|
43
|
-
twiceArea += current.x * next.y - next.x * current.y;
|
|
44
|
-
}
|
|
45
|
-
return Math.abs(twiceArea) / 2;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
40
|
function rotate(points, angle) {
|
|
49
41
|
const cosine = Math.cos(angle);
|
|
50
42
|
const sine = Math.sin(angle);
|
|
@@ -70,6 +62,7 @@ function alternatingPolygon(count, innerRadius, rotation = -Math.PI / 2) {
|
|
|
70
62
|
}
|
|
71
63
|
|
|
72
64
|
function shapePolygon(shape) {
|
|
65
|
+
if (typeof shape === "object") return shape.polygon;
|
|
73
66
|
const arm = 0.32;
|
|
74
67
|
const plus = [
|
|
75
68
|
{ x: -arm, y: -1 }, { x: arm, y: -1 },
|
|
@@ -116,12 +109,17 @@ function shapePolygon(shape) {
|
|
|
116
109
|
}[shape];
|
|
117
110
|
}
|
|
118
111
|
|
|
119
|
-
function normalizePolygon(points, x, y, area) {
|
|
120
|
-
const scale = Math.sqrt(area /
|
|
121
|
-
|
|
112
|
+
function normalizePolygon(points, x, y, area, normalization = "area") {
|
|
113
|
+
const scale = Math.sqrt(area / (normalization === "radius" ? Math.PI : pointPolygonArea(points)));
|
|
114
|
+
if (!Number.isFinite(scale)) throw new RangeError("Point polygon normalization exceeds finite geometry.");
|
|
115
|
+
const normalized = points.map(point => ({
|
|
122
116
|
x: x + point.x * scale,
|
|
123
117
|
y: y + point.y * scale
|
|
124
118
|
}));
|
|
119
|
+
if (normalized.some(point => !Number.isFinite(point.x) || !Number.isFinite(point.y))) {
|
|
120
|
+
throw new RangeError("Point polygon normalization exceeds finite geometry.");
|
|
121
|
+
}
|
|
122
|
+
return normalized;
|
|
125
123
|
}
|
|
126
124
|
|
|
127
125
|
function appearance(fill, stroke, strokeWidth, opacity, strokeDetails) {
|
|
@@ -192,7 +190,8 @@ export function createPointShapeGraphic({
|
|
|
192
190
|
rotate(shapePolygon(validated), (angle ?? 0) * Math.PI / 180),
|
|
193
191
|
x,
|
|
194
192
|
y,
|
|
195
|
-
area
|
|
193
|
+
area,
|
|
194
|
+
validated.normalization
|
|
196
195
|
),
|
|
197
196
|
{ close: true }
|
|
198
197
|
),
|
|
@@ -230,7 +229,8 @@ export function resolvePointShapeExtent({
|
|
|
230
229
|
rotate(shapePolygon(validated), (angle ?? 0) * Math.PI / 180),
|
|
231
230
|
0,
|
|
232
231
|
0,
|
|
233
|
-
area
|
|
232
|
+
area,
|
|
233
|
+
validated.normalization
|
|
234
234
|
);
|
|
235
235
|
return cloneAndFreeze({
|
|
236
236
|
x: Math.max(0, ...points.map(point => Math.abs(point.x))) + strokeExtent,
|
package/src/grammar/rects.js
CHANGED
|
@@ -30,5 +30,5 @@ export function resolveRectMode(layer) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function rectUsesFields(layer) {
|
|
33
|
-
return ["x", "y", "x2", "y2", "color"].some(channel => Object.hasOwn(layer.encoding?.[channel] ?? {}, "field"));
|
|
33
|
+
return ["x", "y", "x2", "y2", "color", "stroke", "opacity", "strokeWidth"].some(channel => Object.hasOwn(layer.encoding?.[channel] ?? {}, "field"));
|
|
34
34
|
}
|
|
@@ -1,195 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
readNominalField,
|
|
4
|
-
readQuantitativeField
|
|
5
|
-
} from "../scales/index.js";
|
|
6
|
-
import { fitRegressionGroup, predictRegressionAt, linearRegressionStatistics } from "./models.js";
|
|
7
|
-
import { requireFiniteResult } from "../numeric.js";
|
|
8
|
-
import {
|
|
9
|
-
validateGeneratedItemLimit,
|
|
10
|
-
validateWorkLimit
|
|
11
|
-
} from "../../core/validation.js";
|
|
12
|
-
import {
|
|
13
|
-
normalizeRegressionParameters,
|
|
14
|
-
requireRegressionField
|
|
15
|
-
} from "./parameters.js";
|
|
16
|
-
|
|
17
|
-
export const REGRESSION_LOWER_FIELD = "__regression_ci_lower";
|
|
18
|
-
export const REGRESSION_UPPER_FIELD = "__regression_ci_upper";
|
|
19
|
-
|
|
20
|
-
export function deriveRegression(values, {
|
|
21
|
-
x,
|
|
22
|
-
y,
|
|
23
|
-
groupBy,
|
|
24
|
-
method,
|
|
25
|
-
degree,
|
|
26
|
-
span,
|
|
27
|
-
robustIterations,
|
|
28
|
-
confidenceMethod,
|
|
29
|
-
level,
|
|
30
|
-
confidence,
|
|
31
|
-
interval,
|
|
32
|
-
predict,
|
|
33
|
-
missing
|
|
34
|
-
} = {}) {
|
|
35
|
-
if (!Array.isArray(values)) {
|
|
36
|
-
throw new TypeError("Regression values must be an array.");
|
|
37
|
-
}
|
|
38
|
-
requireRegressionField(x, "Regression x field");
|
|
39
|
-
requireRegressionField(y, "Regression y field");
|
|
40
|
-
if (groupBy !== undefined) {
|
|
41
|
-
requireRegressionField(groupBy, "Regression groupBy field");
|
|
42
|
-
}
|
|
43
|
-
const parameters = normalizeRegressionParameters({
|
|
44
|
-
method,
|
|
45
|
-
degree,
|
|
46
|
-
span,
|
|
47
|
-
robustIterations,
|
|
48
|
-
confidenceMethod,
|
|
49
|
-
level,
|
|
50
|
-
confidence,
|
|
51
|
-
interval
|
|
52
|
-
});
|
|
53
|
-
let missingRows = 0;
|
|
54
|
-
const eligibleValues = missing === undefined ? values : values.filter((row, index) => {
|
|
55
|
-
const xValue = row?.[x];
|
|
56
|
-
const yValue = row?.[y];
|
|
57
|
-
if (xValue === null || xValue === undefined || yValue === null || yValue === undefined) {
|
|
58
|
-
if (missing === "error") throw new TypeError(`Regression x/y pair is missing at row ${index}.`);
|
|
59
|
-
missingRows += 1;
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
if (!Number.isFinite(xValue) || !Number.isFinite(yValue)) {
|
|
63
|
-
throw new TypeError(`Regression x/y fields must contain finite numbers at row ${index}.`);
|
|
64
|
-
}
|
|
65
|
-
return true;
|
|
66
|
-
});
|
|
67
|
-
readQuantitativeField(eligibleValues, x);
|
|
68
|
-
readQuantitativeField(eligibleValues, y);
|
|
69
|
-
if (groupBy !== undefined) readNominalField(eligibleValues, groupBy);
|
|
70
|
-
|
|
71
|
-
const groupedRows = new Map();
|
|
72
|
-
if (groupBy === undefined) {
|
|
73
|
-
groupedRows.set(undefined, eligibleValues);
|
|
74
|
-
} else {
|
|
75
|
-
for (const row of eligibleValues) {
|
|
76
|
-
const group = row[groupBy];
|
|
77
|
-
const rows = groupedRows.get(group) ?? [];
|
|
78
|
-
rows.push(row);
|
|
79
|
-
groupedRows.set(group, rows);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const groups = [...groupedRows.keys()];
|
|
83
|
-
const models = [];
|
|
84
|
-
const statistics = [];
|
|
85
|
-
const rows = [];
|
|
86
|
-
let work = 0;
|
|
87
|
-
|
|
88
|
-
for (const group of groups) {
|
|
89
|
-
const groupRows = groupedRows.get(group);
|
|
90
|
-
const observedX = [...new Set(groupRows.map(row => row[x]))]
|
|
91
|
-
.sort((left, right) => left - right);
|
|
92
|
-
const xValues = predict?.values ?? (predict?.domain === undefined
|
|
93
|
-
? observedX
|
|
94
|
-
: Array.from({ length: predict.steps }, (_, index) => {
|
|
95
|
-
if (index === 0) return predict.domain[0];
|
|
96
|
-
if (index === predict.steps - 1) return predict.domain[1];
|
|
97
|
-
const ratio = index / (predict.steps - 1);
|
|
98
|
-
return requireFiniteResult(
|
|
99
|
-
predict.domain[0] * (1 - ratio) + predict.domain[1] * ratio,
|
|
100
|
-
"Regression prediction grid"
|
|
101
|
-
);
|
|
102
|
-
}));
|
|
103
|
-
validateGeneratedItemLimit(rows.length + xValues.length, "Regression generated row count");
|
|
104
|
-
if (parameters.method === "polynomial") {
|
|
105
|
-
const size = parameters.degree + 1;
|
|
106
|
-
work += groupRows.length * size ** 2 + size ** 3;
|
|
107
|
-
} else if (parameters.method === "loess") {
|
|
108
|
-
const observedSet = new Set(observedX);
|
|
109
|
-
const newPredictions = predict === undefined ? 0 :
|
|
110
|
-
xValues.filter(value => !observedSet.has(value)).length;
|
|
111
|
-
work += groupRows.length *
|
|
112
|
-
(observedX.length * (1 + (parameters.robustIterations ?? 0)) + newPredictions) *
|
|
113
|
-
Math.ceil(Math.log2(groupRows.length + 1));
|
|
114
|
-
}
|
|
115
|
-
validateWorkLimit(work, "Regression computation");
|
|
116
|
-
const model = fitRegressionGroup(groupRows, {
|
|
117
|
-
x,
|
|
118
|
-
y,
|
|
119
|
-
group,
|
|
120
|
-
parameters
|
|
121
|
-
});
|
|
122
|
-
models.push({ ...(groupBy === undefined ? {} : { group }), ...model, xValues });
|
|
123
|
-
if (parameters.method === "linear") statistics.push({
|
|
124
|
-
...(groupBy === undefined ? {} : { group }),
|
|
125
|
-
...linearRegressionStatistics(groupRows, model, { x, y })
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
for (const xValue of xValues) {
|
|
129
|
-
const prediction = predictRegressionAt(model, xValue, parameters);
|
|
130
|
-
const fitted = requireFiniteResult(
|
|
131
|
-
prediction.prediction,
|
|
132
|
-
`Regression group "${group === undefined ? "all" : String(group)}" prediction`
|
|
133
|
-
);
|
|
134
|
-
const lower = parameters.method === "loess" || parameters.interval === false
|
|
135
|
-
? undefined
|
|
136
|
-
: requireFiniteResult(
|
|
137
|
-
fitted - prediction.margin,
|
|
138
|
-
`Regression group "${group === undefined ? "all" : String(group)}" lower interval`
|
|
139
|
-
);
|
|
140
|
-
const upper = parameters.method === "loess" || parameters.interval === false
|
|
141
|
-
? undefined
|
|
142
|
-
: requireFiniteResult(
|
|
143
|
-
fitted + prediction.margin,
|
|
144
|
-
`Regression group "${group === undefined ? "all" : String(group)}" upper interval`
|
|
145
|
-
);
|
|
146
|
-
rows.push({
|
|
147
|
-
...(groupBy === undefined ? {} : { [groupBy]: group }),
|
|
148
|
-
[x]: xValue,
|
|
149
|
-
[y]: fitted,
|
|
150
|
-
...(parameters.method === "loess" || parameters.interval === false ? {} : {
|
|
151
|
-
[REGRESSION_LOWER_FIELD]: lower,
|
|
152
|
-
[REGRESSION_UPPER_FIELD]: upper
|
|
153
|
-
})
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return cloneAndFreeze({
|
|
159
|
-
fields: {
|
|
160
|
-
x,
|
|
161
|
-
y,
|
|
162
|
-
...(groupBy === undefined ? {} : { group: groupBy }),
|
|
163
|
-
...(parameters.method === "loess" || parameters.interval === false ? {} : {
|
|
164
|
-
lower: REGRESSION_LOWER_FIELD,
|
|
165
|
-
upper: REGRESSION_UPPER_FIELD
|
|
166
|
-
})
|
|
167
|
-
},
|
|
168
|
-
parameters,
|
|
169
|
-
groups,
|
|
170
|
-
models,
|
|
171
|
-
...(parameters.method === "linear" ? {
|
|
172
|
-
fitStatistics: { version: 1, method: "linear", groups: statistics }
|
|
173
|
-
} : {}),
|
|
174
|
-
values: rows,
|
|
175
|
-
...(missing === undefined ? {} : {
|
|
176
|
-
report: {
|
|
177
|
-
version: 1,
|
|
178
|
-
owner: { kind: "data", id: "pending" },
|
|
179
|
-
inputs: [],
|
|
180
|
-
units: [{
|
|
181
|
-
role: "fit",
|
|
182
|
-
group: {},
|
|
183
|
-
inputRows: values.length,
|
|
184
|
-
usedRows: eligibleValues.length,
|
|
185
|
-
excludedRows: missingRows,
|
|
186
|
-
excludedByReason: missingRows === 0 ? {} : { "missing-value": missingRows }
|
|
187
|
-
}]
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export function deriveLinearRegression(values, options = {}) {
|
|
194
|
-
return deriveRegression(values, { ...options, method: "linear" });
|
|
195
|
-
}
|
|
1
|
+
import{cloneAndFreeze as W,isOwned as M,isPlainObject as q}from"../../core/immutable.js";import{readNominalField as J,readQuantitativeField as P}from"../scales/index.js";import{fitRegressionGroup as K,predictRegressionAt as Q,linearRegressionStatistics as U}from"./models.js";import{requireFiniteResult as _}from"../numeric.js";import{validateGeneratedItemLimit as X,validateWorkLimit as C}from"../../core/validation.js";import{normalizeRegressionParameters as H,requireRegressionField as I}from"./parameters.js";const A="__regression_ci_lower",V="__regression_ci_upper",E=new WeakMap,$=["x","y","groupBy","method","degree","span","robustIterations","confidenceMethod","level","confidence","interval","predict","missing"];function Y(s,n={}){if(!M(s)||!q(n)||$.some(e=>{const b=Object.getOwnPropertyDescriptor(n,e);return b?!Object.hasOwn(b,"value"):e in n}))return j(s,n);const a=$.map(e=>n[e]),i=M(s)&&a.every(e=>e==null||typeof e=="string"||typeof e=="boolean"||typeof e=="number"&&Number.isFinite(e)&&!Object.is(e,-0)),h=i?JSON.stringify(a.map(e=>[typeof e,e])):void 0,R=i?E.get(s)?.get(h):void 0;if(R!==void 0)return R;const w=j(s,n);if(i){E.has(s)||E.set(s,new Map);const e=E.get(s);e.size>=32&&e.delete(e.keys().next().value),e.set(h,w)}return w}function j(s,{x:n,y:a,groupBy:i,method:h,degree:R,span:w,robustIterations:e,confidenceMethod:b,level:x,confidence:z,interval:G,predict:f,missing:O}={}){if(!Array.isArray(s))throw new TypeError("Regression values must be an array.");I(n,"Regression x field"),I(a,"Regression y field"),i!==void 0&&I(i,"Regression groupBy field");const t=H({method:h,degree:R,span:w,robustIterations:e,confidenceMethod:b,level:x,confidence:z,interval:G});let S=0;const m=O===void 0?s:s.filter((r,o)=>{const u=r?.[n],c=r?.[a];if(u==null||c===null||c===void 0){if(O==="error")throw new TypeError(`Regression x/y pair is missing at row ${o}.`);return S+=1,!1}if(!Number.isFinite(u)||!Number.isFinite(c))throw new TypeError(`Regression x/y fields must contain finite numbers at row ${o}.`);return!0});P(m,n),P(m,a),i!==void 0&&J(m,i);const p=new Map;if(i===void 0)p.set(void 0,m);else for(const r of m){const o=r[i],u=p.get(o)??[];u.push(r),p.set(o,u)}const N=[...p.keys()],k=[],L=[],F=[];let y=0;for(const r of N){const o=p.get(r),u=[...new Set(o.map(d=>d[n]))].sort((d,l)=>d-l),c=f?.values??(f?.domain===void 0?u:Array.from({length:f.steps},(d,l)=>{if(l===0)return f.domain[0];if(l===f.steps-1)return f.domain[1];const g=l/(f.steps-1);return _(f.domain[0]*(1-g)+f.domain[1]*g,"Regression prediction grid")}));if(X(F.length+c.length,"Regression generated row count"),t.method==="polynomial"){const d=t.degree+1;y+=o.length*d**2+d**3}else if(t.method==="loess"){const d=new Set(u),l=f===void 0?0:c.filter(g=>!d.has(g)).length;y+=o.length*(u.length*(1+(t.robustIterations??0))+l)*Math.ceil(Math.log2(o.length+1))}C(y,"Regression computation");const v=K(o,{x:n,y:a,group:r,parameters:t});k.push({...i===void 0?{}:{group:r},...v,xValues:c}),t.method==="linear"&&L.push({...i===void 0?{}:{group:r},...U(o,v,{x:n,y:a})});for(const d of c){const l=Q(v,d,t),g=_(l.prediction,`Regression group "${r===void 0?"all":String(r)}" prediction`),D=t.method==="loess"||t.interval===!1?void 0:_(g-l.margin,`Regression group "${r===void 0?"all":String(r)}" lower interval`),T=t.method==="loess"||t.interval===!1?void 0:_(g+l.margin,`Regression group "${r===void 0?"all":String(r)}" upper interval`);F.push({...i===void 0?{}:{[i]:r},[n]:d,[a]:g,...t.method==="loess"||t.interval===!1?{}:{[A]:D,[V]:T}})}}return W({fields:{x:n,y:a,...i===void 0?{}:{group:i},...t.method==="loess"||t.interval===!1?{}:{lower:A,upper:V}},parameters:t,groups:N,models:k,...t.method==="linear"?{fitStatistics:{version:1,method:"linear",groups:L}}:{},values:F,...O===void 0?{}:{report:{version:1,owner:{kind:"data",id:"pending"},inputs:[],units:[{role:"fit",group:{},inputRows:s.length,usedRows:m.length,excludedRows:S,excludedByReason:S===0?{}:{"missing-value":S}}]}}})}function ie(s,n={}){return Y(s,{...n,method:"linear"})}export{A as REGRESSION_LOWER_FIELD,V as REGRESSION_UPPER_FIELD,ie as deriveLinearRegression,Y as deriveRegression};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cloneAndFreeze as r,isPlainObject as s}from"../../core/immutable.js";import{validateUnitInterval as
|
|
1
|
+
import{cloneAndFreeze as r,isPlainObject as s}from"../../core/immutable.js";import{validateUnitInterval as h}from"../../core/validation.js";import{POINT_SHAPES as y,validatePointShape as m}from"../pointShapes.js";import{normalizePalette as u,resolvePalette as p}from"../palettes.js";const A=r([[],[8,4],[3,3],[12,4],[8,3,2,3],[12,3,3,3],[2,2],[10,3,2,3,2,3],[14,4,4,4],[6,2,2,2]]),w=r({solid:[],dashed:[6,4],dotted:[1,3],dashdot:[6,3,1,3]});import{POINT_SHAPES as M}from"../pointShapes.js";const v=r([24,196]),S=r([.2,1]),x=r([1,8]);function E(t){if(t==="auto")return t;if(Array.isArray(t)){if(t.length===0||!t.every(e=>typeof e=="string"&&e.length>0))throw new TypeError("Color range must contain non-empty color strings.");return r(t)}if(!s(t)||Object.keys(t).length!==1||!Object.hasOwn(t,"palette"))throw new Error("Color range must contain only a palette descriptor.");return u(t.palette),r(t)}function R(t){return Array.isArray(t)&&t.length%2===0&&t.every(e=>Number.isFinite(e)&&e>=0)&&(t.length===0||t.some(e=>e>0))}function c(t){if(typeof t=="string"){const e=w[t];if(e===void 0)throw new Error(`Unknown stroke dash style "${t}".`);return r(e)}if(!R(t))throw new TypeError("Stroke dash pattern must be a named style or an empty or nonzero even-length array of non-negative finite numbers.");return r(t)}function T(t){if(t==="auto")return t;if(!Array.isArray(t)||t.length===0)throw new TypeError("StrokeDash range must contain one or more named styles or direct patterns.");for(const e of t)c(e);return r(t)}function O(t){if(t==="auto")return t;if(!Array.isArray(t)||t.length===0)throw new TypeError("Shape range must contain supported point shapes.");try{return r(t.map(m))}catch(e){throw new TypeError("Shape range must contain supported point shapes.",{cause:e})}}function l(t,e){if(t==="auto")return t;if(!Array.isArray(t)||t.length!==2||!t.every(o=>Number.isFinite(o)&&o>=0)||t[0]>t[1])throw new TypeError(`${e} must be an ascending pair of non-negative finite ${e==="Size range"?"areas":"widths"}.`);return r(t)}function k(t){return l(t,"Size range")}function D(t){if(t==="auto")return t;if(!Array.isArray(t)||t.length!==2||!t.every(e=>Number.isFinite(e)&&e>=0&&e<=1))throw new TypeError("Opacity range must contain two finite values from 0 to 1.");return r(t)}function P(t){return l(t,"StrokeWidth range")}function U(t,e="Opacity"){return h(t,e)}function I(t){if(t==="auto")return t;if(Array.isArray(t)&&t.length>0)return r(t);if(s(t)&&Object.hasOwn(t,"palette"))return u(t.palette),r(t);throw new TypeError("Scale range has an unsupported value.")}function j(t,e){const o=E(t);return o==="auto"?b:Array.isArray(o)?o:p(o.palette,e)}function C(t){const e=T(t);return e==="auto"?A:r(e.map(c))}function H(t){const e=O(t);return e==="auto"?y:e}function g(t){const e=k(t);return e==="auto"?v:e}function L(t){const e=D(t);return e==="auto"?S:e}function W(t){const e=P(t);return e==="auto"?x:e}function $(t,e,o,a={}){const d=Object.hasOwn(a,"unknown"),f=new Map(e.map((n,i)=>[n,i]));return r(t.map(n=>{const i=f.get(n);if(i===void 0){if(d)return a.unknown;throw new Error(`Value "${n}" is outside the ordinal domain.`)}return o[i%o.length]}))}const b=p({name:"tableau10",count:10},10);export{A as DASH10,S as DEFAULT_OPACITY_RANGE,v as DEFAULT_SIZE_RANGE,x as DEFAULT_STROKE_WIDTH_RANGE,w as NAMED_DASH_PATTERNS,M as POINT_SHAPES,b as TABLEAU10,$ as mapOrdinalValues,c as normalizeStrokeDashPattern,j as resolveColorRange,L as resolveOpacityRange,H as resolveShapeRange,g as resolveSizeRange,C as resolveStrokeDashRange,W as resolveStrokeWidthRange,E as validateColorRange,D as validateOpacityRange,U as validateOpacityValue,I as validateSemanticScaleRange,O as validateShapeRange,k as validateSizeRange,T as validateStrokeDashRange,P as validateStrokeWidthRange};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mapTransformedValues as
|
|
1
|
+
import{mapTransformedValues as z}from"./transformed.js";import{cloneAndFreeze as p,isPlainObject as $}from"../../core/immutable.js";import{validateGeneratedItemLimit as k}from"../../core/validation.js";import{normalizePalette as B,resolveContinuousPalette as S}from"../palettes.js";import{inverseLerp as x}from"../numeric.js";const D=p(["rgb","hsl","hsl-long","lab","hcl","hcl-long","cubehelix","cubehelix-long"]),H=Object.freeze({black:"#000000",blue:"#0000ff",cyan:"#00ffff",gray:"#808080",green:"#008000",grey:"#808080",magenta:"#ff00ff",navy:"#000080",orange:"#ffa500",purple:"#800080",red:"#ff0000",teal:"#008080",white:"#ffffff",yellow:"#ffff00"});function w(t,e=0,n=1){return Math.max(e,Math.min(n,t))}function y(t){return(t%360+360)%360}function q({h:t,s:e,l:n}){const r=(1-Math.abs(2*n-1))*e,o=y(t)/60,i=r*(1-Math.abs(o%2-1)),[s,u,c]=o<1?[r,i,0]:o<2?[i,r,0]:o<3?[0,r,i]:o<4?[0,i,r]:o<5?[i,0,r]:[r,0,i],l=n-r/2;return{r:s+l,g:u+l,b:c+l}}function E(t){if(typeof t!="string"||t.length===0)throw new TypeError("Continuous color range requires non-empty color strings.");const e=H[t.toLowerCase()]??t.toLowerCase();let n=e.match(/^#([0-9a-f]{3})$/i);if(n)return Object.fromEntries(["r","g","b"].map((r,o)=>[r,Number.parseInt(n[1][o].repeat(2),16)/255]));if(n=e.match(/^#([0-9a-f]{6})$/i),n)return{r:Number.parseInt(n[1].slice(0,2),16)/255,g:Number.parseInt(n[1].slice(2,4),16)/255,b:Number.parseInt(n[1].slice(4,6),16)/255};if(n=e.match(/^rgb\(\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*,\s*([\d.]+)%?\s*\)$/),n){const o=e.includes("%")?100:255,i=n.slice(1).map(s=>Number(s)/o);if(i.every(s=>Number.isFinite(s)&&s>=0&&s<=1))return{r:i[0],g:i[1],b:i[2]}}if(n=e.match(/^hsl\(\s*([-\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*\)$/),n){const r=Number(n[1]),o=Number(n[2])/100,i=Number(n[3])/100;if([r,o,i].every(Number.isFinite)&&o>=0&&o<=1&&i>=0&&i<=1)return q({h:r,s:o,l:i})}throw new Error(`Unsupported continuous color "${t}".`)}function T({r:t,g:e,b:n}){return`#${[t,e,n].map(r=>Math.round(w(r)*255).toString(16).padStart(2,"0")).join("")}`}function O({r:t,g:e,b:n}){return[t,e,n].map(r=>Math.round(r*255))}function A({r:t,g:e,b:n}){const r=Math.max(t,e,n),o=Math.min(t,e,n),i=r-o,s=(r+o)/2;if(i===0)return{h:0,s:0,l:s};const u=i/(1-Math.abs(2*s-1)),c=r===t?(e-n)/i%6:r===e?(n-t)/i+2:(t-e)/i+4;return{h:y(c*60),s:u,l:s}}function N(t){return t<=.04045?t/12.92:((t+.055)/1.055)**2.4}function g(t){return t<=.0031308?12.92*t:1.055*t**(1/2.4)-.055}function C(t,e="d65"){const n=N(t.r),r=N(t.g),o=N(t.b),i=e==="d50"?(.4360747*n+.3850649*r+.1430804*o)/.96422:(.4124564*n+.3575761*r+.1804375*o)/.95047,s=e==="d50"?.2225045*n+.7168786*r+.0606169*o:.2126729*n+.7151522*r+.072175*o,u=e==="d50"?(.0139322*n+.0971045*r+.7141733*o)/.82521:(.0193339*n+.119192*r+.9503041*o)/1.08883,c=m=>m>216/24389?Math.cbrt(m):841/108*m+4/29,l=c(i),f=c(s),h=c(u);return{l:116*f-16,a:500*(l-f),b:200*(f-h)}}function I({l:t,a:e,b:n},r="d65"){const o=(t+16)/116,i=o+e/500,s=o-n/200,u=h=>h**3>216/24389?h**3:108/841*(h-4/29),c=(r==="d50"?.96422:.95047)*u(i),l=u(o),f=(r==="d50"?.82521:1.08883)*u(s);return r==="d50"?{r:g(3.1338561*c-1.6168667*l-.4906146*f),g:g(-.9787684*c+1.9161415*l+.033454*f),b:g(.0719453*c-.2289914*l+1.4052427*f)}:{r:g(3.2404542*c-1.5371385*l-.4985314*f),g:g(-.969266*c+1.8760108*l+.041556*f),b:g(.0556434*c-.2040259*l+1.0572252*f)}}function R({l:t,a:e,b:n},r=!1){return{h:r?void 0:y(Math.atan2(n,e)*180/Math.PI),c:r?0:Math.hypot(e,n),l:t}}function U({h:t,c:e,l:n}){const r=t*Math.PI/180;return{l:n,a:Math.cos(r)*e,b:Math.sin(r)*e}}function j({r:t,g:e,b:n}){const c=-1.7884503806,l=1.97294*1.78277,f=1.78277*-.29227- -.90649*-.14861,h=(f*n+c*t-l*e)/(f+c-l),m=n-h,b=(1.97294*(e-h)- -.29227*m)/-.90649,a=1.97294*h*(1-h);return{h:y(Math.atan2(b,m)*180/Math.PI-120),s:a===0?0:Math.hypot(b,m)/a,l:h}}function W({h:t,s:e,l:n}){const r=(t+120)*Math.PI/180,o=e*n*(1-n),i=Math.cos(r),s=Math.sin(r);return{r:n+o*(-.14861*i+1.78277*s),g:n+o*(-.29227*i-.90649*s),b:n+o*(1.97294*i)}}function M(t,e,n,r){let o=e-t;return r||(o=(o+540)%360-180),y(t+o*n)}function _(t,e,n,r,o){const i=o.whitePoint??"d65";if(r.startsWith("hsl")){const a=A(t),d=A(e);return q({h:M(a.h,d.h,n,r.endsWith("-long")),s:a.s+(d.s-a.s)*n,l:a.l+(d.l-a.l)*n})}if(r==="lab"){const a=C(t,i),d=C(e,i);return I({l:a.l+(d.l-a.l)*n,a:a.a+(d.a-a.a)*n,b:a.b+(d.b-a.b)*n},i)}if(r.startsWith("hcl")){const a=R(C(t,i),t.r===t.g&&t.g===t.b),d=R(C(e,i),e.r===e.g&&e.g===e.b);return I(U({h:a.h===void 0?d.h??0:d.h===void 0?a.h:M(a.h,d.h,n,r.endsWith("-long")),c:a.c+(d.c-a.c)*n,l:a.l+(d.l-a.l)*n}),i)}const s=j(t),u=j(e),c=o.gamma??1,l=n**c,f=c<0&&n===0;if(!Number.isFinite(l)&&!f)throw new RangeError("Continuous cubehelix gamma produced a nonfinite interpolation.");const h=s.s+(u.s-s.s)*n,m=f&&s.l===u.l?s.l:s.l+(u.l-s.l)*l;if(f&&h===0){const a=Number.isNaN(m)?0:w(m);return{r:a,g:a,b:a}}const b=W({h:M(s.h,u.h,n,r.endsWith("-long")),s:h,l:m});if(f)return Object.fromEntries(Object.entries(b).map(([a,d])=>[a,Number.isNaN(d)?0:w(d)]));if(c<0&&!Object.values(b).every(Number.isFinite))throw new RangeError("Continuous cubehelix gamma produced a nonfinite interpolation.");return b}function P(t){if(!D.includes(t))throw new Error(`Unsupported continuous color interpolation "${t}".`);return t}function G(t,e={}){const n={};if(e.whitePoint!==void 0){if(!["lab","hcl","hcl-long"].includes(t))throw new Error("Scale whitePoint requires Lab or HCL interpolation.");if(!["d65","d50"].includes(e.whitePoint))throw new Error("Scale whitePoint must be d65 or d50.");n.whitePoint=e.whitePoint}if(e.gamma!==void 0){if(!["cubehelix","cubehelix-long"].includes(t))throw new Error("Scale gamma requires cubehelix interpolation.");if(!Number.isFinite(e.gamma))throw new RangeError("Scale gamma must be a finite number.");n.gamma=e.gamma}return n}function V(t){if(t==="auto")return t;if(Array.isArray(t)){if(k(t.length,"Sequential color range length"),t.length<2)throw new TypeError("Sequential color range requires at least two colors.");return t.forEach(E),p(t)}if(!$(t)||Object.keys(t).length!==1||!Object.hasOwn(t,"palette"))throw new TypeError("Sequential color range requires colors or a palette descriptor.");const e=B(t.palette);if(e.count!==void 0&&e.count<2)throw new RangeError("Sequential palette count must be an integer of at least 2.");return p({palette:e})}function v(t){const e=V(t);return e==="auto"?S("viridis"):Array.isArray(e)?p(e.map(n=>T(E(n)))):S(e.palette)}function F(t,e,n,r,o={}){const s=(r?w(e):e)*(t.length-1),u=Math.max(0,Math.min(t.length-2,Math.floor(s))),c=u+1,l=s-u;if(n==="rgb"){const f=O(t[u]),h=O(t[c]);return`#${f.map((m,b)=>Math.round(w(m+(h[b]-m)*l,0,255)).toString(16).padStart(2,"0")).join("")}`}return T(_(t[u],t[c],l,n,o))}function tt(t,e,n="rgb"){const r=P(n);if(!Array.isArray(t)||t.length<2)throw new TypeError("Color interpolation requires at least two stops.");const o=t.map(E);return F(o,e,r,!0)}function L(t,e,n="auto"){if(!(t===void 0||t==="auto")){if(e!=="sequential")throw new Error(`Scale type "${e}" does not support midpoint.`);if(!Number.isFinite(t))throw new TypeError("Scale midpoint must be finite or auto.");if(n!=="auto"&&(!Array.isArray(n)||n.length!==2||!n.every(Number.isFinite)||!(t>Math.min(...n)&&t<Math.max(...n))))throw new RangeError("Scale midpoint must lie strictly inside its resolved domain.");return t}}function J(t,e,n,r={}){const{interpolation:o="rgb",clamp:i=!1}=r;if(G(o,r),["log","pow","sqrt","symlog"].includes(r.type)){L(r.midpoint,r.type,e);const b=z(t,e,[0,1],{type:r.type,clamp:i,...r.type==="log"?{base:r.base}:{},...r.type==="pow"?{exponent:r.exponent}:{},...r.type==="symlog"?{constant:r.constant}:{},...Object.hasOwn(r,"unknown")?{unknown:NaN}:{}});return J(b,[0,1],n,{...r,type:"sequential"})}const s=Object.hasOwn(r,"unknown");if(!Array.isArray(e)||e.length!==2||!e.every(Number.isFinite))throw new TypeError("Sequential color domain must contain two finite numbers.");if(!Array.isArray(t)||!s&&!t.every(Number.isFinite))throw new TypeError("Sequential color values must be finite numbers.");if(typeof i!="boolean")throw new TypeError("Sequential color clamp must be a boolean.");const u=L(r.midpoint,"sequential",e),[c,l]=e,f=c===l,h=P(o);if(!Array.isArray(n)||n.length<2)throw new TypeError("Color interpolation requires at least two stops.");const m=n.map(E);return p(t.map(b=>{if(!Number.isFinite(b)){if(s)return r.unknown;throw new TypeError("Sequential color values must be finite numbers.")}const a=u===void 0?f?.5:x(b,c,l):(l>c?b<=u:b>=u)?x(b,c,u)/2:.5+x(b,u,l)/2;return F(m,a,h,i,r)}))}export{D as CONTINUOUS_COLOR_INTERPOLATIONS,tt as interpolateColorStops,J as mapSequentialColors,G as normalizeColorInterpolationOptions,v as resolveSequentialColorStops,P as validateContinuousColorInterpolation,V as validateSequentialColorRange,L as validateSequentialMidpoint};
|
|
@@ -1,63 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { validateAggregate, validateAggregateFieldType } from "../aggregate.js";
|
|
3
|
-
import { isDiscretizedColorScaleType } from "./types.js";
|
|
4
|
-
|
|
5
|
-
export function validateContinuousColorConsumer(layer, encoding, scale, {
|
|
6
|
-
inferAggregate = false,
|
|
7
|
-
channel = "color"
|
|
8
|
-
} = {}) {
|
|
9
|
-
const kind = layer.mark?.type;
|
|
10
|
-
if (["log", "pow", "sqrt", "symlog"].includes(scale.type) && encoding.fieldType !== "quantitative") {
|
|
11
|
-
throw new Error(`Scale type "${scale.type}" requires quantitative color.`);
|
|
12
|
-
}
|
|
13
|
-
if (channel === "stroke") {
|
|
14
|
-
if (!["point", "line", "area", "bar", "rect", "arc", "rule", "tick"].includes(kind)) {
|
|
15
|
-
throw new Error("Continuous stroke requires a supported graphical mark consumer.");
|
|
16
|
-
}
|
|
17
|
-
if (!['quantitative', 'temporal'].includes(encoding.fieldType) ||
|
|
18
|
-
(isDiscretizedColorScaleType(scale.type) && encoding.fieldType !== "quantitative")) {
|
|
19
|
-
throw new Error(`Scale type "${scale.type}" has an incompatible stroke field type.`);
|
|
20
|
-
}
|
|
21
|
-
if (encoding.aggregate !== undefined) {
|
|
22
|
-
throw new Error("Continuous stroke does not support aggregate.");
|
|
23
|
-
}
|
|
24
|
-
if (Object.hasOwn(scale, "unknown") && kind !== "point") {
|
|
25
|
-
throw new Error("Continuous stroke scale unknown currently requires a row-owned point mark.");
|
|
26
|
-
}
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
if (!["point", "bar", "rect", "text", "area"].includes(kind)) {
|
|
30
|
-
throw new Error("Continuous color requires a Point, aggregate Bar, Rect, Text, or quantitative Area consumer.");
|
|
31
|
-
}
|
|
32
|
-
if (!["quantitative", "temporal"].includes(encoding.fieldType) ||
|
|
33
|
-
(isDiscretizedColorScaleType(scale.type) && encoding.fieldType !== "quantitative")) {
|
|
34
|
-
throw new Error(`Scale type "${scale.type}" has an incompatible color field type.`);
|
|
35
|
-
}
|
|
36
|
-
if (Object.hasOwn(scale, "unknown") && kind !== "point") {
|
|
37
|
-
throw new Error("Continuous color scale unknown currently requires a row-owned point mark.");
|
|
38
|
-
}
|
|
39
|
-
if (kind === "area" && encoding.fieldType !== "quantitative") {
|
|
40
|
-
throw new Error("Continuous area color requires a quantitative field.");
|
|
41
|
-
}
|
|
42
|
-
if (kind !== "bar") {
|
|
43
|
-
if (encoding.aggregate !== undefined) throw new Error(`${kind} continuous color does not support aggregate.`);
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
if (["nominal", "ordinal"].includes(layer.encoding?.color?.fieldType)) {
|
|
47
|
-
throw new Error("Continuous bar color cannot replace an existing nominal color layout.");
|
|
48
|
-
}
|
|
49
|
-
if (encoding.fieldType !== "quantitative") {
|
|
50
|
-
throw new Error("Aggregate bar color currently requires a quantitative field.");
|
|
51
|
-
}
|
|
52
|
-
if (resolveBarGrain(layer) !== BAR_GRAINS.aggregate) {
|
|
53
|
-
throw new Error("Continuous bar color requires a complete categorical aggregate bar.");
|
|
54
|
-
}
|
|
55
|
-
const measure = layer.encoding?.[resolveBarChannels(layer).measure];
|
|
56
|
-
const aggregate = encoding.aggregate ?? (inferAggregate && measure?.field === encoding.field ? measure.aggregate : undefined);
|
|
57
|
-
if (aggregate === undefined) {
|
|
58
|
-
throw new Error("Continuous bar color requires aggregate when its field differs from the measure field.");
|
|
59
|
-
}
|
|
60
|
-
validateAggregate(aggregate);
|
|
61
|
-
validateAggregateFieldType(aggregate, encoding.fieldType);
|
|
62
|
-
return aggregate;
|
|
63
|
-
}
|
|
1
|
+
import{isNominalValue as d,readNominalField as s}from"./fields.js";import{BAR_GRAINS as f,resolveBarChannels as p,resolveBarGrain as c}from"../bars/policy.js";import{validateAggregate as w,validateAggregateFieldType as g}from"../aggregate.js";import{isDiscretizedColorScaleType as l}from"./types.js";function m(o,e){if(o.mark?.type!=="tick")return;const{color:r,stroke:t}=o.encoding??{};if(r!==void 0&&t!==void 0||e==="color"&&t!==void 0||e==="stroke"&&r!==void 0)throw new Error("Tick color and stroke cannot coexist; remove the existing encoding before switching channels.");if(r!==void 0&&!["quantitative","temporal"].includes(r.fieldType))throw new Error("Tick color requires a quantitative or temporal field.")}function v(o,e,r,{inferAggregate:t=!1,channel:n="color"}={}){const i=o.mark?.type;if(m(o,n),["log","pow","sqrt","symlog"].includes(r.type)&&e.fieldType!=="quantitative")throw new Error(`Scale type "${r.type}" requires quantitative color.`);if(n==="stroke"){if(!["point","line","area","bar","rect","arc","rule","tick"].includes(i))throw new Error("Continuous stroke requires a supported graphical mark consumer.");if(!["quantitative","temporal"].includes(e.fieldType)||l(r.type)&&e.fieldType!=="quantitative")throw new Error(`Scale type "${r.type}" has an incompatible stroke field type.`);if(e.aggregate!==void 0)throw new Error("Continuous stroke does not support aggregate.");if(Object.hasOwn(r,"unknown")&&i!=="point")throw new Error("Continuous stroke scale unknown currently requires a row-owned point mark.");return}if(!["point","line","bar","rect","text","area","arc","tick"].includes(i))throw new Error("Continuous color requires a Point, Line, aggregate Bar, Rect, Text, Arc, Tick, or quantitative Area consumer.");if(!["quantitative","temporal"].includes(e.fieldType)||l(r.type)&&e.fieldType!=="quantitative")throw new Error(`Scale type "${r.type}" has an incompatible color field type.`);if(Object.hasOwn(r,"unknown")&&i!=="point")throw new Error("Continuous color scale unknown currently requires a row-owned point mark.");if(i==="area"&&e.fieldType!=="quantitative")throw new Error("Continuous area color requires a quantitative field.");if(i!=="bar"){if(e.aggregate!==void 0)throw new Error(`${i} continuous color does not support aggregate.`);return}if(["nominal","ordinal"].includes(o.encoding?.color?.fieldType))throw new Error("Continuous bar color cannot replace an existing nominal color layout.");if(e.fieldType!=="quantitative")throw new Error("Aggregate bar color currently requires a quantitative field.");if(c(o)!==f.aggregate)throw new Error("Continuous bar color requires a complete categorical aggregate bar.");const u=o.encoding?.[p(o).measure],a=e.aggregate??(t&&u?.field===e.field?u.aggregate:void 0);if(a===void 0)throw new Error("Continuous bar color requires aggregate when its field differs from the measure field.");return w(a),g(a,e.fieldType),a}function C(o,e,r,t="color"){if(!Object.hasOwn(r,"unknown")||o.mark?.type==="point")return!1;const n=o.encoding??{};if(t==="strokeDash"){if(!["line","rule"].includes(o.mark?.type)||!["nominal","ordinal"].includes(e.fieldType)||r.type!=="ordinal"||n.parallel!==void 0||n.theta!==void 0||n.radius!==void 0||[n.x,n.y,n.x2,n.y2].some(i=>i?.aggregate!==void 0||i?.bin!==void 0))throw new Error("StrokeDash unknown requires a Cartesian line or rule with categorical ordinal mapping.");return!1}if(o.mark?.type!=="line"||t!=="color"||!["nominal","ordinal"].includes(e.fieldType)||r.type!=="ordinal"||!Array.isArray(r.domain)||r.domain.length===0||n.parallel!==void 0||n.theta!==void 0||n.radius!==void 0||[n.x,n.y].some(i=>i?.aggregate!==void 0||i?.bin!==void 0))throw new Error("Categorical unknown requires a row-owned point or a Cartesian line with explicit ordinal domain.");return!0}function T(o,e){return s([],e),o.map((r,t)=>{const n=r[e];if(n==null||d(n))return n;throw new TypeError(`Field "${e}" must contain a nominal value at row ${t}.`)})}export{T as readCategoricalLineColor,C as validateCategoricalColorConsumer,v as validateContinuousColorConsumer,m as validateTickColorChannels};
|