@genome-spy/core 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/index.js +224 -0
  2. package/dist/style.css +1 -0
  3. package/package.json +54 -0
  4. package/src/data/collector.js +178 -0
  5. package/src/data/collector.test.js +82 -0
  6. package/src/data/dataFlow.js +109 -0
  7. package/src/data/dataFlow.test.js +3 -0
  8. package/src/data/facetNode.js +17 -0
  9. package/src/data/flow.test.js +71 -0
  10. package/src/data/flowBatch.d.ts +40 -0
  11. package/src/data/flowNode.js +283 -0
  12. package/src/data/flowNode.test.js +49 -0
  13. package/src/data/flowOptimizer.js +117 -0
  14. package/src/data/flowOptimizer.test.js +192 -0
  15. package/src/data/flowTestUtils.js +63 -0
  16. package/src/data/formats/fasta.js +32 -0
  17. package/src/data/formats/fasta.test.js +26 -0
  18. package/src/data/sources/dataSource.js +22 -0
  19. package/src/data/sources/dataSourceFactory.js +24 -0
  20. package/src/data/sources/dataUtils.js +31 -0
  21. package/src/data/sources/dynamicCallbackSource.js +56 -0
  22. package/src/data/sources/dynamicSource.js +36 -0
  23. package/src/data/sources/inlineSource.js +69 -0
  24. package/src/data/sources/inlineSource.test.js +55 -0
  25. package/src/data/sources/namedSource.js +74 -0
  26. package/src/data/sources/sequenceSource.js +46 -0
  27. package/src/data/sources/sequenceSource.test.js +45 -0
  28. package/src/data/sources/urlSource.js +74 -0
  29. package/src/data/transforms/aggregate.js +69 -0
  30. package/src/data/transforms/clone.js +40 -0
  31. package/src/data/transforms/clone.test.js +10 -0
  32. package/src/data/transforms/coverage.js +187 -0
  33. package/src/data/transforms/coverage.test.js +122 -0
  34. package/src/data/transforms/filter.js +37 -0
  35. package/src/data/transforms/filter.test.js +17 -0
  36. package/src/data/transforms/filterScoredLabels.js +134 -0
  37. package/src/data/transforms/flattenCompressedExons.js +57 -0
  38. package/src/data/transforms/flattenDelimited.js +68 -0
  39. package/src/data/transforms/flattenDelimited.test.js +86 -0
  40. package/src/data/transforms/flattenSequence.js +39 -0
  41. package/src/data/transforms/flattenSequence.test.js +33 -0
  42. package/src/data/transforms/formula.js +39 -0
  43. package/src/data/transforms/formula.test.js +18 -0
  44. package/src/data/transforms/identifier.js +108 -0
  45. package/src/data/transforms/identifier.test.js +82 -0
  46. package/src/data/transforms/linearizeGenomicCoordinate.js +101 -0
  47. package/src/data/transforms/measureText.js +44 -0
  48. package/src/data/transforms/pileup.js +128 -0
  49. package/src/data/transforms/pileup.test.js +69 -0
  50. package/src/data/transforms/project.js +41 -0
  51. package/src/data/transforms/project.test.js +31 -0
  52. package/src/data/transforms/regexExtract.js +61 -0
  53. package/src/data/transforms/regexExtract.test.js +66 -0
  54. package/src/data/transforms/regexFold.js +141 -0
  55. package/src/data/transforms/regexFold.test.js +159 -0
  56. package/src/data/transforms/sample.js +101 -0
  57. package/src/data/transforms/sample.test.js +37 -0
  58. package/src/data/transforms/stack.js +137 -0
  59. package/src/data/transforms/stack.test.js +90 -0
  60. package/src/data/transforms/transformFactory.js +60 -0
  61. package/src/encoder/accessor.js +82 -0
  62. package/src/encoder/accessor.test.js +46 -0
  63. package/src/encoder/encoder.js +369 -0
  64. package/src/encoder/encoder.test.js +97 -0
  65. package/src/fonts/Lato-Regular.json +1267 -0
  66. package/src/fonts/Lato-Regular.png +0 -0
  67. package/src/fonts/OFL.txt +93 -0
  68. package/src/fonts/README.md +3 -0
  69. package/src/fonts/bmFont.d.ts +58 -0
  70. package/src/fonts/bmFontManager.js +357 -0
  71. package/src/fonts/bmFontMetrics.js +108 -0
  72. package/src/genome/genome.js +305 -0
  73. package/src/genome/genome.test.js +152 -0
  74. package/src/genome/genomeStore.js +54 -0
  75. package/src/genome/locusFormat.js +31 -0
  76. package/src/genome/scaleIndex.js +199 -0
  77. package/src/genome/scaleIndex.test.js +61 -0
  78. package/src/genome/scaleLocus.js +112 -0
  79. package/src/genome/scaleLocus.test.js +3 -0
  80. package/src/genomeSpy.js +753 -0
  81. package/src/gl/arrayBuilder.js +199 -0
  82. package/src/gl/dataToVertices.js +621 -0
  83. package/src/gl/includes/common.glsl +63 -0
  84. package/src/gl/includes/fp64-arithmetic.glsl +187 -0
  85. package/src/gl/includes/fp64-utils.js +132 -0
  86. package/src/gl/includes/picking.fragment.glsl +3 -0
  87. package/src/gl/includes/picking.vertex.glsl +29 -0
  88. package/src/gl/includes/sampleFacet.glsl +107 -0
  89. package/src/gl/includes/scales.glsl +79 -0
  90. package/src/gl/includes/scales_fp64.glsl +30 -0
  91. package/src/gl/link.fragment.glsl +18 -0
  92. package/src/gl/link.vertex.glsl +111 -0
  93. package/src/gl/point.fragment.glsl +123 -0
  94. package/src/gl/point.vertex.glsl +128 -0
  95. package/src/gl/rect.fragment.glsl +51 -0
  96. package/src/gl/rect.vertex.glsl +114 -0
  97. package/src/gl/rule.fragment.glsl +52 -0
  98. package/src/gl/rule.vertex.glsl +89 -0
  99. package/src/gl/text.fragment.glsl +31 -0
  100. package/src/gl/text.vertex.glsl +246 -0
  101. package/src/gl/webGLHelper.js +490 -0
  102. package/src/img/bowtie.svg +1 -0
  103. package/src/img/genomespy-favicon.svg +34 -0
  104. package/src/index.html +11 -0
  105. package/src/index.js +151 -0
  106. package/src/marks/link.js +189 -0
  107. package/src/marks/mark.js +867 -0
  108. package/src/marks/markUtils.js +109 -0
  109. package/src/marks/pointMark.js +279 -0
  110. package/src/marks/rectMark.js +236 -0
  111. package/src/marks/rule.js +231 -0
  112. package/src/marks/text.js +274 -0
  113. package/src/options.d.ts +9 -0
  114. package/src/scale/colorUtils.js +184 -0
  115. package/src/scale/glslScaleGenerator.js +462 -0
  116. package/src/scale/scale.js +441 -0
  117. package/src/scale/scale.test.js +323 -0
  118. package/src/scale/ticks.js +198 -0
  119. package/src/scale/ticks.test.js +39 -0
  120. package/src/singlePageApp.js +13 -0
  121. package/src/spec/axis.d.ts +296 -0
  122. package/src/spec/channel.d.ts +127 -0
  123. package/src/spec/data.d.ts +185 -0
  124. package/src/spec/font.d.ts +15 -0
  125. package/src/spec/genome.d.ts +35 -0
  126. package/src/spec/mark.d.ts +432 -0
  127. package/src/spec/root.d.ts +22 -0
  128. package/src/spec/scale.d.ts +265 -0
  129. package/src/spec/tooltip.d.ts +9 -0
  130. package/src/spec/transform.d.ts +479 -0
  131. package/src/spec/view.d.ts +215 -0
  132. package/src/styles/genome-spy.scss +153 -0
  133. package/src/tooltip/dataTooltipHandler.js +59 -0
  134. package/src/tooltip/refseqGeneTooltipHandler.js +77 -0
  135. package/src/tooltip/tooltipHandler.ts +12 -0
  136. package/src/types/filetypes.d.ts +4 -0
  137. package/src/types/flatqueue.d.ts +53 -0
  138. package/src/types/glsl.d.ts +4 -0
  139. package/src/types/object.d.ts +21 -0
  140. package/src/types/vega-scale.d.ts +60 -0
  141. package/src/utils/animator.js +83 -0
  142. package/src/utils/arrayUtils.js +55 -0
  143. package/src/utils/binnedRangeIndex.js +83 -0
  144. package/src/utils/clamp.js +8 -0
  145. package/src/utils/cloner.js +32 -0
  146. package/src/utils/cloner.test.js +23 -0
  147. package/src/utils/coalesce.js +11 -0
  148. package/src/utils/coalesce.test.js +15 -0
  149. package/src/utils/concatIterables.js +26 -0
  150. package/src/utils/concatIterables.test.js +7 -0
  151. package/src/utils/debounce.js +37 -0
  152. package/src/utils/domainArray.js +224 -0
  153. package/src/utils/domainArray.test.js +129 -0
  154. package/src/utils/eerp.js +13 -0
  155. package/src/utils/expression.js +32 -0
  156. package/src/utils/field.js +28 -0
  157. package/src/utils/fisheye.js +60 -0
  158. package/src/utils/formatObject.js +31 -0
  159. package/src/utils/html.js +23 -0
  160. package/src/utils/html.test.js +13 -0
  161. package/src/utils/indexer.js +43 -0
  162. package/src/utils/indexer.test.js +46 -0
  163. package/src/utils/inertia.js +124 -0
  164. package/src/utils/interactionEvent.js +33 -0
  165. package/src/utils/iterateNestedMaps.js +21 -0
  166. package/src/utils/iterateNestedMaps.test.js +32 -0
  167. package/src/utils/kWayMerge.js +42 -0
  168. package/src/utils/kWayMerge.test.js +25 -0
  169. package/src/utils/layout/flexLayout.js +336 -0
  170. package/src/utils/layout/flexLayout.test.js +296 -0
  171. package/src/utils/layout/padding.js +107 -0
  172. package/src/utils/layout/point.js +23 -0
  173. package/src/utils/layout/rectangle.js +282 -0
  174. package/src/utils/layout/rectangle.test.js +171 -0
  175. package/src/utils/mergeObjects.js +99 -0
  176. package/src/utils/mergeObjects.test.js +41 -0
  177. package/src/utils/numberExtractor.js +24 -0
  178. package/src/utils/numberExtractor.test.js +5 -0
  179. package/src/utils/point.js +14 -0
  180. package/src/utils/propertyCacher.js +70 -0
  181. package/src/utils/propertyCacher.test.js +84 -0
  182. package/src/utils/propertyCoalescer.js +37 -0
  183. package/src/utils/propertyCoalescer.test.js +21 -0
  184. package/src/utils/reservationMap.js +103 -0
  185. package/src/utils/reservationMap.test.js +19 -0
  186. package/src/utils/scaleNull.js +19 -0
  187. package/src/utils/setOperations.js +75 -0
  188. package/src/utils/smoothstep.js +10 -0
  189. package/src/utils/throttle.js +34 -0
  190. package/src/utils/topK.js +76 -0
  191. package/src/utils/topK.test.js +63 -0
  192. package/src/utils/transition.js +74 -0
  193. package/src/utils/ui/tooltip.js +189 -0
  194. package/src/utils/url.js +22 -0
  195. package/src/utils/variableTools.js +24 -0
  196. package/src/utils/variableTools.test.js +12 -0
  197. package/src/view/axisResolution.js +135 -0
  198. package/src/view/axisResolution.test.js +200 -0
  199. package/src/view/axisView.js +746 -0
  200. package/src/view/channel.js +5 -0
  201. package/src/view/concatView.js +296 -0
  202. package/src/view/containerView.js +141 -0
  203. package/src/view/decoratorView.js +510 -0
  204. package/src/view/facetView.js +488 -0
  205. package/src/view/flowBuilder.js +362 -0
  206. package/src/view/flowBuilder.test.js +124 -0
  207. package/src/view/importView.js +19 -0
  208. package/src/view/layerView.js +60 -0
  209. package/src/view/rendering.d.ts +44 -0
  210. package/src/view/renderingContext/compositeViewRenderingContext.js +51 -0
  211. package/src/view/renderingContext/deferredViewRenderingContext.js +174 -0
  212. package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +128 -0
  213. package/src/view/renderingContext/simpleViewRenderingContext.js +62 -0
  214. package/src/view/renderingContext/svgViewRenderingContext.js +121 -0
  215. package/src/view/renderingContext/viewRenderingContext.js +41 -0
  216. package/src/view/scaleResolution.js +756 -0
  217. package/src/view/scaleResolution.test.js +571 -0
  218. package/src/view/scaleResolutionApi.d.ts +40 -0
  219. package/src/view/testUtils.js +48 -0
  220. package/src/view/unitView.js +368 -0
  221. package/src/view/view.js +589 -0
  222. package/src/view/view.test.js +213 -0
  223. package/src/view/viewContext.d.ts +57 -0
  224. package/src/view/viewFactory.js +179 -0
  225. package/src/view/viewFactory.test.js +16 -0
  226. package/src/view/viewUtils.js +420 -0
