@perspective-dev/viewer-charts 5.0.0 → 5.2.0
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/dist/cdn/perspective-viewer-charts.js +2 -2
- package/dist/cdn/perspective-viewer-charts.js.map +3 -3
- package/dist/esm/charts/series/glyphs/draw-areas.d.ts +7 -2
- package/dist/esm/charts/series/glyphs/draw-lines.d.ts +7 -2
- package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +7 -4
- package/dist/esm/charts/series/series-interact.d.ts +3 -2
- package/dist/esm/charts/series/series.d.ts +51 -0
- package/dist/esm/perspective-viewer-charts.js +2 -2
- package/dist/esm/perspective-viewer-charts.js.map +3 -3
- package/dist/esm/plugin/charts.d.ts +24 -0
- package/dist/esm/plugin/plugin.d.ts +9 -0
- package/dist/esm/transport/protocol.d.ts +8 -0
- package/dist/esm/transport/renderer-transport.d.ts +1 -0
- package/package.json +1 -1
- package/src/ts/charts/series/glyphs/draw-areas.ts +12 -1
- package/src/ts/charts/series/glyphs/draw-lines.ts +12 -1
- package/src/ts/charts/series/glyphs/draw-scatter.ts +17 -5
- package/src/ts/charts/series/series-interact.ts +137 -78
- package/src/ts/charts/series/series-render.ts +189 -102
- package/src/ts/charts/series/series.ts +113 -0
- package/src/ts/plugin/charts.ts +69 -3
- package/src/ts/plugin/plugin.ts +17 -1
- package/src/ts/transport/protocol.ts +9 -0
- package/src/ts/transport/renderer-transport.ts +13 -0
- package/src/ts/worker/renderer.worker.ts +3 -0
|
@@ -26,6 +26,30 @@ export interface ChartTypeConfig {
|
|
|
26
26
|
* sunburst). See {@link PluginConfig} for field semantics.
|
|
27
27
|
*/
|
|
28
28
|
applicable_plugin_fields: readonly PluginConfigField[];
|
|
29
|
+
/**
|
|
30
|
+
* What `group_by` DRAWS in this chart, e.g. `"X Axis"` for the
|
|
31
|
+
* Y-series charts. Omitted where the field has no visual role of
|
|
32
|
+
* its own and is a plain aggregation key — the X/Y and map charts,
|
|
33
|
+
* whose axes both come from `columns`.
|
|
34
|
+
*
|
|
35
|
+
* The `group_by` counterpart of `initial.names`: one declaration
|
|
36
|
+
* says what every view field draws, so the settings UI's labels and
|
|
37
|
+
* the agent's `list_plugins` contract read the same source instead
|
|
38
|
+
* of restating the mapping.
|
|
39
|
+
*/
|
|
40
|
+
group_by_role?: string;
|
|
41
|
+
/** What `split_by` DRAWS in this chart. See {@link group_by_role}. */
|
|
42
|
+
split_by_role?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Set on the charts that CONNECT their points in row order, so the
|
|
45
|
+
* view's row order shows up in the drawing: without a `sort` the
|
|
46
|
+
* line follows the table's natural order, which reads as a tangle
|
|
47
|
+
* unless the rows already arrive ordered along the X axis. The point
|
|
48
|
+
* charts (scatter, density) are unaffected, and a pre-ordered table
|
|
49
|
+
* needs no `sort` — which is why this is declared per chart rather
|
|
50
|
+
* than inferred from an empty `sort`.
|
|
51
|
+
*/
|
|
52
|
+
connects_row_order?: boolean;
|
|
29
53
|
/**
|
|
30
54
|
* Per-chart-type overrides for `DEFAULT_PLUGIN_CONFIG`. Used when a
|
|
31
55
|
* field's sensible default differs by chart family — currently
|
|
@@ -25,6 +25,15 @@ export declare class HTMLPerspectiveViewerWebGLPluginElement extends HTMLElement
|
|
|
25
25
|
* resolved values in the `InitMsg`.
|
|
26
26
|
*/
|
|
27
27
|
private _pluginConfigStore;
|
|
28
|
+
/**
|
|
29
|
+
* Per-column config (`alt_axis`, `chart_type`, formats, …), held on
|
|
30
|
+
* the element for the same reason as `_pluginConfigStore`: the host
|
|
31
|
+
* calls `restore()` BEFORE the first draw builds the renderer, so
|
|
32
|
+
* forwarding only to a live renderer silently drops the initial
|
|
33
|
+
* `columns_config` — `_buildRenderer` ships it in the `InitMsg`
|
|
34
|
+
* instead.
|
|
35
|
+
*/
|
|
36
|
+
private _columnsConfig;
|
|
28
37
|
private get _pluginConfig();
|
|
29
38
|
private set _pluginConfig(value);
|
|
30
39
|
private _effectiveDefaults;
|
|
@@ -104,6 +104,14 @@ export interface InitMsg {
|
|
|
104
104
|
* a `setPluginConfig` control msg.
|
|
105
105
|
*/
|
|
106
106
|
pluginConfig: PluginConfig;
|
|
107
|
+
/**
|
|
108
|
+
* Initial per-column config (`alt_axis`, `chart_type`, formats, …).
|
|
109
|
+
* Seeds the chart impl before the first `loadAndRender` — the host
|
|
110
|
+
* calls `plugin.restore` before the renderer exists, so without
|
|
111
|
+
* this the initial `columns_config` never reaches the worker.
|
|
112
|
+
* Later changes arrive as `setColumnsConfig` control msgs.
|
|
113
|
+
*/
|
|
114
|
+
columnsConfig?: Record<string, any>;
|
|
107
115
|
defaultChartType?: string;
|
|
108
116
|
/**
|
|
109
117
|
* Pre-resolved CSS-variable theme snapshot from the host.
|
|
@@ -133,6 +133,7 @@ export declare class RendererTransport {
|
|
|
133
133
|
chrome: HTMLCanvasElement;
|
|
134
134
|
facetConfig: FacetConfig;
|
|
135
135
|
pluginConfig: PluginConfig;
|
|
136
|
+
columnsConfig?: Record<string, any>;
|
|
136
137
|
defaultChartType?: string;
|
|
137
138
|
renderBlitMode: "blit" | "direct";
|
|
138
139
|
}): Promise<void>;
|
package/package.json
CHANGED
|
@@ -188,7 +188,9 @@ export class AreaGlyph {
|
|
|
188
188
|
/**
|
|
189
189
|
* Bind persistent strip buffers and dispatch one TRIANGLE_STRIP per
|
|
190
190
|
* series-run. Skips hidden series. `splitFilter` (faceted frames)
|
|
191
|
-
* draws only the series whose `splitIdx` matches
|
|
191
|
+
* draws only the series whose `splitIdx` matches; `aggRange` (mixed
|
|
192
|
+
* glyph-run frames) only those whose `aggIdx` lies in the inclusive
|
|
193
|
+
* run span.
|
|
192
194
|
*/
|
|
193
195
|
draw(
|
|
194
196
|
chart: SeriesChart,
|
|
@@ -198,6 +200,7 @@ export class AreaGlyph {
|
|
|
198
200
|
projRight: Float32Array,
|
|
199
201
|
opacity: number,
|
|
200
202
|
splitFilter?: number,
|
|
203
|
+
aggRange?: { start: number; end: number },
|
|
201
204
|
): void {
|
|
202
205
|
const buf = this._buffers;
|
|
203
206
|
const cache = this._program;
|
|
@@ -221,6 +224,14 @@ export class AreaGlyph {
|
|
|
221
224
|
continue;
|
|
222
225
|
}
|
|
223
226
|
|
|
227
|
+
const aggIdx = chart._series[s.seriesId].aggIdx;
|
|
228
|
+
if (
|
|
229
|
+
aggRange !== undefined &&
|
|
230
|
+
(aggIdx < aggRange.start || aggIdx > aggRange.end)
|
|
231
|
+
) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
224
235
|
gl.uniformMatrix4fv(
|
|
225
236
|
cache.u_projection,
|
|
226
237
|
false,
|
|
@@ -272,7 +272,9 @@ export class LineGlyph {
|
|
|
272
272
|
* per series. Skips hidden series via `_hiddenSeries`. Gap /
|
|
273
273
|
* transparency rendering is governed by `u_interp_alpha`, set per
|
|
274
274
|
* series. `splitFilter` (faceted frames) draws only the series
|
|
275
|
-
* whose `splitIdx` matches — one call per facet.
|
|
275
|
+
* whose `splitIdx` matches — one call per facet. `aggRange` (mixed
|
|
276
|
+
* glyph-run frames) draws only the series whose `aggIdx` lies in
|
|
277
|
+
* the inclusive run span.
|
|
276
278
|
*/
|
|
277
279
|
draw(
|
|
278
280
|
chart: SeriesChart,
|
|
@@ -281,6 +283,7 @@ export class LineGlyph {
|
|
|
281
283
|
projLeft: Float32Array,
|
|
282
284
|
projRight: Float32Array,
|
|
283
285
|
splitFilter?: number,
|
|
286
|
+
aggRange?: { start: number; end: number },
|
|
284
287
|
): void {
|
|
285
288
|
const buf = this._buffers;
|
|
286
289
|
const cache = this._program;
|
|
@@ -319,6 +322,14 @@ export class LineGlyph {
|
|
|
319
322
|
continue;
|
|
320
323
|
}
|
|
321
324
|
|
|
325
|
+
const aggIdx = chart._series[s.seriesId].aggIdx;
|
|
326
|
+
if (
|
|
327
|
+
aggRange !== undefined &&
|
|
328
|
+
(aggIdx < aggRange.start || aggIdx > aggRange.end)
|
|
329
|
+
) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
|
|
322
333
|
gl.uniformMatrix4fv(
|
|
323
334
|
cache.u_projection,
|
|
324
335
|
false,
|
|
@@ -268,9 +268,9 @@ export class ScatterGlyph {
|
|
|
268
268
|
/**
|
|
269
269
|
* Bind the persistent left/right buffers and issue up to two draw
|
|
270
270
|
* calls. No per-frame allocations or buffer uploads. `splitFilter`
|
|
271
|
-
* (faceted frames)
|
|
272
|
-
* bucket sub-range —
|
|
273
|
-
*
|
|
271
|
+
* (faceted frames) and/or `aggRange` (mixed glyph-run frames)
|
|
272
|
+
* instead draw each matching series' contiguous bucket sub-range —
|
|
273
|
+
* one `drawArrays(POINTS, first, count)` per matching series.
|
|
274
274
|
*/
|
|
275
275
|
draw(
|
|
276
276
|
chart: SeriesChart,
|
|
@@ -279,6 +279,7 @@ export class ScatterGlyph {
|
|
|
279
279
|
projLeft: Float32Array,
|
|
280
280
|
projRight: Float32Array,
|
|
281
281
|
splitFilter?: number,
|
|
282
|
+
aggRange?: { start: number; end: number },
|
|
282
283
|
): void {
|
|
283
284
|
const buf = this._buffers;
|
|
284
285
|
const cache = this._program;
|
|
@@ -297,9 +298,20 @@ export class ScatterGlyph {
|
|
|
297
298
|
chart._pluginConfig.point_size_px * dpr,
|
|
298
299
|
);
|
|
299
300
|
|
|
300
|
-
if (splitFilter !== undefined) {
|
|
301
|
+
if (splitFilter !== undefined || aggRange !== undefined) {
|
|
301
302
|
for (const r of buf.seriesRanges) {
|
|
302
|
-
if (
|
|
303
|
+
if (
|
|
304
|
+
splitFilter !== undefined &&
|
|
305
|
+
chart._series[r.seriesId].splitIdx !== splitFilter
|
|
306
|
+
) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const aggIdx = chart._series[r.seriesId].aggIdx;
|
|
311
|
+
if (
|
|
312
|
+
aggRange !== undefined &&
|
|
313
|
+
(aggIdx < aggRange.start || aggIdx > aggRange.end)
|
|
314
|
+
) {
|
|
303
315
|
continue;
|
|
304
316
|
}
|
|
305
317
|
|
|
@@ -47,8 +47,9 @@ export function getHoveredBar(chart: SeriesChart): SeriesChartRecord | null {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Handle mouse-move across all glyph types. Tests
|
|
51
|
-
* so top glyphs win
|
|
50
|
+
* Handle mouse-move across all glyph types. Tests glyph runs in reverse
|
|
51
|
+
* paint order so top glyphs win — paint order is the `columns`
|
|
52
|
+
* declaration order (see `_glyphRuns` / `drawGlyphRuns`).
|
|
52
53
|
* Updates `_hoveredBarIdx` or `_hoveredSample` and re-renders on change.
|
|
53
54
|
*
|
|
54
55
|
* Faceted frames first resolve the cell under the cursor: that cell's
|
|
@@ -153,96 +154,131 @@ export function handleBarHover(
|
|
|
153
154
|
let nextBarIdx = -1;
|
|
154
155
|
let nextSample: SeriesChartRecord | null = null;
|
|
155
156
|
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
157
|
+
// Test topmost-first: the REVERSE of the paint order, which is the
|
|
158
|
+
// `columns` declaration order (`_glyphRuns`, ascending `aggIdx` —
|
|
159
|
+
// see `drawGlyphRuns`). A single-run chart passes no `aggRange`,
|
|
160
|
+
// reducing to the legacy whole-type scan.
|
|
161
|
+
const runs = chart._glyphRuns;
|
|
162
|
+
const single = runs.length <= 1;
|
|
163
|
+
for (let r = runs.length - 1; r >= 0; r--) {
|
|
164
|
+
const run = runs[r];
|
|
165
|
+
const aggRange = single
|
|
166
|
+
? undefined
|
|
167
|
+
: { start: run.aggStart, end: run.aggEnd };
|
|
168
|
+
switch (run.chartType) {
|
|
169
|
+
case "scatter":
|
|
170
|
+
case "line":
|
|
171
|
+
nextSample = hitTestPoints(
|
|
172
|
+
chart,
|
|
173
|
+
run.chartType,
|
|
174
|
+
dataX,
|
|
175
|
+
dataYLeft,
|
|
176
|
+
dataYRight,
|
|
177
|
+
pxPerDataX,
|
|
178
|
+
pxPerDataYLeft,
|
|
179
|
+
pxPerDataYRight,
|
|
180
|
+
splitFilter,
|
|
181
|
+
aggRange,
|
|
182
|
+
);
|
|
183
|
+
break;
|
|
184
|
+
case "bar":
|
|
185
|
+
nextBarIdx = hitTestBars(
|
|
186
|
+
chart,
|
|
187
|
+
dataX,
|
|
188
|
+
dataYLeft,
|
|
189
|
+
dataYRight,
|
|
190
|
+
splitFilter,
|
|
191
|
+
aggRange,
|
|
192
|
+
);
|
|
193
|
+
break;
|
|
194
|
+
case "area": {
|
|
195
|
+
const areaHit = hitTestAreas(
|
|
196
|
+
chart,
|
|
197
|
+
dataX,
|
|
198
|
+
dataYLeft,
|
|
199
|
+
dataYRight,
|
|
200
|
+
splitFilter,
|
|
201
|
+
aggRange,
|
|
202
|
+
);
|
|
203
|
+
if (areaHit) {
|
|
204
|
+
if (areaHit.idx >= 0) {
|
|
205
|
+
nextBarIdx = areaHit.idx;
|
|
206
|
+
} else {
|
|
207
|
+
nextSample = areaHit.bar;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
168
210
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
dataYRight,
|
|
177
|
-
pxPerDataX,
|
|
178
|
-
pxPerDataYLeft,
|
|
179
|
-
pxPerDataYRight,
|
|
180
|
-
splitFilter,
|
|
181
|
-
);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (nextBarIdx >= 0 || nextSample) {
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
182
218
|
}
|
|
183
219
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const bars = chart._bars;
|
|
187
|
-
const ct = bars.chartType;
|
|
188
|
-
const sid = bars.seriesId;
|
|
189
|
-
const xC = bars.xCenter;
|
|
190
|
-
const hw = bars.halfWidth;
|
|
191
|
-
const by0 = bars.y0;
|
|
192
|
-
const by1 = bars.y1;
|
|
193
|
-
const ax = bars.axis;
|
|
194
|
-
const hidden = chart._hiddenSeries;
|
|
195
|
-
const P = chart._splitPrefixes.length;
|
|
196
|
-
for (let i = 0; i < bars.count; i++) {
|
|
197
|
-
if (ct[i] !== BAR_TYPE_BAR) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
220
|
+
applyHover(chart, nextBarIdx, nextSample);
|
|
221
|
+
}
|
|
200
222
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Rect-intersect hit-test over the bar-typed `_bars` records. Returns
|
|
225
|
+
* the record index, or `-1`.
|
|
226
|
+
*/
|
|
227
|
+
function hitTestBars(
|
|
228
|
+
chart: SeriesChart,
|
|
229
|
+
dataX: number,
|
|
230
|
+
dataYLeft: number,
|
|
231
|
+
dataYRight: number,
|
|
232
|
+
splitFilter?: number,
|
|
233
|
+
aggRange?: { start: number; end: number },
|
|
234
|
+
): number {
|
|
235
|
+
const bars = chart._bars;
|
|
236
|
+
const ct = bars.chartType;
|
|
237
|
+
const sid = bars.seriesId;
|
|
238
|
+
const xC = bars.xCenter;
|
|
239
|
+
const hw = bars.halfWidth;
|
|
240
|
+
const by0 = bars.y0;
|
|
241
|
+
const by1 = bars.y1;
|
|
242
|
+
const ax = bars.axis;
|
|
243
|
+
const hidden = chart._hiddenSeries;
|
|
244
|
+
const P = Math.max(1, chart._splitPrefixes.length);
|
|
245
|
+
for (let i = 0; i < bars.count; i++) {
|
|
246
|
+
if (ct[i] !== BAR_TYPE_BAR) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
204
249
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
250
|
+
if (hidden.has(sid[i])) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
208
253
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
254
|
+
if (splitFilter !== undefined && sid[i] % P !== splitFilter) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (aggRange !== undefined) {
|
|
259
|
+
const aggIdx = Math.floor(sid[i] / P);
|
|
260
|
+
if (aggIdx < aggRange.start || aggIdx > aggRange.end) {
|
|
212
261
|
continue;
|
|
213
262
|
}
|
|
263
|
+
}
|
|
214
264
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const hi = y0 < y1 ? y1 : y0;
|
|
220
|
-
if (dy >= lo && dy <= hi) {
|
|
221
|
-
nextBarIdx = i;
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
265
|
+
const xc = xC[i];
|
|
266
|
+
const halfW = hw[i];
|
|
267
|
+
if (dataX < xc - halfW || dataX > xc + halfW) {
|
|
268
|
+
continue;
|
|
224
269
|
}
|
|
225
|
-
}
|
|
226
270
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
splitFilter,
|
|
235
|
-
);
|
|
236
|
-
if (areaHit) {
|
|
237
|
-
if (areaHit.idx >= 0) {
|
|
238
|
-
nextBarIdx = areaHit.idx;
|
|
239
|
-
} else {
|
|
240
|
-
nextSample = areaHit.bar;
|
|
241
|
-
}
|
|
271
|
+
const dy = ax[i] === 0 ? dataYLeft : dataYRight;
|
|
272
|
+
const y0 = by0[i];
|
|
273
|
+
const y1 = by1[i];
|
|
274
|
+
const lo = y0 < y1 ? y0 : y1;
|
|
275
|
+
const hi = y0 < y1 ? y1 : y0;
|
|
276
|
+
if (dy >= lo && dy <= hi) {
|
|
277
|
+
return i;
|
|
242
278
|
}
|
|
243
279
|
}
|
|
244
280
|
|
|
245
|
-
|
|
281
|
+
return -1;
|
|
246
282
|
}
|
|
247
283
|
|
|
248
284
|
function hitTestPoints(
|
|
@@ -255,6 +291,7 @@ function hitTestPoints(
|
|
|
255
291
|
pxPerDataYLeft: number,
|
|
256
292
|
pxPerDataYRight: number,
|
|
257
293
|
splitFilter?: number,
|
|
294
|
+
aggRange?: { start: number; end: number },
|
|
258
295
|
): SeriesChartRecord | null {
|
|
259
296
|
const N = chart._numCategories;
|
|
260
297
|
const S = chart._series.length;
|
|
@@ -283,6 +320,13 @@ function hitTestPoints(
|
|
|
283
320
|
continue;
|
|
284
321
|
}
|
|
285
322
|
|
|
323
|
+
if (
|
|
324
|
+
aggRange !== undefined &&
|
|
325
|
+
(s.aggIdx < aggRange.start || s.aggIdx > aggRange.end)
|
|
326
|
+
) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
286
330
|
const dataY = s.axis === 1 ? dataYRight : dataYLeft;
|
|
287
331
|
const pyPerData = s.axis === 1 ? pxPerDataYRight : pxPerDataYLeft;
|
|
288
332
|
|
|
@@ -340,6 +384,7 @@ function hitTestAreas(
|
|
|
340
384
|
dataYLeft: number,
|
|
341
385
|
dataYRight: number,
|
|
342
386
|
splitFilter?: number,
|
|
387
|
+
aggRange?: { start: number; end: number },
|
|
343
388
|
): { idx: number; bar: SeriesChartRecord | null } | null {
|
|
344
389
|
// Closest category to the mouse; an area covers every [cat - 0.5, cat + 0.5]
|
|
345
390
|
// slot, so use `round(dataX)` as the candidate index.
|
|
@@ -382,6 +427,13 @@ function hitTestAreas(
|
|
|
382
427
|
continue;
|
|
383
428
|
}
|
|
384
429
|
|
|
430
|
+
if (aggRange !== undefined) {
|
|
431
|
+
const aggIdx = Math.floor(sid[i] / Math.max(1, P));
|
|
432
|
+
if (aggIdx < aggRange.start || aggIdx > aggRange.end) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
385
437
|
const dy = ax[i] === 0 ? dataYLeft : dataYRight;
|
|
386
438
|
const y0 = by0[i];
|
|
387
439
|
const y1 = by1[i];
|
|
@@ -406,6 +458,13 @@ function hitTestAreas(
|
|
|
406
458
|
continue;
|
|
407
459
|
}
|
|
408
460
|
|
|
461
|
+
if (
|
|
462
|
+
aggRange !== undefined &&
|
|
463
|
+
(s.aggIdx < aggRange.start || s.aggIdx > aggRange.end)
|
|
464
|
+
) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
|
|
409
468
|
const idx = cat * S + s.seriesId;
|
|
410
469
|
if (!((valid[idx >> 3] >> (idx & 7)) & 1)) {
|
|
411
470
|
continue;
|