@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.
Files changed (117) hide show
  1. package/dist/bundle/index--cKb-dKG.js +615 -0
  2. package/dist/bundle/{index-gn8bhQ8w.js → index-d7k3kkin.js} +365 -366
  3. package/dist/bundle/index.es.js +4225 -4040
  4. package/dist/bundle/index.js +122 -79
  5. package/dist/schema.json +58 -6
  6. package/dist/src/data/sources/dynamic/axisGenomeSource.js +1 -1
  7. package/dist/src/data/sources/dynamic/axisTickSource.js +3 -3
  8. package/dist/src/data/sources/dynamic/bamSource.d.ts +3 -21
  9. package/dist/src/data/sources/dynamic/bamSource.d.ts.map +1 -1
  10. package/dist/src/data/sources/dynamic/bamSource.js +38 -55
  11. package/dist/src/data/sources/dynamic/bigBedSource.d.ts +2 -38
  12. package/dist/src/data/sources/dynamic/bigBedSource.d.ts.map +1 -1
  13. package/dist/src/data/sources/dynamic/bigBedSource.js +14 -71
  14. package/dist/src/data/sources/dynamic/bigWigSource.d.ts +4 -42
  15. package/dist/src/data/sources/dynamic/bigWigSource.d.ts.map +1 -1
  16. package/dist/src/data/sources/dynamic/bigWigSource.js +23 -60
  17. package/dist/src/data/sources/dynamic/gff3Source.d.ts.map +1 -1
  18. package/dist/src/data/sources/dynamic/gff3Source.js +1 -0
  19. package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts +2 -20
  20. package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts.map +1 -1
  21. package/dist/src/data/sources/dynamic/indexedFastaSource.js +23 -41
  22. package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts +23 -4
  23. package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts.map +1 -1
  24. package/dist/src/data/sources/dynamic/singleAxisLazySource.js +29 -4
  25. package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts +60 -0
  26. package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts.map +1 -0
  27. package/dist/src/data/sources/dynamic/singleAxisWindowedSource.js +152 -0
  28. package/dist/src/data/sources/dynamic/tabixSource.d.ts +6 -40
  29. package/dist/src/data/sources/dynamic/tabixSource.d.ts.map +1 -1
  30. package/dist/src/data/sources/dynamic/tabixSource.js +29 -78
  31. package/dist/src/data/transforms/regexFold.d.ts.map +1 -1
  32. package/dist/src/data/transforms/regexFold.js +8 -0
  33. package/dist/src/data/transforms/regexFold.test.js +28 -0
  34. package/dist/src/encoder/encoder.d.ts +6 -1
  35. package/dist/src/encoder/encoder.d.ts.map +1 -1
  36. package/dist/src/encoder/encoder.js +10 -0
  37. package/dist/src/genomeSpy.d.ts +14 -0
  38. package/dist/src/genomeSpy.d.ts.map +1 -1
  39. package/dist/src/genomeSpy.js +114 -8
  40. package/dist/src/gl/arrayBuilder.js +1 -1
  41. package/dist/src/gl/colorUtils.d.ts.map +1 -0
  42. package/dist/src/{scale → gl}/colorUtils.js +1 -1
  43. package/dist/src/gl/dataToVertices.d.ts +1 -9
  44. package/dist/src/gl/dataToVertices.d.ts.map +1 -1
  45. package/dist/src/gl/dataToVertices.js +33 -73
  46. package/dist/src/{scale → gl}/glslScaleGenerator.d.ts +23 -1
  47. package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -0
  48. package/dist/src/{scale → gl}/glslScaleGenerator.js +59 -8
  49. package/dist/src/gl/webGLHelper.d.ts +6 -21
  50. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  51. package/dist/src/gl/webGLHelper.js +8 -39
  52. package/dist/src/img/90-ring-with-bg.svg +1 -0
  53. package/dist/src/img/README.md +5 -0
  54. package/dist/src/marks/link.d.ts +7 -0
  55. package/dist/src/marks/link.d.ts.map +1 -1
  56. package/dist/src/marks/link.js +74 -39
  57. package/dist/src/marks/mark.d.ts +2 -1
  58. package/dist/src/marks/mark.d.ts.map +1 -1
  59. package/dist/src/marks/mark.js +31 -2
  60. package/dist/src/marks/{pointMark.d.ts → point.d.ts} +1 -1
  61. package/dist/src/marks/point.d.ts.map +1 -0
  62. package/dist/src/marks/{pointMark.js → point.js} +3 -3
  63. package/dist/src/marks/{rectMark.d.ts → rect.d.ts} +1 -1
  64. package/dist/src/marks/rect.d.ts.map +1 -0
  65. package/dist/src/marks/{rectMark.js → rect.js} +2 -3
  66. package/dist/src/marks/rect.vertex.glsl.js +2 -0
  67. package/dist/src/marks/rule.js +3 -3
  68. package/dist/src/marks/text.d.ts.map +1 -1
  69. package/dist/src/marks/text.js +19 -20
  70. package/dist/src/spec/data.d.ts +28 -13
  71. package/dist/src/spec/mark.d.ts +0 -8
  72. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  73. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  74. package/dist/src/styles/genome-spy.css.js +33 -4
  75. package/dist/src/styles/genome-spy.scss +40 -4
  76. package/dist/src/types/viewContext.d.ts +9 -0
  77. package/dist/src/utils/binnedIndex.d.ts +2 -0
  78. package/dist/src/utils/binnedIndex.d.ts.map +1 -1
  79. package/dist/src/utils/binnedIndex.js +59 -10
  80. package/dist/src/utils/binnedIndex.test.js +46 -0
  81. package/dist/src/utils/indexer.d.ts.map +1 -1
  82. package/dist/src/utils/indexer.js +10 -1
  83. package/dist/src/utils/indexer.test.js +2 -0
  84. package/dist/src/view/gridView.d.ts.map +1 -1
  85. package/dist/src/view/gridView.js +2 -0
  86. package/dist/src/view/layerView.d.ts.map +1 -1
  87. package/dist/src/view/layerView.js +2 -0
  88. package/dist/src/view/unitView.d.ts +1 -7
  89. package/dist/src/view/unitView.d.ts.map +1 -1
  90. package/dist/src/view/unitView.js +4 -11
  91. package/dist/src/view/view.d.ts +6 -0
  92. package/dist/src/view/view.d.ts.map +1 -1
  93. package/dist/src/view/view.js +11 -0
  94. package/dist/src/view/view.test.js +1 -1
  95. package/package.json +3 -3
  96. package/dist/bundle/index-Cbz74kpR.js +0 -638
  97. package/dist/src/data/sources/dynamic/windowedMixin.d.ts +0 -32
  98. package/dist/src/data/sources/dynamic/windowedMixin.d.ts.map +0 -1
  99. package/dist/src/data/sources/dynamic/windowedMixin.js +0 -53
  100. package/dist/src/gl/rect.vertex.glsl.js +0 -2
  101. package/dist/src/marks/pointMark.d.ts.map +0 -1
  102. package/dist/src/marks/rectMark.d.ts.map +0 -1
  103. package/dist/src/scale/colorUtils.d.ts.map +0 -1
  104. package/dist/src/scale/glslScaleGenerator.d.ts.map +0 -1
  105. /package/dist/src/{scale → gl}/colorUtils.d.ts +0 -0
  106. /package/dist/src/{gl → marks}/link.fragment.glsl.js +0 -0
  107. /package/dist/src/{gl → marks}/link.vertex.glsl.js +0 -0
  108. /package/dist/src/{gl → marks}/point.common.glsl.js +0 -0
  109. /package/dist/src/{gl → marks}/point.fragment.glsl.js +0 -0
  110. /package/dist/src/{gl → marks}/point.vertex.glsl.js +0 -0
  111. /package/dist/src/{gl → marks}/rect.fragment.glsl.js +0 -0
  112. /package/dist/src/{gl → marks}/rule.common.glsl.js +0 -0
  113. /package/dist/src/{gl → marks}/rule.fragment.glsl.js +0 -0
  114. /package/dist/src/{gl → marks}/rule.vertex.glsl.js +0 -0
  115. /package/dist/src/{gl → marks}/text.common.glsl.js +0 -0
  116. /package/dist/src/{gl → marks}/text.fragment.glsl.js +0 -0
  117. /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 "../scale/glslScaleGenerator.js";
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
- this.variableBuilder.addConverter(channel, {
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
- frac[0] = 0;
298
- frac[1] = 0;
299
-
300
- // Tessellate segments
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(channel, scale, channelDef) {
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 = ATTRIBUTE_PREFIX + channel;
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
- if (isDatumDef(channelDef)) {
274
- glsl.push(`uniform highp ${attributeType} ${attributeName};`);
275
- } else {
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: number;
36
- height: number;
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: number;
58
- height: number;
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: number;
88
- height: number;
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":"AA6bA;;;;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;AAleD;IACI;;;;;;;OAOG;IACH,uBANW,WAAW,eACX,MAAM;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,eAGrC,MAAM,EA2FhB;IAxFG,wBAA2B;IAC3B,mBAPa;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAOf;IAE7B,uCAAuC;IACvC,cADW,IAAI,MAAM,EAAE,WAAW,CAAC,CACN;IAE7B,oDAAoD;IACpD,YADW;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,WAAU;KAAC,EAAE,CAC5B;IAEpB,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;IAMD,gCAGE;IAQF,+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,iBAGC;IAED;;;;OAIG;IACH,oCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;MAQ3C;IAED;;OAEG;IACH;;;MA0BC;IAED;;;OAGG;IACH,4BAHW,QAAQ,GAAC,QAAQ,4BAK3B;IAED;;OAEG;IACH,iBAFW,MAAM,QAQhB;IAED;;;;OAIG;IACH,oBAHW,MAAM,KACN,MAAM,cAwBhB;IAED,iBAOC;IAED;;;;;;;;;OASG;IACH,+BAHW,OAAO,4BAA4B,EAAE,OAAO,WAC5C,OAAO,QA4GjB;CACJ"}
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 "../scale/colorUtils.js";
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 = 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>
@@ -0,0 +1,5 @@
1
+ # Image assets
2
+
3
+ ## Spinner svg
4
+
5
+ Spinner: 90-ring-with-bg.svg, MIT License, https://github.com/n3r4zzurr0/svg-spinners
@@ -1,4 +1,11 @@
1
1
  export default class LinkMark extends Mark {
2
+ /**
3
+ * Only available if "WebGL Draft Extensions" is enabled in chrome://flags
4
+ * But seems to work.
5
+ *
6
+ * @private
7
+ */
8
+ private _baseInstanceExt;
2
9
  arrays: {
3
10
  [k: string]: {
4
11
  data: any;
@@ -1 +1 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/marks/link.js"],"names":[],"mappings":"AAWA;IA+HQ;;;;;;MAKC;CA2DR;iBArMgB,WAAW"}
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"}
@@ -1,6 +1,6 @@
1
- import { drawBufferInfo, setBuffersAndAttributes } from "twgl.js";
2
- import VERTEX_SHADER from "../gl/link.vertex.glsl.js";
3
- import FRAGMENT_SHADER from "../gl/link.fragment.glsl.js";
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
- return this.createRenderCallback((offset, count) => {
167
- // We are using instanced drawing here.
168
- // However, WebGL does not provide glDrawArraysInstancedBaseInstance and thus,
169
- // we have to hack with offsets in vertexAttribPointer
170
- // TODO: Use VAOs more intelligently to reduce WebGL calls
171
- // TODO: Explore multiDrawArraysInstancedWEBGL
172
- // There's also a promising extension draft:
173
- // https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_instanced_base_vertex_base_instance/
174
- // (and https://www.khronos.org/registry/webgl/extensions/WEBGL_multi_draw_instanced_base_vertex_base_instance/)
175
-
176
- this.gl.bindVertexArray(this.vertexArrayInfo.vertexArrayObject);
177
-
178
- for (const attribInfoObject of Object.entries(
179
- this.bufferInfo.attribs
180
- )) {
181
- const [attribute, attribInfo] = attribInfoObject;
182
- if (
183
- attribInfo.buffer &&
184
- attribInfo.numComponents &&
185
- attribInfo.divisor
186
- ) {
187
- attribInfo.offset =
188
- offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
189
- }
190
- }
191
- setBuffersAndAttributes(gl, this.programInfo, this.bufferInfo);
192
-
193
- drawBufferInfo(
194
- gl,
195
- this.bufferInfo,
196
- gl.TRIANGLE_STRIP,
197
- (this.properties.segments + 1) * 2, // number of vertices in a triangle strip
198
- 0,
199
- count
200
- );
201
- }, options);
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
 
@@ -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: {