ggaction 0.0.19 → 0.0.20

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.
@@ -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" &&
@@ -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 { cloneAndFreeze } from "../core/immutable.js";
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};