@genome-spy/core 0.41.0 → 0.42.1
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/bundle/index--cKb-dKG.js +615 -0
- package/dist/bundle/{index-gn8bhQ8w.js → index-d7k3kkin.js} +365 -366
- package/dist/bundle/index.es.js +4225 -4040
- package/dist/bundle/index.js +122 -79
- package/dist/schema.json +58 -6
- package/dist/src/data/sources/dynamic/axisGenomeSource.js +1 -1
- package/dist/src/data/sources/dynamic/axisTickSource.js +3 -3
- package/dist/src/data/sources/dynamic/bamSource.d.ts +3 -21
- package/dist/src/data/sources/dynamic/bamSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/bamSource.js +38 -55
- package/dist/src/data/sources/dynamic/bigBedSource.d.ts +2 -38
- package/dist/src/data/sources/dynamic/bigBedSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/bigBedSource.js +14 -71
- package/dist/src/data/sources/dynamic/bigWigSource.d.ts +4 -42
- package/dist/src/data/sources/dynamic/bigWigSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/bigWigSource.js +23 -60
- package/dist/src/data/sources/dynamic/gff3Source.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/gff3Source.js +1 -0
- package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts +2 -20
- package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/indexedFastaSource.js +23 -41
- package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts +23 -4
- package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/singleAxisLazySource.js +29 -4
- package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts +60 -0
- package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts.map +1 -0
- package/dist/src/data/sources/dynamic/singleAxisWindowedSource.js +152 -0
- package/dist/src/data/sources/dynamic/tabixSource.d.ts +6 -40
- package/dist/src/data/sources/dynamic/tabixSource.d.ts.map +1 -1
- package/dist/src/data/sources/dynamic/tabixSource.js +29 -78
- package/dist/src/data/transforms/regexFold.d.ts.map +1 -1
- package/dist/src/data/transforms/regexFold.js +8 -0
- package/dist/src/data/transforms/regexFold.test.js +28 -0
- package/dist/src/encoder/encoder.d.ts +6 -1
- package/dist/src/encoder/encoder.d.ts.map +1 -1
- package/dist/src/encoder/encoder.js +10 -0
- package/dist/src/genomeSpy.d.ts +14 -0
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +114 -8
- package/dist/src/gl/arrayBuilder.js +1 -1
- package/dist/src/gl/colorUtils.d.ts.map +1 -0
- package/dist/src/{scale → gl}/colorUtils.js +1 -1
- package/dist/src/gl/dataToVertices.d.ts +1 -9
- package/dist/src/gl/dataToVertices.d.ts.map +1 -1
- package/dist/src/gl/dataToVertices.js +33 -73
- package/dist/src/{scale → gl}/glslScaleGenerator.d.ts +23 -1
- package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -0
- package/dist/src/{scale → gl}/glslScaleGenerator.js +59 -8
- package/dist/src/gl/webGLHelper.d.ts +6 -21
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +8 -39
- package/dist/src/img/90-ring-with-bg.svg +1 -0
- package/dist/src/img/README.md +5 -0
- package/dist/src/marks/link.d.ts +7 -0
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +74 -39
- package/dist/src/marks/mark.d.ts +2 -1
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +31 -2
- package/dist/src/marks/{pointMark.d.ts → point.d.ts} +1 -1
- package/dist/src/marks/point.d.ts.map +1 -0
- package/dist/src/marks/{pointMark.js → point.js} +3 -3
- package/dist/src/marks/{rectMark.d.ts → rect.d.ts} +1 -1
- package/dist/src/marks/rect.d.ts.map +1 -0
- package/dist/src/marks/{rectMark.js → rect.js} +2 -3
- package/dist/src/marks/rect.vertex.glsl.js +2 -0
- package/dist/src/marks/rule.js +3 -3
- package/dist/src/marks/text.d.ts.map +1 -1
- package/dist/src/marks/text.js +19 -20
- package/dist/src/spec/data.d.ts +28 -13
- package/dist/src/spec/mark.d.ts +0 -8
- package/dist/src/styles/genome-spy.css.d.ts +1 -1
- package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
- package/dist/src/styles/genome-spy.css.js +33 -4
- package/dist/src/styles/genome-spy.scss +40 -4
- package/dist/src/types/viewContext.d.ts +9 -0
- package/dist/src/utils/binnedIndex.d.ts +2 -0
- package/dist/src/utils/binnedIndex.d.ts.map +1 -1
- package/dist/src/utils/binnedIndex.js +59 -10
- package/dist/src/utils/binnedIndex.test.js +46 -0
- package/dist/src/utils/indexer.d.ts.map +1 -1
- package/dist/src/utils/indexer.js +10 -1
- package/dist/src/utils/indexer.test.js +2 -0
- package/dist/src/view/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView.js +2 -0
- package/dist/src/view/layerView.d.ts.map +1 -1
- package/dist/src/view/layerView.js +2 -0
- package/dist/src/view/unitView.d.ts +1 -7
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +4 -11
- package/dist/src/view/view.d.ts +6 -0
- package/dist/src/view/view.d.ts.map +1 -1
- package/dist/src/view/view.js +11 -0
- package/dist/src/view/view.test.js +1 -1
- package/package.json +3 -3
- package/dist/bundle/index-Cbz74kpR.js +0 -638
- package/dist/src/data/sources/dynamic/windowedMixin.d.ts +0 -32
- package/dist/src/data/sources/dynamic/windowedMixin.d.ts.map +0 -1
- package/dist/src/data/sources/dynamic/windowedMixin.js +0 -53
- package/dist/src/gl/rect.vertex.glsl.js +0 -2
- package/dist/src/marks/pointMark.d.ts.map +0 -1
- package/dist/src/marks/rectMark.d.ts.map +0 -1
- package/dist/src/scale/colorUtils.d.ts.map +0 -1
- package/dist/src/scale/glslScaleGenerator.d.ts.map +0 -1
- /package/dist/src/{scale → gl}/colorUtils.d.ts +0 -0
- /package/dist/src/{gl → marks}/link.fragment.glsl.js +0 -0
- /package/dist/src/{gl → marks}/link.vertex.glsl.js +0 -0
- /package/dist/src/{gl → marks}/point.common.glsl.js +0 -0
- /package/dist/src/{gl → marks}/point.fragment.glsl.js +0 -0
- /package/dist/src/{gl → marks}/point.vertex.glsl.js +0 -0
- /package/dist/src/{gl → marks}/rect.fragment.glsl.js +0 -0
- /package/dist/src/{gl → marks}/rule.common.glsl.js +0 -0
- /package/dist/src/{gl → marks}/rule.fragment.glsl.js +0 -0
- /package/dist/src/{gl → marks}/rule.vertex.glsl.js +0 -0
- /package/dist/src/{gl → marks}/text.common.glsl.js +0 -0
- /package/dist/src/{gl → marks}/text.fragment.glsl.js +0 -0
- /package/dist/src/{gl → marks}/text.vertex.glsl.js +0 -0
|
@@ -6,9 +6,11 @@ import { SDF_PADDING } from "../fonts/bmFontMetrics.js";
|
|
|
6
6
|
import { createBinningRangeIndexer } from "../utils/binnedIndex.js";
|
|
7
7
|
import { isValueDef } from "../encoder/encoder.js";
|
|
8
8
|
import {
|
|
9
|
+
dedupeEncodingFields,
|
|
9
10
|
isHighPrecisionScale,
|
|
11
|
+
makeAttributeName,
|
|
10
12
|
splitHighPrecision,
|
|
11
|
-
} from "
|
|
13
|
+
} from "./glslScaleGenerator.js";
|
|
12
14
|
import { isContinuous } from "vega-scale";
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -41,6 +43,12 @@ export class GeometryBuilder {
|
|
|
41
43
|
)
|
|
42
44
|
);
|
|
43
45
|
|
|
46
|
+
const dedupedEncodingFields = [
|
|
47
|
+
...dedupeEncodingFields(encoders).entries(),
|
|
48
|
+
]
|
|
49
|
+
.filter(([key, channels]) => key[1] && channels.length > 1)
|
|
50
|
+
.map(([_key, channels]) => channels);
|
|
51
|
+
|
|
44
52
|
this.allocatedVertices = numVertices;
|
|
45
53
|
|
|
46
54
|
this.variableBuilder = new ArrayBuilder(numVertices);
|
|
@@ -49,6 +57,16 @@ export class GeometryBuilder {
|
|
|
49
57
|
// TODO: If more than one channels use the same field with the same data type, convert the field only once.
|
|
50
58
|
|
|
51
59
|
for (const [channel, ce] of Object.entries(this.variableEncoders)) {
|
|
60
|
+
// Only add the first of the shared channels as all the rest are same
|
|
61
|
+
// For example, if both x and x2 are using the same field, only x is
|
|
62
|
+
// added to the array builder with the name "x_x2".
|
|
63
|
+
const sharedChannels = dedupedEncodingFields.find((channels) =>
|
|
64
|
+
channels.find((c) => c == channel)
|
|
65
|
+
);
|
|
66
|
+
if (sharedChannels && channel != sharedChannels[0]) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
const accessor = ce.accessor;
|
|
53
71
|
|
|
54
72
|
const doubleArray = [0, 0];
|
|
@@ -69,7 +87,11 @@ export class GeometryBuilder {
|
|
|
69
87
|
? (d) => splitHighPrecision(accessor(d), doubleArray)
|
|
70
88
|
: accessor;
|
|
71
89
|
|
|
72
|
-
|
|
90
|
+
const attributeName = sharedChannels
|
|
91
|
+
? makeAttributeName(sharedChannels)
|
|
92
|
+
: channel;
|
|
93
|
+
|
|
94
|
+
this.variableBuilder.addConverter(attributeName, {
|
|
73
95
|
f,
|
|
74
96
|
numComponents: hp ? 2 : 1,
|
|
75
97
|
arrayReference: hp ? doubleArray : undefined,
|
|
@@ -217,30 +239,14 @@ export class RectVertexBuilder extends GeometryBuilder {
|
|
|
217
239
|
* @param {Object} object
|
|
218
240
|
* @param {Record<string, Encoder>} object.encoders
|
|
219
241
|
* @param {string[]} object.attributes
|
|
220
|
-
* @param {number} [object.tessellationThreshold]
|
|
221
|
-
* If the rect is wider than the threshold, tessellate it into pieces
|
|
222
|
-
* @param {number[]} [object.visibleRange]
|
|
223
242
|
* @param {number} [object.numItems] Number of data items
|
|
224
243
|
*/
|
|
225
|
-
constructor({
|
|
226
|
-
encoders,
|
|
227
|
-
attributes,
|
|
228
|
-
tessellationThreshold = Infinity,
|
|
229
|
-
visibleRange = [-Infinity, Infinity],
|
|
230
|
-
numItems,
|
|
231
|
-
}) {
|
|
244
|
+
constructor({ encoders, attributes, numItems }) {
|
|
232
245
|
super({
|
|
233
246
|
encoders,
|
|
234
247
|
attributes,
|
|
235
|
-
numVertices:
|
|
236
|
-
tessellationThreshold == Infinity ? numItems * 6 : undefined,
|
|
248
|
+
numVertices: numItems * 6,
|
|
237
249
|
});
|
|
238
|
-
|
|
239
|
-
this.visibleRange = visibleRange;
|
|
240
|
-
|
|
241
|
-
this.tessellationThreshold = tessellationThreshold || Infinity;
|
|
242
|
-
|
|
243
|
-
this.updateFrac = this.variableBuilder.createUpdater("frac", 2);
|
|
244
250
|
}
|
|
245
251
|
|
|
246
252
|
/**
|
|
@@ -253,69 +259,23 @@ export class RectVertexBuilder extends GeometryBuilder {
|
|
|
253
259
|
return;
|
|
254
260
|
}
|
|
255
261
|
|
|
256
|
-
const e =
|
|
257
|
-
/** @type {Object.<string, import("../types/encoder.js").NumberEncoder>} */ (
|
|
258
|
-
this.encoders
|
|
259
|
-
);
|
|
260
|
-
const [lower, upper] = this.visibleRange;
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* @param {import("../types/encoder.js").Encoder} encoder
|
|
264
|
-
*/
|
|
265
|
-
const a = (encoder) => encoder.accessor || ((x) => 0);
|
|
266
|
-
|
|
267
|
-
const xAccessor = a(e.x);
|
|
268
|
-
const x2Accessor = a(e.x2);
|
|
269
|
-
|
|
270
262
|
this.prepareXIndexer(data, lo, hi);
|
|
271
263
|
|
|
272
|
-
const frac = [0, 0];
|
|
273
|
-
this.updateFrac(frac);
|
|
274
|
-
|
|
275
264
|
for (let i = lo; i < hi; i++) {
|
|
276
265
|
const d = data[i];
|
|
277
266
|
|
|
278
|
-
let x = xAccessor(d),
|
|
279
|
-
x2 = x2Accessor(d);
|
|
280
|
-
|
|
281
|
-
if (x > x2) {
|
|
282
|
-
[x, x2] = [x2, x];
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Skip rects that fall outside the visible range. TODO: Optimize by using binary search / interval tree
|
|
286
|
-
if (x2 < lower || x > upper) {
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Truncate to prevent tessellation of parts that are outside the viewport
|
|
291
|
-
if (x < lower) x = lower;
|
|
292
|
-
if (x2 > upper) x2 = upper;
|
|
293
|
-
|
|
294
267
|
// Start a new segment.
|
|
295
268
|
this.variableBuilder.updateFromDatum(d);
|
|
296
269
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const tileCount = 1;
|
|
302
|
-
// width < Infinity
|
|
303
|
-
// ? Math.ceil(width / this.tessellationThreshold)
|
|
304
|
-
// : 1;
|
|
305
|
-
|
|
306
|
-
// Duplicate the first vertex to produce degenerate triangles
|
|
270
|
+
// Six vertices per rect. The vertex shader is using gl_VertexID to
|
|
271
|
+
// determine the vertex position within the rect.
|
|
272
|
+
this.variableBuilder.pushAll();
|
|
273
|
+
this.variableBuilder.pushAll();
|
|
307
274
|
this.variableBuilder.pushAll();
|
|
308
|
-
|
|
309
|
-
for (let i = 0; i <= tileCount; i++) {
|
|
310
|
-
frac[0] = i / tileCount;
|
|
311
|
-
frac[1] = 0;
|
|
312
|
-
this.variableBuilder.pushAll();
|
|
313
|
-
frac[1] = 1;
|
|
314
|
-
this.variableBuilder.pushAll();
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Duplicate the last vertex to produce a degenerate triangle between the segments
|
|
318
275
|
this.variableBuilder.pushAll();
|
|
276
|
+
this.variableBuilder.pushAll();
|
|
277
|
+
this.variableBuilder.pushAll();
|
|
278
|
+
|
|
319
279
|
this.addToXIndex(d);
|
|
320
280
|
}
|
|
321
281
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="external-typings/internmap.js" />
|
|
1
2
|
/**
|
|
2
3
|
*
|
|
3
4
|
* @param {Channel} channel
|
|
@@ -9,8 +10,10 @@ export function generateValueGlsl(channel: Channel, value: number | number[] | s
|
|
|
9
10
|
* @param {Channel} channel
|
|
10
11
|
* @param {any} scale TODO: typing
|
|
11
12
|
* @param {import("../spec/channel.js").ChannelDef} channelDef
|
|
13
|
+
* @param {Channel[]} [sharedQuantitativeChannels] Channels that share the same quantitative field
|
|
12
14
|
*/
|
|
13
|
-
export function generateScaleGlsl(channel: Channel, scale: any, channelDef: import("../spec/channel.js").ChannelDef): {
|
|
15
|
+
export function generateScaleGlsl(channel: Channel, scale: any, channelDef: import("../spec/channel.js").ChannelDef, sharedQuantitativeChannels?: Channel[]): {
|
|
16
|
+
attributeGlsl: string;
|
|
14
17
|
glsl: string;
|
|
15
18
|
domainUniform: string;
|
|
16
19
|
};
|
|
@@ -28,6 +31,20 @@ export function splitHighPrecision(x: number, arr?: number[]): number[];
|
|
|
28
31
|
* @param {number[]} domain
|
|
29
32
|
*/
|
|
30
33
|
export function toHighPrecisionDomainUniform(domain: number[]): number[];
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {[string, boolean]} FieldKey Tuple: [channel, isQuantitative]]
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Finds duplicated quantitative fields in the encoding block.
|
|
39
|
+
* They need to be uploaded to the GPU only once.
|
|
40
|
+
*
|
|
41
|
+
* @param {Partial<Record<import("../spec/channel.js").Channel, import("../types/encoder.js").Encoder>>} encoders
|
|
42
|
+
*/
|
|
43
|
+
export function dedupeEncodingFields(encoders: Partial<Record<import("../spec/channel.js").Channel, import("../types/encoder.js").Encoder>>): InternMap<FieldKey, import("../spec/channel.js").Channel[]>;
|
|
44
|
+
/**
|
|
45
|
+
* @param {import("../spec/channel.js").Channel | import("../spec/channel.js").Channel[]} channel
|
|
46
|
+
*/
|
|
47
|
+
export function makeAttributeName(channel: import("../spec/channel.js").Channel | import("../spec/channel.js").Channel[]): string;
|
|
31
48
|
export const ATTRIBUTE_PREFIX: "attr_";
|
|
32
49
|
export const DOMAIN_PREFIX: "uDomain_";
|
|
33
50
|
export const RANGE_PREFIX: "range_";
|
|
@@ -42,4 +59,9 @@ export type VectorizedValue = string & {
|
|
|
42
59
|
type: string;
|
|
43
60
|
numComponents: number;
|
|
44
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* Tuple: [channel, isQuantitative]]
|
|
64
|
+
*/
|
|
65
|
+
export type FieldKey = [string, boolean];
|
|
66
|
+
import { InternMap } from "internmap";
|
|
45
67
|
//# sourceMappingURL=glslScaleGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glslScaleGenerator.d.ts","sourceRoot":"","sources":["../../../src/gl/glslScaleGenerator.js"],"names":[],"mappings":";AAmDA;;;;GAIG;AACH,2CAHW,OAAO,SACP,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,UAsC9C;AAED;;;;;;GAMG;AAEH,2CANW,OAAO,SACP,GAAG,cACH,OAAO,oBAAoB,EAAE,UAAU,+BACvC,OAAO,EAAE;;;;EAmRnB;AA+FD;;;GAGG;AACH,2CAFW,MAAM,WAIhB;AAOD;;;GAGG;AACH,sCAHW,MAAM,QACN,MAAM,EAAE,YAYlB;AAYD;;GAEG;AACH,qDAFW,MAAM,EAAE,YAIlB;AAED;;GAEG;AAEH;;;;;GAKG;AACH,+CAFW,QAAQ,OAAO,OAAO,oBAAoB,EAAE,OAAO,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CAAC,+DA0BtG;AAED;;GAEG;AACH,2CAFW,OAAO,oBAAoB,EAAE,OAAO,GAAG,OAAO,oBAAoB,EAAE,OAAO,EAAE,UAIvF;AAvhBD,uCAAwC;AACxC,uCAAwC;AACxC,oCAAqC;AACrC,6CAA8C;AAC9C,kDAAmD;AACnD,oDAAqD;sBAMxC,OAAO,oBAAoB,EAAE,OAAO;;;;8BAsXpC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;uBA8GhD,CAAC,MAAM,EAAE,OAAO,CAAC;0BAjfJ,WAAW"}
|
|
@@ -14,8 +14,10 @@ import {
|
|
|
14
14
|
isDiscreteChannel,
|
|
15
15
|
getPrimaryChannel,
|
|
16
16
|
isValueDef,
|
|
17
|
+
isFieldDef,
|
|
17
18
|
} from "../encoder/encoder.js";
|
|
18
|
-
import { peek } from "../utils/arrayUtils.js";
|
|
19
|
+
import { asArray, peek } from "../utils/arrayUtils.js";
|
|
20
|
+
import { InternMap } from "internmap";
|
|
19
21
|
|
|
20
22
|
export const ATTRIBUTE_PREFIX = "attr_";
|
|
21
23
|
export const DOMAIN_PREFIX = "uDomain_";
|
|
@@ -95,9 +97,15 @@ ${vec.type} ${SCALED_FUNCTION_PREFIX}${channel}() {
|
|
|
95
97
|
* @param {Channel} channel
|
|
96
98
|
* @param {any} scale TODO: typing
|
|
97
99
|
* @param {import("../spec/channel.js").ChannelDef} channelDef
|
|
100
|
+
* @param {Channel[]} [sharedQuantitativeChannels] Channels that share the same quantitative field
|
|
98
101
|
*/
|
|
99
102
|
// eslint-disable-next-line complexity
|
|
100
|
-
export function generateScaleGlsl(
|
|
103
|
+
export function generateScaleGlsl(
|
|
104
|
+
channel,
|
|
105
|
+
scale,
|
|
106
|
+
channelDef,
|
|
107
|
+
sharedQuantitativeChannels = [channel]
|
|
108
|
+
) {
|
|
101
109
|
if (isValueDef(channelDef)) {
|
|
102
110
|
throw new Error(
|
|
103
111
|
`Cannot create scale for "value": ${JSON.stringify(channelDef)}`
|
|
@@ -109,7 +117,8 @@ export function generateScaleGlsl(channel, scale, channelDef) {
|
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
const primary = getPrimaryChannel(channel);
|
|
112
|
-
const attributeName =
|
|
120
|
+
const attributeName =
|
|
121
|
+
ATTRIBUTE_PREFIX + makeAttributeName(sharedQuantitativeChannels);
|
|
113
122
|
const domainUniformName = DOMAIN_PREFIX + primary;
|
|
114
123
|
const rangeName = RANGE_PREFIX + primary;
|
|
115
124
|
|
|
@@ -270,11 +279,9 @@ export function generateScaleGlsl(channel, scale, channelDef) {
|
|
|
270
279
|
interpolate = `getDiscreteColor(${textureUniformName}, int(transformed)).r`;
|
|
271
280
|
}
|
|
272
281
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
glsl.push(`in highp ${attributeType} ${attributeName};`);
|
|
277
|
-
}
|
|
282
|
+
const attributeGlsl = isDatumDef(channelDef)
|
|
283
|
+
? `uniform highp ${attributeType} ${attributeName};`
|
|
284
|
+
: `in highp ${attributeType} ${attributeName};`;
|
|
278
285
|
|
|
279
286
|
/** @type {string[]} Channel's scale function*/
|
|
280
287
|
const scaleBody = [];
|
|
@@ -361,6 +368,7 @@ ${returnType} ${SCALED_FUNCTION_PREFIX}${channel}() {
|
|
|
361
368
|
}
|
|
362
369
|
|
|
363
370
|
return {
|
|
371
|
+
attributeGlsl,
|
|
364
372
|
glsl: concatenated,
|
|
365
373
|
domainUniform,
|
|
366
374
|
};
|
|
@@ -504,3 +512,46 @@ function exactSplitHighPrecision(x) {
|
|
|
504
512
|
export function toHighPrecisionDomainUniform(domain) {
|
|
505
513
|
return [...exactSplitHighPrecision(domain[0]), domain[1] - domain[0]];
|
|
506
514
|
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* @typedef {[string, boolean]} FieldKey Tuple: [channel, isQuantitative]]
|
|
518
|
+
*/
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Finds duplicated quantitative fields in the encoding block.
|
|
522
|
+
* They need to be uploaded to the GPU only once.
|
|
523
|
+
*
|
|
524
|
+
* @param {Partial<Record<import("../spec/channel.js").Channel, import("../types/encoder.js").Encoder>>} encoders
|
|
525
|
+
*/
|
|
526
|
+
export function dedupeEncodingFields(encoders) {
|
|
527
|
+
/**
|
|
528
|
+
* Value: an array of channels
|
|
529
|
+
* @type {InternMap<FieldKey, import("../spec/channel.js").Channel[]>}
|
|
530
|
+
*/
|
|
531
|
+
const deduped = new InternMap([], JSON.stringify);
|
|
532
|
+
|
|
533
|
+
for (const [channel, encoder] of Object.entries(encoders)) {
|
|
534
|
+
const channelDef = encoder.channelDef;
|
|
535
|
+
if (isFieldDef(channelDef)) {
|
|
536
|
+
const field = channelDef.field;
|
|
537
|
+
|
|
538
|
+
/** @type {[string, boolean]} */
|
|
539
|
+
const key = [
|
|
540
|
+
field,
|
|
541
|
+
encoder.scale
|
|
542
|
+
? isContinuous(encoder.scale.type) ?? false
|
|
543
|
+
: false,
|
|
544
|
+
];
|
|
545
|
+
|
|
546
|
+
deduped.set(key, [...(deduped.get(key) ?? []), channel]);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return deduped;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @param {import("../spec/channel.js").Channel | import("../spec/channel.js").Channel[]} channel
|
|
554
|
+
*/
|
|
555
|
+
export function makeAttributeName(channel) {
|
|
556
|
+
return asArray(channel).join("_");
|
|
557
|
+
}
|
|
@@ -32,16 +32,11 @@ export default class WebGLHelper {
|
|
|
32
32
|
}, clearColor?: string);
|
|
33
33
|
_container: HTMLElement;
|
|
34
34
|
_sizeSource: () => {
|
|
35
|
-
width:
|
|
36
|
-
height:
|
|
35
|
+
width: any;
|
|
36
|
+
height: any;
|
|
37
37
|
};
|
|
38
38
|
/** @type {Map<string, WebGLShader>} */
|
|
39
39
|
_shaderCache: Map<string, WebGLShader>;
|
|
40
|
-
/** @type {{ type: string, listener: function}[]} */
|
|
41
|
-
_listeners: {
|
|
42
|
-
type: string;
|
|
43
|
-
listener: Function;
|
|
44
|
-
}[];
|
|
45
40
|
/** @type {WeakMap<import("../view/scaleResolution.js").default, WebGLTexture>} */
|
|
46
41
|
rangeTextures: WeakMap<import("../view/scaleResolution.js").default, WebGLTexture>;
|
|
47
42
|
canvas: HTMLCanvasElement;
|
|
@@ -49,13 +44,12 @@ export default class WebGLHelper {
|
|
|
49
44
|
/** @type {import("twgl.js").AttachmentOptions[]} */
|
|
50
45
|
_pickingAttachmentOptions: import("twgl.js").AttachmentOptions[];
|
|
51
46
|
_pickingBufferInfo: import("twgl.js").FramebufferInfo;
|
|
52
|
-
_resizeObserver: ResizeObserver;
|
|
53
47
|
/** @type {[number, number, number, number]} */
|
|
54
48
|
_clearColor: [number, number, number, number];
|
|
55
49
|
invalidateSize(): void;
|
|
56
50
|
_logicalCanvasSize: {
|
|
57
|
-
width:
|
|
58
|
-
height:
|
|
51
|
+
width: any;
|
|
52
|
+
height: any;
|
|
59
53
|
};
|
|
60
54
|
_updateDpr(): void;
|
|
61
55
|
dpr: number;
|
|
@@ -84,18 +78,9 @@ export default class WebGLHelper {
|
|
|
84
78
|
* Returns the canvas size in logical pixels (without devicePixelRatio correction)
|
|
85
79
|
*/
|
|
86
80
|
getLogicalCanvasSize(): {
|
|
87
|
-
width:
|
|
88
|
-
height:
|
|
81
|
+
width: any;
|
|
82
|
+
height: any;
|
|
89
83
|
};
|
|
90
|
-
/**
|
|
91
|
-
* @param {"render"|"resize"} eventType
|
|
92
|
-
* @param {function} listener
|
|
93
|
-
*/
|
|
94
|
-
addEventListener(eventType: "render" | "resize", listener: Function): void;
|
|
95
|
-
/**
|
|
96
|
-
* @param {string} eventType
|
|
97
|
-
*/
|
|
98
|
-
_emit(eventType: string): void;
|
|
99
84
|
/**
|
|
100
85
|
*
|
|
101
86
|
* @param {number} x
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webGLHelper.d.ts","sourceRoot":"","sources":["../../../src/gl/webGLHelper.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webGLHelper.d.ts","sourceRoot":"","sources":["../../../src/gl/webGLHelper.js"],"names":[],"mappings":"AA8ZA;;;;GAIG;AACH,kCAJW,sBAAsB,gBACtB,WAAW,kBACX,WAAW;;;;;;EA8CrB;AAED;;;;;GAKG;AACH,0CALW,qBAAqB,WACrB,KAAK,OAAO,SAAS,EAAE,cAAc,EAAE,KAAK,CAAC,OAC7C,MAAM,EAAE,GAAG,eAAe,YAC1B,YAAY,gBAYtB;AAncD;IACI;;;;;;;OAOG;IACH,uBANW,WAAW,eACX,MAAM;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,eAGrC,MAAM,EAmFhB;IAhFG,wBAA2B;IAC3B;;;MAKO;IAEP,uCAAuC;IACvC,cADW,IAAI,MAAM,EAAE,WAAW,CAAC,CACN;IAE7B,kFAAkF;IAClF,eADW,QAAQ,OAAO,4BAA4B,EAAE,OAAO,EAAE,YAAY,CAAC,CAC5C;IAuClC,0BAAoB;IACpB,2BAAY;IAGZ,oDAAoD;IACpD,2BADW,OAAO,SAAS,EAAE,iBAAiB,EAAE,CAQ/C;IACD,sDAGC;IAOD,+CAA+C;IAC/C,aADW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CACZ;IAOnC,uBAIC;IAHG;;;MAAmC;IAKvC,mBAEC;IADG,YAAkC;IAGtC;;;;;OAKG;IACH,oBAHW,MAAM,QACN,MAAM,GAAG,MAAM,EAAE,eA2B3B;IAED,iBAcC;IAED,iBAEC;IAED;;;;OAIG;IACH,oCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;MAQ3C;IAED;;OAEG;IACH;;;MAuBC;IAED;;;;OAIG;IACH,oBAHW,MAAM,KACN,MAAM,cAwBhB;IAED,iBAOC;IAED;;;;;;;;;OASG;IACH,+BAHW,OAAO,4BAA4B,EAAE,OAAO,WAC5C,OAAO,QA4GjB;CACJ"}
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
createDiscreteTexture,
|
|
21
21
|
createInterpolatedColorTexture,
|
|
22
22
|
createSchemeTexture,
|
|
23
|
-
} from "
|
|
23
|
+
} from "./colorUtils.js";
|
|
24
24
|
import {
|
|
25
25
|
getDiscreteRangeMapper,
|
|
26
26
|
isColorChannel,
|
|
@@ -39,14 +39,16 @@ export default class WebGLHelper {
|
|
|
39
39
|
*/
|
|
40
40
|
constructor(container, sizeSource, clearColor) {
|
|
41
41
|
this._container = container;
|
|
42
|
-
this._sizeSource =
|
|
42
|
+
this._sizeSource =
|
|
43
|
+
sizeSource ??
|
|
44
|
+
(() => ({
|
|
45
|
+
width: undefined,
|
|
46
|
+
height: undefined,
|
|
47
|
+
}));
|
|
43
48
|
|
|
44
49
|
/** @type {Map<string, WebGLShader>} */
|
|
45
50
|
this._shaderCache = new Map();
|
|
46
51
|
|
|
47
|
-
/** @type {{ type: string, listener: function}[]} */
|
|
48
|
-
this._listeners = [];
|
|
49
|
-
|
|
50
52
|
/** @type {WeakMap<import("../view/scaleResolution.js").default, WebGLTexture>} */
|
|
51
53
|
this.rangeTextures = new WeakMap();
|
|
52
54
|
|
|
@@ -108,16 +110,6 @@ export default class WebGLHelper {
|
|
|
108
110
|
|
|
109
111
|
this.adjustGl();
|
|
110
112
|
|
|
111
|
-
// TODO: Size should be observed only if the content is not absolutely sized
|
|
112
|
-
this._resizeObserver = new ResizeObserver((entries) => {
|
|
113
|
-
this.invalidateSize();
|
|
114
|
-
this._emit("resize");
|
|
115
|
-
});
|
|
116
|
-
this._resizeObserver.observe(this._container);
|
|
117
|
-
|
|
118
|
-
// TODO: Observe devicePixelRatio
|
|
119
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes
|
|
120
|
-
|
|
121
113
|
this._updateDpr();
|
|
122
114
|
|
|
123
115
|
/** @type {[number, number, number, number]} */
|
|
@@ -188,7 +180,6 @@ export default class WebGLHelper {
|
|
|
188
180
|
}
|
|
189
181
|
|
|
190
182
|
finalize() {
|
|
191
|
-
this._resizeObserver.unobserve(this._container);
|
|
192
183
|
this.canvas.remove();
|
|
193
184
|
}
|
|
194
185
|
|
|
@@ -214,10 +205,7 @@ export default class WebGLHelper {
|
|
|
214
205
|
}
|
|
215
206
|
|
|
216
207
|
// TODO: The size should never be smaller than the minimum content size!
|
|
217
|
-
const contentSize = this._sizeSource
|
|
218
|
-
width: undefined,
|
|
219
|
-
height: undefined,
|
|
220
|
-
};
|
|
208
|
+
const contentSize = this._sizeSource();
|
|
221
209
|
|
|
222
210
|
const cs = window.getComputedStyle(this._container, null);
|
|
223
211
|
const width =
|
|
@@ -236,25 +224,6 @@ export default class WebGLHelper {
|
|
|
236
224
|
return this._logicalCanvasSize;
|
|
237
225
|
}
|
|
238
226
|
|
|
239
|
-
/**
|
|
240
|
-
* @param {"render"|"resize"} eventType
|
|
241
|
-
* @param {function} listener
|
|
242
|
-
*/
|
|
243
|
-
addEventListener(eventType, listener) {
|
|
244
|
-
this._listeners.push({ type: eventType, listener });
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* @param {string} eventType
|
|
249
|
-
*/
|
|
250
|
-
_emit(eventType) {
|
|
251
|
-
for (const entry of this._listeners) {
|
|
252
|
-
if (entry.type === eventType) {
|
|
253
|
-
entry.listener();
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
227
|
/**
|
|
259
228
|
*
|
|
260
229
|
* @param {number} x
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_ajPY{transform-origin:center;animation:spinner_AtaB .75s infinite linear}@keyframes spinner_AtaB{100%{transform:rotate(360deg)}}</style><path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z" class="spinner_ajPY"/></svg>
|
package/dist/src/marks/link.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/marks/link.js"],"names":[],"mappings":"AAWA;IA+
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/marks/link.js"],"names":[],"mappings":"AAWA;IA+BQ;;;;;OAKG;IACH,yBAEC;IAkGD;;;;;;MAKC;CAoFR;iBAxOgB,WAAW"}
|
package/dist/src/marks/link.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import VERTEX_SHADER from "
|
|
3
|
-
import FRAGMENT_SHADER from "
|
|
1
|
+
import { setBuffersAndAttributes } from "twgl.js";
|
|
2
|
+
import VERTEX_SHADER from "./link.vertex.glsl.js";
|
|
3
|
+
import FRAGMENT_SHADER from "./link.fragment.glsl.js";
|
|
4
4
|
import { LinkVertexBuilder } from "../gl/dataToVertices.js";
|
|
5
5
|
|
|
6
6
|
import Mark from "./mark.js";
|
|
@@ -39,6 +39,16 @@ export default class LinkMark extends Mark {
|
|
|
39
39
|
orient: "vertical",
|
|
40
40
|
})
|
|
41
41
|
);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Only available if "WebGL Draft Extensions" is enabled in chrome://flags
|
|
45
|
+
* But seems to work.
|
|
46
|
+
*
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
this._baseInstanceExt = this.gl.getExtension(
|
|
50
|
+
"WEBGL_draw_instanced_base_vertex_base_instance"
|
|
51
|
+
);
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
getAttributes() {
|
|
@@ -154,6 +164,18 @@ export default class LinkMark extends Mark {
|
|
|
154
164
|
|
|
155
165
|
ops.push(() => this.bindOrSetMarkUniformBlock());
|
|
156
166
|
|
|
167
|
+
if (this._baseInstanceExt) {
|
|
168
|
+
ops.push(() =>
|
|
169
|
+
setBuffersAndAttributes(
|
|
170
|
+
this.gl,
|
|
171
|
+
this.programInfo,
|
|
172
|
+
this.vertexArrayInfo
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
} else {
|
|
176
|
+
ops.push(() => this.gl.bindVertexArray(null));
|
|
177
|
+
}
|
|
178
|
+
|
|
157
179
|
return ops;
|
|
158
180
|
}
|
|
159
181
|
|
|
@@ -163,42 +185,55 @@ export default class LinkMark extends Mark {
|
|
|
163
185
|
render(options) {
|
|
164
186
|
const gl = this.gl;
|
|
165
187
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
188
|
+
const arcVertexCount = (this.properties.segments + 1) * 2;
|
|
189
|
+
|
|
190
|
+
return this._baseInstanceExt
|
|
191
|
+
? this.createRenderCallback((offset, count) => {
|
|
192
|
+
// Using the following extension, which, however, is only a draft and
|
|
193
|
+
// available if "WebGL Draft Extensions" is enabled in chrome://flags
|
|
194
|
+
// https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_instanced_base_vertex_base_instance/
|
|
195
|
+
|
|
196
|
+
this._baseInstanceExt.drawArraysInstancedBaseInstanceWEBGL(
|
|
197
|
+
gl.TRIANGLE_STRIP,
|
|
198
|
+
0,
|
|
199
|
+
arcVertexCount,
|
|
200
|
+
count,
|
|
201
|
+
offset
|
|
202
|
+
);
|
|
203
|
+
}, options)
|
|
204
|
+
: this.createRenderCallback((offset, count) => {
|
|
205
|
+
// Because vanilla WebGL 2 does not provide glDrawArraysInstancedBaseInstance,
|
|
206
|
+
// we have to hack with offsets in vertexAttribPointer
|
|
207
|
+
//
|
|
208
|
+
// TODO: Use VAOs more intelligently to reduce WebGL calls. In other words,
|
|
209
|
+
// reserve one VAO for each facet/sample.
|
|
210
|
+
|
|
211
|
+
for (const attribInfoObject of Object.entries(
|
|
212
|
+
this.bufferInfo.attribs
|
|
213
|
+
)) {
|
|
214
|
+
const [attribute, attribInfo] = attribInfoObject;
|
|
215
|
+
if (
|
|
216
|
+
attribInfo.buffer &&
|
|
217
|
+
attribInfo.numComponents &&
|
|
218
|
+
attribInfo.divisor
|
|
219
|
+
) {
|
|
220
|
+
attribInfo.offset =
|
|
221
|
+
offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
setBuffersAndAttributes(
|
|
225
|
+
gl,
|
|
226
|
+
this.programInfo,
|
|
227
|
+
this.bufferInfo
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
gl.drawArraysInstanced(
|
|
231
|
+
gl.TRIANGLE_STRIP,
|
|
232
|
+
0,
|
|
233
|
+
arcVertexCount,
|
|
234
|
+
count
|
|
235
|
+
);
|
|
236
|
+
}, options);
|
|
202
237
|
}
|
|
203
238
|
}
|
|
204
239
|
|
package/dist/src/marks/mark.d.ts
CHANGED
|
@@ -125,8 +125,9 @@ export default class Mark {
|
|
|
125
125
|
* @param {string} vertexShader
|
|
126
126
|
* @param {string} fragmentShader
|
|
127
127
|
* @param {string[]} [extraHeaders]
|
|
128
|
+
* @protected
|
|
128
129
|
*/
|
|
129
|
-
createAndLinkShaders(vertexShader: string, fragmentShader: string, extraHeaders?: string[]): void;
|
|
130
|
+
protected createAndLinkShaders(vertexShader: string, fragmentShader: string, extraHeaders?: string[]): void;
|
|
130
131
|
/** @type {string[]} */
|
|
131
132
|
domainUniforms: string[];
|
|
132
133
|
programStatus: {
|