@genome-spy/core 0.29.0 → 0.30.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/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "contributors": [],
9
9
  "license": "MIT",
10
- "version": "0.29.0",
10
+ "version": "0.30.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": "465004bcc575262ef7cee3932502a3bfee1da4e1"
56
+ "gitHead": "c8fe26f3421a1bec7323428f0bd7da46cda34dba"
57
57
  }
package/src/genomeSpy.js CHANGED
@@ -378,6 +378,7 @@ export default class GenomeSpy {
378
378
  // Now that all data have been loaded, the domains may need adjusting
379
379
  this.viewRoot.visit((view) => {
380
380
  for (const resolution of Object.values(view.resolutions.scale)) {
381
+ // TODO: Don't reconfigure multiple times
381
382
  // IMPORTANT TODO: Check that discrete domains and indexers match!!!!!!!!!
382
383
  resolution.reconfigure();
383
384
  }
@@ -9,7 +9,12 @@ import {
9
9
  } from "twgl.js";
10
10
  import { isArray, isString } from "vega-util";
11
11
 
12
- import { isDiscrete, isDiscretizing, isInterpolating } from "vega-scale";
12
+ import {
13
+ isContinuous,
14
+ isDiscrete,
15
+ isDiscretizing,
16
+ isInterpolating,
17
+ } from "vega-scale";
13
18
  import {
14
19
  createDiscreteColorTexture,
15
20
  createDiscreteTexture,
@@ -321,43 +326,53 @@ export default class WebGLHelper {
321
326
  const props = resolution.getScaleProps();
322
327
 
323
328
  const scale = resolution.getScale();
329
+ const range = /** @type {any[]} */ (scale.range());
324
330
 
325
331
  /** @type {WebGLTexture} */
326
332
  let texture;
327
333
 
328
334
  if (props.scheme) {
329
- let count = isString(props.scheme)
330
- ? undefined
331
- : props.scheme.count;
332
-
333
- count = fixCount(count, scale);
334
-
335
- texture = createSchemeTexture(
336
- props.scheme,
337
- this.gl,
338
- count,
339
- existingTexture
340
- );
341
- } else {
342
- // No scheme, assume that colors are specified in the range
343
-
344
- const range = /** @type {any[]} */ (scale.range());
345
-
346
- if (isInterpolating(scale.type)) {
347
- texture = createInterpolatedColorTexture(
335
+ if (scale.type == "threshold" && range) {
336
+ // Scale initialization may have configured the range. Let's use it.
337
+ texture = createDiscreteColorTexture(
348
338
  range,
349
- props.interpolate,
350
339
  this.gl,
340
+ scale.domain().length,
351
341
  existingTexture
352
342
  );
353
343
  } else {
354
- texture = createDiscreteColorTexture(
355
- range,
344
+ let count = isString(props.scheme)
345
+ ? undefined
346
+ : props.scheme.count;
347
+
348
+ count = fixCount(count, scale);
349
+
350
+ texture = createSchemeTexture(
351
+ props.scheme,
356
352
  this.gl,
357
- scale.domain().length,
353
+ count,
358
354
  existingTexture
359
355
  );
360
356
  }
357
+ } else if (
358
+ // Interpolating
359
+ isInterpolating(scale.type) ||
360
+ // Or piecewise
361
+ (isContinuous(scale.type) && range.length > 2)
362
+ ) {
363
+ texture = createInterpolatedColorTexture(
364
+ range,
365
+ props.interpolate,
366
+ this.gl,
367
+ existingTexture
368
+ );
369
+ } else {
370
+ texture = createDiscreteColorTexture(
371
+ range,
372
+ this.gl,
373
+ scale.domain().length,
374
+ existingTexture
375
+ );
361
376
  }
362
377
 
363
378
  this.rangeTextures.set(resolution, texture);
@@ -314,7 +314,7 @@ export function generateScaleGlsl(channel, scale, channelDef) {
314
314
  if (piecewise) {
315
315
  // TODO: Handle range correctly. Now this assumes unit range.
316
316
  scaleBody.push(
317
- `transformed = (float(slot) + transformed) / (float(${name}.length()) - 1.0);`
317
+ `transformed = (float(slot) + transformed) / (float(${name}.length() - 1));`
318
318
  );
319
319
  }
320
320
  } else {
@@ -463,26 +463,40 @@ export function isHighPrecisionScale(type) {
463
463
  return type == "index" || type == "locus";
464
464
  }
465
465
 
466
+ // Maximum precise index number is 2^(23 + 11) ~ 17G
467
+ // Higher number increases precision but makes zooming unstable
468
+ const BS = 2 ** 11;
469
+ const BM = BS - 1;
470
+
466
471
  /**
467
- * @param {number} x
472
+ * @param {number} x Must be an integer
468
473
  * @param {number[]} [arr]
469
474
  */
470
- export function splitHighPrecision(x, arr) {
471
- // Maximum precise index number is 2^(23 + 11) ~ 17G
472
- // Higher number increases precision but makes zooming unstable
473
- const bs = 2 ** 11;
474
-
475
- const lo = x % bs;
476
- const hi = Math.round(x - lo);
477
- arr ??= [];
475
+ export function splitHighPrecision(x, arr = []) {
476
+ // Using a bitmask is MUCH faster than using modulo (at least on Chrome 112)
477
+ // https://www.wikiwand.com/en/Modulo#Performance_issues
478
+ const lo = x & BM;
479
+ const hi = x - lo;
480
+
478
481
  arr[0] = hi;
479
482
  arr[1] = lo;
483
+
480
484
  return arr;
481
485
  }
482
486
 
487
+ /**
488
+ * @param {number} x
489
+ */
490
+ function exactSplitHighPrecision(x) {
491
+ const lo = x % BS;
492
+ const hi = x - lo;
493
+
494
+ return [hi, lo];
495
+ }
496
+
483
497
  /**
484
498
  * @param {number[]} domain
485
499
  */
486
500
  export function toHighPrecisionDomainUniform(domain) {
487
- return [...splitHighPrecision(domain[0]), domain[1] - domain[0]];
501
+ return [...exactSplitHighPrecision(domain[0]), domain[1] - domain[0]];
488
502
  }
@@ -220,6 +220,12 @@ export default class ScaleResolution {
220
220
  props.domain = domain;
221
221
  } else if (isDiscrete(props.type)) {
222
222
  props.domain = new NominalDomain();
223
+ } else if (props.scheme) {
224
+ // An initial domain is required when using a scheme.
225
+ // Otherwise configureScale() does something weird when the domain is set later,
226
+ // resulting in an interpolator between just two colors.
227
+ // TODO: Fix configureScale().
228
+ props.domain = [0, 1];
223
229
  }
224
230
 
225
231
  if (!props.domain && props.domainMid !== undefined) {
package/src/view/zoom.js CHANGED
@@ -30,10 +30,10 @@ export default function interactionToZoom(event, coords, handleZoom, hover) {
30
30
 
31
31
  if (hover) {
32
32
  const e = hover.mark.encoders;
33
- if (e.x && !e.x2) {
33
+ if (e.x && !e.x2 && !e.x.constantValue) {
34
34
  x = +e.x(hover.datum) * coords.width + coords.x;
35
35
  }
36
- if (e.y && !e.y2) {
36
+ if (e.y && !e.y2 && !e.y.constantValue) {
37
37
  y = (1 - +e.y(hover.datum)) * coords.height + coords.y;
38
38
  }
39
39
  }