@genome-spy/core 0.20.0 → 0.21.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/schema.json CHANGED
@@ -1816,9 +1816,6 @@
1816
1816
  "description": "The vertical offset between the text and its anchor point, in pixels. Applied after the rotation by `angle`.",
1817
1817
  "type": "number"
1818
1818
  },
1819
- "dynamicData": {
1820
- "type": "boolean"
1821
- },
1822
1819
  "fill": {
1823
1820
  "type": "string"
1824
1821
  },
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "contributors": [],
9
9
  "license": "BSD-2-Clause",
10
- "version": "0.20.0",
10
+ "version": "0.21.0",
11
11
  "main": "dist/index.js",
12
12
  "module": "src/index.js",
13
13
  "exports": {
@@ -53,5 +53,5 @@
53
53
  "vega-scale": "^7.1.1",
54
54
  "vega-util": "^1.16.0"
55
55
  },
56
- "gitHead": "dfeadc37891908cf775423c61a566358ecada9fa"
56
+ "gitHead": "293e743d4b887427d8fadc49460a08a572eaff12"
57
57
  }
@@ -3,19 +3,19 @@ import { format } from "d3-format";
3
3
  import { isString } from "vega-util";
4
4
  import ArrayBuilder from "./arrayBuilder";
5
5
  import { SDF_PADDING } from "../fonts/bmFontMetrics";
6
- import { peek } from "../utils/arrayUtils";
7
- import createBinningRangeIndexer from "../utils/binnedRangeIndex";
6
+ import { createBinningRangeIndexer } from "../utils/binnedIndex";
8
7
  import { isValueDef } from "../encoder/encoder";
9
8
  import {
10
9
  isHighPrecisionScale,
11
10
  splitHighPrecision,
12
11
  } from "../scale/glslScaleGenerator";
12
+ import { isContinuous } from "vega-scale";
13
13
 
14
14
  /**
15
15
  * @typedef {object} RangeEntry Represents a location of a vertex subset
16
16
  * @prop {number} offset in vertices
17
17
  * @prop {number} count in vertices
18
- * @prop {import("../utils/binnedRangeIndex").Lookup} xIndex
18
+ * @prop {import("../utils/binnedIndex").Lookup} xIndex
19
19
  *
20
20
  * @typedef {import("./arraybuilder").ConverterMetadata} Converter
21
21
  * @typedef {import("../encoder/encoder").Encoder} Encoder
@@ -28,16 +28,9 @@ export class GeometryBuilder {
28
28
  * @param {string[]} [object.attributes]
29
29
  * @param {number} [object.numVertices] If the number of data items is known, a
30
30
  * preallocated TypedArray is used
31
- * @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
32
31
  */
