ggaction 0.0.19 → 0.0.21
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 +10 -0
- package/README.md +2 -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 +1 -1
- package/src/actions/encodings/color/continuous.js +7 -1
- package/src/actions/guides/legends/categorical/actions.js +2 -2
- package/src/actions/guides/legends/continuous/common.js +1 -1
- package/src/actions/marks/area/materialize.js +1 -190
- package/src/actions/scales/consumers/common.js +7 -1
- package/src/grammar/aggregate.js +3 -2
- package/src/grammar/areaSeries.js +1 -415
- package/src/grammar/interval.js +26 -9
- package/src/grammar/pathSeries.js +2 -2
- package/src/grammar/scales/colorConsumers.js +5 -2
- package/src/grammar/statistics/confidenceInterval.js +1 -101
- package/src/grammar/summary.js +1 -320
- package/src/materialization/marks/grouping.js +1 -1
- package/src/version.js +1 -1
- package/types/index.d.ts +2 -0
- package/types/program.d.ts +14 -3
|
@@ -1,190 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
deriveCenteredAreaSeries,
|
|
3
|
-
deriveAreaSeries,
|
|
4
|
-
deriveDensityAreaSeries,
|
|
5
|
-
layoutDensityAreaSeries
|
|
6
|
-
} from "../../../grammar/areaSeries.js";
|
|
7
|
-
import {
|
|
8
|
-
mapContinuousScaleValues,
|
|
9
|
-
mapOrdinalPositionValues,
|
|
10
|
-
mapOrdinalValues
|
|
11
|
-
} from "../../../grammar/scales/index.js";
|
|
12
|
-
import { mapScaleConsumerValues } from
|
|
13
|
-
"../../../materialization/scales/map.js";
|
|
14
|
-
import { buildAreaCurvePathCommands } from
|
|
15
|
-
"../../../grammar/curveCommands.js";
|
|
16
|
-
import { buildLinearPathCommands } from
|
|
17
|
-
"../../../grammar/pathCommands.js";
|
|
18
|
-
import { buildCategoricalDensityPaths } from
|
|
19
|
-
"../../../grammar/categoricalDensity.js";
|
|
20
|
-
import { derivePathSeriesFieldValues } from "../../../grammar/pathSeries.js";
|
|
21
|
-
|
|
22
|
-
function resolveAreaPaths({
|
|
23
|
-
derived,
|
|
24
|
-
densityTransform,
|
|
25
|
-
categoricalDensity,
|
|
26
|
-
xScale,
|
|
27
|
-
yScale,
|
|
28
|
-
densityScale,
|
|
29
|
-
layout,
|
|
30
|
-
curve
|
|
31
|
-
}) {
|
|
32
|
-
if (categoricalDensity) {
|
|
33
|
-
return buildCategoricalDensityPaths(derived, {
|
|
34
|
-
categoryScale: densityTransform.placement.channel === "x" ? xScale : yScale,
|
|
35
|
-
valueScale: densityTransform.placement.channel === "x" ? yScale : xScale,
|
|
36
|
-
curve
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return derived.series.map(series => {
|
|
40
|
-
if (densityTransform === undefined && derived.orientation === "horizontal") {
|
|
41
|
-
const y = (["point", "band"].includes(yScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
|
|
42
|
-
series.values.map(value => value.y), yScale
|
|
43
|
-
);
|
|
44
|
-
const lower = mapContinuousScaleValues(
|
|
45
|
-
series.values.map(value => value.x),
|
|
46
|
-
xScale
|
|
47
|
-
);
|
|
48
|
-
const upper = mapContinuousScaleValues(
|
|
49
|
-
series.values.map(value => value.x2),
|
|
50
|
-
xScale
|
|
51
|
-
);
|
|
52
|
-
return buildAreaCurvePathCommands(
|
|
53
|
-
y.map((value, index) => ({ x: lower[index], y: value })),
|
|
54
|
-
y.map((value, index) => ({ x: upper[index], y: value })),
|
|
55
|
-
curve,
|
|
56
|
-
{ independentAxis: "y" }
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
if (densityTransform !== undefined && derived.mode === "x-density") {
|
|
60
|
-
const y = (["point", "band"].includes(yScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
|
|
61
|
-
series.values.map(value => value.y), yScale
|
|
62
|
-
);
|
|
63
|
-
const upper = mapContinuousScaleValues(
|
|
64
|
-
series.values.map(value => value.x),
|
|
65
|
-
xScale
|
|
66
|
-
);
|
|
67
|
-
const baseline = mapContinuousScaleValues([0], densityScale)[0];
|
|
68
|
-
if (curve === "linear") {
|
|
69
|
-
return buildLinearPathCommands([
|
|
70
|
-
{ x: baseline, y: y[0] },
|
|
71
|
-
...y.map((value, index) => ({ x: upper[index], y: value })),
|
|
72
|
-
{ x: baseline, y: y.at(-1) }
|
|
73
|
-
], { close: true });
|
|
74
|
-
}
|
|
75
|
-
return buildAreaCurvePathCommands(
|
|
76
|
-
y.map(value => ({ x: baseline, y: value })),
|
|
77
|
-
y.map((value, index) => ({ x: upper[index], y: value })),
|
|
78
|
-
curve,
|
|
79
|
-
{ independentAxis: "y" }
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
const x = (["point", "band"].includes(xScale.type) ? mapOrdinalPositionValues : mapContinuousScaleValues)(
|
|
83
|
-
series.values.map(value => value.x),
|
|
84
|
-
xScale
|
|
85
|
-
);
|
|
86
|
-
const lowerValues = densityTransform === undefined
|
|
87
|
-
? derived.mode === "y-center"
|
|
88
|
-
? series.values.map(value => value.lower)
|
|
89
|
-
: series.values.map(value => value.y)
|
|
90
|
-
: derived.mode === "y-density"
|
|
91
|
-
? series.values.map(value => value.lower)
|
|
92
|
-
: series.values.map(value => value.y);
|
|
93
|
-
const lower = mapContinuousScaleValues(lowerValues, yScale);
|
|
94
|
-
const upperValues = densityTransform === undefined
|
|
95
|
-
? derived.mode === "y-center"
|
|
96
|
-
? series.values.map(value => value.upper)
|
|
97
|
-
: series.values.map(value => value.y2)
|
|
98
|
-
: series.values.map(value => value.upper);
|
|
99
|
-
const upper = mapContinuousScaleValues(upperValues, yScale);
|
|
100
|
-
if (densityTransform !== undefined && layout === "overlay") {
|
|
101
|
-
const baseline = mapContinuousScaleValues([0], yScale)[0];
|
|
102
|
-
if (curve === "linear") {
|
|
103
|
-
return buildLinearPathCommands([
|
|
104
|
-
{ x: x[0], y: baseline },
|
|
105
|
-
...x.map((value, index) => ({ x: value, y: upper[index] })),
|
|
106
|
-
{ x: x.at(-1), y: baseline }
|
|
107
|
-
], { close: true });
|
|
108
|
-
}
|
|
109
|
-
return buildAreaCurvePathCommands(
|
|
110
|
-
x.map(value => ({ x: value, y: baseline })),
|
|
111
|
-
x.map((value, index) => ({ x: value, y: upper[index] })),
|
|
112
|
-
curve
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
return buildAreaCurvePathCommands(
|
|
116
|
-
x.map((value, index) => ({ x: value, y: lower[index] })),
|
|
117
|
-
x.map((value, index) => ({ x: value, y: upper[index] })),
|
|
118
|
-
curve
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function resolveAreaMaterialization({
|
|
124
|
-
rows,
|
|
125
|
-
layer,
|
|
126
|
-
densityTransform,
|
|
127
|
-
resolvedScales,
|
|
128
|
-
config
|
|
129
|
-
}) {
|
|
130
|
-
const xScale = resolvedScales[layer.encoding.x.scale];
|
|
131
|
-
const yScale = resolvedScales[layer.encoding.y.scale];
|
|
132
|
-
const centered = densityTransform === undefined &&
|
|
133
|
-
layer.encoding?.y?.stack === "center";
|
|
134
|
-
const rawDerived = densityTransform === undefined
|
|
135
|
-
? centered
|
|
136
|
-
? deriveCenteredAreaSeries(rows, layer, { xDomain: xScale.domain })
|
|
137
|
-
: deriveAreaSeries(rows, layer, { xDomain: xScale.domain, yDomain: yScale.domain })
|
|
138
|
-
: deriveDensityAreaSeries(rows, layer, densityTransform);
|
|
139
|
-
const colorEncoding = layer.encoding?.color;
|
|
140
|
-
const layout = layer.layout?.mode ?? colorEncoding?.layout ?? "overlay";
|
|
141
|
-
const categoricalDensity = densityTransform?.placement?.type === "category";
|
|
142
|
-
const derived = densityTransform === undefined || categoricalDensity
|
|
143
|
-
? rawDerived
|
|
144
|
-
: layoutDensityAreaSeries(rawDerived, layout);
|
|
145
|
-
const densityScale = densityTransform === undefined || categoricalDensity
|
|
146
|
-
? undefined
|
|
147
|
-
: derived.mode === "y-density" ? yScale : xScale;
|
|
148
|
-
if (
|
|
149
|
-
densityScale !== undefined &&
|
|
150
|
-
(densityScale.domain[0] > 0 || densityScale.domain[1] < 0)
|
|
151
|
-
) {
|
|
152
|
-
throw new Error(`Density area mark "${layer.id}" requires a scale domain containing zero.`);
|
|
153
|
-
}
|
|
154
|
-
const paths = resolveAreaPaths({
|
|
155
|
-
derived,
|
|
156
|
-
densityTransform,
|
|
157
|
-
categoricalDensity,
|
|
158
|
-
xScale,
|
|
159
|
-
yScale,
|
|
160
|
-
densityScale,
|
|
161
|
-
layout,
|
|
162
|
-
curve: config.curve ?? "linear"
|
|
163
|
-
});
|
|
164
|
-
const fills = config.errorBand?.fill !== undefined
|
|
165
|
-
? paths.map(() => config.errorBand.fill)
|
|
166
|
-
: colorEncoding?.scale === undefined
|
|
167
|
-
? paths.map(() => config.fill)
|
|
168
|
-
: mapOrdinalValues(
|
|
169
|
-
densityTransform !== undefined || centered
|
|
170
|
-
? derived.series.map(series => series.key[colorEncoding.field])
|
|
171
|
-
: derivePathSeriesFieldValues(rows, derived.series, colorEncoding.field, "color"),
|
|
172
|
-
resolvedScales[colorEncoding.scale].domain,
|
|
173
|
-
resolvedScales[colorEncoding.scale].range
|
|
174
|
-
);
|
|
175
|
-
const strokeEncoding = layer.encoding?.stroke;
|
|
176
|
-
const strokes = strokeEncoding?.scale === undefined
|
|
177
|
-
? undefined
|
|
178
|
-
: mapScaleConsumerValues(
|
|
179
|
-
derivePathSeriesFieldValues(
|
|
180
|
-
rows,
|
|
181
|
-
derived.series,
|
|
182
|
-
strokeEncoding.field,
|
|
183
|
-
"stroke",
|
|
184
|
-
strokeEncoding
|
|
185
|
-
),
|
|
186
|
-
resolvedScales[strokeEncoding.scale],
|
|
187
|
-
"stroke"
|
|
188
|
-
);
|
|
189
|
-
return { paths, fills, strokes };
|
|
190
|
-
}
|
|
1
|
+
import{deriveCenteredAreaSeries as P,deriveAreaSeries as V,deriveDensityAreaSeries as w,layoutDensityAreaSeries as z}from"../../../grammar/areaSeries.js";import{mapContinuousScaleValues as c,mapOrdinalPositionValues as D}from"../../../grammar/scales/index.js";import{mapScaleConsumerValues as b}from"../../../materialization/scales/map.js";import{buildAreaCurvePathCommands as A}from"../../../grammar/curveCommands.js";import{buildLinearPathCommands as k}from"../../../grammar/pathCommands.js";import{buildCategoricalDensityPaths as E}from"../../../grammar/categoricalDensity.js";import{derivePathSeriesFieldValues as C}from"../../../grammar/pathSeries.js";function q({derived:d,densityTransform:a,categoricalDensity:m,xScale:l,yScale:u,densityScale:x,layout:v,curve:r}){return m?E(d,{categoryScale:a.placement.channel==="x"?l:u,valueScale:a.placement.channel==="x"?u:l,curve:r}):d.series.map(o=>{if(a===void 0&&d.orientation==="horizontal"){const e=(["point","band"].includes(u.type)?D:c)(o.values.map(n=>n.y),u),t=c(o.values.map(n=>n.x),l),p=c(o.values.map(n=>n.x2),l);return A(e.map((n,s)=>({x:t[s],y:n})),e.map((n,s)=>({x:p[s],y:n})),r,{independentAxis:"y"})}if(a!==void 0&&d.mode==="x-density"){const e=(["point","band"].includes(u.type)?D:c)(o.values.map(n=>n.y),u),t=c(o.values.map(n=>n.x),l),p=c([0],x)[0];return r==="linear"?k([{x:p,y:e[0]},...e.map((n,s)=>({x:t[s],y:n})),{x:p,y:e.at(-1)}],{close:!0}):A(e.map(n=>({x:p,y:n})),e.map((n,s)=>({x:t[s],y:n})),r,{independentAxis:"y"})}const i=(["point","band"].includes(l.type)?D:c)(o.values.map(e=>e.x),l),g=a===void 0?d.mode==="y-center"?o.values.map(e=>e.lower):o.values.map(e=>e.y):d.mode==="y-density"?o.values.map(e=>e.lower):o.values.map(e=>e.y),h=c(g,u),y=a===void 0?d.mode==="y-center"?o.values.map(e=>e.upper):o.values.map(e=>e.y2):o.values.map(e=>e.upper),f=c(y,u);if(a!==void 0&&v==="overlay"){const e=c([0],u)[0];return r==="linear"?k([{x:i[0],y:e},...i.map((t,p)=>({x:t,y:f[p]})),{x:i.at(-1),y:e}],{close:!0}):A(i.map(t=>({x:t,y:e})),i.map((t,p)=>({x:t,y:f[p]})),r)}return A(i.map((e,t)=>({x:e,y:h[t]})),i.map((e,t)=>({x:e,y:f[t]})),r)})}function G({rows:d,layer:a,densityTransform:m,resolvedScales:l,config:u}){const x=l[a.encoding.x.scale],v=l[a.encoding.y.scale],r=m===void 0&&a.encoding?.y?.stack==="center",o=m===void 0?r?P(d,a,{xDomain:x.domain}):V(d,a,{xDomain:x.domain,yDomain:v.domain}):w(d,a,m),i=a.encoding?.color,g=a.layout?.mode??i?.layout??"overlay",h=m?.placement?.type==="category",y=m===void 0||h?o:z(o,g),f=m===void 0||h?void 0:y.mode==="y-density"?v:x;if(f!==void 0&&(f.domain[0]>0||f.domain[1]<0))throw new Error(`Density area mark "${a.id}" requires a scale domain containing zero.`);const e=q({derived:y,densityTransform:m,categoricalDensity:h,xScale:x,yScale:v,densityScale:f,layout:g,curve:u.curve??"linear"}),t=u.errorBand?.fill!==void 0?e.map(()=>u.errorBand.fill):i?.scale===void 0?e.map(()=>u.fill):b(i.fieldType!=="quantitative"&&(m!==void 0||r)?y.series.map(s=>s.key[i.field]):C(d,y.series,i.field,"color",i),l[i.scale],"color"),p=a.encoding?.stroke,n=p?.scale===void 0?void 0:b(C(d,y.series,p.field,"stroke",p),l[p.scale],"stroke");return{paths:e,fills:t,strokes:n}}export{G as resolveAreaMaterialization};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isSourceOwnedText } from "../../../grammar/text.js";
|
|
2
|
-
import { readSeriesIdentity } from "../../../grammar/pathSeries.js";
|
|
2
|
+
import { readSeriesIdentity, validatePathSeriesAppearance } from "../../../grammar/pathSeries.js";
|
|
3
3
|
import { readAreaEndpoint } from "../../../grammar/areaEndpoints.js";
|
|
4
4
|
import { readScaleField } from "../../../grammar/scales/index.js";
|
|
5
5
|
import { findDataset } from "../../../selectors/datasets.js";
|
|
@@ -59,6 +59,12 @@ export function readConsumerFieldValues(
|
|
|
59
59
|
if (consumer.layer.mark.type === "bar" && consumer.layer.encoding?.group !== undefined && ["xOffset", "yOffset"].includes(consumer.channel)) {
|
|
60
60
|
return readSeriesIdentity(dataset.values, consumer.layer).values;
|
|
61
61
|
}
|
|
62
|
+
if (consumer.layer.mark.type === "area" && consumer.channel === "color" && fieldType === "quantitative") {
|
|
63
|
+
validatePathSeriesAppearance(dataset.values, consumer.layer);
|
|
64
|
+
const identities = readSeriesIdentity(dataset.values, consumer.layer).values;
|
|
65
|
+
const values = readScaleField(dataset.values, field, fieldType);
|
|
66
|
+
return [...new Map(identities.map((identity, index) => [identity, values[index]])).values()];
|
|
67
|
+
}
|
|
62
68
|
const parallel = consumer.role === "parallelDimension";
|
|
63
69
|
if (!parallel && !isDirectCategoricalConsumer(consumer) &&
|
|
64
70
|
fieldType !== "quantitative" && fieldType !== "temporal" &&
|
package/src/grammar/aggregate.js
CHANGED
|
@@ -40,7 +40,7 @@ export function isAggregate(value) {
|
|
|
40
40
|
return isScalarAggregate(value) || isParameterizedAggregate(value);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function validateAggregate(value) {
|
|
43
|
+
export function validateAggregate(value, { allowBootstrap = false } = {}) {
|
|
44
44
|
if (typeof value === "string") {
|
|
45
45
|
if (!SCALAR_OPERATIONS.has(value)) {
|
|
46
46
|
throw new Error(`Unsupported aggregate "${value}".`);
|
|
@@ -97,7 +97,8 @@ export function validateAggregate(value) {
|
|
|
97
97
|
op: value.op,
|
|
98
98
|
...normalizeConfidenceInterval(value, {
|
|
99
99
|
defaultMethod: "normal",
|
|
100
|
-
label: "Aggregate confidence interval"
|
|
100
|
+
label: "Aggregate confidence interval",
|
|
101
|
+
allowBootstrap
|
|
101
102
|
})
|
|
102
103
|
};
|
|
103
104
|
}
|
|
@@ -1,415 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readNominalField, readQuantitativeField, readTemporalField } from "./scales/index.js";
|
|
3
|
-
import { layoutSeriesPartition, validateColorLayout } from "./seriesLayout.js";
|
|
4
|
-
import { stableOrderPathValues } from "./pathOrder.js";
|
|
5
|
-
import { deriveCategoricalDensitySeries } from "./categoricalDensity.js";
|
|
6
|
-
import { validatePathSeriesAppearance } from "./pathSeries.js";
|
|
7
|
-
import { readAreaEndpoint, validateAreaEndpointPair } from "./areaEndpoints.js";
|
|
8
|
-
import { validateGeneratedItemLimit } from "../core/validation.js";
|
|
9
|
-
|
|
10
|
-
function indexSegments(segments) {
|
|
11
|
-
const indexed = [];
|
|
12
|
-
for (const segment of segments) indexed[segment.index] = segment;
|
|
13
|
-
return indexed;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function deriveAreaSeries(rows, layer, options = {}) {
|
|
17
|
-
if (layer?.mark?.type !== "area") {
|
|
18
|
-
throw new Error("Area series derivation requires a semantic area mark.");
|
|
19
|
-
}
|
|
20
|
-
const { x, y, x2, y2 } = layer.encoding ?? {};
|
|
21
|
-
const vertical =
|
|
22
|
-
["quantitative", "temporal", "nominal", "ordinal"].includes(x?.fieldType) &&
|
|
23
|
-
y?.fieldType === "quantitative" &&
|
|
24
|
-
y2?.fieldType === "quantitative" &&
|
|
25
|
-
x2 === undefined;
|
|
26
|
-
const horizontal =
|
|
27
|
-
["quantitative", "temporal", "nominal", "ordinal"].includes(y?.fieldType) &&
|
|
28
|
-
x?.fieldType === "quantitative" &&
|
|
29
|
-
x2?.fieldType === "quantitative" &&
|
|
30
|
-
y2 === undefined;
|
|
31
|
-
if (vertical === horizontal) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
`Area mark "${layer.id}" requires exactly one quantitative x/x2 or y/y2 range and one quantitative, temporal, or categorical independent position.`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
const grouping = validatePathSeriesAppearance(rows, layer);
|
|
37
|
-
const orientation = vertical ? "vertical" : "horizontal";
|
|
38
|
-
const independent = vertical ? x : y;
|
|
39
|
-
const categorical = ["nominal", "ordinal"].includes(independent.fieldType);
|
|
40
|
-
const independentValues = categorical ? readNominalField(rows, independent.field) : independent.fieldType === "temporal"
|
|
41
|
-
? readTemporalField(rows, independent.field, independent.temporalUnit)
|
|
42
|
-
: readQuantitativeField(rows, independent.field);
|
|
43
|
-
if (Object.hasOwn(independent, "datum")) throw new Error("Area independent position requires a field.");
|
|
44
|
-
const primary = vertical ? y : x;
|
|
45
|
-
const secondary = vertical ? y2 : x2;
|
|
46
|
-
validateAreaEndpointPair(primary, secondary);
|
|
47
|
-
const missing = layer.mark.missing ?? "error";
|
|
48
|
-
const lower = readAreaEndpoint(rows, primary, missing);
|
|
49
|
-
const upper = readAreaEndpoint(rows, secondary, missing);
|
|
50
|
-
const categoryOrder = categorical ? options[vertical ? "xDomain" : "yDomain"] ?? [...new Set(independentValues)] : undefined;
|
|
51
|
-
const ranks = new Map(categoryOrder?.map((value, index) => [value, index]));
|
|
52
|
-
if (categorical && independentValues.some(value => !ranks.has(value))) throw new Error("Area categorical domain must contain every observed position.");
|
|
53
|
-
const pathOrder = layer.encoding?.pathOrder;
|
|
54
|
-
const orderValues = pathOrder === undefined
|
|
55
|
-
? undefined
|
|
56
|
-
: readQuantitativeField(rows, pathOrder.field);
|
|
57
|
-
const groups = new Map();
|
|
58
|
-
|
|
59
|
-
for (let index = 0; index < rows.length; index += 1) {
|
|
60
|
-
const tuple = grouping.map(field => rows[index][field]);
|
|
61
|
-
const key = JSON.stringify(tuple);
|
|
62
|
-
const series = groups.get(key) ?? {
|
|
63
|
-
key: Object.fromEntries(grouping.map((field, i) => [field, tuple[i]])),
|
|
64
|
-
values: []
|
|
65
|
-
};
|
|
66
|
-
series.values.push(vertical
|
|
67
|
-
? {
|
|
68
|
-
x: independentValues[index],
|
|
69
|
-
y: lower[index],
|
|
70
|
-
y2: upper[index],
|
|
71
|
-
...(missing === "break" ? { sourceIndex: index } : {}),
|
|
72
|
-
...(orderValues === undefined ? {} : { pathOrder: orderValues[index] })
|
|
73
|
-
}
|
|
74
|
-
: {
|
|
75
|
-
x: lower[index],
|
|
76
|
-
x2: upper[index],
|
|
77
|
-
...(missing === "break" ? { sourceIndex: index } : {}),
|
|
78
|
-
y: independentValues[index],
|
|
79
|
-
...(orderValues === undefined ? {} : { pathOrder: orderValues[index] })
|
|
80
|
-
});
|
|
81
|
-
groups.set(key, series);
|
|
82
|
-
}
|
|
83
|
-
if (groups.size === 0) {
|
|
84
|
-
throw new Error(`Area mark "${layer.id}" has no values.`);
|
|
85
|
-
}
|
|
86
|
-
layoutRawAreaGroups(groups, { layer, vertical, primary, secondary });
|
|
87
|
-
const series = [...groups.values()].flatMap(item => {
|
|
88
|
-
const key = vertical ? "x" : "y";
|
|
89
|
-
const values = pathOrder === undefined
|
|
90
|
-
? item.values.sort((left, right) => categorical ? ranks.get(left[key]) - ranks.get(right[key]) : left[key] - right[key])
|
|
91
|
-
: stableOrderPathValues(
|
|
92
|
-
item.values.map(({ pathOrder: _pathOrder, ...value }) => value),
|
|
93
|
-
item.values.map(value => value.pathOrder),
|
|
94
|
-
pathOrder.order
|
|
95
|
-
);
|
|
96
|
-
if (missing === "break") {
|
|
97
|
-
const segments = [];
|
|
98
|
-
let segment = [];
|
|
99
|
-
const emit = () => {
|
|
100
|
-
if (segment.length >= 2) segments.push({ key: item.key,
|
|
101
|
-
sourceIndices: segment.map(value => value.sourceIndex),
|
|
102
|
-
values: segment.map(({ sourceIndex, ...value }) => value) });
|
|
103
|
-
segment = [];
|
|
104
|
-
};
|
|
105
|
-
for (const value of values) {
|
|
106
|
-
if ((vertical ? value.y == null || value.y2 == null : value.x == null || value.x2 == null)) emit();
|
|
107
|
-
else segment.push(value);
|
|
108
|
-
}
|
|
109
|
-
emit();
|
|
110
|
-
return segments;
|
|
111
|
-
}
|
|
112
|
-
if (values.length < 2) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`Area series on mark "${layer.id}" requires at least two points.`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
return { key: item.key, values };
|
|
118
|
-
});
|
|
119
|
-
if (series.length === 0) throw new Error(`Area mark "${layer.id}" has no valid segment with at least two points.`);
|
|
120
|
-
return cloneAndFreeze({
|
|
121
|
-
orientation,
|
|
122
|
-
xValues: series.flatMap(item => item.values.flatMap(value =>
|
|
123
|
-
vertical ? [value.x] : [value.x, value.x2]
|
|
124
|
-
)),
|
|
125
|
-
yValues: series.flatMap(item => item.values.flatMap(value =>
|
|
126
|
-
vertical ? [value.y, value.y2] : [value.y]
|
|
127
|
-
)),
|
|
128
|
-
series
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function deriveCenteredAreaSeries(rows, layer, options = {}) {
|
|
133
|
-
if (layer?.mark?.type !== "area") {
|
|
134
|
-
throw new Error("Centered area series derivation requires an area mark.");
|
|
135
|
-
}
|
|
136
|
-
const { x, y, x2, y2, group, color } = layer.encoding ?? {};
|
|
137
|
-
if (
|
|
138
|
-
!["quantitative", "temporal", "nominal", "ordinal"].includes(x?.fieldType) ||
|
|
139
|
-
y?.fieldType !== "quantitative" ||
|
|
140
|
-
y?.stack !== "center" ||
|
|
141
|
-
x2 !== undefined ||
|
|
142
|
-
y2 !== undefined
|
|
143
|
-
) {
|
|
144
|
-
throw new Error(
|
|
145
|
-
`Centered area mark "${layer.id}" requires one x field and one quantitative y field without ranged endpoints.`
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
if (group?.fieldType !== "nominal") {
|
|
149
|
-
throw new Error(`Centered area mark "${layer.id}" requires a nominal group encoding.`);
|
|
150
|
-
}
|
|
151
|
-
if (color !== undefined && color.field !== group.field) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`Centered area color on mark "${layer.id}" must match its group field.`
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
const categorical = ["nominal", "ordinal"].includes(x.fieldType);
|
|
157
|
-
const xValues = categorical ? readNominalField(rows, x.field) : x.fieldType === "temporal"
|
|
158
|
-
? readTemporalField(rows, x.field, x.temporalUnit)
|
|
159
|
-
: readQuantitativeField(rows, x.field);
|
|
160
|
-
const yValues = readQuantitativeField(rows, y.field);
|
|
161
|
-
const groups = readNominalField(rows, group.field);
|
|
162
|
-
const groupOrder = [];
|
|
163
|
-
const positions = new Set();
|
|
164
|
-
const byGroup = new Map();
|
|
165
|
-
for (let index = 0; index < rows.length; index += 1) {
|
|
166
|
-
const key = groups[index];
|
|
167
|
-
if (!byGroup.has(key)) {
|
|
168
|
-
groupOrder.push(key);
|
|
169
|
-
byGroup.set(key, new Map());
|
|
170
|
-
}
|
|
171
|
-
const values = byGroup.get(key);
|
|
172
|
-
if (values.has(xValues[index])) {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`Centered area mark "${layer.id}" has duplicate ${group.field}/${x.field} rows.`
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
values.set(xValues[index], yValues[index]);
|
|
178
|
-
positions.add(xValues[index]);
|
|
179
|
-
}
|
|
180
|
-
const orderedPositions = categorical
|
|
181
|
-
? (options.xDomain ?? [...positions]).filter(value => positions.has(value))
|
|
182
|
-
: [...positions].sort((left, right) => left - right);
|
|
183
|
-
if (orderedPositions.length !== positions.size) throw new Error("Area categorical domain must contain every observed position.");
|
|
184
|
-
if (groupOrder.length === 0 || orderedPositions.length < 2) {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`Centered area mark "${layer.id}" requires at least two aligned positions.`
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
for (const [key, values] of byGroup) {
|
|
190
|
-
if (
|
|
191
|
-
values.size !== orderedPositions.length ||
|
|
192
|
-
orderedPositions.some(position => !values.has(position))
|
|
193
|
-
) {
|
|
194
|
-
throw new Error(
|
|
195
|
-
`Centered area series "${String(key)}" requires one aligned value at every x position.`
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const valuesBySeries = groupOrder.map(() => []);
|
|
201
|
-
for (const position of orderedPositions) {
|
|
202
|
-
const partition = groupOrder.map(key => byGroup.get(key).get(position));
|
|
203
|
-
const resolvedSegments = layoutSeriesPartition(partition, "center");
|
|
204
|
-
const segments = indexSegments(resolvedSegments);
|
|
205
|
-
let endpoint = resolvedSegments[0]?.start ?? 0;
|
|
206
|
-
for (let index = 0; index < groupOrder.length; index += 1) {
|
|
207
|
-
const segment = segments[index];
|
|
208
|
-
valuesBySeries[index].push({
|
|
209
|
-
x: position,
|
|
210
|
-
y: partition[index],
|
|
211
|
-
lower: segment?.start ?? endpoint,
|
|
212
|
-
upper: segment?.end ?? endpoint
|
|
213
|
-
});
|
|
214
|
-
if (segment !== undefined) endpoint = segment.end;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
const series = groupOrder.map((key, index) => ({
|
|
218
|
-
key: {
|
|
219
|
-
[group.field]: key,
|
|
220
|
-
...(color === undefined ? {} : { [color.field]: key })
|
|
221
|
-
},
|
|
222
|
-
values: valuesBySeries[index]
|
|
223
|
-
}));
|
|
224
|
-
return cloneAndFreeze({
|
|
225
|
-
mode: "y-center",
|
|
226
|
-
orientation: "vertical",
|
|
227
|
-
xValues: orderedPositions,
|
|
228
|
-
yValues: series.flatMap(item => item.values.flatMap(value => [
|
|
229
|
-
value.lower,
|
|
230
|
-
value.upper
|
|
231
|
-
])),
|
|
232
|
-
series
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export function deriveDensityAreaSeries(rows, layer, transform) {
|
|
237
|
-
if (layer?.mark?.type !== "area") {
|
|
238
|
-
throw new Error("Density area derivation requires a semantic area mark.");
|
|
239
|
-
}
|
|
240
|
-
if (layer.encoding?.pathOrder !== undefined) {
|
|
241
|
-
throw new Error(
|
|
242
|
-
`Density area mark "${layer.id}" does not support path order.`
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
if (transform?.type !== "density" || !Array.isArray(transform.as)) {
|
|
246
|
-
throw new Error(`Area mark "${layer.id}" requires density provenance.`);
|
|
247
|
-
}
|
|
248
|
-
if (transform.placement?.type === "category") {
|
|
249
|
-
return deriveCategoricalDensitySeries(rows, layer, transform);
|
|
250
|
-
}
|
|
251
|
-
const { x, y, group } = layer.encoding ?? {};
|
|
252
|
-
if (
|
|
253
|
-
x?.fieldType !== "quantitative" ||
|
|
254
|
-
y?.fieldType !== "quantitative"
|
|
255
|
-
) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`Density area mark "${layer.id}" requires quantitative x and y encodings.`
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
const [valueField, densityField] = transform.as;
|
|
261
|
-
const mode = x.field === valueField && y.field === densityField
|
|
262
|
-
? "y-density"
|
|
263
|
-
: x.field === densityField && y.field === valueField
|
|
264
|
-
? "x-density"
|
|
265
|
-
: undefined;
|
|
266
|
-
if (mode === undefined) {
|
|
267
|
-
throw new Error(
|
|
268
|
-
`Density area mark "${layer.id}" must encode its value and density fields.`
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
if (transform.groupBy === undefined) {
|
|
272
|
-
if (group !== undefined) {
|
|
273
|
-
throw new Error(`Ungrouped density area mark "${layer.id}" cannot encode group.`);
|
|
274
|
-
}
|
|
275
|
-
} else if (
|
|
276
|
-
group?.field !== transform.groupBy ||
|
|
277
|
-
group.fieldType !== "nominal"
|
|
278
|
-
) {
|
|
279
|
-
throw new Error(
|
|
280
|
-
`Density area mark "${layer.id}" must group by "${transform.groupBy}".`
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const xValues = readQuantitativeField(rows, x.field);
|
|
285
|
-
const yValues = readQuantitativeField(rows, y.field);
|
|
286
|
-
const groupValues = transform.groupBy === undefined
|
|
287
|
-
? rows.map(() => undefined)
|
|
288
|
-
: readNominalField(rows, transform.groupBy);
|
|
289
|
-
const groups = new Map();
|
|
290
|
-
for (let index = 0; index < rows.length; index += 1) {
|
|
291
|
-
const key = groupValues[index];
|
|
292
|
-
const series = groups.get(key) ?? {
|
|
293
|
-
key: transform.groupBy === undefined ? {} : { [transform.groupBy]: key },
|
|
294
|
-
values: []
|
|
295
|
-
};
|
|
296
|
-
series.values.push({ x: xValues[index], y: yValues[index] });
|
|
297
|
-
groups.set(key, series);
|
|
298
|
-
}
|
|
299
|
-
if (groups.size === 0) {
|
|
300
|
-
throw new Error(`Density area mark "${layer.id}" has no values.`);
|
|
301
|
-
}
|
|
302
|
-
const valueKey = mode === "y-density" ? "x" : "y";
|
|
303
|
-
const series = [...groups.values()].map(item => {
|
|
304
|
-
const values = item.values.sort((left, right) => left[valueKey] - right[valueKey]);
|
|
305
|
-
if (values.length < 2) {
|
|
306
|
-
throw new Error(
|
|
307
|
-
`Density area series on mark "${layer.id}" requires at least two points.`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
return { key: item.key, values };
|
|
311
|
-
});
|
|
312
|
-
return cloneAndFreeze({ mode, series });
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
export function layoutDensityAreaSeries(derived, layout = "overlay") {
|
|
316
|
-
validateColorLayout(layout);
|
|
317
|
-
if (layout === "group") {
|
|
318
|
-
throw new Error('Density area series do not support "group" layout.');
|
|
319
|
-
}
|
|
320
|
-
if (derived?.mode !== "y-density") {
|
|
321
|
-
if (layout === "overlay") return derived;
|
|
322
|
-
throw new Error(
|
|
323
|
-
`Area layout "${layout}" currently requires vertical density series.`
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
const sampleCount = derived.series[0]?.values.length ?? 0;
|
|
327
|
-
if (
|
|
328
|
-
sampleCount === 0 ||
|
|
329
|
-
derived.series.some(series => series.values.length !== sampleCount)
|
|
330
|
-
) {
|
|
331
|
-
throw new Error("Density area layout requires aligned non-empty samples.");
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const valuesBySeries = derived.series.map(() => []);
|
|
335
|
-
for (let sample = 0; sample < sampleCount; sample += 1) {
|
|
336
|
-
const x = derived.series[0].values[sample].x;
|
|
337
|
-
const densities = derived.series.map(series => {
|
|
338
|
-
if (series.values[sample].x !== x) {
|
|
339
|
-
throw new Error("Density area layout requires one shared sample grid.");
|
|
340
|
-
}
|
|
341
|
-
return series.values[sample].y;
|
|
342
|
-
});
|
|
343
|
-
const resolvedSegments = layoutSeriesPartition(densities, layout);
|
|
344
|
-
const segments = indexSegments(resolvedSegments);
|
|
345
|
-
let zeroThicknessEndpoint = layout === "center"
|
|
346
|
-
? resolvedSegments[0]?.start ?? 0
|
|
347
|
-
: 0;
|
|
348
|
-
for (let index = 0; index < derived.series.length; index += 1) {
|
|
349
|
-
const segment = segments[index];
|
|
350
|
-
valuesBySeries[index].push({
|
|
351
|
-
x,
|
|
352
|
-
lower: segment?.start ?? zeroThicknessEndpoint,
|
|
353
|
-
upper: segment?.end ?? zeroThicknessEndpoint
|
|
354
|
-
});
|
|
355
|
-
if (segment !== undefined && ["stack", "fill", "center"].includes(layout)) {
|
|
356
|
-
zeroThicknessEndpoint = segment.end;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return cloneAndFreeze({
|
|
362
|
-
mode: derived.mode,
|
|
363
|
-
series: derived.series.map((series, index) => ({
|
|
364
|
-
key: series.key,
|
|
365
|
-
values: valuesBySeries[index]
|
|
366
|
-
}))
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
export function layoutRawAreaGroups(groups, { layer, vertical, primary, secondary }) {
|
|
371
|
-
const mode = layer.layout?.mode ?? "overlay";
|
|
372
|
-
validateColorLayout(mode);
|
|
373
|
-
if (mode === "overlay") return;
|
|
374
|
-
if (mode === "group" || (mode === "center" && !vertical)) {
|
|
375
|
-
throw new Error(`Area layout "${mode}" is incompatible with this orientation.`);
|
|
376
|
-
}
|
|
377
|
-
const primaryDatum = Object.hasOwn(primary, "datum");
|
|
378
|
-
const secondaryDatum = Object.hasOwn(secondary, "datum");
|
|
379
|
-
if (primaryDatum === secondaryDatum || (primaryDatum ? primary.datum : secondary.datum) !== 0) {
|
|
380
|
-
throw new Error("Stacked area requires one value field and one zero datum endpoint.");
|
|
381
|
-
}
|
|
382
|
-
const position = vertical ? "x" : "y";
|
|
383
|
-
const lower = vertical ? "y" : "x", upper = `${lower}2`;
|
|
384
|
-
const measure = primaryDatum ? upper : lower;
|
|
385
|
-
const series = [...groups.values()];
|
|
386
|
-
const indices = series.map(item => {
|
|
387
|
-
const index = new Map();
|
|
388
|
-
for (const value of item.values) {
|
|
389
|
-
if (index.has(value[position])) throw new Error("Area layout requires unique group/position rows.");
|
|
390
|
-
index.set(value[position], value);
|
|
391
|
-
}
|
|
392
|
-
return index;
|
|
393
|
-
});
|
|
394
|
-
const positions = [...new Set(series.flatMap(item => item.values.map(value => value[position])))].sort((a, b) => a - b);
|
|
395
|
-
validateGeneratedItemLimit(positions.length * series.length, "Area layout cell count");
|
|
396
|
-
if (indices.some(index => index.size !== positions.length || positions.some(value => !index.has(value)))) {
|
|
397
|
-
throw new Error("Area layout requires an aligned row for every group and position.");
|
|
398
|
-
}
|
|
399
|
-
for (const position of positions) {
|
|
400
|
-
const points = indices.map(index => index.get(position));
|
|
401
|
-
if (points.some(point => point[measure] == null)) {
|
|
402
|
-
for (const point of points) { point[lower] = null; point[upper] = null; }
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
const segments = layoutSeriesPartition(points.map(point => point[measure]), mode);
|
|
406
|
-
const byIndex = new Map(segments.map(segment => [segment.index, segment]));
|
|
407
|
-
let endpoint = mode === "center" ? segments[0]?.start ?? 0 : 0;
|
|
408
|
-
points.forEach((point, index) => {
|
|
409
|
-
const segment = byIndex.get(index);
|
|
410
|
-
point[lower] = segment?.start ?? (mode === "diverging" ? 0 : endpoint);
|
|
411
|
-
point[upper] = segment?.end ?? (mode === "diverging" ? 0 : endpoint);
|
|
412
|
-
if (segment !== undefined && mode !== "diverging") endpoint = segment.end;
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
}
|
|
1
|
+
import{cloneAndFreeze as C}from"../core/immutable.js";import{readNominalField as z,readQuantitativeField as b,readTemporalField as I}from"./scales/index.js";import{layoutSeriesPartition as B,validateColorLayout as F}from"./seriesLayout.js";import{stableOrderPathValues as L}from"./pathOrder.js";import{deriveCategoricalDensitySeries as N}from"./categoricalDensity.js";import{validatePathSeriesAppearance as P}from"./pathSeries.js";import{readAreaEndpoint as j,validateAreaEndpointPair as J}from"./areaEndpoints.js";import{validateGeneratedItemLimit as K}from"../core/validation.js";function G(n){const e=[];for(const a of n)e[a.index]=a;return e}function ne(n,e,a={}){if(e?.mark?.type!=="area")throw new Error("Area series derivation requires a semantic area mark.");const{x:p,y:d,x2:l,y2:k}=e.encoding??{},u=["quantitative","temporal","nominal","ordinal"].includes(p?.fieldType)&&d?.fieldType==="quantitative"&&k?.fieldType==="quantitative"&&l===void 0,m=["quantitative","temporal","nominal","ordinal"].includes(d?.fieldType)&&p?.fieldType==="quantitative"&&l?.fieldType==="quantitative"&&k===void 0;if(u===m)throw new Error(`Area mark "${e.id}" requires exactly one quantitative x/x2 or y/y2 range and one quantitative, temporal, or categorical independent position.`);const w=P(n,e),c=u?"vertical":"horizontal",f=u?p:d,x=["nominal","ordinal"].includes(f.fieldType),y=x?z(n,f.field):f.fieldType==="temporal"?I(n,f.field,f.temporalUnit):b(n,f.field);if(Object.hasOwn(f,"datum"))throw new Error("Area independent position requires a field.");const v=u?d:p,o=u?k:l;J(v,o);const r=e.mark.missing??"error",g=j(n,v,r),O=j(n,o,r),s=x?a[u?"xDomain":"yDomain"]??[...new Set(y)]:void 0,t=new Map(s?.map((i,h)=>[i,h]));if(x&&y.some(i=>!t.has(i)))throw new Error("Area categorical domain must contain every observed position.");const E=e.encoding?.pathOrder,$=E===void 0?void 0:b(n,E.field),D=new Map;for(let i=0;i<n.length;i+=1){const h=w.map(q=>n[i][q]),M=JSON.stringify(h),T=D.get(M)??{key:Object.fromEntries(w.map((q,V)=>[q,h[V]])),values:[]};T.values.push(u?{x:y[i],y:g[i],y2:O[i],...r==="break"?{sourceIndex:i}:{},...$===void 0?{}:{pathOrder:$[i]}}:{x:g[i],x2:O[i],...r==="break"?{sourceIndex:i}:{},y:y[i],...$===void 0?{}:{pathOrder:$[i]}}),D.set(M,T)}if(D.size===0)throw new Error(`Area mark "${e.id}" has no values.`);Q(D,{layer:e,vertical:u,primary:v,secondary:o});const A=[...D.values()].flatMap(i=>{const h=u?"x":"y",M=E===void 0?i.values.sort((T,q)=>x?t.get(T[h])-t.get(q[h]):T[h]-q[h]):L(i.values.map(({pathOrder:T,...q})=>q),i.values.map(T=>T.pathOrder),E.order);if(r==="break"){const T=[];let q=[];const V=()=>{q.length>=2&&T.push({key:i.key,sourceIndices:q.map(S=>S.sourceIndex),values:q.map(({sourceIndex:S,...U})=>U)}),q=[]};for(const S of M)(u?S.y==null||S.y2==null:S.x==null||S.x2==null)?V():q.push(S);return V(),T}if(M.length<2)throw new Error(`Area series on mark "${e.id}" requires at least two points.`);return{key:i.key,values:M}});if(A.length===0)throw new Error(`Area mark "${e.id}" has no valid segment with at least two points.`);return C({orientation:c,xValues:A.flatMap(i=>i.values.flatMap(h=>u?[h.x]:[h.x,h.x2])),yValues:A.flatMap(i=>i.values.flatMap(h=>u?[h.y,h.y2]:[h.y])),series:A})}function te(n,e,a={}){if(e?.mark?.type!=="area")throw new Error("Centered area series derivation requires an area mark.");const{x:p,y:d,x2:l,y2:k,group:u,color:m}=e.encoding??{};if(!["quantitative","temporal","nominal","ordinal"].includes(p?.fieldType)||d?.fieldType!=="quantitative"||d?.stack!=="center"||l!==void 0||k!==void 0)throw new Error(`Centered area mark "${e.id}" requires one x field and one quantitative y field without ranged endpoints.`);if(u?.fieldType!=="nominal")throw new Error(`Centered area mark "${e.id}" requires a nominal group encoding.`);if(m!==void 0&&m.fieldType!=="quantitative"&&m.field!==u.field)throw new Error(`Centered area color on mark "${e.id}" must match its group field.`);P(n,e);const w=["nominal","ordinal"].includes(p.fieldType),c=w?z(n,p.field):p.fieldType==="temporal"?I(n,p.field,p.temporalUnit):b(n,p.field),f=b(n,d.field),x=z(n,u.field),y=[],v=new Set,o=new Map;for(let s=0;s<n.length;s+=1){const t=x[s];o.has(t)||(y.push(t),o.set(t,new Map));const E=o.get(t);if(E.has(c[s]))throw new Error(`Centered area mark "${e.id}" has duplicate ${u.field}/${p.field} rows.`);E.set(c[s],f[s]),v.add(c[s])}const r=w?(a.xDomain??[...v]).filter(s=>v.has(s)):[...v].sort((s,t)=>s-t);if(r.length!==v.size)throw new Error("Area categorical domain must contain every observed position.");if(y.length===0||r.length<2)throw new Error(`Centered area mark "${e.id}" requires at least two aligned positions.`);for(const[s,t]of o)if(t.size!==r.length||r.some(E=>!t.has(E)))throw new Error(`Centered area series "${String(s)}" requires one aligned value at every x position.`);const g=y.map(()=>[]);for(const s of r){const t=y.map(A=>o.get(A).get(s)),E=B(t,"center"),$=G(E);let D=E[0]?.start??0;for(let A=0;A<y.length;A+=1){const i=$[A];g[A].push({x:s,y:t[A],lower:i?.start??D,upper:i?.end??D}),i!==void 0&&(D=i.end)}}const O=y.map((s,t)=>({key:{[u.field]:s,...m===void 0||m.fieldType==="quantitative"?{}:{[m.field]:s}},values:g[t]}));return C({mode:"y-center",orientation:"vertical",xValues:r,yValues:O.flatMap(s=>s.values.flatMap(t=>[t.lower,t.upper])),series:O})}function re(n,e,a){if(e?.mark?.type!=="area")throw new Error("Density area derivation requires a semantic area mark.");if(e.encoding?.pathOrder!==void 0)throw new Error(`Density area mark "${e.id}" does not support path order.`);if(a?.type!=="density"||!Array.isArray(a.as))throw new Error(`Area mark "${e.id}" requires density provenance.`);if(a.placement?.type==="category")return N(n,e,a);const{x:p,y:d,group:l}=e.encoding??{};if(p?.fieldType!=="quantitative"||d?.fieldType!=="quantitative")throw new Error(`Density area mark "${e.id}" requires quantitative x and y encodings.`);const[k,u]=a.as,m=p.field===k&&d.field===u?"y-density":p.field===u&&d.field===k?"x-density":void 0;if(m===void 0)throw new Error(`Density area mark "${e.id}" must encode its value and density fields.`);if(a.groupBy===void 0){if(l!==void 0)throw new Error(`Ungrouped density area mark "${e.id}" cannot encode group.`)}else if(l?.field!==a.groupBy||l.fieldType!=="nominal")throw new Error(`Density area mark "${e.id}" must group by "${a.groupBy}".`);const w=b(n,p.field),c=b(n,d.field),f=a.groupBy===void 0?n.map(()=>{}):z(n,a.groupBy),x=new Map;for(let o=0;o<n.length;o+=1){const r=f[o],g=x.get(r)??{key:a.groupBy===void 0?{}:{[a.groupBy]:r},values:[]};g.values.push({x:w[o],y:c[o]}),x.set(r,g)}if(x.size===0)throw new Error(`Density area mark "${e.id}" has no values.`);const y=m==="y-density"?"x":"y",v=[...x.values()].map(o=>{const r=o.values.sort((g,O)=>g[y]-O[y]);if(r.length<2)throw new Error(`Density area series on mark "${e.id}" requires at least two points.`);return{key:o.key,values:r}});return C({mode:m,series:v})}function ie(n,e="overlay"){if(F(e),e==="group")throw new Error('Density area series do not support "group" layout.');if(n?.mode!=="y-density"){if(e==="overlay")return n;throw new Error(`Area layout "${e}" currently requires vertical density series.`)}const a=n.series[0]?.values.length??0;if(a===0||n.series.some(d=>d.values.length!==a))throw new Error("Density area layout requires aligned non-empty samples.");const p=n.series.map(()=>[]);for(let d=0;d<a;d+=1){const l=n.series[0].values[d].x,k=n.series.map(c=>{if(c.values[d].x!==l)throw new Error("Density area layout requires one shared sample grid.");return c.values[d].y}),u=B(k,e),m=G(u);let w=e==="center"?u[0]?.start??0:0;for(let c=0;c<n.series.length;c+=1){const f=m[c];p[c].push({x:l,lower:f?.start??w,upper:f?.end??w}),f!==void 0&&["stack","fill","center"].includes(e)&&(w=f.end)}}return C({mode:n.mode,series:n.series.map((d,l)=>({key:d.key,values:p[l]}))})}function Q(n,{layer:e,vertical:a,primary:p,secondary:d}){const l=e.layout?.mode??"overlay";if(F(l),l==="overlay")return;if(l==="group"||l==="center"&&!a)throw new Error(`Area layout "${l}" is incompatible with this orientation.`);const k=Object.hasOwn(p,"datum"),u=Object.hasOwn(d,"datum");if(k===u||(k?p.datum:d.datum)!==0)throw new Error("Stacked area requires one value field and one zero datum endpoint.");const m=a?"x":"y",w=a?"y":"x",c=`${w}2`,f=k?c:w,x=[...n.values()],y=x.map(o=>{const r=new Map;for(const g of o.values){if(r.has(g[m]))throw new Error("Area layout requires unique group/position rows.");r.set(g[m],g)}return r}),v=[...new Set(x.flatMap(o=>o.values.map(r=>r[m])))].sort((o,r)=>o-r);if(K(v.length*x.length,"Area layout cell count"),y.some(o=>o.size!==v.length||v.some(r=>!o.has(r))))throw new Error("Area layout requires an aligned row for every group and position.");for(const o of v){const r=y.map(t=>t.get(o));if(r.some(t=>t[f]==null)){for(const t of r)t[w]=null,t[c]=null;continue}const g=B(r.map(t=>t[f]),l),O=new Map(g.map(t=>[t.index,t]));let s=l==="center"?g[0]?.start??0:0;r.forEach((t,E)=>{const $=O.get(E);t[w]=$?.start??(l==="diverging"?0:s),t[c]=$?.end??(l==="diverging"?0:s),$!==void 0&&l!=="diverging"&&(s=$.end)})}}export{ne as deriveAreaSeries,te as deriveCenteredAreaSeries,re as deriveDensityAreaSeries,ie as layoutDensityAreaSeries,Q as layoutRawAreaGroups};
|