@genome-spy/core 0.14.0 → 0.16.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/LICENSE +24 -0
- package/README.md +16 -0
- package/dist/genome-spy-schema.json +4068 -0
- package/dist/index.js +42 -42
- package/dist/style.css +1 -1
- package/package.json +5 -4
- package/src/data/sources/dataUtils.js +50 -3
- package/src/data/sources/dynamicCallbackSource.js +2 -1
- package/src/data/sources/dynamicSource.js +2 -1
- package/src/data/sources/inlineSource.js +3 -5
- package/src/data/sources/namedSource.js +3 -7
- package/src/data/sources/urlSource.js +1 -1
- package/src/data/transforms/aggregate.js +1 -0
- package/src/data/transforms/flattenDelimited.js +6 -0
- package/src/data/transforms/regexFold.js +1 -1
- package/src/data/transforms/stack.js +3 -3
- package/src/embedApi.d.ts +59 -0
- package/src/encoder/accessor.js +6 -6
- package/src/encoder/encoder.js +47 -22
- package/src/genome/scaleIndex.d.ts +38 -0
- package/src/genome/scaleIndex.js +18 -52
- package/src/genome/scaleLocus.d.ts +11 -0
- package/src/genome/scaleLocus.js +12 -16
- package/src/genomeSpy.js +2 -5
- package/src/gl/dataToVertices.js +14 -6
- package/src/gl/includes/fp64-utils.js +10 -0
- package/src/gl/includes/scales.glsl +2 -0
- package/src/gl/webGLHelper.js +3 -1
- package/src/index.js +9 -35
- package/src/marks/link.js +1 -12
- package/src/marks/mark.js +27 -5
- package/src/marks/markUtils.js +41 -25
- package/src/marks/pointMark.js +5 -2
- package/src/marks/rule.js +11 -2
- package/src/scale/glslScaleGenerator.js +16 -29
- package/src/scale/scale.js +10 -0
- package/src/scale/ticks.js +11 -6
- package/src/spec/channel.d.ts +343 -43
- package/src/spec/data.d.ts +14 -3
- package/src/spec/scale.d.ts +18 -1
- package/src/spec/view.d.ts +12 -6
- package/src/tooltip/refseqGeneTooltipHandler.js +1 -0
- package/src/types/filetypes.d.ts +10 -0
- package/src/types/internmap.d.ts +22 -0
- package/src/types/vega-loader.d.ts +1 -0
- package/src/utils/arrayUtils.js +12 -6
- package/src/utils/cloner.js +5 -3
- package/src/utils/concatIterables.js +2 -2
- package/src/utils/domainArray.js +0 -8
- package/src/utils/propertyCoalescer.js +9 -4
- package/src/view/axisResolution.js +11 -6
- package/src/view/axisView.js +8 -5
- package/src/view/decoratorView.js +6 -3
- package/src/view/facetView.js +3 -0
- package/src/view/flowBuilder.js +2 -1
- package/src/view/renderingContext/svgViewRenderingContext.js +7 -3
- package/src/view/scaleResolution.js +52 -32
- package/src/view/testUtils.js +7 -4
- package/src/view/unitView.js +15 -9
- package/src/view/view.js +10 -8
- package/src/view/viewFactory.js +2 -0
- package/src/view/viewUtils.js +4 -4
- package/src/options.d.ts +0 -9
- package/src/utils/fisheye.js +0 -60
- package/src/utils/html.js +0 -23
- package/src/utils/html.test.js +0 -13
- package/src/view/channel.js +0 -5
package/src/genome/scaleIndex.js
CHANGED
|
@@ -6,6 +6,8 @@ const minimumDomainSpan = 1;
|
|
|
6
6
|
/**
|
|
7
7
|
* Creates a "index" scale, which works similarly to d3's band scale but the domain
|
|
8
8
|
* consists of integer indexes.
|
|
9
|
+
*
|
|
10
|
+
* @returns {import("./scaleIndex").ScaleIndex}
|
|
9
11
|
*/
|
|
10
12
|
export default function scaleIndex() {
|
|
11
13
|
let domain = [0, 1];
|
|
@@ -21,29 +23,20 @@ export default function scaleIndex() {
|
|
|
21
23
|
/** The number of the first element. This affects the generated ticks and their labels. */
|
|
22
24
|
let numberingOffset = 0;
|
|
23
25
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
* @param {number} x
|
|
27
|
-
*/
|
|
28
|
-
function scale(x) {
|
|
29
|
-
// In principle, the domain consists of integer indices. However,
|
|
30
|
-
// we accept real numbers so that items can be centered inside a band.
|
|
31
|
-
// TODO: paddingInner/paddingOuter/align. Now they are implemented in GLSL.
|
|
32
|
-
return ((x - domain[0]) / domainSpan) * rangeSpan + range[0];
|
|
33
|
-
}
|
|
26
|
+
const scaleFunction = (/** @type {number} */ x) =>
|
|
27
|
+
((x - domain[0]) / domainSpan) * rangeSpan + range[0];
|
|
34
28
|
|
|
35
29
|
/**
|
|
30
|
+
* In principle, the domain consists of integer indices. However,
|
|
31
|
+
* we accept real numbers so that items can be centered inside a band.
|
|
36
32
|
*
|
|
37
|
-
* @
|
|
33
|
+
* @type {import("./scaleIndex").ScaleIndex}
|
|
38
34
|
*/
|
|
39
|
-
scale
|
|
40
|
-
return ((y - range[0]) / rangeSpan) * domainSpan + domain[0];
|
|
41
|
-
};
|
|
35
|
+
const scale = /** @type {any} */ (scaleFunction);
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*/
|
|
37
|
+
scale.invert = (y) => ((y - range[0]) / rangeSpan) * domainSpan + domain[0];
|
|
38
|
+
|
|
39
|
+
// @ts-expect-error
|
|
47
40
|
scale.domain = function (_) {
|
|
48
41
|
if (arguments.length) {
|
|
49
42
|
domain = extent(_);
|
|
@@ -62,10 +55,7 @@ export default function scaleIndex() {
|
|
|
62
55
|
}
|
|
63
56
|
};
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
* @param {Iterable<number>} [_]
|
|
68
|
-
*/
|
|
58
|
+
// @ts-expect-error
|
|
69
59
|
scale.range = function (_) {
|
|
70
60
|
if (arguments.length) {
|
|
71
61
|
range = [..._];
|
|
@@ -76,10 +66,7 @@ export default function scaleIndex() {
|
|
|
76
66
|
}
|
|
77
67
|
};
|
|
78
68
|
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* @param {number} [_]
|
|
82
|
-
*/
|
|
69
|
+
// @ts-expect-error
|
|
83
70
|
scale.numberingOffset = function (_) {
|
|
84
71
|
if (arguments.length) {
|
|
85
72
|
numberingOffset = _;
|
|
@@ -89,10 +76,7 @@ export default function scaleIndex() {
|
|
|
89
76
|
}
|
|
90
77
|
};
|
|
91
78
|
|
|
92
|
-
|
|
93
|
-
*
|
|
94
|
-
* @param {number} _
|
|
95
|
-
*/
|
|
79
|
+
// @ts-expect-error
|
|
96
80
|
scale.padding = function (_) {
|
|
97
81
|
if (arguments.length) {
|
|
98
82
|
paddingOuter = _;
|
|
@@ -103,10 +87,7 @@ export default function scaleIndex() {
|
|
|
103
87
|
}
|
|
104
88
|
};
|
|
105
89
|
|
|
106
|
-
|
|
107
|
-
*
|
|
108
|
-
* @param {number} _
|
|
109
|
-
*/
|
|
90
|
+
// @ts-expect-error
|
|
110
91
|
scale.paddingInner = function (_) {
|
|
111
92
|
if (arguments.length) {
|
|
112
93
|
paddingInner = Math.min(1, _);
|
|
@@ -116,10 +97,7 @@ export default function scaleIndex() {
|
|
|
116
97
|
}
|
|
117
98
|
};
|
|
118
99
|
|
|
119
|
-
|
|
120
|
-
*
|
|
121
|
-
* @param {number} _
|
|
122
|
-
*/
|
|
100
|
+
// @ts-expect-error
|
|
123
101
|
scale.paddingOuter = function (_) {
|
|
124
102
|
if (arguments.length) {
|
|
125
103
|
paddingOuter = _;
|
|
@@ -129,10 +107,7 @@ export default function scaleIndex() {
|
|
|
129
107
|
}
|
|
130
108
|
};
|
|
131
109
|
|
|
132
|
-
|
|
133
|
-
*
|
|
134
|
-
* @param {number} _
|
|
135
|
-
*/
|
|
110
|
+
// @ts-expect-error
|
|
136
111
|
scale.align = function (_) {
|
|
137
112
|
if (arguments.length) {
|
|
138
113
|
align = Math.max(0, Math.min(1, _));
|
|
@@ -146,10 +121,6 @@ export default function scaleIndex() {
|
|
|
146
121
|
|
|
147
122
|
scale.bandwidth = () => scale.step();
|
|
148
123
|
|
|
149
|
-
/**
|
|
150
|
-
* @param {number} count
|
|
151
|
-
* @returns {number[]}
|
|
152
|
-
*/
|
|
153
124
|
scale.ticks = (count) => {
|
|
154
125
|
const align = /** @type {number} */ (scale.align());
|
|
155
126
|
const offset = /** @type {number} */ (scale.numberingOffset());
|
|
@@ -162,12 +133,7 @@ export default function scaleIndex() {
|
|
|
162
133
|
.map((x) => x - numberingOffset);
|
|
163
134
|
};
|
|
164
135
|
|
|
165
|
-
|
|
166
|
-
*
|
|
167
|
-
* @param {number} [count]
|
|
168
|
-
* @param {string} [specifier]
|
|
169
|
-
*/
|
|
170
|
-
scale.tickFormat = function (count, specifier) {
|
|
136
|
+
scale.tickFormat = (count, specifier) => {
|
|
171
137
|
if (specifier) {
|
|
172
138
|
throw new Error(
|
|
173
139
|
"Index scale's tickFormat does not support a specifier!"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import Genome from "./genome";
|
|
2
|
+
import { ScaleIndex } from "./scaleIndex";
|
|
3
|
+
|
|
4
|
+
export default function scaleLocus(): ScaleLocus;
|
|
5
|
+
|
|
6
|
+
export interface ScaleLocus extends ScaleIndex {
|
|
7
|
+
genome(): Genome;
|
|
8
|
+
genome(genome: Genome): this;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isScaleLocus(scale: any): scale is ScaleLocus;
|
package/src/genome/scaleLocus.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import { tickStep } from "d3-array";
|
|
2
2
|
import { format as d3format } from "d3-format";
|
|
3
|
-
import scaleIndex from "./scaleIndex";
|
|
3
|
+
import scaleIndex from "./scaleIndex.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates a "locus" scale, which works similarly to band scale but the domain
|
|
7
7
|
* consists of integer indexes.
|
|
8
8
|
*
|
|
9
9
|
* @typedef {import("./genome").default} Genome
|
|
10
|
+
* @returns {import("./scaleLocus").ScaleLocus}
|
|
10
11
|
*/
|
|
11
12
|
export default function scaleLocus() {
|
|
12
|
-
|
|
13
|
+
/** @type {import("./scaleLocus").ScaleLocus} */
|
|
14
|
+
const scale = /** @type {any} */ (scaleIndex().numberingOffset(1));
|
|
13
15
|
|
|
14
16
|
/** @type {Genome} */
|
|
15
17
|
let genome;
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
* @param {Genome} [_]
|
|
20
|
-
* @deprecated
|
|
21
|
-
*/
|
|
19
|
+
// @ts-expect-error
|
|
22
20
|
scale.genome = function (_) {
|
|
23
21
|
if (arguments.length) {
|
|
24
22
|
genome = _;
|
|
@@ -28,10 +26,6 @@ export default function scaleLocus() {
|
|
|
28
26
|
}
|
|
29
27
|
};
|
|
30
28
|
|
|
31
|
-
/**
|
|
32
|
-
* @param {number} count
|
|
33
|
-
* @returns {number[]}
|
|
34
|
-
*/
|
|
35
29
|
scale.ticks = (count) => {
|
|
36
30
|
if (!genome) {
|
|
37
31
|
return [];
|
|
@@ -68,11 +62,6 @@ export default function scaleLocus() {
|
|
|
68
62
|
return ticks;
|
|
69
63
|
};
|
|
70
64
|
|
|
71
|
-
/**
|
|
72
|
-
*
|
|
73
|
-
* @param {number} [count]
|
|
74
|
-
* @param {string} [specifier]
|
|
75
|
-
*/
|
|
76
65
|
scale.tickFormat = (count, specifier) => {
|
|
77
66
|
if (!genome) {
|
|
78
67
|
return;
|
|
@@ -110,3 +99,10 @@ export default function scaleLocus() {
|
|
|
110
99
|
|
|
111
100
|
return scale;
|
|
112
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @type {import("./scaleLocus").isScaleLocus}
|
|
105
|
+
*/
|
|
106
|
+
export function isScaleLocus(scale) {
|
|
107
|
+
return scale.type == "locus";
|
|
108
|
+
}
|
package/src/genomeSpy.js
CHANGED
|
@@ -54,15 +54,12 @@ vegaScale("null", scaleNull, []);
|
|
|
54
54
|
|
|
55
55
|
vegaFormats("fasta", fasta);
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
* The actual browser without any toolbars etc
|
|
59
|
-
*/
|
|
60
57
|
export default class GenomeSpy {
|
|
61
58
|
/**
|
|
62
59
|
*
|
|
63
60
|
* @param {HTMLElement} container
|
|
64
61
|
* @param {RootSpec} spec
|
|
65
|
-
* @param {import("./
|
|
62
|
+
* @param {import("./embedApi").EmbedOptions} [options]
|
|
66
63
|
*/
|
|
67
64
|
constructor(container, spec, options = {}) {
|
|
68
65
|
this.container = container;
|
|
@@ -133,7 +130,7 @@ export default class GenomeSpy {
|
|
|
133
130
|
|
|
134
131
|
/**
|
|
135
132
|
*
|
|
136
|
-
* @param {
|
|
133
|
+
* @param {(name: string) => any[]} provider
|
|
137
134
|
*/
|
|
138
135
|
registerNamedDataProvider(provider) {
|
|
139
136
|
this.namedDataProviders.unshift(provider);
|
package/src/gl/dataToVertices.js
CHANGED
|
@@ -6,6 +6,7 @@ import ArrayBuilder from "./arrayBuilder";
|
|
|
6
6
|
import { SDF_PADDING } from "../fonts/bmFontMetrics";
|
|
7
7
|
import { peek } from "../utils/arrayUtils";
|
|
8
8
|
import createBinningRangeIndexer from "../utils/binnedRangeIndex";
|
|
9
|
+
import { isValueDef } from "../encoder/encoder";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {object} RangeEntry Represents a location of a vertex subset
|
|
@@ -56,6 +57,8 @@ export class GeometryBuilder {
|
|
|
56
57
|
const doubleArray = [0, 0];
|
|
57
58
|
const fp64 = ce.scale.fp64;
|
|
58
59
|
|
|
60
|
+
const indexer = ce.indexer;
|
|
61
|
+
|
|
59
62
|
/**
|
|
60
63
|
* Discrete variables both numeric and strings must be "indexed",
|
|
61
64
|
* 64 bit floats must be converted to vec2.
|
|
@@ -63,8 +66,8 @@ export class GeometryBuilder {
|
|
|
63
66
|
*
|
|
64
67
|
* @type {function(any):(number | number[])}
|
|
65
68
|
*/
|
|
66
|
-
const f =
|
|
67
|
-
?
|
|
69
|
+
const f = indexer
|
|
70
|
+
? (d) => indexer(accessor(d))
|
|
68
71
|
: fp64
|
|
69
72
|
? (d) => fp64ify(accessor(d), doubleArray)
|
|
70
73
|
: accessor;
|
|
@@ -462,10 +465,15 @@ export class TextVertexBuilder extends GeometryBuilder {
|
|
|
462
465
|
|
|
463
466
|
const e = encoders;
|
|
464
467
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
468
|
+
const channelDef =
|
|
469
|
+
/** @type {import("../spec/channel").TextDef<string>} */ (
|
|
470
|
+
e.text.channelDef
|
|
471
|
+
);
|
|
472
|
+
/** @type {(value: any) => string} */
|
|
473
|
+
this.numberFormat =
|
|
474
|
+
!isValueDef(channelDef) && channelDef.format
|
|
475
|
+
? format(channelDef.format)
|
|
476
|
+
: (d) => d;
|
|
469
477
|
|
|
470
478
|
this.updateVertexCoord = this.variableBuilder.createUpdater(
|
|
471
479
|
"vertexCoord",
|
|
@@ -45,6 +45,9 @@ export function fp64LowPart(a) {
|
|
|
45
45
|
return a - Math.fround(a);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @param {WebGLRenderingContext | WebGL2RenderingContext} gl
|
|
50
|
+
*/
|
|
48
51
|
export function getPlatformShaderDefines(gl) {
|
|
49
52
|
const debugInfo = getContextInfo(gl);
|
|
50
53
|
|
|
@@ -94,6 +97,9 @@ const GL_RENDERER = 0x1f01;
|
|
|
94
97
|
const GL_VERSION = 0x1f02;
|
|
95
98
|
const GL_SHADING_LANGUAGE_VERSION = 0x8b8c;
|
|
96
99
|
|
|
100
|
+
/**
|
|
101
|
+
* @param {WebGLRenderingContext | WebGL2RenderingContext} gl
|
|
102
|
+
*/
|
|
97
103
|
export function getContextInfo(gl) {
|
|
98
104
|
const info = gl.getExtension("WEBGL_debug_renderer_info");
|
|
99
105
|
const vendor = gl.getParameter(
|
|
@@ -113,6 +119,10 @@ export function getContextInfo(gl) {
|
|
|
113
119
|
return gpuInfo;
|
|
114
120
|
}
|
|
115
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {string} vendor
|
|
124
|
+
* @param {string} renderer
|
|
125
|
+
*/
|
|
116
126
|
function identifyGPUVendor(vendor, renderer) {
|
|
117
127
|
if (vendor.match(/NVIDIA/i) || renderer.match(/NVIDIA/i)) {
|
|
118
128
|
return "NVIDIA";
|
|
@@ -70,6 +70,8 @@ float scaleBand(float value, vec2 domainExtent, vec2 range,
|
|
|
70
70
|
|
|
71
71
|
float n = domainExtent[1] - domainExtent[0];
|
|
72
72
|
|
|
73
|
+
paddingInner = int(n) > 1 ? paddingInner : 0.0;
|
|
74
|
+
|
|
73
75
|
// Adapted from: https://github.com/d3/d3-scale/blob/master/src/band.js
|
|
74
76
|
float step = (stop - start) / max(1.0, n - paddingInner + paddingOuter * 2.0);
|
|
75
77
|
start += (stop - start - step * (n - paddingInner)) * align;
|
package/src/gl/webGLHelper.js
CHANGED
|
@@ -395,6 +395,8 @@ export default class WebGLHelper {
|
|
|
395
395
|
* Copy-pasted from twgl.js:
|
|
396
396
|
* https://github.com/greggman/twgl.js/blob/master/src/programs.js
|
|
397
397
|
* Copyright 2019 Gregg Tavares, MIT license
|
|
398
|
+
*
|
|
399
|
+
* @param {string} src
|
|
398
400
|
*/
|
|
399
401
|
function addLineNumbersWithError(src, log = "", lineOffset = 0) {
|
|
400
402
|
const errorRE = /ERROR:\s*\d+:(\d+)/gi;
|
|
@@ -426,7 +428,7 @@ function addLineNumbersWithError(src, log = "", lineOffset = 0) {
|
|
|
426
428
|
* @param {WebGLShader} fragmentShader
|
|
427
429
|
*/
|
|
428
430
|
export function createProgram(gl, vertexShader, fragmentShader) {
|
|
429
|
-
|
|
431
|
+
const program = gl.createProgram();
|
|
430
432
|
gl.attachShader(program, vertexShader);
|
|
431
433
|
gl.attachShader(program, fragmentShader);
|
|
432
434
|
gl.linkProgram(program);
|
package/src/index.js
CHANGED
|
@@ -10,9 +10,7 @@ export { GenomeSpy, html, icon };
|
|
|
10
10
|
/**
|
|
11
11
|
* Embeds GenomeSpy into the DOM
|
|
12
12
|
*
|
|
13
|
-
* @
|
|
14
|
-
* @param {object | string} spec a spec object or an url to a json spec
|
|
15
|
-
* @param {import("./options.js").EmbedOptions} [options] options
|
|
13
|
+
* @type {import("./embedApi.js").EmbedFunction}
|
|
16
14
|
*/
|
|
17
15
|
export async function embed(el, spec, options = {}) {
|
|
18
16
|
/** @type {HTMLElement} */
|
|
@@ -33,19 +31,11 @@ export async function embed(el, spec, options = {}) {
|
|
|
33
31
|
let genomeSpy;
|
|
34
32
|
|
|
35
33
|
try {
|
|
36
|
-
const specObject =
|
|
37
|
-
isObject(spec) ? spec : await loadSpec(spec)
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
specObject.baseUrl = specObject.baseUrl || "";
|
|
34
|
+
const specObject = isObject(spec) ? spec : await loadSpec(spec);
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!("padding" in specObject)) {
|
|
47
|
-
specObject.padding = 10;
|
|
48
|
-
}
|
|
36
|
+
specObject.baseUrl ??= "";
|
|
37
|
+
specObject.width ??= "container";
|
|
38
|
+
specObject.padding ??= 10;
|
|
49
39
|
|
|
50
40
|
if (element == document.body) {
|
|
51
41
|
// Need to add a wrapper to make sizing behavior more stable
|
|
@@ -74,10 +64,6 @@ export async function embed(el, spec, options = {}) {
|
|
|
74
64
|
}
|
|
75
65
|
},
|
|
76
66
|
|
|
77
|
-
/**
|
|
78
|
-
* @param {string} type
|
|
79
|
-
* @param {(event: any) => void} listener
|
|
80
|
-
*/
|
|
81
67
|
addEventListener(type, listener) {
|
|
82
68
|
const listenersByType = genomeSpy._eventListeners;
|
|
83
69
|
|
|
@@ -90,20 +76,12 @@ export async function embed(el, spec, options = {}) {
|
|
|
90
76
|
listeners.add(listener);
|
|
91
77
|
},
|
|
92
78
|
|
|
93
|
-
/**
|
|
94
|
-
* @param {string} type
|
|
95
|
-
* @param {(event: any) => void} listener
|
|
96
|
-
*/
|
|
97
79
|
removeEventListener(type, listener) {
|
|
98
80
|
const listenersByType = genomeSpy._eventListeners;
|
|
99
81
|
|
|
100
82
|
listenersByType.get(type)?.delete(listener);
|
|
101
83
|
},
|
|
102
84
|
|
|
103
|
-
/**
|
|
104
|
-
* @param {string} name
|
|
105
|
-
* @returns {import("./view/scaleResolutionApi").ScaleResolutionApi}
|
|
106
|
-
*/
|
|
107
85
|
getScaleResolutionByName(name) {
|
|
108
86
|
return genomeSpy.getNamedScaleResolutions().get(name);
|
|
109
87
|
},
|
|
@@ -113,15 +91,11 @@ export async function embed(el, spec, options = {}) {
|
|
|
113
91
|
/**
|
|
114
92
|
*
|
|
115
93
|
* @param {import("./genomeSpy").default} genomeSpy
|
|
116
|
-
* @param {
|
|
94
|
+
* @param {import("./embedApi.js").EmbedOptions} options options
|
|
117
95
|
*/
|
|
118
|
-
function applyOptions(genomeSpy,
|
|
119
|
-
if (
|
|
120
|
-
genomeSpy.registerNamedDataProvider(
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (opt.beforeLaunchCallback) {
|
|
124
|
-
opt.beforeLaunchCallback(genomeSpy);
|
|
96
|
+
function applyOptions(genomeSpy, options) {
|
|
97
|
+
if (options.namedDataProvider) {
|
|
98
|
+
genomeSpy.registerNamedDataProvider(options.namedDataProvider);
|
|
125
99
|
}
|
|
126
100
|
}
|
|
127
101
|
|
package/src/marks/link.js
CHANGED
|
@@ -20,9 +20,7 @@ export default class LinkMark extends Mark {
|
|
|
20
20
|
y: 0.0,
|
|
21
21
|
y2: undefined,
|
|
22
22
|
size: 1.0,
|
|
23
|
-
size2: undefined,
|
|
24
23
|
color: "black",
|
|
25
|
-
color2: undefined,
|
|
26
24
|
opacity: 1.0,
|
|
27
25
|
|
|
28
26
|
segments: 101, // Performance is affected more by the fill rate, i.e. number of pixels
|
|
@@ -41,24 +39,15 @@ export default class LinkMark extends Mark {
|
|
|
41
39
|
"y",
|
|
42
40
|
"y2",
|
|
43
41
|
"size",
|
|
44
|
-
"size2",
|
|
45
42
|
"height",
|
|
46
43
|
"color",
|
|
47
|
-
"color2",
|
|
48
44
|
"opacity",
|
|
49
45
|
];
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
/** @return {import("../spec/channel").Channel[]} */
|
|
53
49
|
getSupportedChannels() {
|
|
54
|
-
return [
|
|
55
|
-
...super.getSupportedChannels(),
|
|
56
|
-
"x2",
|
|
57
|
-
"y2",
|
|
58
|
-
"size",
|
|
59
|
-
"size2",
|
|
60
|
-
"color2",
|
|
61
|
-
];
|
|
50
|
+
return [...super.getSupportedChannels(), "x2", "y2", "size"];
|
|
62
51
|
}
|
|
63
52
|
|
|
64
53
|
/**
|
package/src/marks/mark.js
CHANGED
|
@@ -11,6 +11,7 @@ import { isDiscrete } from "vega-scale";
|
|
|
11
11
|
import { fp64ify } from "../gl/includes/fp64-utils";
|
|
12
12
|
import createEncoders, {
|
|
13
13
|
isChannelDefWithScale,
|
|
14
|
+
isDatumDef,
|
|
14
15
|
isValueDef,
|
|
15
16
|
} from "../encoder/encoder";
|
|
16
17
|
import {
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
generateValueGlsl,
|
|
19
20
|
generateScaleGlsl,
|
|
20
21
|
RANGE_TEXTURE_PREFIX,
|
|
22
|
+
ATTRIBUTE_PREFIX,
|
|
21
23
|
} from "../scale/glslScaleGenerator";
|
|
22
24
|
import FP64 from "../gl/includes/fp64-arithmetic.glsl";
|
|
23
25
|
import GLSL_COMMON from "../gl/includes/common.glsl";
|
|
@@ -29,6 +31,7 @@ import GLSL_PICKING_FRAGMENT from "../gl/includes/picking.fragment.glsl";
|
|
|
29
31
|
import { getCachedOrCall } from "../utils/propertyCacher";
|
|
30
32
|
import { createProgram } from "../gl/webGLHelper";
|
|
31
33
|
import coalesceProperties from "../utils/propertyCoalescer";
|
|
34
|
+
import { isScalar } from "../utils/variableTools";
|
|
32
35
|
|
|
33
36
|
export const SAMPLE_FACET_UNIFORM = "SAMPLE_FACET_UNIFORM";
|
|
34
37
|
export const SAMPLE_FACET_TEXTURE = "SAMPLE_FACET_TEXTURE";
|
|
@@ -159,8 +162,6 @@ export default class Mark {
|
|
|
159
162
|
if (this.isPickingParticipant()) {
|
|
160
163
|
encoding.uniqueId = {
|
|
161
164
|
field: "_uniqueId", // TODO: Use constant
|
|
162
|
-
type: "nominal",
|
|
163
|
-
scale: null,
|
|
164
165
|
};
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -191,9 +192,7 @@ export default class Mark {
|
|
|
191
192
|
const propToValueDef = (property) => {
|
|
192
193
|
const value =
|
|
193
194
|
this.properties[/** @type {keyof MarkConfig} */ (property)];
|
|
194
|
-
|
|
195
|
-
return { value };
|
|
196
|
-
//}
|
|
195
|
+
return isScalar(value) && { value };
|
|
197
196
|
};
|
|
198
197
|
|
|
199
198
|
const propertyValues = Object.fromEntries(
|
|
@@ -401,6 +400,28 @@ export default class Mark {
|
|
|
401
400
|
"Domains"
|
|
402
401
|
);
|
|
403
402
|
}
|
|
403
|
+
|
|
404
|
+
this.gl.useProgram(this.programInfo.program);
|
|
405
|
+
|
|
406
|
+
this._setDatums();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
_setDatums() {
|
|
410
|
+
for (const [channel, channelDef] of Object.entries(this.encoding)) {
|
|
411
|
+
if (isDatumDef(channelDef)) {
|
|
412
|
+
const encoder = this.encoders[channel];
|
|
413
|
+
|
|
414
|
+
const datum = encoder.indexer
|
|
415
|
+
? encoder.indexer(channelDef.datum)
|
|
416
|
+
: encoder.scale.fp64
|
|
417
|
+
? fp64ify(+channelDef.datum)
|
|
418
|
+
: +channelDef.datum;
|
|
419
|
+
|
|
420
|
+
setUniforms(this.programInfo, {
|
|
421
|
+
[ATTRIBUTE_PREFIX + channel]: datum,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
404
425
|
}
|
|
405
426
|
|
|
406
427
|
/**
|
|
@@ -656,6 +677,7 @@ export default class Mark {
|
|
|
656
677
|
// inferior performance. Based on profiling, this optimization gives
|
|
657
678
|
// a significant performance boost.
|
|
658
679
|
this.gl.uniform4f(
|
|
680
|
+
// @ts-expect-error
|
|
659
681
|
locationSetter.location, // TODO: Make a twgl pull request to fix typing
|
|
660
682
|
pos,
|
|
661
683
|
height,
|
package/src/marks/markUtils.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isValueDef,
|
|
3
|
+
getSecondaryChannel,
|
|
4
|
+
isChannelDefWithScale,
|
|
5
|
+
} from "../encoder/encoder";
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
*
|
|
@@ -8,46 +12,58 @@ import { isValueDef, getSecondaryChannel } from "../encoder/encoder";
|
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* @param {Encoding} encoding
|
|
11
|
-
* @param {
|
|
15
|
+
* @param {import("../spec/channel").PrimaryPositionalChannel} channel
|
|
12
16
|
*/
|
|
13
17
|
export function fixPositional(encoding, channel) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const secondaryChannel = getSecondaryChannel(channel);
|
|
19
|
+
|
|
20
|
+
// Must make copies because the definition may be shared with other views/marks
|
|
21
|
+
let primary = encoding[channel] && { ...encoding[channel] };
|
|
22
|
+
let secondary = encoding[secondaryChannel] && {
|
|
23
|
+
...encoding[secondaryChannel],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if (isValueDef(primary) || isValueDef(secondary)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (primary) {
|
|
31
|
+
// TODO: fix. May not be a proper type guard.
|
|
32
|
+
if (!isChannelDefWithScale(encoding[channel])) {
|
|
33
|
+
// nop
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!secondary) {
|
|
38
|
+
if (primary.type == "quantitative") {
|
|
18
39
|
// Bar plot, anchor the other end to zero
|
|
19
|
-
|
|
20
|
-
datum: 0,
|
|
21
|
-
};
|
|
40
|
+
secondary = { datum: 0 };
|
|
22
41
|
} else {
|
|
23
|
-
|
|
24
|
-
encoding[channel] = { ...encoding[channel] };
|
|
25
|
-
encoding[secondary] = { ...encoding[channel] };
|
|
42
|
+
secondary = { ...primary };
|
|
26
43
|
|
|
27
44
|
// Fill the bands (bar plot / heatmap)
|
|
28
45
|
// We are following the Vega-Lite convention:
|
|
29
46
|
// the band property works differently on rectangular marks, i.e., it adjusts the band coverage.
|
|
30
|
-
const adjustment = (1 - (
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
const adjustment = (1 - (primary.band ?? 1)) / 2;
|
|
48
|
+
primary.band = 0 + adjustment;
|
|
49
|
+
secondary.band = 1 - adjustment;
|
|
33
50
|
|
|
34
51
|
// TODO: If the secondary channel duplicates the primary channel
|
|
35
52
|
// the data should be uploaded to the GPU only once.
|
|
36
53
|
}
|
|
37
|
-
} else if (
|
|
38
|
-
const adjustment = (1 - (
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
} else if (primary.type != "quantitative") {
|
|
55
|
+
const adjustment = (1 - (primary.band || 1)) / 2;
|
|
56
|
+
primary.band = adjustment;
|
|
57
|
+
secondary.band = -adjustment;
|
|
41
58
|
}
|
|
42
|
-
} else if (encoding[secondary]) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`Only secondary channel ${secondary} has been specified!`
|
|
45
|
-
);
|
|
46
59
|
} else {
|
|
47
60
|
// Nothing specified, fill the whole viewport
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
primary = { value: 0 };
|
|
62
|
+
secondary = { value: 1 };
|
|
50
63
|
}
|
|
64
|
+
|
|
65
|
+
encoding[channel] = primary;
|
|
66
|
+
encoding[secondaryChannel] = secondary;
|
|
51
67
|
}
|
|
52
68
|
|
|
53
69
|
/**
|
package/src/marks/pointMark.js
CHANGED
|
@@ -178,7 +178,9 @@ export default class PointMark extends Mark {
|
|
|
178
178
|
if (e.constant) {
|
|
179
179
|
return e(null);
|
|
180
180
|
} else {
|
|
181
|
-
return e.scale.range().reduce((a, b) =>
|
|
181
|
+
return /** @type {number[]} */ (e.scale.range()).reduce((a, b) =>
|
|
182
|
+
Math.max(a, b)
|
|
183
|
+
);
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
|
|
@@ -196,7 +198,8 @@ export default class PointMark extends Mark {
|
|
|
196
198
|
} else if (p >= 1) {
|
|
197
199
|
return Infinity;
|
|
198
200
|
} else {
|
|
199
|
-
|
|
201
|
+
const scores = /** @type {any} */ (this.sampledSemanticScores);
|
|
202
|
+
return quantileSorted(/** @type {number[]} */ (scores), p);
|
|
200
203
|
}
|
|
201
204
|
} else {
|
|
202
205
|
return -1;
|