33
- constructor({
34
- encoders,
35
- numVertices = undefined,
36
- attributes = [],
37
- buildXIndex = false,
38
- }) {
32
+ constructor({ encoders, numVertices = undefined, attributes = [] }) {
39
33
  this.encoders = encoders;
40
- this._buildXIndex = buildXIndex;
41
34
 
42
35
  // Encoders for variable channels
43
36
  this.variableEncoders = Object.fromEntries(
@@ -84,7 +77,7 @@ export class GeometryBuilder {
84
77
 
85
78
  this.lastOffset = 0;
86
79
 
87
- /** @type {Map<any, RangeEntry>} keep track of sample locations within the vertex array */
80
+ /** @type {Map<any, RangeEntry>} keep track of facet locations within the vertex array */
88
81
  this.rangeMap = new InternMap([], JSON.stringify);
89
82
  }
90
83
 
@@ -121,32 +114,50 @@ export class GeometryBuilder {
121
114
  * @param {object[]} data
122
115
  */
123
116
  addBatch(key, data, lo = 0, hi = data.length) {
117
+ this.prepareXIndexer(data, lo, hi);
118
+
124
119
  for (let i = lo; i < hi; i++) {
125
- this.variableBuilder.pushFromDatum(data[i]);
120
+ const d = data[i];
121
+ this.variableBuilder.pushFromDatum(d);
122
+ this.addToXIndex(d);
126
123
  }
127
124
 
128
125
  this.registerBatch(key);
129
126
  }
130
127
 
131
128
  /**
132
- * @param {any[]} data
129
+ * @param {import("../data/flowNode").Data} data Domain, but specified using datums
130
+ * @param {number} [lo]
131
+ * @param {number} [hi]
133
132
  */
134
- prepareXIndexer(data) {
135
- if (!this._buildXIndex) {
133
+ prepareXIndexer(data, lo = 0, hi = lo + data.length) {
134
+ if (!data.length || hi - lo < 0) {
135
+ /**
136
+ * @param {import("../data/flowNode").Datum} datum
137
+ */
138
+ this.addToXIndex = (datum) => {
139
+ // nop
140
+ };
136
141
  return;
137
142
  }
138
143
 
139
- const xe = this.variableEncoders.x;
140
- const x2e = this.variableEncoders.x2;
144
+ /** @param {Encoder} encoder */
145
+ const getContinuousEncoder = (encoder) =>
146
+ encoder && isContinuous(encoder.scale?.type) && encoder;
147
+
148
+ const xe = getContinuousEncoder(this.variableEncoders.x);
149
+ const x2e = getContinuousEncoder(this.variableEncoders.x2);
141
150
 
142
- if (xe && x2e) {
151
+ if (xe) {
143
152
  const xa = xe.accessor;
144
- const x2a = x2e.accessor;
153
+ const x2a = x2e ? x2e.accessor : xa;
145
154
 
146
- this.xIndexer = createBinningRangeIndexer(50, [
147
- xa(data[0]),
148
- x2a(peek(data)),
149
- ]);
155
+ this.xIndexer = createBinningRangeIndexer(
156
+ 50,
157
+ [xa(data[lo]), x2a(data[hi - 1])],
158
+ xa,
159
+ x2a
160
+ );
150
161
 
151
162
  let lastVertexCount = this.variableBuilder.vertexCount;
152
163
 
@@ -155,21 +166,16 @@ export class GeometryBuilder {
155
166
  */
156
167
  this.addToXIndex = (datum) => {
157
168
  let currentVertexCount = this.variableBuilder.vertexCount;
158
- this.xIndexer(
159
- xa(datum),
160
- x2a(datum),
161
- lastVertexCount,
162
- currentVertexCount
163
- );
169
+ this.xIndexer(datum, lastVertexCount, currentVertexCount);
164
170
  lastVertexCount = currentVertexCount;
165
171
  };
166
172
  } else {
167
173
  this.xIndexer = undefined;
168
174
  /**
169
- * @param {any} datum
175
+ * @param {import("../data/flowNode").Datum} datum
170
176
  */
171
177
  this.addToXIndex = (datum) => {
172
- //
178
+ // nop
173
179
  };
174
180
  }
175
181
  }
@@ -178,7 +184,7 @@ export class GeometryBuilder {
178
184
  * Add the datum to an index, which allows for efficient rendering of ranges
179
185
  * on the x axis. Must be called after a datum has been pushed to the ArrayBuilder.
180
186
  *
181
- * @param {any} datum
187
+ * @param {import("../data/flowNode").Datum} datum
182
188
  */
183
189
  addToXIndex(datum) {
184
190
  //
@@ -207,7 +213,6 @@ export class RectVertexBuilder extends GeometryBuilder {
207
213
  * If the rect is wider than the threshold, tessellate it into pieces
208
214
  * @param {number[]} [object.visibleRange]
209
215
  * @param {number} [object.numItems] Number of data items
210
- * @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
211
216
  */
212
217
  constructor({
213
218
  encoders,
@@ -215,14 +220,12 @@ export class RectVertexBuilder extends GeometryBuilder {
215
220
  tessellationThreshold = Infinity,
216
221
  visibleRange = [-Infinity, Infinity],
217
222
  numItems,
218
- buildXIndex = false,
219
223
  }) {
220
224
  super({
221
225
  encoders,
222
226
  attributes,
223
227
  numVertices:
224
228
  tessellationThreshold == Infinity ? numItems * 6 : undefined,
225
- buildXIndex,
226
229
  });
227
230
 
228
231
  this.visibleRange = visibleRange;
@@ -256,7 +259,7 @@ export class RectVertexBuilder extends GeometryBuilder {
256
259
  const xAccessor = a(e.x);
257
260
  const x2Accessor = a(e.x2);
258
261
 
259
- this.prepareXIndexer(data);
262
+ this.prepareXIndexer(data, lo, hi);
260
263
 
261
264
  const frac = [0, 0];
262
265
  this.updateFrac(frac);
@@ -322,7 +325,6 @@ export class RuleVertexBuilder extends GeometryBuilder {
322
325
  * If the rule is wider than the threshold, tessellate it into pieces
323
326
  * @param {number[]} [object.visibleRange]
324
327
  * @param {number} [object.numItems] Number of data items
325
- * @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
326
328
  */
327
329
  constructor({
328
330
  encoders,
@@ -330,14 +332,12 @@ export class RuleVertexBuilder extends GeometryBuilder {
330
332
  tessellationThreshold = Infinity,
331
333
  visibleRange = [-Infinity, Infinity],
332
334
  numItems,
333
- buildXIndex,
334
335
  }) {
335
336
  super({
336
337
  encoders,
337
338
  attributes,
338
339
  numVertices:
339
340
  tessellationThreshold == Infinity ? numItems * 6 : undefined,
340
- buildXIndex,
341
341
  });
342
342
 
343
343
  this.visibleRange = visibleRange;
@@ -357,7 +357,7 @@ export class RuleVertexBuilder extends GeometryBuilder {
357
357
  addBatch(key, data, lo = 0, hi = data.length) {
358
358
  //const [lower, upper] = this.visibleRange; // TODO
359
359
 
360
- this.prepareXIndexer(data);
360
+ this.prepareXIndexer(data, lo, hi);
361
361
 
362
362
  for (let i = lo; i < hi; i++) {
363
363
  const d = data[i];
@@ -443,7 +443,6 @@ export class TextVertexBuilder extends GeometryBuilder {
443
443
  * @param {import("../fonts/bmFontMetrics").BMFontMetrics} object.fontMetrics
444
444
  * @param {Record<string, any>} object.properties
445
445
  * @param {number} [object.numCharacters] number of characters
446
- * @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
447
446
  * @param {boolean} [object.logoLetters]
448
447
  */
449
448
  constructor({
@@ -452,13 +451,11 @@ export class TextVertexBuilder extends GeometryBuilder {
452
451
  fontMetrics,
453
452
  properties,
454
453
  numCharacters = undefined,
455
- buildXIndex = false,
456
454
  }) {
457
455
  super({
458
456
  encoders,
459
457
  attributes,
460
458
  numVertices: numCharacters * 6, // six vertices per quad (character)
461
- buildXIndex,
462
459
  });
463
460
 
464
461
  this.metadata = fontMetrics;
@@ -524,7 +521,7 @@ export class TextVertexBuilder extends GeometryBuilder {
524
521
  const textureCoord = [0, 0];
525
522
  this.updateTextureCoord(textureCoord);
526
523
 
527
- this.prepareXIndexer(data);
524
+ this.prepareXIndexer(data, lo, hi);
528
525
 
529
526
  for (let i = lo; i < hi; i++) {
530
527
  const d = data[i];
@@ -624,7 +621,7 @@ export class TextVertexBuilder extends GeometryBuilder {
624
621
  x += advance;
625
622
  }
626
623
 
627
- this.addToXIndex(data);
624
+ this.addToXIndex(d);
628
625
  }
629
626
 
630
627
  this.registerBatch(key);
@@ -1,18 +1,18 @@
1
1
  #define PI 3.141593
2
2
 
3
- /** Offset in "unit" units */
4
- uniform mediump vec2 uViewOffset;
3
+ uniform View {
4
+ /** Offset in "unit" units */
5
+ mediump vec2 uViewOffset;
6
+ mediump vec2 uViewScale;
7
+ /** Size of the logical viewport in pixels, i.e., the view */
8
+ mediump vec2 uViewportSize;
9
+ lowp float uDevicePixelRatio;
10
+ // TODO: Views with opacity less than 1.0 should be rendered into a texture
11
+ // that is rendered with the specified opacity.
12
+ lowp float uViewOpacity;
13
+ bool uPickingEnabled;
14
+ };
5
15
 
6
- uniform mediump vec2 uViewScale;
7
-
8
- /** Size of the logical viewport in pixels, i.e., the view */
9
- uniform mediump vec2 uViewportSize;
10
-
11
- uniform lowp float uDevicePixelRatio;
12
-
13
- // TODO: Views with opacity less than 1.0 should be rendered into a texture
14
- // that is rendered with the specified opacity.
15
- uniform lowp float uViewOpacity;
16
16
 
17
17
  /**
18
18
  * Maps a coordinate on the unit scale to a normalized device coordinate.
@@ -1,3 +1 @@
1
- uniform bool uPickingEnabled;
2
-
3
1
  flat in highp vec4 vPickingColor;
@@ -4,8 +4,6 @@
4
4
  * https://deck.gl/docs/developer-guide/custom-layers/picking
5
5
  */
6
6
 
7
- uniform bool uPickingEnabled;
8
-
9
7
  flat out highp vec4 vPickingColor;
10
8
 
11
9
  /**
package/src/marks/link.js CHANGED
@@ -104,7 +104,7 @@ export default class LinkMark extends Mark {
104
104
  numComponents: 2,
105
105
  };
106
106
 
107
- this.rangeMap = vertexData.rangeMap;
107
+ this.rangeMap.migrateEntries(vertexData.rangeMap);
108
108
 
109
109
  this.arrays = Object.fromEntries(
110
110
  Object.entries(vertexData.arrays).map(([k, v]) => [
@@ -124,45 +124,38 @@ export default class LinkMark extends Mark {
124
124
 
125
125
  // TODO: Vertical clipping in faceted view
126
126
 
127
- return this.createRenderCallback(
128
- (offset, count) => {
129
- // We are using instanced drawing here.
130
- // However, WebGL does not provide glDrawArraysInstancedBaseInstance and thus,
131
- // we have to hack with offsets in vertexAttribPointer
132
- // TODO: Use VAOs more intelligently to reduce WebGL calls
133
- // TODO: Explore multiDrawArraysInstancedWEBGL
134
- // There's also a promising extension draft:
135
- // https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_instanced_base_vertex_base_instance/
136
- // (and https://www.khronos.org/registry/webgl/extensions/WEBGL_multi_draw_instanced_base_vertex_base_instance/)
137
-
138
- this.gl.bindVertexArray(this.vertexArrayInfo.vertexArrayObject);
139
-
140
- for (const attribInfoObject of Object.entries(
141
- this.bufferInfo.attribs
142
- )) {
143
- const [attribute, attribInfo] = attribInfoObject;
144
- if (
145
- attribInfo.buffer &&
146
- this.arrays[attribute].numComponents
147
- ) {
148
- attribInfo.offset =
149
- offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
150
- }
127
+ return this.createRenderCallback((offset, count) => {
128
+ // We are using instanced drawing here.
129
+ // However, WebGL does not provide glDrawArraysInstancedBaseInstance and thus,
130
+ // we have to hack with offsets in vertexAttribPointer
131
+ // TODO: Use VAOs more intelligently to reduce WebGL calls
132
+ // TODO: Explore multiDrawArraysInstancedWEBGL
133
+ // There's also a promising extension draft:
134
+ // https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_instanced_base_vertex_base_instance/
135
+ // (and https://www.khronos.org/registry/webgl/extensions/WEBGL_multi_draw_instanced_base_vertex_base_instance/)
136
+
137
+ this.gl.bindVertexArray(this.vertexArrayInfo.vertexArrayObject);
138
+
139
+ for (const attribInfoObject of Object.entries(
140
+ this.bufferInfo.attribs
141
+ )) {
142
+ const [attribute, attribInfo] = attribInfoObject;
143
+ if (attribInfo.buffer && this.arrays[attribute].numComponents) {
144
+ attribInfo.offset =
145
+ offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
151
146
  }
152
- setBuffersAndAttributes(gl, this.programInfo, this.bufferInfo);
153
-
154
- drawBufferInfo(
155
- gl,
156
- this.bufferInfo,
157
- gl.TRIANGLE_STRIP,
158
- (this.properties.segments + 1) * 2, // number of vertices in a triangle strip
159
- 0,
160
- count
161
- );
162
- },
163
- options,
164
- () => this.rangeMap
165
- );
147
+ }
148
+ setBuffersAndAttributes(gl, this.programInfo, this.bufferInfo);
149
+
150
+ drawBufferInfo(
151
+ gl,
152
+ this.bufferInfo,
153
+ gl.TRIANGLE_STRIP,
154
+ (this.properties.segments + 1) * 2, // number of vertices in a triangle strip
155
+ 0,
156
+ count
157
+ );
158
+ }, options);
166
159
  }
167
160
  }
168
161