@genome-spy/core 0.30.0 → 0.30.2
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/index.es.js +16373 -0
- package/dist/index.js +43 -43
- package/package.json +10 -7
- package/src/data/collector.js +0 -183
- package/src/data/collector.test.js +0 -84
- package/src/data/dataFlow.js +0 -148
- package/src/data/dataFlow.test.js +0 -5
- package/src/data/facetNode.js +0 -17
- package/src/data/flow.test.js +0 -72
- package/src/data/flowBatch.d.ts +0 -40
- package/src/data/flowNode.js +0 -283
- package/src/data/flowNode.test.js +0 -50
- package/src/data/flowOptimizer.js +0 -123
- package/src/data/flowOptimizer.test.js +0 -193
- package/src/data/flowTestUtils.js +0 -63
- package/src/data/formats/fasta.js +0 -32
- package/src/data/formats/fasta.test.js +0 -27
- package/src/data/sources/dataSource.js +0 -22
- package/src/data/sources/dataSourceFactory.js +0 -24
- package/src/data/sources/dataUtils.js +0 -78
- package/src/data/sources/dynamicCallbackSource.js +0 -57
- package/src/data/sources/dynamicSource.js +0 -37
- package/src/data/sources/inlineSource.js +0 -67
- package/src/data/sources/inlineSource.test.js +0 -56
- package/src/data/sources/namedSource.js +0 -79
- package/src/data/sources/sequenceSource.js +0 -46
- package/src/data/sources/sequenceSource.test.js +0 -46
- package/src/data/sources/urlSource.js +0 -74
- package/src/data/transforms/aggregate.js +0 -70
- package/src/data/transforms/clone.js +0 -40
- package/src/data/transforms/clone.test.js +0 -11
- package/src/data/transforms/coverage.js +0 -187
- package/src/data/transforms/coverage.test.js +0 -123
- package/src/data/transforms/filter.js +0 -37
- package/src/data/transforms/filter.test.js +0 -18
- package/src/data/transforms/filterScoredLabels.js +0 -134
- package/src/data/transforms/flattenCompressedExons.js +0 -57
- package/src/data/transforms/flattenDelimited.js +0 -74
- package/src/data/transforms/flattenDelimited.test.js +0 -87
- package/src/data/transforms/flattenSequence.js +0 -39
- package/src/data/transforms/flattenSequence.test.js +0 -34
- package/src/data/transforms/formula.js +0 -39
- package/src/data/transforms/formula.test.js +0 -19
- package/src/data/transforms/identifier.js +0 -108
- package/src/data/transforms/identifier.test.js +0 -83
- package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
- package/src/data/transforms/measureText.js +0 -44
- package/src/data/transforms/pileup.js +0 -128
- package/src/data/transforms/pileup.test.js +0 -70
- package/src/data/transforms/project.js +0 -41
- package/src/data/transforms/project.test.js +0 -32
- package/src/data/transforms/regexExtract.js +0 -61
- package/src/data/transforms/regexExtract.test.js +0 -67
- package/src/data/transforms/regexFold.js +0 -141
- package/src/data/transforms/regexFold.test.js +0 -160
- package/src/data/transforms/sample.js +0 -101
- package/src/data/transforms/sample.test.js +0 -38
- package/src/data/transforms/stack.js +0 -137
- package/src/data/transforms/stack.test.js +0 -91
- package/src/data/transforms/transformFactory.js +0 -60
- package/src/embedApi.d.ts +0 -67
- package/src/encoder/accessor.js +0 -82
- package/src/encoder/accessor.test.js +0 -47
- package/src/encoder/encoder.js +0 -394
- package/src/encoder/encoder.test.js +0 -98
- package/src/fonts/Lato-Regular.json +0 -1267
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +0 -93
- package/src/fonts/README.md +0 -3
- package/src/fonts/bmFont.d.ts +0 -58
- package/src/fonts/bmFontManager.js +0 -357
- package/src/fonts/bmFontMetrics.js +0 -108
- package/src/genome/genome.js +0 -317
- package/src/genome/genome.test.js +0 -188
- package/src/genome/genomeStore.js +0 -54
- package/src/genome/locusFormat.js +0 -31
- package/src/genome/scaleIndex.d.ts +0 -38
- package/src/genome/scaleIndex.js +0 -166
- package/src/genome/scaleIndex.test.js +0 -78
- package/src/genome/scaleLocus.d.ts +0 -11
- package/src/genome/scaleLocus.js +0 -108
- package/src/genome/scaleLocus.test.js +0 -4
- package/src/genomeSpy.js +0 -785
- package/src/gl/arrayBuilder.js +0 -199
- package/src/gl/dataToVertices.js +0 -636
- package/src/gl/includes/common.glsl +0 -63
- package/src/gl/includes/picking.fragment.glsl +0 -1
- package/src/gl/includes/picking.vertex.glsl +0 -27
- package/src/gl/includes/sampleFacet.glsl +0 -107
- package/src/gl/includes/scales.glsl +0 -112
- package/src/gl/link.fragment.glsl +0 -18
- package/src/gl/link.vertex.glsl +0 -111
- package/src/gl/point.fragment.glsl +0 -123
- package/src/gl/point.vertex.glsl +0 -129
- package/src/gl/rect.fragment.glsl +0 -51
- package/src/gl/rect.vertex.glsl +0 -114
- package/src/gl/rule.fragment.glsl +0 -52
- package/src/gl/rule.vertex.glsl +0 -89
- package/src/gl/text.fragment.glsl +0 -31
- package/src/gl/text.vertex.glsl +0 -246
- package/src/gl/webGLHelper.js +0 -504
- package/src/img/bowtie.svg +0 -1
- package/src/img/genomespy-favicon.svg +0 -34
- package/src/index.html +0 -11
- package/src/index.js +0 -128
- package/src/marks/link.js +0 -175
- package/src/marks/mark.js +0 -975
- package/src/marks/markUtils.js +0 -125
- package/src/marks/pointMark.js +0 -251
- package/src/marks/rectMark.js +0 -241
- package/src/marks/rule.js +0 -250
- package/src/marks/text.js +0 -278
- package/src/node_modules/.vitest/results.json +0 -1
- package/src/scale/colorUtils.js +0 -184
- package/src/scale/glslScaleGenerator.js +0 -502
- package/src/scale/scale.js +0 -451
- package/src/scale/scale.test.js +0 -324
- package/src/scale/ticks.js +0 -203
- package/src/scale/ticks.test.js +0 -40
- package/src/singlePageApp.js +0 -13
- package/src/spec/axis.d.ts +0 -296
- package/src/spec/channel.d.ts +0 -430
- package/src/spec/data.d.ts +0 -196
- package/src/spec/font.d.ts +0 -15
- package/src/spec/genome.d.ts +0 -35
- package/src/spec/mark.d.ts +0 -429
- package/src/spec/root.d.ts +0 -17
- package/src/spec/sampleView.d.ts +0 -180
- package/src/spec/scale.d.ts +0 -273
- package/src/spec/title.d.ts +0 -102
- package/src/spec/tooltip.d.ts +0 -9
- package/src/spec/transform.d.ts +0 -479
- package/src/spec/view.d.ts +0 -201
- package/src/styles/genome-spy.scss +0 -153
- package/src/tooltip/dataTooltipHandler.js +0 -64
- package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
- package/src/tooltip/tooltipHandler.ts +0 -12
- package/src/types/filetypes.d.ts +0 -14
- package/src/types/flatqueue.d.ts +0 -53
- package/src/types/glsl.d.ts +0 -4
- package/src/types/internmap.d.ts +0 -22
- package/src/types/object.d.ts +0 -21
- package/src/types/vega-loader.d.ts +0 -1
- package/src/types/vega-scale.d.ts +0 -60
- package/src/utils/addBaseUrl.js +0 -19
- package/src/utils/addBaseUrl.test.js +0 -22
- package/src/utils/animator.js +0 -83
- package/src/utils/arrayUtils.js +0 -61
- package/src/utils/binnedIndex.js +0 -167
- package/src/utils/binnedIndex.test.js +0 -155
- package/src/utils/clamp.js +0 -8
- package/src/utils/cloner.js +0 -34
- package/src/utils/cloner.test.js +0 -24
- package/src/utils/coalesce.js +0 -11
- package/src/utils/coalesce.test.js +0 -16
- package/src/utils/concatIterables.js +0 -26
- package/src/utils/concatIterables.test.js +0 -8
- package/src/utils/debounce.js +0 -37
- package/src/utils/domainArray.js +0 -216
- package/src/utils/domainArray.test.js +0 -130
- package/src/utils/eerp.js +0 -13
- package/src/utils/expression.js +0 -32
- package/src/utils/field.js +0 -28
- package/src/utils/formatObject.js +0 -31
- package/src/utils/indexer.js +0 -43
- package/src/utils/indexer.test.js +0 -47
- package/src/utils/inertia.js +0 -124
- package/src/utils/interactionEvent.js +0 -33
- package/src/utils/iterateNestedMaps.js +0 -21
- package/src/utils/iterateNestedMaps.test.js +0 -33
- package/src/utils/kWayMerge.js +0 -42
- package/src/utils/kWayMerge.test.js +0 -26
- package/src/utils/layout/flexLayout.js +0 -368
- package/src/utils/layout/flexLayout.test.js +0 -311
- package/src/utils/layout/grid.js +0 -95
- package/src/utils/layout/grid.test.js +0 -71
- package/src/utils/layout/padding.js +0 -120
- package/src/utils/layout/point.js +0 -23
- package/src/utils/layout/rectangle.js +0 -288
- package/src/utils/layout/rectangle.test.js +0 -172
- package/src/utils/mergeObjects.js +0 -99
- package/src/utils/mergeObjects.test.js +0 -42
- package/src/utils/numberExtractor.js +0 -24
- package/src/utils/numberExtractor.test.js +0 -6
- package/src/utils/point.js +0 -14
- package/src/utils/propertyCacher.js +0 -70
- package/src/utils/propertyCacher.test.js +0 -85
- package/src/utils/propertyCoalescer.js +0 -42
- package/src/utils/propertyCoalescer.test.js +0 -22
- package/src/utils/reservationMap.js +0 -103
- package/src/utils/reservationMap.test.js +0 -20
- package/src/utils/scaleNull.js +0 -19
- package/src/utils/setOperations.js +0 -75
- package/src/utils/smoothstep.js +0 -10
- package/src/utils/throttle.js +0 -34
- package/src/utils/topK.js +0 -76
- package/src/utils/topK.test.js +0 -64
- package/src/utils/transition.js +0 -74
- package/src/utils/ui/tooltip.js +0 -189
- package/src/utils/url.js +0 -22
- package/src/utils/variableTools.js +0 -24
- package/src/utils/variableTools.test.js +0 -13
- package/src/view/axisResolution.js +0 -140
- package/src/view/axisResolution.test.js +0 -201
- package/src/view/axisView.js +0 -747
- package/src/view/concatView.js +0 -45
- package/src/view/containerView.js +0 -159
- package/src/view/facetView.js +0 -491
- package/src/view/flowBuilder.js +0 -367
- package/src/view/flowBuilder.test.js +0 -125
- package/src/view/gridView.js +0 -786
- package/src/view/implicitRootView.js +0 -14
- package/src/view/importView.js +0 -19
- package/src/view/layerView.js +0 -74
- package/src/view/rendering.d.ts +0 -44
- package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
- package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
- package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
- package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
- package/src/view/renderingContext/viewRenderingContext.js +0 -41
- package/src/view/scaleResolution.js +0 -797
- package/src/view/scaleResolution.test.js +0 -572
- package/src/view/scaleResolutionApi.d.ts +0 -40
- package/src/view/testUtils.js +0 -51
- package/src/view/title.js +0 -165
- package/src/view/unitView.js +0 -382
- package/src/view/view.js +0 -612
- package/src/view/view.test.js +0 -214
- package/src/view/viewContext.d.ts +0 -62
- package/src/view/viewFactory.js +0 -181
- package/src/view/viewFactory.test.js +0 -17
- package/src/view/viewUtils.js +0 -327
- package/src/view/zoom.js +0 -89
package/src/scale/colorUtils.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { color as d3color } from "d3-color";
|
|
2
|
-
import { range } from "d3-array";
|
|
3
|
-
import { scheme as vegaScheme, interpolateColors } from "vega-scale";
|
|
4
|
-
import { isString, isArray, isFunction } from "vega-util";
|
|
5
|
-
import { peek } from "../utils/arrayUtils";
|
|
6
|
-
import { createOrUpdateTexture } from "../gl/webGLHelper";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {string | import("../spec/scale").SchemeParams} schemeParams
|
|
10
|
-
* @param {WebGL2RenderingContext} gl
|
|
11
|
-
* @param {number} [count]
|
|
12
|
-
* @param {WebGLTexture} [existingTexture]
|
|
13
|
-
*/
|
|
14
|
-
export function createSchemeTexture(schemeParams, gl, count, existingTexture) {
|
|
15
|
-
const schemeName = isString(schemeParams)
|
|
16
|
-
? schemeParams
|
|
17
|
-
: schemeParams.name;
|
|
18
|
-
const extent = (!isString(schemeParams) && schemeParams.extent) || [0, 1];
|
|
19
|
-
|
|
20
|
-
if (count === undefined && !isString(schemeParams)) {
|
|
21
|
-
count = schemeParams.count;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (schemeName) {
|
|
25
|
-
const scheme = vegaScheme(schemeName);
|
|
26
|
-
if (isFunction(scheme)) {
|
|
27
|
-
// TODO: Reverse
|
|
28
|
-
const textureData = interpolatorToTextureData(scheme, {
|
|
29
|
-
extent,
|
|
30
|
-
count,
|
|
31
|
-
});
|
|
32
|
-
return createOrUpdateTexture(
|
|
33
|
-
gl,
|
|
34
|
-
{
|
|
35
|
-
minMag: gl.LINEAR,
|
|
36
|
-
format: gl.RGB,
|
|
37
|
-
height: 1,
|
|
38
|
-
wrap: gl.CLAMP_TO_EDGE,
|
|
39
|
-
},
|
|
40
|
-
textureData,
|
|
41
|
-
existingTexture
|
|
42
|
-
);
|
|
43
|
-
} else if (isArray(scheme)) {
|
|
44
|
-
return createDiscreteColorTexture(scheme, gl);
|
|
45
|
-
} else {
|
|
46
|
-
throw new Error("Unknown scheme: " + schemeName);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @param {string[]} colors
|
|
53
|
-
* @param {import("../spec/scale").ScaleInterpolate | import("../spec/scale").ScaleInterpolateParams} interpolateParams
|
|
54
|
-
* @param {WebGL2RenderingContext} gl
|
|
55
|
-
* @param {WebGLTexture} [existingTexture]
|
|
56
|
-
*/
|
|
57
|
-
export function createInterpolatedColorTexture(
|
|
58
|
-
colors,
|
|
59
|
-
interpolateParams = "rgb",
|
|
60
|
-
gl,
|
|
61
|
-
existingTexture
|
|
62
|
-
) {
|
|
63
|
-
const interpolator = interpolateColors(
|
|
64
|
-
colors,
|
|
65
|
-
isString(interpolateParams)
|
|
66
|
-
? interpolateParams
|
|
67
|
-
: interpolateParams.type,
|
|
68
|
-
isString(interpolateParams) ? undefined : interpolateParams.gamma
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
// TODO: Reverse
|
|
72
|
-
const textureData = interpolatorToTextureData(interpolator);
|
|
73
|
-
return createOrUpdateTexture(
|
|
74
|
-
gl,
|
|
75
|
-
{
|
|
76
|
-
minMag: gl.LINEAR,
|
|
77
|
-
format: gl.RGB,
|
|
78
|
-
height: 1,
|
|
79
|
-
wrap: gl.CLAMP_TO_EDGE,
|
|
80
|
-
},
|
|
81
|
-
textureData,
|
|
82
|
-
existingTexture
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Creates a texture that maps integer indices to discrete 32bit floats.
|
|
88
|
-
* The range may represent point shapes, for example.
|
|
89
|
-
*
|
|
90
|
-
* @param {number[]} range
|
|
91
|
-
* @param {WebGL2RenderingContext} gl
|
|
92
|
-
* @param {number} [count]
|
|
93
|
-
* @param {WebGLTexture} [existingTexture]
|
|
94
|
-
*/
|
|
95
|
-
export function createDiscreteTexture(range, gl, count, existingTexture) {
|
|
96
|
-
const size = Math.max(range.length, count || 0);
|
|
97
|
-
const textureData = new Float32Array(size);
|
|
98
|
-
|
|
99
|
-
for (let i = 0; i < size; i++) {
|
|
100
|
-
textureData[i] = range[i % range.length];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return createOrUpdateTexture(
|
|
104
|
-
gl,
|
|
105
|
-
{
|
|
106
|
-
minMag: gl.NEAREST,
|
|
107
|
-
format: gl.RED,
|
|
108
|
-
internalFormat: gl.R32F,
|
|
109
|
-
height: 1,
|
|
110
|
-
},
|
|
111
|
-
textureData,
|
|
112
|
-
existingTexture
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Creates a texture that maps integer indices to discrete RGB colors.
|
|
118
|
-
*
|
|
119
|
-
* @param {string[]} colors
|
|
120
|
-
* @param {WebGL2RenderingContext} gl
|
|
121
|
-
* @param {number} [count]
|
|
122
|
-
* @param {WebGLTexture} [existingTexture]
|
|
123
|
-
*/
|
|
124
|
-
export function createDiscreteColorTexture(colors, gl, count, existingTexture) {
|
|
125
|
-
const textureData = colorArrayToTextureData(colors, count);
|
|
126
|
-
return createOrUpdateTexture(
|
|
127
|
-
gl,
|
|
128
|
-
{
|
|
129
|
-
minMag: gl.NEAREST,
|
|
130
|
-
format: gl.RGB,
|
|
131
|
-
height: 1,
|
|
132
|
-
},
|
|
133
|
-
textureData,
|
|
134
|
-
existingTexture
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Renders an interpolator to a texture, which can be used for mapping
|
|
140
|
-
* quantitative values to colors (sequential scale).
|
|
141
|
-
*
|
|
142
|
-
* @param {function(number):string} interpolator
|
|
143
|
-
* @param {object} options
|
|
144
|
-
* @param {number[]} [options.extent]
|
|
145
|
-
* @param {boolean} [options.reverse]
|
|
146
|
-
* @param {number} [options.count]
|
|
147
|
-
*/
|
|
148
|
-
function interpolatorToTextureData(
|
|
149
|
-
interpolator,
|
|
150
|
-
{ extent = [0, 1], reverse = false, count = 256 } = {}
|
|
151
|
-
) {
|
|
152
|
-
const start = extent[0];
|
|
153
|
-
const span = peek(extent) - start;
|
|
154
|
-
|
|
155
|
-
const steps = range(count)
|
|
156
|
-
.map((x) => x / (count - 1))
|
|
157
|
-
.map((x) => start + x / span)
|
|
158
|
-
.map(interpolator);
|
|
159
|
-
|
|
160
|
-
if (reverse) {
|
|
161
|
-
steps.reverse();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return colorArrayToTextureData(steps);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Renders a scheme (an array of colors) to a texture.
|
|
169
|
-
*
|
|
170
|
-
* @param {string[]} scheme
|
|
171
|
-
* @param {number} [count]
|
|
172
|
-
*/
|
|
173
|
-
function colorArrayToTextureData(scheme, count) {
|
|
174
|
-
const size = Math.max(scheme.length, count || 0);
|
|
175
|
-
|
|
176
|
-
const textureData = new Uint8Array(size * 3);
|
|
177
|
-
for (let i = 0; i < size; i++) {
|
|
178
|
-
const color = d3color(scheme[i % scheme.length]).rgb();
|
|
179
|
-
textureData[i * 3 + 0] = color.r;
|
|
180
|
-
textureData[i * 3 + 1] = color.g;
|
|
181
|
-
textureData[i * 3 + 2] = color.b;
|
|
182
|
-
}
|
|
183
|
-
return textureData;
|
|
184
|
-
}
|
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isContinuous,
|
|
3
|
-
isDiscrete,
|
|
4
|
-
isDiscretizing,
|
|
5
|
-
isInterpolating,
|
|
6
|
-
} from "vega-scale";
|
|
7
|
-
import { isArray, isBoolean, isNumber, isString } from "vega-util";
|
|
8
|
-
import { color as d3color } from "d3-color";
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
getDiscreteRangeMapper,
|
|
12
|
-
isColorChannel,
|
|
13
|
-
isDatumDef,
|
|
14
|
-
isDiscreteChannel,
|
|
15
|
-
getPrimaryChannel,
|
|
16
|
-
isValueDef,
|
|
17
|
-
} from "../encoder/encoder";
|
|
18
|
-
import { peek } from "../utils/arrayUtils";
|
|
19
|
-
|
|
20
|
-
export const ATTRIBUTE_PREFIX = "attr_";
|
|
21
|
-
export const DOMAIN_PREFIX = "uDomain_";
|
|
22
|
-
export const RANGE_PREFIX = "range_";
|
|
23
|
-
export const SCALE_FUNCTION_PREFIX = "scale_";
|
|
24
|
-
export const SCALED_FUNCTION_PREFIX = "getScaled_";
|
|
25
|
-
export const RANGE_TEXTURE_PREFIX = "uRangeTexture_";
|
|
26
|
-
|
|
27
|
-
// https://stackoverflow.com/a/47543127
|
|
28
|
-
const FLT_MAX = 3.402823466e38;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @typedef {import("../spec/channel").Channel} Channel
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Splits a vega-scale type (e.g., linear, sequential-linear) to components.
|
|
36
|
-
*
|
|
37
|
-
* @param {string} type
|
|
38
|
-
*/
|
|
39
|
-
function splitScaleType(type) {
|
|
40
|
-
const match = type.match(/^(?:(\w+)-)?(\w+)$/);
|
|
41
|
-
if (!match) {
|
|
42
|
-
throw new Error("Not a scale type: " + type);
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
family: match[1] || "continuous",
|
|
46
|
-
transform: match[2],
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
* @param {Channel} channel
|
|
53
|
-
* @param {number | number[] | string | boolean} value
|
|
54
|
-
*/
|
|
55
|
-
export function generateValueGlsl(channel, value) {
|
|
56
|
-
/** @type {VectorizedValue} */
|
|
57
|
-
let vec;
|
|
58
|
-
if (isDiscreteChannel(channel)) {
|
|
59
|
-
vec = vectorize(getDiscreteRangeMapper(channel)(value));
|
|
60
|
-
} else if (isString(value)) {
|
|
61
|
-
if (isColorChannel(channel)) {
|
|
62
|
-
vec = vectorizeCssColor(value);
|
|
63
|
-
} else {
|
|
64
|
-
throw new Error(
|
|
65
|
-
`String values are not supported on the "${channel}" channel: ${value}`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
} else if (isBoolean(value)) {
|
|
69
|
-
vec = vectorize(value ? 1 : 0);
|
|
70
|
-
} else if (value === null) {
|
|
71
|
-
if (isColorChannel(channel)) {
|
|
72
|
-
vec = vectorize([0, 0, 0]);
|
|
73
|
-
} else {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`null value is not supported on the "${channel}" chanel.`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
vec = vectorize(value);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// These could also be passed as uniforms because GPU drivers often handle
|
|
83
|
-
// uniforms as constants and recompile the shader to eliminate dead code etc.
|
|
84
|
-
let glsl = `
|
|
85
|
-
#define ${channel}_DEFINED
|
|
86
|
-
${vec.type} ${SCALED_FUNCTION_PREFIX}${channel}() {
|
|
87
|
-
// Constant value
|
|
88
|
-
return ${vec};
|
|
89
|
-
}`;
|
|
90
|
-
return glsl;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
*
|
|
95
|
-
* @param {Channel} channel
|
|
96
|
-
* @param {any} scale TODO: typing
|
|
97
|
-
* @param {import("../spec/channel").ChannelDef} channelDef
|
|
98
|
-
*/
|
|
99
|
-
// eslint-disable-next-line complexity
|
|
100
|
-
export function generateScaleGlsl(channel, scale, channelDef) {
|
|
101
|
-
if (isValueDef(channelDef)) {
|
|
102
|
-
throw new Error(
|
|
103
|
-
`Cannot create scale for "value": ${JSON.stringify(channelDef)}`
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const primary = getPrimaryChannel(channel);
|
|
108
|
-
const attributeName = ATTRIBUTE_PREFIX + channel;
|
|
109
|
-
const domainUniformName = DOMAIN_PREFIX + primary;
|
|
110
|
-
const rangeName = RANGE_PREFIX + primary;
|
|
111
|
-
|
|
112
|
-
const hp = isHighPrecisionScale(scale.type);
|
|
113
|
-
const attributeType = hp ? "vec2" : "float";
|
|
114
|
-
|
|
115
|
-
const domainLength = scale.domain ? scale.domain().length : undefined;
|
|
116
|
-
|
|
117
|
-
/** @type {string} */
|
|
118
|
-
let domainUniform;
|
|
119
|
-
|
|
120
|
-
/** @type {string[]} The generated shader (concatenated at the end) */
|
|
121
|
-
const glsl = [];
|
|
122
|
-
|
|
123
|
-
// For debugging
|
|
124
|
-
glsl.push("");
|
|
125
|
-
glsl.push("/".repeat(70));
|
|
126
|
-
glsl.push(`// Channel: ${channel}`);
|
|
127
|
-
glsl.push("");
|
|
128
|
-
|
|
129
|
-
glsl.push(`#define ${channel}_DEFINED`);
|
|
130
|
-
|
|
131
|
-
const { transform } = splitScaleType(scale.type);
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @param {string} name
|
|
135
|
-
* @param {...any} args
|
|
136
|
-
*/
|
|
137
|
-
const makeScaleCall = (name, ...args) =>
|
|
138
|
-
// eslint-disable-next-line no-useless-call
|
|
139
|
-
makeFunctionCall.apply(null, [name, "value", ...args]);
|
|
140
|
-
|
|
141
|
-
let functionCall;
|
|
142
|
-
switch (transform) {
|
|
143
|
-
case "linear":
|
|
144
|
-
functionCall = makeScaleCall("scaleLinear", "domain", rangeName);
|
|
145
|
-
break;
|
|
146
|
-
|
|
147
|
-
case "log":
|
|
148
|
-
functionCall = makeScaleCall(
|
|
149
|
-
"scaleLog",
|
|
150
|
-
"domain",
|
|
151
|
-
rangeName,
|
|
152
|
-
scale.base()
|
|
153
|
-
);
|
|
154
|
-
break;
|
|
155
|
-
|
|
156
|
-
case "symlog":
|
|
157
|
-
functionCall = makeScaleCall(
|
|
158
|
-
"scaleSymlog",
|
|
159
|
-
"domain",
|
|
160
|
-
rangeName,
|
|
161
|
-
scale.constant()
|
|
162
|
-
);
|
|
163
|
-
break;
|
|
164
|
-
|
|
165
|
-
case "pow":
|
|
166
|
-
case "sqrt":
|
|
167
|
-
functionCall = makeScaleCall(
|
|
168
|
-
"scalePow",
|
|
169
|
-
"domain",
|
|
170
|
-
rangeName,
|
|
171
|
-
scale.exponent()
|
|
172
|
-
);
|
|
173
|
-
break;
|
|
174
|
-
|
|
175
|
-
case "index":
|
|
176
|
-
case "locus":
|
|
177
|
-
functionCall = makeScaleCall(
|
|
178
|
-
"scaleBandHp",
|
|
179
|
-
"domain",
|
|
180
|
-
rangeName,
|
|
181
|
-
scale.paddingInner(),
|
|
182
|
-
scale.paddingOuter(),
|
|
183
|
-
scale.align(),
|
|
184
|
-
// @ts-expect-error TODO: fix typing
|
|
185
|
-
channelDef.band ?? 0.5
|
|
186
|
-
);
|
|
187
|
-
break;
|
|
188
|
-
case "point":
|
|
189
|
-
case "band":
|
|
190
|
-
functionCall = makeScaleCall(
|
|
191
|
-
"scaleBand",
|
|
192
|
-
"domain",
|
|
193
|
-
rangeName,
|
|
194
|
-
scale.paddingInner(),
|
|
195
|
-
scale.paddingOuter(),
|
|
196
|
-
scale.align(),
|
|
197
|
-
// @ts-expect-error TODO: fix typing
|
|
198
|
-
channelDef.band ?? 0.5
|
|
199
|
-
);
|
|
200
|
-
break;
|
|
201
|
-
|
|
202
|
-
case "ordinal": // Use identity transform and lookup the value from a texture
|
|
203
|
-
case "null":
|
|
204
|
-
case "identity":
|
|
205
|
-
functionCall = makeScaleCall("scaleIdentity");
|
|
206
|
-
break;
|
|
207
|
-
|
|
208
|
-
case "threshold":
|
|
209
|
-
// TODO: Quantile (it's a specialization of threshold scale)
|
|
210
|
-
// TODO: Quantize
|
|
211
|
-
break;
|
|
212
|
-
|
|
213
|
-
default:
|
|
214
|
-
// TODO: Implement log, sqrt, etc...
|
|
215
|
-
throw new Error(
|
|
216
|
-
`Unsupported scale type: ${
|
|
217
|
-
scale.type
|
|
218
|
-
}! ${channel}: ${JSON.stringify(channelDef)}`
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// N.B. Interpolating scales require unit range
|
|
223
|
-
// TODO: Reverse
|
|
224
|
-
const range =
|
|
225
|
-
isInterpolating(scale.type) ||
|
|
226
|
-
(isContinuous(scale.type) && isColorChannel(channel))
|
|
227
|
-
? [0, 1]
|
|
228
|
-
: scale.range
|
|
229
|
-
? scale.range()
|
|
230
|
-
: undefined;
|
|
231
|
-
|
|
232
|
-
if (range && channel == primary && range.length && range.every(isNumber)) {
|
|
233
|
-
const vectorizedRange = vectorizeRange(range);
|
|
234
|
-
|
|
235
|
-
// Range needs no runtime adjustment (at least for now). Thus, pass it as a constant that the
|
|
236
|
-
// GLSL compiler can optimize away in the case of unit ranges.
|
|
237
|
-
glsl.push(
|
|
238
|
-
`const ${vectorizedRange.type} ${rangeName} = ${vectorizedRange};`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const returnType = isColorChannel(channel) ? "vec3" : "float";
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* An optional interpolator function that maps the transformed value to the range.
|
|
246
|
-
* @type {string}
|
|
247
|
-
*/
|
|
248
|
-
let interpolate;
|
|
249
|
-
if (isColorChannel(channel)) {
|
|
250
|
-
const textureUniformName = RANGE_TEXTURE_PREFIX + primary;
|
|
251
|
-
if (channel == primary) {
|
|
252
|
-
glsl.push(`uniform sampler2D ${textureUniformName};`);
|
|
253
|
-
}
|
|
254
|
-
if (isContinuous(scale.type)) {
|
|
255
|
-
interpolate = `getInterpolatedColor(${textureUniformName}, transformed)`;
|
|
256
|
-
} else if (isDiscrete(scale.type) || isDiscretizing(scale.type)) {
|
|
257
|
-
interpolate = `getDiscreteColor(${textureUniformName}, int(transformed))`;
|
|
258
|
-
} else {
|
|
259
|
-
throw new Error("Problem with color scale!");
|
|
260
|
-
}
|
|
261
|
-
} else if (scale.type === "ordinal" || isDiscretizing(scale.type)) {
|
|
262
|
-
const textureUniformName = RANGE_TEXTURE_PREFIX + primary;
|
|
263
|
-
if (channel == primary) {
|
|
264
|
-
glsl.push(`uniform sampler2D ${textureUniformName};`);
|
|
265
|
-
}
|
|
266
|
-
interpolate = `getDiscreteColor(${textureUniformName}, int(transformed)).r`;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (isDatumDef(channelDef)) {
|
|
270
|
-
glsl.push(`uniform highp ${attributeType} ${attributeName};`);
|
|
271
|
-
} else {
|
|
272
|
-
glsl.push(`in highp ${attributeType} ${attributeName};`);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/** @type {string[]} Channel's scale function*/
|
|
276
|
-
const scaleBody = [];
|
|
277
|
-
|
|
278
|
-
const piecewise = isContinuous(scale.type) && domainLength > 2;
|
|
279
|
-
const needsSlot = isDiscretizing(scale.type) || piecewise;
|
|
280
|
-
|
|
281
|
-
// 1. If scale is piecewise or discretizing, find a matching slot
|
|
282
|
-
scaleBody.push(`int slot = 0;`);
|
|
283
|
-
if (needsSlot) {
|
|
284
|
-
const name = domainUniformName;
|
|
285
|
-
// Use a simple linear search.
|
|
286
|
-
// This cannot be put into a function because GLSL requires fixed array lengths for parameters.
|
|
287
|
-
scaleBody.push(
|
|
288
|
-
piecewise
|
|
289
|
-
? `while (slot < ${name}.length() - 2 && value >= ${name}[slot + 1]) { slot++; }`
|
|
290
|
-
: `while (slot < ${name}.length() && value >= ${name}[slot]) { slot++; }`
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const usesDomain =
|
|
295
|
-
isContinuous(scale.type) ||
|
|
296
|
-
isDiscretizing(scale.type) ||
|
|
297
|
-
["band", "point"].includes(scale.type);
|
|
298
|
-
|
|
299
|
-
// 2. transform
|
|
300
|
-
if (functionCall) {
|
|
301
|
-
const name = domainUniformName;
|
|
302
|
-
if (usesDomain) {
|
|
303
|
-
if (hp) {
|
|
304
|
-
scaleBody.push(`vec3 domain = ${name};`);
|
|
305
|
-
} else {
|
|
306
|
-
scaleBody.push(
|
|
307
|
-
`vec2 domain = vec2(${name}[slot], ${name}[slot + 1]);`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
scaleBody.push(`float transformed = ${functionCall};`);
|
|
313
|
-
|
|
314
|
-
if (piecewise) {
|
|
315
|
-
// TODO: Handle range correctly. Now this assumes unit range.
|
|
316
|
-
scaleBody.push(
|
|
317
|
-
`transformed = (float(slot) + transformed) / (float(${name}.length() - 1));`
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
} else {
|
|
321
|
-
// Discretizing scale
|
|
322
|
-
scaleBody.push(`float transformed = float(slot);`);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// 3. clamp
|
|
326
|
-
if ("clamp" in scale && scale.clamp()) {
|
|
327
|
-
scaleBody.push(
|
|
328
|
-
`transformed = clampToRange(transformed, ${vectorizeRange(range)});`
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// 4. interpolate or map to a discrete value
|
|
333
|
-
scaleBody.push(`return ${interpolate ?? "transformed"};`);
|
|
334
|
-
|
|
335
|
-
glsl.push(`
|
|
336
|
-
${returnType} ${SCALE_FUNCTION_PREFIX}${channel}(${attributeType} value) {
|
|
337
|
-
${scaleBody.map((x) => ` ${x}\n`).join("")}
|
|
338
|
-
}`);
|
|
339
|
-
|
|
340
|
-
// A convenience getter for the scaled value
|
|
341
|
-
glsl.push(`
|
|
342
|
-
${returnType} ${SCALED_FUNCTION_PREFIX}${channel}() {
|
|
343
|
-
return ${SCALE_FUNCTION_PREFIX}${channel}(${attributeName});
|
|
344
|
-
}`);
|
|
345
|
-
|
|
346
|
-
const concatenated = glsl.join("\n");
|
|
347
|
-
|
|
348
|
-
if (usesDomain && channel == primary) {
|
|
349
|
-
// Band, point, index, and locus scale need the domain extent (the first and last elements).
|
|
350
|
-
const length =
|
|
351
|
-
isContinuous(scale.type) || isDiscretizing(scale.type)
|
|
352
|
-
? domainLength
|
|
353
|
-
: 2;
|
|
354
|
-
domainUniform = hp
|
|
355
|
-
? `highp vec3 ${domainUniformName};`
|
|
356
|
-
: `mediump float ${domainUniformName}[${length}];`;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
glsl: concatenated,
|
|
361
|
-
domainUniform,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Adds a trailing decimal zero so that GLSL is happy.
|
|
367
|
-
*
|
|
368
|
-
* @param {number} number
|
|
369
|
-
* @returns {string}
|
|
370
|
-
*/
|
|
371
|
-
function toDecimal(number) {
|
|
372
|
-
if (!isNumber(number)) {
|
|
373
|
-
throw new Error(`Not a number: ${number}`);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (number == Infinity) {
|
|
377
|
-
return "" + FLT_MAX;
|
|
378
|
-
} else if (number == -Infinity) {
|
|
379
|
-
return "" + -FLT_MAX;
|
|
380
|
-
} else {
|
|
381
|
-
let str = `${number}`;
|
|
382
|
-
if (/^(-)?\d+$/.test(str)) {
|
|
383
|
-
str += ".0";
|
|
384
|
-
}
|
|
385
|
-
return str;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Turns a number or number array to float or vec[234] string.
|
|
391
|
-
*
|
|
392
|
-
* @param {number | number[]} value
|
|
393
|
-
* @returns {VectorizedValue}
|
|
394
|
-
*
|
|
395
|
-
* @typedef {string & { type: string, numComponents: number }} VectorizedValue
|
|
396
|
-
*/
|
|
397
|
-
function vectorize(value) {
|
|
398
|
-
if (typeof value == "number") {
|
|
399
|
-
value = [value];
|
|
400
|
-
}
|
|
401
|
-
const numComponents = value.length;
|
|
402
|
-
if (numComponents < 1 || numComponents > 4) {
|
|
403
|
-
throw new Error("Invalid number of components: " + numComponents);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
let type;
|
|
407
|
-
let str;
|
|
408
|
-
|
|
409
|
-
if (numComponents > 1) {
|
|
410
|
-
type = `vec${numComponents}`;
|
|
411
|
-
str = `${type}(${value.map(toDecimal).join(", ")})`;
|
|
412
|
-
} else {
|
|
413
|
-
type = "float";
|
|
414
|
-
str = toDecimal(value[0]);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return Object.assign(str, { type, numComponents });
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* @param {string} color
|
|
422
|
-
*/
|
|
423
|
-
function vectorizeCssColor(color) {
|
|
424
|
-
const rgb = d3color(color).rgb();
|
|
425
|
-
return vectorize([rgb.r, rgb.g, rgb.b].map((x) => x / 255));
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
*
|
|
430
|
-
* @param {number[]} range
|
|
431
|
-
*/
|
|
432
|
-
function vectorizeRange(range) {
|
|
433
|
-
return vectorize([range[0], peek(range)]);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
*
|
|
438
|
-
* @param {string} name
|
|
439
|
-
* @param {...any} args
|
|
440
|
-
*/
|
|
441
|
-
function makeFunctionCall(name, ...args) {
|
|
442
|
-
/** @type {string[]} */
|
|
443
|
-
const fixedArgs = [];
|
|
444
|
-
|
|
445
|
-
for (const arg of args) {
|
|
446
|
-
if (isNumber(arg)) {
|
|
447
|
-
fixedArgs.push(toDecimal(arg));
|
|
448
|
-
} else if (isArray(arg)) {
|
|
449
|
-
fixedArgs.push(vectorize(arg));
|
|
450
|
-
} else {
|
|
451
|
-
fixedArgs.push(arg);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return `${name}(${fixedArgs.join(", ")})`;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
*
|
|
460
|
-
* @param {string} type
|
|
461
|
-
*/
|
|
462
|
-
export function isHighPrecisionScale(type) {
|
|
463
|
-
return type == "index" || type == "locus";
|
|
464
|
-
}
|
|
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
|
-
|
|
471
|
-
/**
|
|
472
|
-
* @param {number} x Must be an integer
|
|
473
|
-
* @param {number[]} [arr]
|
|
474
|
-
*/
|
|
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
|
-
|
|
481
|
-
arr[0] = hi;
|
|
482
|
-
arr[1] = lo;
|
|
483
|
-
|
|
484
|
-
return arr;
|
|
485
|
-
}
|
|
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
|
-
|
|
497
|
-
/**
|
|
498
|
-
* @param {number[]} domain
|
|
499
|
-
*/
|
|
500
|
-
export function toHighPrecisionDomainUniform(domain) {
|
|
501
|
-
return [...exactSplitHighPrecision(domain[0]), domain[1] - domain[0]];
|
|
502
|
-
}
|