@@ -0,0 +1,231 @@
1
+ import Mark from "./mark.js";
2
+ import {
3
+ createTexture,
4
+ drawBufferInfo,
5
+ setBuffersAndAttributes,
6
+ setUniforms,
7
+ } from "twgl.js";
8
+ import VERTEX_SHADER from "../gl/rule.vertex.glsl";
9
+ import FRAGMENT_SHADER from "../gl/rule.fragment.glsl";
10
+ import { RuleVertexBuilder } from "../gl/dataToVertices";
11
+
12
+ export default class RuleMark extends Mark {
13
+ /**
14
+ * @param {import("../view/unitView").default} unitView
15
+ */
16
+ constructor(unitView) {
17
+ super(unitView);
18
+
19
+ this.dashTextureSize = 0;
20
+
21
+ Object.defineProperties(
22
+ this.defaultProperties,
23
+ Object.getOwnPropertyDescriptors({
24
+ x2: undefined,
25
+ y2: undefined,
26
+ size: 1,
27
+ color: "black",
28
+ opacity: 1.0,
29
+
30
+ minLength: 0.0,
31
+ /** @type {number[]} */
32
+ strokeDash: null,
33
+ strokeDashOffset: 0,
34
+ strokeCap: "butt",
35
+ })
36
+ );
37
+ }
38
+
39
+ getAttributes() {
40
+ return [
41
+ "uniqueId",
42
+ "facetIndex",
43
+ "x",
44
+ "x2",
45
+ "y",
46
+ "y2",
47
+ "size",
48
+ "color",
49
+ "opacity",
50
+ ];
51
+ }
52
+
53
+ /**
54
+ * @returns {import("../spec/channel").Channel[]}
55
+ */
56
+ getSupportedChannels() {
57
+ return [...super.getSupportedChannels(), "x2", "y2", "size"];
58
+ }
59
+
60
+ /**
61
+ * @param {import("../spec/channel").Encoding} encoding
62
+ * @returns {import("../spec/channel").Encoding}
63
+ */
64
+ // eslint-disable-next-line complexity
65
+ fixEncoding(encoding) {
66
+ // TODO: Write test for this mess
67
+ if (encoding.x && encoding.y && encoding.x2 && encoding.y2) {
68
+ // Everything is defined
69
+ } else if (encoding.x && encoding.x2 && !encoding.y) {
70
+ encoding.y = { value: 0.5 };
71
+ encoding.y2 = encoding.y;
72
+ } else if (encoding.y && encoding.y2 && !encoding.x) {
73
+ encoding.x = { value: 0.5 };
74
+ encoding.x2 = encoding.x;
75
+ } else if (encoding.x && !encoding.y) {
76
+ // Vertical rule
77
+ encoding.y = { value: 0 };
78
+ encoding.y2 = { value: 1 };
79
+ encoding.x2 = encoding.x;
80
+ } else if (encoding.y && !encoding.x) {
81
+ // Horizontal rule
82
+ encoding.x = { value: 0 };
83
+ encoding.x2 = { value: 1 };
84
+ encoding.y2 = encoding.y;
85
+ } else if (encoding.x && encoding.y && encoding.y2) {
86
+ // Limited vertical rule
87
+ encoding.x2 = encoding.x;
88
+ } else if (encoding.y && encoding.x && encoding.x2) {
89
+ // Limited horizontal rule
90
+ encoding.y2 = encoding.y;
91
+ } else if (encoding.y && encoding.x) {
92
+ if (!encoding.x2 && encoding.y.type == "quantitative") {
93
+ encoding.x2 = encoding.x;
94
+ encoding.y2 = { datum: 0 };
95
+ } else if (!encoding.y2 && encoding.x.type == "quantitative") {
96
+ encoding.y2 = encoding.y;
97
+ encoding.x2 = { datum: 0 };
98
+ } else {
99
+ throw new Error("A bug!"); // Should be unreachable
100
+ }
101
+ } else {
102
+ throw new Error(
103
+ "Invalid x and y encodings for rule mark: " +
104
+ JSON.stringify(encoding)
105
+ );
106
+ }
107
+
108
+ return encoding;
109
+ }
110
+
111
+ async initializeGraphics() {
112
+ await super.initializeGraphics();
113
+
114
+ if (this.properties.strokeDash) {
115
+ const gl = this.gl;
116
+ const textureData = createDashTextureArray(
117
+ this.properties.strokeDash
118
+ );
119
+ this.dashTexture = createTexture(gl, {
120
+ mag: gl.NEAREST,
121
+ min: gl.NEAREST,
122
+ internalFormat: gl.R8,
123
+ format: gl.RED,
124
+ src: textureData,
125
+ height: 1,
126
+ });
127
+ this.dashTextureSize = textureData.length; // Not needed with WebGL2
128
+ }
129
+
130
+ this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER);
131
+ }
132
+
133
+ updateGraphicsData() {
134
+ const collector = this.unitView.getCollector();
135
+ const itemCount = collector.getItemCount();
136
+
137
+ const builder = new RuleVertexBuilder({
138
+ encoders: this.encoders,
139
+ attributes: this.getAttributes(),
140
+ numItems: Math.max(itemCount, this.properties.minBufferSize || 0),
141
+ buildXIndex: this.properties.buildIndex,
142
+ });
143
+
144
+ builder.addBatches(collector.facetBatches);
145
+
146
+ const vertexData = builder.toArrays();
147
+ this.rangeMap = vertexData.rangeMap;
148
+
149
+ this.updateBufferInfo(vertexData);
150
+ }
151
+
152
+ /**
153
+ * @param {import("../view/rendering").GlobalRenderingOptions} options
154
+ */
155
+ prepareRender(options) {
156
+ super.prepareRender(options);
157
+
158
+ setUniforms(this.programInfo, {
159
+ uMinLength: this.properties.minLength,
160
+ uDashTextureSize: this.dashTextureSize,
161
+ uStrokeCap: ["butt", "square", "round"].indexOf(
162
+ this.properties.strokeCap
163
+ ),
164
+ });
165
+
166
+ if (this.dashTexture) {
167
+ setUniforms(this.programInfo, {
168
+ uDashTexture: this.dashTexture,
169
+ uStrokeDashOffset: this.properties.strokeDashOffset,
170
+ });
171
+ }
172
+
173
+ setBuffersAndAttributes(
174
+ this.gl,
175
+ this.programInfo,
176
+ this.vertexArrayInfo
177
+ );
178
+ }
179
+
180
+ /**
181
+ * @param {import("./Mark").MarkRenderingOptions} options
182
+ */
183
+ render(options) {
184
+ const gl = this.gl;
185
+
186
+ return this.createRenderCallback(
187
+ (offset, count) =>
188
+ drawBufferInfo(
189
+ gl,
190
+ this.vertexArrayInfo,
191
+ gl.TRIANGLE_STRIP,
192
+ count,
193
+ offset
194
+ ),
195
+ options,
196
+ () => this.rangeMap
197
+ );
198
+ }
199
+ }
200
+
201
+ /**
202
+ *
203
+ * @param {number[]} pattern
204
+ */
205
+ function createDashTextureArray(pattern) {
206
+ if (
207
+ pattern.length == 0 ||
208
+ pattern.length % 2 ||
209
+ pattern.findIndex((s) => Math.round(s) != s || s < 1 || s > 1000) >= 0
210
+ ) {
211
+ throw new Error(
212
+ "Invalid stroke dash pattern: " + JSON.stringify(pattern)
213
+ );
214
+ }
215
+
216
+ const len = pattern.reduce((a, b) => a + b);
217
+
218
+ const texture = new Uint8Array(len);
219
+
220
+ let state = true;
221
+ let i = 0;
222
+ for (let segment of pattern) {
223
+ while (segment) {
224
+ texture[i++] = (state && 255) || 0;
225
+ segment--;
226
+ }
227
+ state = !state;
228
+ }
229
+
230
+ return texture;
231
+ }
@@ -0,0 +1,274 @@
1
+ import { isString } from "vega-util";
2
+ import { format } from "d3-format";
3
+ import { drawBufferInfo, setBuffersAndAttributes, setUniforms } from "twgl.js";
4
+ import VERTEX_SHADER from "../gl/text.vertex.glsl";
5
+ import FRAGMENT_SHADER from "../gl/text.fragment.glsl";
6
+ import { TextVertexBuilder } from "../gl/dataToVertices";
7
+
8
+ import Mark from "./mark";
9
+ import { fixPositional } from "./markUtils";
10
+ import { primaryPositionalChannels } from "../encoder/encoder";
11
+
12
+ /** For GLSL uniforms */
13
+ const alignments = {
14
+ left: -1,
15
+ center: 0,
16
+ right: 1,
17
+ };
18
+
19
+ /** For GLSL uniforms */
20
+ const baselines = {
21
+ top: -1,
22
+ middle: 0,
23
+ bottom: 1,
24
+ alphabetic: 1,
25
+ };
26
+
27
+ /**
28
+ * Renders text using SDF fonts
29
+ *
30
+ * Some resources:
31
+ * - Valve's SDF paper: https://doi.org/10.1145/1281500.1281665
32
+ * - Multi-channel SDF fonts: https://github.com/Chlumsky/msdfgen
33
+ * - Google's web fonts as SDFs: https://github.com/etiennepinchon/aframe-fonts
34
+ */
35
+ export default class TextMark extends Mark {
36
+ /**
37
+ * @param {import("../view/unitView").default} unitView
38
+ */
39
+ constructor(unitView) {
40
+ super(unitView);
41
+
42
+ Object.defineProperties(
43
+ this.defaultProperties,
44
+ Object.getOwnPropertyDescriptors({
45
+ x: 0.5,
46
+ y: 0.5,
47
+ x2: undefined,
48
+ y2: undefined,
49
+ text: "",
50
+ size: 11.0,
51
+ color: "black",
52
+ opacity: 1.0,
53
+
54
+ // Use the built-in default
55
+ font: undefined,
56
+ fontStyle: undefined,
57
+ fontWeight: undefined,
58
+
59
+ align: "center",
60
+ baseline: "middle",
61
+ dx: 0,
62
+ dy: 0,
63
+ angle: 0,
64
+
65
+ /** When only primary channel is defined with band/locus scale */
66
+ fitToBand: false,
67
+
68
+ squeeze: true,
69
+ paddingX: 0,
70
+ paddingY: 0,
71
+ flushX: true,
72
+ flushY: true,
73
+
74
+ /** Stretch letters so that they can be used with sequence logos etc... */
75
+ logoLetters: false,
76
+
77
+ viewportEdgeFadeWidthTop: 0,
78
+ viewportEdgeFadeWidthRight: 0,
79
+ viewportEdgeFadeWidthBottom: 0,
80
+ viewportEdgeFadeWidthLeft: 0,
81
+
82
+ viewportEdgeFadeDistanceTop: -Infinity,
83
+ viewportEdgeFadeDistanceRight: -Infinity,
84
+ viewportEdgeFadeDistanceBottom: -Infinity,
85
+ viewportEdgeFadeDistanceLeft: -Infinity,
86
+ })
87
+ );
88
+
89
+ this.font = this.properties.font
90
+ ? unitView.context.fontManager.getFont(
91
+ this.properties.font,
92
+ this.properties.fontStyle,
93
+ this.properties.fontWeight
94
+ )
95
+ : unitView.context.fontManager.getDefaultFont();
96
+ }
97
+
98
+ getAttributes() {
99
+ return [
100
+ "uniqueId",
101
+ "facetIndex",
102
+ "x",
103
+ "x2",
104
+ "y",
105
+ "y2",
106
+ "color",
107
+ "size",
108
+ "opacity",
109
+ "angle",
110
+ ];
111
+ }
112
+
113
+ /**
114
+ * @returns {import("../spec/channel").Channel[]}
115
+ */
116
+ getSupportedChannels() {
117
+ return [
118
+ ...super.getSupportedChannels(),
119
+ "x2",
120
+ "y2",
121
+ "size",
122
+ "text",
123
+ "angle",
124
+ ];
125
+ }
126
+
127
+ /**
128
+ * @param {import("../spec/channel").Encoding} encoding
129
+ * @returns {import("../spec/channel").Encoding}
130
+ */
131
+ fixEncoding(encoding) {
132
+ // TODO: Ensure that both the primary and secondary channel are either variables or constants (values)
133
+
134
+ for (const channel of primaryPositionalChannels) {
135
+ if (this.properties.fitToBand) {
136
+ fixPositional(encoding, channel);
137
+ }
138
+ }
139
+
140
+ return encoding;
141
+ }
142
+
143
+ async initializeGraphics() {
144
+ await super.initializeGraphics();
145
+ this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER);
146
+ }
147
+
148
+ finalizeGraphicsInitialization() {
149
+ super.finalizeGraphicsInitialization();
150
+
151
+ this.gl.useProgram(this.programInfo.program);
152
+
153
+ const props = this.properties;
154
+
155
+ // TODO: Use uniform block.
156
+ setUniforms(this.programInfo, {
157
+ uPaddingX: props.paddingX,
158
+ uPaddingY: props.paddingY,
159
+ uFlushX: !!props.flushX,
160
+ uFlushY: !!props.flushY,
161
+
162
+ uAlign: [alignments[props.align], baselines[props.baseline]],
163
+
164
+ uD: [props.dx, -props.dy],
165
+
166
+ uLogoLetter: !!props.logoLetters,
167
+ uSqueeze: !!props.squeeze,
168
+
169
+ uViewportEdgeFadeWidth: [
170
+ props.viewportEdgeFadeWidthTop,
171
+ props.viewportEdgeFadeWidthRight,
172
+ props.viewportEdgeFadeWidthBottom,
173
+ props.viewportEdgeFadeWidthLeft,
174
+ ],
175
+
176
+ uViewportEdgeFadeDistance: [
177
+ props.viewportEdgeFadeDistanceTop,
178
+ props.viewportEdgeFadeDistanceRight,
179
+ props.viewportEdgeFadeDistanceBottom,
180
+ props.viewportEdgeFadeDistanceLeft,
181
+ ],
182
+ });
183
+ }
184
+
185
+ updateGraphicsData() {
186
+ const collector = this.unitView.getCollector();
187
+ const data = collector.getData();
188
+ const encoding = this.encoding;
189
+
190
+ // Count the total number of characters to that we can pre-allocate a typed array
191
+ const accessor = this.encoders.text.accessor || this.encoders.text; // accessor or constant value
192
+ let charCount = 0;
193
+ /** @type {function(any):any} */
194
+ const numberFormat = encoding.text.format
195
+ ? format(encoding.text.format)
196
+ : (d) => d;
197
+ for (const d of data) {
198
+ // TODO: Optimization: don't format twice (calculation and actual encoding)
199
+ const value = numberFormat(accessor(d));
200
+ const str = isString(value)
201
+ ? value
202
+ : value === null
203
+ ? ""
204
+ : "" + value;
205
+ charCount += (str && str.length) || 0;
206
+ }
207
+
208
+ const builder = new TextVertexBuilder({
209
+ encoders: this.encoders,
210
+ attributes: this.getAttributes(),
211
+ properties: this.properties,
212
+ fontMetrics: this.font.metrics,
213
+ numCharacters: Math.max(
214
+ charCount,
215
+ this.properties.minBufferSize || 0
216
+ ),
217
+ buildXIndex: this.properties.buildIndex,
218
+ });
219
+
220
+ builder.addBatches(collector.facetBatches);
221
+
222
+ const vertexData = builder.toArrays();
223
+ this.rangeMap = vertexData.rangeMap;
224
+
225
+ this.updateBufferInfo(vertexData);
226
+ }
227
+
228
+ /**
229
+ * @param {import("../view/rendering").GlobalRenderingOptions} options
230
+ */
231
+ prepareRender(options) {
232
+ super.prepareRender(options);
233
+
234
+ let q = 0.35; // TODO: Ensure that this makes sense. Now chosen by trial & error
235
+ if (this.properties.logoLetters) {
236
+ // Adjust to make stretched letters a bit less blurry
237
+ // A proper solution would probably be to compute gradients in the fragment shader
238
+ // to find a suitable divisor.
239
+ q /= 2;
240
+ }
241
+
242
+ setUniforms(this.programInfo, {
243
+ uTexture: this.font.texture,
244
+ uSdfNumerator:
245
+ this.font.metrics.common.base / (this.glHelper.dpr / q),
246
+ });
247
+
248
+ setBuffersAndAttributes(
249
+ this.gl,
250
+ this.programInfo,
251
+ this.vertexArrayInfo
252
+ );
253
+ }
254
+
255
+ /**
256
+ * @param {import("./Mark").MarkRenderingOptions} options
257
+ */
258
+ render(options) {
259
+ const gl = this.gl;
260
+
261
+ return this.createRenderCallback(
262
+ (offset, count) =>
263
+ drawBufferInfo(
264
+ gl,
265
+ this.vertexArrayInfo,
266
+ gl.TRIANGLES,
267
+ count,
268
+ offset
269
+ ),
270
+ options,
271
+ () => this.rangeMap
272
+ );
273
+ }
274
+ }
@@ -0,0 +1,9 @@
1
+ import { TooltipHandler } from "./tooltip/tooltipHandler";
2
+
3
+ export interface EmbedOptions {
4
+ /** If true, don't display the toolbar. */
5
+ bare?: boolean;
6
+
7
+ /** Custom tooltip handlers. Use "default" to override the default handler */
8
+ tooltipHandlers?: Record<string, TooltipHandler>;
9
+ }
@@ -0,0 +1,184 @@
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
+ }