@genome-spy/core 0.29.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.
Files changed (234) hide show
  1. package/dist/index.es.js +16373 -0
  2. package/dist/index.js +43 -43
  3. package/package.json +10 -7
  4. package/src/data/collector.js +0 -183
  5. package/src/data/collector.test.js +0 -84
  6. package/src/data/dataFlow.js +0 -148
  7. package/src/data/dataFlow.test.js +0 -5
  8. package/src/data/facetNode.js +0 -17
  9. package/src/data/flow.test.js +0 -72
  10. package/src/data/flowBatch.d.ts +0 -40
  11. package/src/data/flowNode.js +0 -283
  12. package/src/data/flowNode.test.js +0 -50
  13. package/src/data/flowOptimizer.js +0 -123
  14. package/src/data/flowOptimizer.test.js +0 -193
  15. package/src/data/flowTestUtils.js +0 -63
  16. package/src/data/formats/fasta.js +0 -32
  17. package/src/data/formats/fasta.test.js +0 -27
  18. package/src/data/sources/dataSource.js +0 -22
  19. package/src/data/sources/dataSourceFactory.js +0 -24
  20. package/src/data/sources/dataUtils.js +0 -78
  21. package/src/data/sources/dynamicCallbackSource.js +0 -57
  22. package/src/data/sources/dynamicSource.js +0 -37
  23. package/src/data/sources/inlineSource.js +0 -67
  24. package/src/data/sources/inlineSource.test.js +0 -56
  25. package/src/data/sources/namedSource.js +0 -79
  26. package/src/data/sources/sequenceSource.js +0 -46
  27. package/src/data/sources/sequenceSource.test.js +0 -46
  28. package/src/data/sources/urlSource.js +0 -74
  29. package/src/data/transforms/aggregate.js +0 -70
  30. package/src/data/transforms/clone.js +0 -40
  31. package/src/data/transforms/clone.test.js +0 -11
  32. package/src/data/transforms/coverage.js +0 -187
  33. package/src/data/transforms/coverage.test.js +0 -123
  34. package/src/data/transforms/filter.js +0 -37
  35. package/src/data/transforms/filter.test.js +0 -18
  36. package/src/data/transforms/filterScoredLabels.js +0 -134
  37. package/src/data/transforms/flattenCompressedExons.js +0 -57
  38. package/src/data/transforms/flattenDelimited.js +0 -74
  39. package/src/data/transforms/flattenDelimited.test.js +0 -87
  40. package/src/data/transforms/flattenSequence.js +0 -39
  41. package/src/data/transforms/flattenSequence.test.js +0 -34
  42. package/src/data/transforms/formula.js +0 -39
  43. package/src/data/transforms/formula.test.js +0 -19
  44. package/src/data/transforms/identifier.js +0 -108
  45. package/src/data/transforms/identifier.test.js +0 -83
  46. package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
  47. package/src/data/transforms/measureText.js +0 -44
  48. package/src/data/transforms/pileup.js +0 -128
  49. package/src/data/transforms/pileup.test.js +0 -70
  50. package/src/data/transforms/project.js +0 -41
  51. package/src/data/transforms/project.test.js +0 -32
  52. package/src/data/transforms/regexExtract.js +0 -61
  53. package/src/data/transforms/regexExtract.test.js +0 -67
  54. package/src/data/transforms/regexFold.js +0 -141
  55. package/src/data/transforms/regexFold.test.js +0 -160
  56. package/src/data/transforms/sample.js +0 -101
  57. package/src/data/transforms/sample.test.js +0 -38
  58. package/src/data/transforms/stack.js +0 -137
  59. package/src/data/transforms/stack.test.js +0 -91
  60. package/src/data/transforms/transformFactory.js +0 -60
  61. package/src/embedApi.d.ts +0 -67
  62. package/src/encoder/accessor.js +0 -82
  63. package/src/encoder/accessor.test.js +0 -47
  64. package/src/encoder/encoder.js +0 -394
  65. package/src/encoder/encoder.test.js +0 -98
  66. package/src/fonts/Lato-Regular.json +0 -1267
  67. package/src/fonts/Lato-Regular.png +0 -0
  68. package/src/fonts/OFL.txt +0 -93
  69. package/src/fonts/README.md +0 -3
  70. package/src/fonts/bmFont.d.ts +0 -58
  71. package/src/fonts/bmFontManager.js +0 -357
  72. package/src/fonts/bmFontMetrics.js +0 -108
  73. package/src/genome/genome.js +0 -317
  74. package/src/genome/genome.test.js +0 -188
  75. package/src/genome/genomeStore.js +0 -54
  76. package/src/genome/locusFormat.js +0 -31
  77. package/src/genome/scaleIndex.d.ts +0 -38
  78. package/src/genome/scaleIndex.js +0 -166
  79. package/src/genome/scaleIndex.test.js +0 -78
  80. package/src/genome/scaleLocus.d.ts +0 -11
  81. package/src/genome/scaleLocus.js +0 -108
  82. package/src/genome/scaleLocus.test.js +0 -4
  83. package/src/genomeSpy.js +0 -784
  84. package/src/gl/arrayBuilder.js +0 -199
  85. package/src/gl/dataToVertices.js +0 -636
  86. package/src/gl/includes/common.glsl +0 -63
  87. package/src/gl/includes/picking.fragment.glsl +0 -1
  88. package/src/gl/includes/picking.vertex.glsl +0 -27
  89. package/src/gl/includes/sampleFacet.glsl +0 -107
  90. package/src/gl/includes/scales.glsl +0 -112
  91. package/src/gl/link.fragment.glsl +0 -18
  92. package/src/gl/link.vertex.glsl +0 -111
  93. package/src/gl/point.fragment.glsl +0 -123
  94. package/src/gl/point.vertex.glsl +0 -129
  95. package/src/gl/rect.fragment.glsl +0 -51
  96. package/src/gl/rect.vertex.glsl +0 -114
  97. package/src/gl/rule.fragment.glsl +0 -52
  98. package/src/gl/rule.vertex.glsl +0 -89
  99. package/src/gl/text.fragment.glsl +0 -31
  100. package/src/gl/text.vertex.glsl +0 -246
  101. package/src/gl/webGLHelper.js +0 -489
  102. package/src/img/bowtie.svg +0 -1
  103. package/src/img/genomespy-favicon.svg +0 -34
  104. package/src/index.html +0 -11
  105. package/src/index.js +0 -128
  106. package/src/marks/link.js +0 -175
  107. package/src/marks/mark.js +0 -975
  108. package/src/marks/markUtils.js +0 -125
  109. package/src/marks/pointMark.js +0 -251
  110. package/src/marks/rectMark.js +0 -241
  111. package/src/marks/rule.js +0 -250
  112. package/src/marks/text.js +0 -278
  113. package/src/node_modules/.vitest/results.json +0 -1
  114. package/src/scale/colorUtils.js +0 -184
  115. package/src/scale/glslScaleGenerator.js +0 -488
  116. package/src/scale/scale.js +0 -451
  117. package/src/scale/scale.test.js +0 -324
  118. package/src/scale/ticks.js +0 -203
  119. package/src/scale/ticks.test.js +0 -40
  120. package/src/singlePageApp.js +0 -13
  121. package/src/spec/axis.d.ts +0 -296
  122. package/src/spec/channel.d.ts +0 -430
  123. package/src/spec/data.d.ts +0 -196
  124. package/src/spec/font.d.ts +0 -15
  125. package/src/spec/genome.d.ts +0 -35
  126. package/src/spec/mark.d.ts +0 -429
  127. package/src/spec/root.d.ts +0 -17
  128. package/src/spec/sampleView.d.ts +0 -180
  129. package/src/spec/scale.d.ts +0 -273
  130. package/src/spec/title.d.ts +0 -102
  131. package/src/spec/tooltip.d.ts +0 -9
  132. package/src/spec/transform.d.ts +0 -479
  133. package/src/spec/view.d.ts +0 -201
  134. package/src/styles/genome-spy.scss +0 -153
  135. package/src/tooltip/dataTooltipHandler.js +0 -64
  136. package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
  137. package/src/tooltip/tooltipHandler.ts +0 -12
  138. package/src/types/filetypes.d.ts +0 -14
  139. package/src/types/flatqueue.d.ts +0 -53
  140. package/src/types/glsl.d.ts +0 -4
  141. package/src/types/internmap.d.ts +0 -22
  142. package/src/types/object.d.ts +0 -21
  143. package/src/types/vega-loader.d.ts +0 -1
  144. package/src/types/vega-scale.d.ts +0 -60
  145. package/src/utils/addBaseUrl.js +0 -19
  146. package/src/utils/addBaseUrl.test.js +0 -22
  147. package/src/utils/animator.js +0 -83
  148. package/src/utils/arrayUtils.js +0 -61
  149. package/src/utils/binnedIndex.js +0 -167
  150. package/src/utils/binnedIndex.test.js +0 -155
  151. package/src/utils/clamp.js +0 -8
  152. package/src/utils/cloner.js +0 -34
  153. package/src/utils/cloner.test.js +0 -24
  154. package/src/utils/coalesce.js +0 -11
  155. package/src/utils/coalesce.test.js +0 -16
  156. package/src/utils/concatIterables.js +0 -26
  157. package/src/utils/concatIterables.test.js +0 -8
  158. package/src/utils/debounce.js +0 -37
  159. package/src/utils/domainArray.js +0 -216
  160. package/src/utils/domainArray.test.js +0 -130
  161. package/src/utils/eerp.js +0 -13
  162. package/src/utils/expression.js +0 -32
  163. package/src/utils/field.js +0 -28
  164. package/src/utils/formatObject.js +0 -31
  165. package/src/utils/indexer.js +0 -43
  166. package/src/utils/indexer.test.js +0 -47
  167. package/src/utils/inertia.js +0 -124
  168. package/src/utils/interactionEvent.js +0 -33
  169. package/src/utils/iterateNestedMaps.js +0 -21
  170. package/src/utils/iterateNestedMaps.test.js +0 -33
  171. package/src/utils/kWayMerge.js +0 -42
  172. package/src/utils/kWayMerge.test.js +0 -26
  173. package/src/utils/layout/flexLayout.js +0 -368
  174. package/src/utils/layout/flexLayout.test.js +0 -311
  175. package/src/utils/layout/grid.js +0 -95
  176. package/src/utils/layout/grid.test.js +0 -71
  177. package/src/utils/layout/padding.js +0 -120
  178. package/src/utils/layout/point.js +0 -23
  179. package/src/utils/layout/rectangle.js +0 -288
  180. package/src/utils/layout/rectangle.test.js +0 -172
  181. package/src/utils/mergeObjects.js +0 -99
  182. package/src/utils/mergeObjects.test.js +0 -42
  183. package/src/utils/numberExtractor.js +0 -24
  184. package/src/utils/numberExtractor.test.js +0 -6
  185. package/src/utils/point.js +0 -14
  186. package/src/utils/propertyCacher.js +0 -70
  187. package/src/utils/propertyCacher.test.js +0 -85
  188. package/src/utils/propertyCoalescer.js +0 -42
  189. package/src/utils/propertyCoalescer.test.js +0 -22
  190. package/src/utils/reservationMap.js +0 -103
  191. package/src/utils/reservationMap.test.js +0 -20
  192. package/src/utils/scaleNull.js +0 -19
  193. package/src/utils/setOperations.js +0 -75
  194. package/src/utils/smoothstep.js +0 -10
  195. package/src/utils/throttle.js +0 -34
  196. package/src/utils/topK.js +0 -76
  197. package/src/utils/topK.test.js +0 -64
  198. package/src/utils/transition.js +0 -74
  199. package/src/utils/ui/tooltip.js +0 -189
  200. package/src/utils/url.js +0 -22
  201. package/src/utils/variableTools.js +0 -24
  202. package/src/utils/variableTools.test.js +0 -13
  203. package/src/view/axisResolution.js +0 -140
  204. package/src/view/axisResolution.test.js +0 -201
  205. package/src/view/axisView.js +0 -747
  206. package/src/view/concatView.js +0 -45
  207. package/src/view/containerView.js +0 -159
  208. package/src/view/facetView.js +0 -491
  209. package/src/view/flowBuilder.js +0 -367
  210. package/src/view/flowBuilder.test.js +0 -125
  211. package/src/view/gridView.js +0 -786
  212. package/src/view/implicitRootView.js +0 -14
  213. package/src/view/importView.js +0 -19
  214. package/src/view/layerView.js +0 -74
  215. package/src/view/rendering.d.ts +0 -44
  216. package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
  217. package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
  218. package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
  219. package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
  220. package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
  221. package/src/view/renderingContext/viewRenderingContext.js +0 -41
  222. package/src/view/scaleResolution.js +0 -791
  223. package/src/view/scaleResolution.test.js +0 -572
  224. package/src/view/scaleResolutionApi.d.ts +0 -40
  225. package/src/view/testUtils.js +0 -51
  226. package/src/view/title.js +0 -165
  227. package/src/view/unitView.js +0 -382
  228. package/src/view/view.js +0 -612
  229. package/src/view/view.test.js +0 -214
  230. package/src/view/viewContext.d.ts +0 -62
  231. package/src/view/viewFactory.js +0 -181
  232. package/src/view/viewFactory.test.js +0 -17
  233. package/src/view/viewUtils.js +0 -327
  234. package/src/view/zoom.js +0 -89
@@ -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,488 +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.0);`
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
- /**
467
- * @param {number} x
468
- * @param {number[]} [arr]
469
- */
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 ??= [];
478
- arr[0] = hi;
479
- arr[1] = lo;
480
- return arr;
481
- }
482
-
483
- /**
484
- * @param {number[]} domain
485
- */
486
- export function toHighPrecisionDomainUniform(domain) {
487
- return [...splitHighPrecision(domain[0]), domain[1] - domain[0]];
488
- }