@genome-spy/core 0.30.0 → 0.30.3

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 +16379 -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 -785
  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 -504
  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 -502
  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 -797
  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
package/src/marks/mark.js DELETED
@@ -1,975 +0,0 @@
1
- import {
2
- createBufferInfoFromArrays,
3
- createProgramInfoFromProgram,
4
- createUniformBlockInfo,
5
- createVertexArrayInfo,
6
- setAttribInfoBufferFromArray,
7
- setBlockUniforms,
8
- setUniformBlock,
9
- setUniforms,
10
- } from "twgl.js";
11
- import { isContinuous, isDiscrete } from "vega-scale";
12
- import createEncoders, {
13
- isChannelDefWithScale,
14
- isDatumDef,
15
- isValueDef,
16
- } from "../encoder/encoder";
17
- import {
18
- DOMAIN_PREFIX,
19
- generateValueGlsl,
20
- generateScaleGlsl,
21
- RANGE_TEXTURE_PREFIX,
22
- ATTRIBUTE_PREFIX,
23
- isHighPrecisionScale,
24
- toHighPrecisionDomainUniform,
25
- splitHighPrecision,
26
- } from "../scale/glslScaleGenerator";
27
- import GLSL_COMMON from "../gl/includes/common.glsl";
28
- import GLSL_SCALES from "../gl/includes/scales.glsl";
29
- import GLSL_SAMPLE_FACET from "../gl/includes/sampleFacet.glsl";
30
- import GLSL_PICKING_VERTEX from "../gl/includes/picking.vertex.glsl";
31
- import GLSL_PICKING_FRAGMENT from "../gl/includes/picking.fragment.glsl";
32
- import { getCachedOrCall } from "../utils/propertyCacher";
33
- import { createProgram } from "../gl/webGLHelper";
34
- import coalesceProperties from "../utils/propertyCoalescer";
35
- import { isScalar } from "../utils/variableTools";
36
- import { InternMap } from "internmap";
37
-
38
- export const SAMPLE_FACET_UNIFORM = "SAMPLE_FACET_UNIFORM";
39
- export const SAMPLE_FACET_TEXTURE = "SAMPLE_FACET_TEXTURE";
40
-
41
- /**
42
- * @typedef {import("../spec/mark").MarkConfig} MarkConfig
43
- * @typedef {import("../spec/channel").Channel} Channel
44
- * @typedef {import("../spec/channel").Encoding} Encoding
45
- * @typedef {import("../spec/channel").ValueDef} ValueDef
46
- *
47
- * @typedef {import("../view/view").RenderingOptions} RenderingOptions
48
- * @typedef {object} _MarkRenderingOptions
49
- * @prop {boolean} [skipViewportSetup] Don't configure viewport. Allows for
50
- * optimized faceted rendering
51
- * @typedef {RenderingOptions & _MarkRenderingOptions} MarkRenderingOptions
52
- *
53
- * @callback DrawFunction
54
- * @param {number} offset
55
- * @param {number} count
56
- *
57
- */
58
- export default class Mark {
59
- /**
60
- * @param {import("../view/unitView").default} unitView
61
- */
62
- constructor(unitView) {
63
- this.unitView = unitView;
64
-
65
- /** @type {Record<string, import("../encoder/encoder").Encoder>} */
66
- this.encoders = undefined;
67
-
68
- // TODO: Consolidate the following webgl stuff into a single object
69
-
70
- /** @type {import("twgl.js").BufferInfo & { allocatedVertices?: number }} WebGL buffers */
71
- this.bufferInfo = undefined;
72
-
73
- /** @type {import("twgl.js").ProgramInfo} WebGL buffers */
74
- this.programInfo = undefined;
75
-
76
- /** @type {import("twgl.js").VertexArrayInfo} WebGL buffers */
77
- this.vertexArrayInfo = undefined;
78
-
79
- /** @type {import("twgl.js").UniformBlockInfo} WebGL buffers */
80
- this.domainUniformInfo = undefined;
81
-
82
- /** @type {import("twgl.js").UniformBlockInfo} WebGL buffers */
83
- this.viewUniformInfo = undefined;
84
-
85
- /** @type {RangeMap<any>} keep track of facet locations within the vertex array */
86
- this.rangeMap = new RangeMap();
87
-
88
- // TODO: Implement https://vega.github.io/vega-lite/docs/config.html
89
- /** @type {MarkConfig} */
90
- this.defaultProperties = {
91
- get clip() {
92
- // TODO: Cache once the scales have been resolved
93
- // TODO: Only check channels that are used
94
- // TODO: provide more fine-grained xClip and yClip props
95
- return /** @type {Channel[]} */ (["x", "y"])
96
- .map((channel) => unitView.getScaleResolution(channel))
97
- .some((resolution) => resolution?.isZoomable() ?? false);
98
- },
99
- xOffset: 0,
100
- yOffset: 0,
101
-
102
- /**
103
- * Minimum size for WebGL buffers (number of data items).
104
- * Allows for using bufferSubData to update graphics.
105
- * This property is intended for internal usage.
106
- */
107
- minBufferSize: 0,
108
- };
109
-
110
- /**
111
- * A properties object that contains the configured mark properties or
112
- * default values as fallback.
113
- *
114
- * TODO: Proper and comprehensive typings for mark properties
115
- *
116
- * @type {Partial<MarkConfig>}
117
- * @readonly
118
- */
119
- this.properties = coalesceProperties(
120
- typeof this.unitView.spec.mark == "object"
121
- ? () => /** @type {MarkConfig} */ (this.unitView.spec.mark)
122
- : () => /** @type {MarkConfig} */ ({}),
123
- () => this.defaultProperties
124
- );
125
- }
126
-
127
- get opaque() {
128
- return false;
129
- }
130
-
131
- /**
132
- * Returns attribute info for WebGL attributes that match visual channels.
133
- *
134
- * Note: attributes and channels do not necessarily match.
135
- * For example, rectangles have x, y, x2, and y2 channels but only x and y as attributes.
136
- *
137
- * @returns {string[]}
138
- */
139
- getAttributes() {
140
- // override
141
- throw new Error("Not implemented!");
142
- }
143
-
144
- /**
145
- * @returns {Channel[]}
146
- */
147
- getSupportedChannels() {
148
- return [
149
- "sample",
150
- "facetIndex",
151
- "x",
152
- "y",
153
- "color",
154
- "opacity",
155
- "search",
156
- "uniqueId",
157
- ];
158
- }
159
-
160
- /**
161
- * @returns {Encoding}
162
- */
163
- getDefaultEncoding() {
164
- /** @type {Encoding} */
165
- const encoding = {
166
- sample: undefined,
167
- uniqueId: undefined,
168
- };
169
-
170
- if (this.isPickingParticipant()) {
171
- encoding.uniqueId = {
172
- field: "_uniqueId", // TODO: Use constant
173
- };
174
- }
175
-
176
- return encoding;
177
- }
178
-
179
- /**
180
- * Adds intelligent defaults etc to the encoding.
181
- *
182
- * @param {Encoding} encoding
183
- * @returns {Encoding}
184
- */
185
- fixEncoding(encoding) {
186
- return encoding;
187
- }
188
-
189
- /**
190
- * Returns the encoding spec supplemented with mark's default encodings
191
- *
192
- * @returns {Encoding}
193
- */
194
- get encoding() {
195
- return getCachedOrCall(this, "encoding", () => {
196
- const defaults = this.getDefaultEncoding();
197
- const configured = this.unitView.getEncoding();
198
-
199
- /** @type {(property: string) => ValueDef} */
200
- const propToValueDef = (property) => {
201
- const value =
202
- this.properties[/** @type {keyof MarkConfig} */ (property)];
203
- return isScalar(value) && { value };
204
- };
205
-
206
- const propertyValues = Object.fromEntries(
207
- this.getSupportedChannels()
208
- .map(
209
- (channel) =>
210
- /** @type {[Channel, ValueDef]} */ ([
211
- channel,
212
- propToValueDef(channel),
213
- ])
214
- )
215
- .filter((entry) => entry[1].value !== undefined)
216
- );
217
-
218
- const encoding = this.fixEncoding({
219
- ...defaults,
220
- ...propertyValues,
221
- ...configured,
222
- });
223
-
224
- for (const channel of Object.keys(encoding)) {
225
- if (!this.getSupportedChannels().includes(channel)) {
226
- // TODO: Only delete channels that were inherited
227
- // Should complain about unsupported channels that were
228
- // explicitly specified.
229
- delete encoding[channel];
230
- }
231
- }
232
-
233
- return encoding;
234
- });
235
- }
236
-
237
- getContext() {
238
- return this.unitView.context;
239
- }
240
-
241
- getType() {
242
- return this.unitView.getMarkType();
243
- }
244
-
245
- initializeData() {
246
- //
247
- }
248
-
249
- /**
250
- * Initialize encoders that encode fields of the data (or constants) to
251
- * the ranges of the visual channels.
252
- */
253
- initializeEncoders() {
254
- this.encoders = createEncoders(this);
255
- }
256
-
257
- /**
258
- * Initialize shaders etc.
259
- */
260
- async initializeGraphics() {
261
- //override
262
- }
263
-
264
- /**
265
- * Update WebGL buffers from the data
266
- */
267
- updateGraphicsData() {
268
- // override
269
- }
270
-
271
- getSampleFacetMode() {
272
- if (this.encoders.facetIndex) {
273
- return SAMPLE_FACET_TEXTURE;
274
- } else if (
275
- // If the UnitView is inside app's SampleView.
276
- // TODO: This may break if non-faceted stuff is added to SampleView,
277
- // e.g., view background or an x axis.
278
- // This could also be more generic and work with other faceting views
279
- // that will be available in the future.
280
- [...this.unitView.getAncestors()].find(
281
- (view) => "samples" in view.spec
282
- )
283
- ) {
284
- return SAMPLE_FACET_UNIFORM;
285
- }
286
- }
287
-
288
- /**
289
- *
290
- * @param {string} vertexShader
291
- * @param {string} fragmentShader
292
- * @param {string[]} [extraHeaders]
293
- */
294
- createAndLinkShaders(vertexShader, fragmentShader, extraHeaders = []) {
295
- const attributes = this.getAttributes();
296
-
297
- // For debugging
298
- extraHeaders.push("// view: " + this.unitView.getPathString());
299
-
300
- // TODO: This is a temporary variable, don't store it in the mark object
301
- /** @type {string[]} */
302
- this.domainUniforms = [];
303
-
304
- /** @type {string[]} */
305
- let scaleCode = [];
306
-
307
- const sampleFacetMode = this.getSampleFacetMode();
308
- if (sampleFacetMode) {
309
- extraHeaders.push(`#define ${sampleFacetMode}`);
310
- }
311
-
312
- for (const attribute of attributes) {
313
- /** @type {Channel} */
314
- let channel;
315
- if (attribute in this.encoding) {
316
- channel = /** @type {Channel} */ (attribute);
317
- } else {
318
- continue;
319
- }
320
-
321
- const channelDef = this.encoding[channel];
322
-
323
- if (!channelDef) {
324
- continue;
325
- }
326
-
327
- if (isValueDef(channelDef)) {
328
- scaleCode.push(generateValueGlsl(channel, channelDef.value));
329
- } else {
330
- const resolutionChannel =
331
- (isChannelDefWithScale(channelDef) &&
332
- channelDef.resolutionChannel) ||
333
- channel;
334
-
335
- const scale = this.unitView
336
- .getScaleResolution(resolutionChannel)
337
- .getScale();
338
-
339
- const generated = generateScaleGlsl(channel, scale, channelDef);
340
-
341
- scaleCode.push(generated.glsl);
342
- if (generated.domainUniform) {
343
- this.domainUniforms.push(generated.domainUniform);
344
- }
345
- }
346
- }
347
-
348
- const domainUniformBlock = this.domainUniforms.length
349
- ? "layout(std140) uniform Domains {\n" +
350
- this.domainUniforms.map((u) => ` ${u}\n`).join("") +
351
- "};\n\n"
352
- : "";
353
-
354
- const vertexPrecision = "precision highp float;\n";
355
-
356
- const vertexParts = [
357
- vertexPrecision,
358
- ...extraHeaders,
359
- GLSL_COMMON,
360
- GLSL_SCALES,
361
- domainUniformBlock,
362
- ...scaleCode,
363
- GLSL_SAMPLE_FACET,
364
- GLSL_PICKING_VERTEX,
365
- vertexShader,
366
- ];
367
-
368
- const fragmentParts = [
369
- ...extraHeaders,
370
- GLSL_COMMON,
371
- GLSL_PICKING_FRAGMENT,
372
- fragmentShader,
373
- ];
374
-
375
- const gl = this.gl;
376
-
377
- // Postpone status checking to allow for background compilation
378
- // See: https://toji.github.io/shader-perf/
379
- // TODO: It might make sense to cache and share identical programs between mark instances.
380
- this.programStatus = createProgram(
381
- gl,
382
- this.glHelper.compileShader(gl.VERTEX_SHADER, vertexParts),
383
- this.glHelper.compileShader(gl.FRAGMENT_SHADER, fragmentParts)
384
- );
385
- }
386
-
387
- /**
388
- * Check WebGL shader/program compilation/linking status and finalize
389
- * initialization.
390
- */
391
- finalizeGraphicsInitialization() {
392
- const error = this.programStatus.getProgramErrors();
393
- if (error) {
394
- if (error.detail) {
395
- console.warn(error.detail);
396
- }
397
- /** @type {Error & { view?: import("../view/view").default}} */
398
- const err = new Error(
399
- "Cannot create shader program: " + error.message
400
- );
401
- err.view = this.unitView;
402
- throw err;
403
- }
404
-
405
- this.programInfo = createProgramInfoFromProgram(
406
- this.gl,
407
- this.programStatus.program
408
- );
409
- delete this.programStatus;
410
-
411
- if (this.domainUniforms.length) {
412
- this.domainUniformInfo = createUniformBlockInfo(
413
- this.gl,
414
- this.programInfo,
415
- "Domains"
416
- );
417
- }
418
-
419
- this.viewUniformInfo = createUniformBlockInfo(
420
- this.gl,
421
- this.programInfo,
422
- "View"
423
- );
424
-
425
- this.gl.useProgram(this.programInfo.program);
426
-
427
- this._setDatums();
428
-
429
- setUniforms(this.programInfo, {
430
- // left pos, left height, right pos, right height
431
- uSampleFacet: [0, 1, 0, 1],
432
- uTransitionOffset: 0.0,
433
- });
434
- }
435
-
436
- _setDatums() {
437
- for (const [channel, channelDef] of Object.entries(this.encoding)) {
438
- if (isDatumDef(channelDef)) {
439
- const encoder = this.encoders[channel];
440
-
441
- const datum = encoder.indexer
442
- ? encoder.indexer(channelDef.datum)
443
- : isHighPrecisionScale(encoder.scale.type)
444
- ? splitHighPrecision(+channelDef.datum)
445
- : +channelDef.datum;
446
-
447
- setUniforms(this.programInfo, {
448
- [ATTRIBUTE_PREFIX + channel]: datum,
449
- });
450
- }
451
- }
452
- }
453
-
454
- /**
455
- * Delete WebGL buffers etc.
456
- */
457
- deleteGraphicsData() {
458
- if (this.bufferInfo) {
459
- const gl = this.gl;
460
- // A hack to prevent WebGL: INVALID_OPERATION: drawArrays: no buffer is bound to enabled attribute
461
- // TODO: Consider using bufferSubData or DYNAMIC_DRAW etc...
462
- for (let i = 0; i < 8; i++) {
463
- gl.disableVertexAttribArray(i);
464
- }
465
-
466
- Object.values(this.bufferInfo.attribs).forEach((attribInfo) =>
467
- this.gl.deleteBuffer(attribInfo.buffer)
468
- );
469
- if (this.bufferInfo.indices) {
470
- this.gl.deleteBuffer(this.bufferInfo.indices);
471
- }
472
- this.bufferInfo = undefined;
473
- }
474
- }
475
-
476
- /**
477
- *
478
- * @param {any} vertexData TODO: Extract type from VertexBuilder
479
- */
480
- updateBufferInfo(vertexData) {
481
- // Ensure that no VAOs are inadvertently altered
482
- this.gl.bindVertexArray(null);
483
-
484
- if (
485
- this.bufferInfo &&
486
- vertexData.vertexCount <= this.bufferInfo.allocatedVertices
487
- ) {
488
- for (const [attribute, attributeData] of Object.entries(
489
- vertexData.arrays
490
- )) {
491
- // Skip constants
492
- if (attributeData.data) {
493
- // TODO: Check that all attributes and numComponents match
494
- setAttribInfoBufferFromArray(
495
- this.gl,
496
- this.bufferInfo.attribs[attribute],
497
- attributeData.data,
498
- 0
499
- );
500
- // TODO: Consider double buffering:
501
- // https://community.khronos.org/t/texture-buffers-are-much-slower-than-uniform-buffers/77139
502
- }
503
- }
504
- } else {
505
- this.deleteGraphicsData();
506
- this.bufferInfo = createBufferInfoFromArrays(
507
- this.gl,
508
- vertexData.arrays,
509
- { numElements: vertexData.vertexCount }
510
- );
511
- this.bufferInfo.allocatedVertices = vertexData.allocatedVertices;
512
- this.vertexArrayInfo = undefined;
513
- }
514
- }
515
-
516
- /** Convenience method */
517
- get glHelper() {
518
- return this.getContext().glHelper;
519
- }
520
-
521
- /** Convenience method */
522
- get gl() {
523
- return this.glHelper.gl;
524
- }
525
-
526
- onBeforeSampleAnimation() {
527
- // override
528
- }
529
-
530
- onAfterSampleAnimation() {
531
- // override
532
- }
533
-
534
- isReady() {
535
- return this.bufferInfo && this.programInfo;
536
- }
537
-
538
- /**
539
- * Returns true if this mark instance participates in picking.
540
- *
541
- * TODO: Check if tooltip is enabled,
542
- * TODO: Check if selection (when it's implemented) is enabled
543
- */
544
- isPickingParticipant() {
545
- // TODO: Should check encoding instead
546
- if (this.properties.tooltip === null) {
547
- // Disabled
548
- return false;
549
- }
550
-
551
- for (const view of this.unitView.getAncestors()) {
552
- if (!view.isPickingSupported()) {
553
- return false;
554
- }
555
- }
556
-
557
- return true;
558
- }
559
-
560
- /**
561
- * Configures the WebGL state for rendering the mark instances.
562
- * A separate preparation stage allows for efficient rendering of faceted
563
- * views, i.e., multiple views share the uniforms (such as mark properties
564
- * and scales) and buffers.
565
- *
566
- * @param {import("../view/rendering").GlobalRenderingOptions} options
567
- * @returns {(() => void)[]}
568
- */
569
- // eslint-disable-next-line complexity
570
- prepareRender(options) {
571
- const glHelper = this.glHelper;
572
- const gl = this.gl;
573
-
574
- /** @type {(() => void)[]} */
575
- const ops = [];
576
-
577
- ops.push(() => {
578
- if (!this.vertexArrayInfo) {
579
- this.vertexArrayInfo = createVertexArrayInfo(
580
- this.gl,
581
- this.programInfo,
582
- this.bufferInfo
583
- );
584
- }
585
-
586
- gl.useProgram(this.programInfo.program);
587
- });
588
-
589
- if (this.domainUniformInfo) {
590
- // TODO: Only update the domains that have changed
591
-
592
- for (const [uniform, setter] of Object.entries(
593
- this.domainUniformInfo.setters
594
- )) {
595
- // TODO: isChannel()
596
- const channel = /** @type {Channel} */ (
597
- uniform.substring(DOMAIN_PREFIX.length)
598
- );
599
-
600
- const channelDef = this.encoding[channel];
601
- const resolutionChannel =
602
- (isChannelDefWithScale(channelDef) &&
603
- channelDef.resolutionChannel) ||
604
- channel;
605
- const resolution =
606
- this.unitView.getScaleResolution(resolutionChannel);
607
-
608
- if (resolution) {
609
- const scale = resolution.getScale();
610
-
611
- ops.push(() => {
612
- const domain = isDiscrete(scale.type)
613
- ? [0, scale.domain().length]
614
- : scale.domain();
615
-
616
- setter(
617
- isHighPrecisionScale(scale.type)
618
- ? toHighPrecisionDomainUniform(domain)
619
- : domain
620
- );
621
- });
622
- }
623
- }
624
-
625
- ops.push(() =>
626
- setUniformBlock(gl, this.programInfo, this.domainUniformInfo)
627
- );
628
- }
629
-
630
- for (const [channel, channelDef] of Object.entries(this.encoding)) {
631
- if (isChannelDefWithScale(channelDef)) {
632
- const resolutionChannel =
633
- (isChannelDefWithScale(channelDef) &&
634
- channelDef.resolutionChannel) ||
635
- channel;
636
-
637
- const resolution =
638
- this.unitView.getScaleResolution(resolutionChannel);
639
-
640
- const texture = glHelper.rangeTextures.get(resolution);
641
- if (texture) {
642
- ops.push(() =>
643
- setUniforms(this.programInfo, {
644
- [RANGE_TEXTURE_PREFIX + channel]: texture,
645
- })
646
- );
647
- }
648
- }
649
- }
650
-
651
- if (this.getSampleFacetMode() == SAMPLE_FACET_TEXTURE) {
652
- ops.push(() => {
653
- /** @type {WebGLTexture} */
654
- let facetTexture;
655
- for (const view of this.unitView.getAncestors()) {
656
- facetTexture = view.getSampleFacetTexture();
657
- if (facetTexture) {
658
- break;
659
- }
660
- }
661
-
662
- if (!facetTexture) {
663
- throw new Error("No facet texture available. This is bug.");
664
- }
665
-
666
- setUniforms(this.programInfo, {
667
- uSampleFacetTexture: facetTexture,
668
- });
669
- });
670
- }
671
-
672
- // TODO: Rendering of the mark should be completely skipped if it doesn't
673
- // participate picking
674
- const picking =
675
- (options.picking ?? false) && this.isPickingParticipant();
676
-
677
- // Note: the block is sent to GPU in setViewport(), which is repeated for each facet
678
- ops.push(() =>
679
- setBlockUniforms(this.viewUniformInfo, {
680
- uViewOpacity: this.unitView.getEffectiveOpacity(),
681
- uPickingEnabled: picking,
682
- })
683
- );
684
-
685
- if (this.opaque || options.picking) {
686
- ops.push(() => gl.disable(gl.BLEND));
687
- } else {
688
- ops.push(() => gl.enable(gl.BLEND));
689
- }
690
-
691
- return ops;
692
- }
693
-
694
- /**
695
- * Prepares rendering of a single sample facet.
696
- *
697
- * @param {MarkRenderingOptions} options
698
- * @returns {boolean} true if rendering should proceed,
699
- * false if it should be skipped
700
- */
701
- prepareSampleFacetRendering(options) {
702
- const opts = options.sampleFacetRenderingOptions;
703
- const locationSetter = this.programInfo.uniformSetters.uSampleFacet;
704
-
705
- if (opts && locationSetter) {
706
- const pos = opts.locSize ? opts.locSize.location : 0.0;
707
- const height = opts.locSize ? opts.locSize.size : 1.0;
708
-
709
- if (pos > 1.0 || pos + height < 0.0) {
710
- // Not visible
711
- return false;
712
- }
713
-
714
- const targetPos = opts.targetLocSize
715
- ? opts.targetLocSize.location
716
- : pos;
717
- const targetHeight = opts.targetLocSize
718
- ? opts.targetLocSize.size
719
- : height;
720
-
721
- // Use WebGL directly, because twgl uses gl.uniform4fv, which has an
722
- // inferior performance. Based on profiling, this optimization gives
723
- // a significant performance boost.
724
- this.gl.uniform4f(
725
- // @ts-expect-error
726
- locationSetter.location, // TODO: Make a twgl pull request to fix typing
727
- pos,
728
- height,
729
- targetPos,
730
- targetHeight
731
- );
732
- }
733
-
734
- return true;
735
- }
736
-
737
- /**
738
- * Returns a callback function that the ViewRenderingContext calls to
739
- * perform the actual rendering either immediately or at a later time.
740
- *
741
- * @param {MarkRenderingOptions} options
742
- * @returns {function():void} A function that renderingContext calls to
743
- * trigger the actual rendering
744
- */
745
- render(options) {
746
- // Override
747
- return undefined;
748
- }
749
-
750
- /**
751
- * @param {DrawFunction} draw A function that draws a range of vertices
752
- * @param {import("./Mark").MarkRenderingOptions} options
753
- */
754
- createRenderCallback(draw, options) {
755
- // eslint-disable-next-line consistent-this
756
- const self = this;
757
-
758
- /** @type {function(import("../gl/dataToVertices").RangeEntry):void} rangeEntry */
759
- let drawWithRangeEntry;
760
-
761
- const scale = this.unitView.getScaleResolution("x")?.getScale();
762
- const continuous = scale && isContinuous(scale.type);
763
- const domainStartOffset = ["index", "locus"].includes(scale?.type)
764
- ? -1
765
- : 0;
766
-
767
- /** @type {[number, number]} Recycle to ease garbage collector's work */
768
- const arr = [0, 0];
769
-
770
- drawWithRangeEntry = (rangeEntry) => {
771
- if (continuous && rangeEntry.xIndex) {
772
- const domain = scale.domain();
773
- const vertexIndices = rangeEntry.xIndex(
774
- domain[0] + domainStartOffset,
775
- domain[1],
776
- arr
777
- );
778
- const offset = vertexIndices[0];
779
- const count = vertexIndices[1] - offset;
780
- if (count > 0) {
781
- draw(offset, count);
782
- }
783
- } else {
784
- draw(rangeEntry.offset, rangeEntry.count);
785
- }
786
- };
787
-
788
- // If is either faceted or non-faceted, not both.
789
- // An undefined key with vertices means that the mark is non-faceted.
790
- // In such case, the same non-faceted data is repeated for each facet.
791
- const facetId =
792
- this.rangeMap.get(undefined).count == 0
793
- ? options.facetId
794
- : undefined;
795
- const rangeEntry = this.rangeMap.get(facetId);
796
-
797
- return options.sampleFacetRenderingOptions
798
- ? function renderSampleFacetRange() {
799
- if (rangeEntry.count) {
800
- if (self.prepareSampleFacetRendering(options)) {
801
- drawWithRangeEntry(rangeEntry);
802
- }
803
- }
804
- }
805
- : function renderRange() {
806
- if (rangeEntry.count) {
807
- drawWithRangeEntry(rangeEntry);
808
- }
809
- };
810
- }
811
-
812
- /**
813
- * Sets viewport, clipping, and uniforms related to scaling and translation
814
- *
815
- * @param {import("../utils/layout/rectangle").default} coords
816
- * @param {import("../utils/layout/rectangle").default} [clipRect]
817
- * @returns {boolean} true if the viewport is renderable (size > 0)
818
- */
819
- setViewport(coords, clipRect) {
820
- const dpr = this.glHelper.dpr;
821
- const gl = this.gl;
822
- const props = this.properties;
823
-
824
- const logicalSize = this.glHelper.getLogicalCanvasSize();
825
-
826
- // Translate by half a pixel to place vertical / horizontal
827
- // rules inside pixels, not between pixels.
828
- const pixelOffset = 0.5;
829
-
830
- // Note: we also handle xOffset/yOffset mark properties here
831
- const xOffset = (props.xOffset || 0) + pixelOffset;
832
- const yOffset = (props.yOffset || 0) + pixelOffset;
833
-
834
- /** @type {object} */
835
- let uniforms;
836
-
837
- let clippedCoords = coords;
838
-
839
- if (props.clip || clipRect) {
840
- let xClipOffset = 0;
841
- let yClipOffset = 0;
842
-
843
- /** @type {[number, number]} */
844
- let uViewScale;
845
-
846
- if (clipRect) {
847
- clippedCoords = props.clip
848
- ? coords.intersect(clipRect)
849
- : clipRect;
850
-
851
- uViewScale = [
852
- coords.width / clippedCoords.width,
853
- coords.height / clippedCoords.height,
854
- ];
855
-
856
- yClipOffset = Math.max(0, coords.y2 - clipRect.y2);
857
- xClipOffset = Math.max(0, coords.x2 - clipRect.x2); // TODO: Check sign
858
- } else {
859
- uViewScale = [1, 1];
860
- }
861
-
862
- const physicalGlCoords = [
863
- coords.x,
864
- logicalSize.height - clippedCoords.y2,
865
- Math.max(0, clippedCoords.width),
866
- Math.max(0, clippedCoords.height),
867
- ].map((x) => x * dpr);
868
-
869
- // Because glViewport accepts only integers, we subtract the rounding
870
- // errors from xyOffsets to guarantee that graphics in clipped
871
- // and non-clipped viewports align correctly
872
- const flooredCoords = physicalGlCoords.map((x) => Math.floor(x));
873
- const [xError, yError] = physicalGlCoords.map(
874
- (x, i) => x - flooredCoords[i]
875
- );
876
-
877
- // @ts-ignore
878
- gl.viewport(...flooredCoords);
879
- // @ts-ignore
880
- gl.scissor(...flooredCoords);
881
- gl.enable(gl.SCISSOR_TEST);
882
-
883
- uniforms = {
884
- uViewOffset: [
885
- (xOffset + xClipOffset + xError) / clippedCoords.width,
886
- -(yOffset + yClipOffset - yError) / clippedCoords.height,
887
- ],
888
- uViewScale,
889
- };
890
- } else {
891
- // Viewport comprises the full canvas
892
- gl.viewport(
893
- 0,
894
- 0,
895
- logicalSize.width * dpr,
896
- logicalSize.height * dpr
897
- );
898
- gl.disable(gl.SCISSOR_TEST);
899
-
900
- // Offset and scale all drawing to the view rectangle
901
- uniforms = {
902
- uViewOffset: [
903
- (coords.x + xOffset) / logicalSize.width,
904
- (logicalSize.height - coords.y - yOffset - coords.height) /
905
- logicalSize.height,
906
- ],
907
- uViewScale: [
908
- coords.width / logicalSize.width,
909
- coords.height / logicalSize.height,
910
- ],
911
- };
912
- }
913
-
914
- setBlockUniforms(this.viewUniformInfo, {
915
- ...uniforms,
916
- uViewportSize: [coords.width, coords.height],
917
- uDevicePixelRatio: this.glHelper.dpr,
918
- });
919
-
920
- setUniformBlock(this.gl, this.programInfo, this.viewUniformInfo);
921
-
922
- // TODO: Optimize: don't set viewport and stuff if rect is outside clipRect or screen
923
- return clippedCoords.height > 0 && clippedCoords.width > 0;
924
- }
925
-
926
- /**
927
- * Finds a datum that overlaps the given value on the x domain.
928
- * The result is unspecified if multiple data are found.
929
- *
930
- * This is highly specific to SampleView and its sorting/filtering functionality.
931
- *
932
- * @param {string} facetId
933
- * @param {number} x position on the x domain
934
- * @returns {any}
935
- */
936
- findDatumAt(facetId, x) {
937
- // override
938
- }
939
- }
940
-
941
- /**
942
- * @augments {InternMap<K, import("../gl/dataToVertices").RangeEntry>}
943
- * @template K
944
- */
945
- class RangeMap extends InternMap {
946
- constructor() {
947
- super([], JSON.stringify);
948
- }
949
-
950
- /**
951
- * @param {K} key
952
- */
953
- get(key) {
954
- let value = super.get(key);
955
- if (value === undefined) {
956
- value = {
957
- offset: 0,
958
- count: 0,
959
- xIndex: undefined,
960
- };
961
- super.set(key, value);
962
- }
963
- return value;
964
- }
965
-
966
- /**
967
- *
968
- * @param {Map<K, import("../gl/dataToVertices").RangeEntry>} anotherMap
969
- */
970
- migrateEntries(anotherMap) {
971
- for (const [key, value] of anotherMap.entries()) {
972
- Object.assign(this.get(key), value);
973
- }
974
- }
975
- }