@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,174 @@
1
+ import { group } from "d3-array";
2
+
3
+ import ViewRenderingContext from "./viewRenderingContext";
4
+
5
+ /**
6
+ *
7
+ * @typedef {object} DeferredRenderingRequest Allows for collecting marks for
8
+ * optimized rendering order.
9
+ * @prop {import("../../marks/mark").default} mark
10
+ * @prop {function():void} callback
11
+ * @prop {import("../../utils/layout/rectangle").default} coords
12
+ * @prop {import("../../utils/layout/rectangle").default} [clipRect]
13
+ */
14
+ export default class DeferredViewRenderingContext extends ViewRenderingContext {
15
+ /**
16
+ * @param {import("../rendering").GlobalRenderingOptions} globalOptions
17
+ * @param {import("../../gl/webGLHelper").default} webGLHelper
18
+ */
19
+ constructor(globalOptions, webGLHelper) {
20
+ super(globalOptions);
21
+
22
+ this.webGLHelper = webGLHelper;
23
+
24
+ /**
25
+ * @type {DeferredRenderingRequest[]}
26
+ */
27
+ this.buffer = [];
28
+
29
+ /** @type {import("../../utils/layout/rectangle").default} */
30
+ this.coords = undefined;
31
+
32
+ /** @type {Set<import("../view").default>} */
33
+ this.views = new Set();
34
+ }
35
+
36
+ /**
37
+ * Must be called when a view's render() method is entered
38
+ *
39
+ * @param {import("../view").default} view
40
+ * @param {import("../../utils/layout/rectangle").default} coords View coordinates
41
+ * inside the padding.
42
+ */
43
+ pushView(view, coords) {
44
+ this.views.add(view);
45
+ this.coords = coords;
46
+ }
47
+
48
+ /**
49
+ *
50
+ * @param {import("../../marks/mark").default} mark
51
+ * @param {import("../view").RenderingOptions} options
52
+ */
53
+ renderMark(mark, options) {
54
+ if (this.globalOptions.picking && !mark.isPickingParticipant()) {
55
+ return;
56
+ }
57
+
58
+ const callback = mark.render(options);
59
+ if (callback) {
60
+ this.buffer.push({
61
+ mark,
62
+ callback,
63
+ coords: this.coords,
64
+ clipRect: options.clipRect,
65
+ });
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Renders marks in an optimized order, minimizing the number of WebGL state
71
+ * changes.
72
+ */
73
+ renderDeferred() {
74
+ if (!this.batch) {
75
+ this._buildBatch();
76
+ }
77
+
78
+ if (this.batch.length == 0) {
79
+ return;
80
+ }
81
+
82
+ const gl = this.webGLHelper.gl;
83
+ const picking = this.globalOptions.picking;
84
+
85
+ gl.bindFramebuffer(
86
+ gl.FRAMEBUFFER,
87
+ picking ? this.webGLHelper._pickingBufferInfo.framebuffer : null
88
+ );
89
+
90
+ this.webGLHelper.clearAll();
91
+
92
+ for (const view of this.views) {
93
+ view.onBeforeRender();
94
+ }
95
+
96
+ // Execute the batch
97
+ for (const op of this.batch) {
98
+ op();
99
+ }
100
+
101
+ if (picking) {
102
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
103
+ }
104
+ }
105
+
106
+ _buildBatch() {
107
+ /**
108
+ * Store the operations as a sequence of commands for cheap subsequent rendering.
109
+ *
110
+ * @type {(function():void)[]}
111
+ */
112
+ this.batch = [];
113
+
114
+ /**
115
+ * Is drawing enabled or not. As an optimization this is toggled off for invisible views.
116
+ */
117
+ let enabled = true;
118
+
119
+ let viewportVisible = true;
120
+
121
+ /**
122
+ * @type {function(function():void):(function():void)}
123
+ */
124
+ const ifEnabled = (op) => () => {
125
+ if (enabled) op();
126
+ };
127
+
128
+ /**
129
+ * @type {function(function():void):(function():void)}
130
+ */
131
+ const ifEnabledAndVisible = (op) => () => {
132
+ if (enabled && viewportVisible) op();
133
+ };
134
+
135
+ // Group by marks in order to minimize program changes
136
+ const requestByMark = group(this.buffer, (request) => request.mark);
137
+
138
+ for (const [mark, requests] of requestByMark.entries()) {
139
+ if (!mark.isReady()) {
140
+ continue;
141
+ }
142
+
143
+ // eslint-disable-next-line no-loop-func
144
+ this.batch.push(() => {
145
+ enabled = mark.unitView.getEffectiveOpacity() > 0;
146
+ });
147
+ // Change program, set common uniforms (mark properties, shared domains)
148
+ this.batch.push(
149
+ ifEnabled(() => mark.prepareRender(this.globalOptions))
150
+ );
151
+
152
+ /** @type {import("../../utils/layout/rectangle").default} */
153
+ let previousCoords;
154
+ for (const request of requests) {
155
+ const coords = request.coords;
156
+ // Render each facet
157
+ if (!coords.equals(previousCoords)) {
158
+ this.batch.push(
159
+ // eslint-disable-next-line no-loop-func
160
+ ifEnabled(() => {
161
+ // Suppress rendering if viewport is outside the clipRect
162
+ viewportVisible = mark.setViewport(
163
+ coords,
164
+ request.clipRect
165
+ );
166
+ })
167
+ );
168
+ }
169
+ this.batch.push(ifEnabledAndVisible(request.callback));
170
+ previousCoords = request.coords;
171
+ }
172
+ }
173
+ }
174
+ }
@@ -0,0 +1,128 @@
1
+ import { peek } from "../../utils/arrayUtils";
2
+ import ViewRenderingContext from "./viewRenderingContext";
3
+
4
+ /**
5
+ * A Rendering context that doesn't render anything. It creates a hierarchy
6
+ * of view coordinates, including faceted views that are repeated multiple times.
7
+ * The coordinates can be used for mouse events / interactions, for example.
8
+ *
9
+ * @typedef {import("../view").default} View
10
+ * @typedef {import("../../utils/layout/rectangle").default} Rectangle
11
+ *
12
+ */
13
+ export default class LayoutRecorderViewRenderingContext extends ViewRenderingContext {
14
+ /**
15
+ * @param {import("../rendering").GlobalRenderingOptions} globalOptions
16
+ */
17
+ constructor(globalOptions) {
18
+ super(globalOptions);
19
+
20
+ /** @type {ViewCoords} */
21
+ this.root = undefined;
22
+
23
+ /** @type {ViewCoords[]} */
24
+ this.stack = [];
25
+
26
+ /** @type {ViewCoords} */
27
+ this.lastAddition = undefined;
28
+ }
29
+
30
+ /**
31
+ * Must be called when a view's render() method is entered
32
+ *
33
+ * @param {View} view
34
+ * @param {Rectangle} coords View coordinates
35
+ * inside the padding.
36
+ */
37
+ pushView(view, coords) {
38
+ // TODO: Facet id
39
+
40
+ const viewCoords = new ViewCoords(view, coords);
41
+
42
+ if (!this.root) {
43
+ this.root = viewCoords;
44
+ } else {
45
+ peek(this.stack).addChild(viewCoords);
46
+ }
47
+ this.stack.push(viewCoords);
48
+ }
49
+
50
+ /**
51
+ * Must be called when a view's render() method is being exited
52
+ *
53
+ * @param {View} view
54
+ */
55
+ popView(view) {
56
+ this.stack.pop();
57
+ }
58
+
59
+ getLayout() {
60
+ return this.root;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Represents coordinates of view instances. Faceted views objects may have
66
+ * been rendered at multiple locations.
67
+ */
68
+ class ViewCoords {
69
+ /**
70
+ * @param {View} view
71
+ * @param {Rectangle} coords
72
+ */
73
+ constructor(view, coords) {
74
+ this.view = view;
75
+ this.coords = coords;
76
+ /** @type {ViewCoords[]} */
77
+ this.children = [];
78
+ }
79
+
80
+ /**
81
+ *
82
+ * @param {ViewCoords} viewCoords
83
+ */
84
+ addChild(viewCoords) {
85
+ const last = peek(this.children);
86
+ if (
87
+ last &&
88
+ viewCoords.view === last.view &&
89
+ viewCoords.coords.equals(last.coords)
90
+ ) {
91
+ // Skip extra copies of sample facets. They all have the same coords.
92
+ return;
93
+ }
94
+
95
+ this.children.push(viewCoords);
96
+ }
97
+
98
+ /**
99
+ * Broadcasts a message to views that include the given (x, y) point.
100
+ * This is mainly intended for mouse events.
101
+ *
102
+ * @param {import("../../utils/interactionEvent").default} event
103
+ */
104
+ dispatchInteractionEvent(event) {
105
+ if (this.coords.containsPoint(event.point.x, event.point.y)) {
106
+ this.view.handleInteractionEvent(this.coords, event, true);
107
+ if (event.stopped) {
108
+ return;
109
+ }
110
+
111
+ if (this.children.length == 0) {
112
+ event.target = this.view;
113
+ } else {
114
+ for (const child of this.children) {
115
+ child.dispatchInteractionEvent(event);
116
+ if (event.target) {
117
+ break;
118
+ }
119
+ if (event.stopped) {
120
+ return;
121
+ }
122
+ }
123
+ }
124
+
125
+ this.view.handleInteractionEvent(this.coords, event, false);
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,62 @@
1
+ import ViewRenderingContext from "./viewRenderingContext";
2
+
3
+ /**
4
+ * This class is mainly for illustrative purpose, i.e., how the rendering
5
+ * would be performed in the most straightforward, unoptimized way.
6
+ *
7
+ * @typedef {import("../view").default} View
8
+ */
9
+ export default class SimpleViewRenderingContext extends ViewRenderingContext {
10
+ /**
11
+ * @param {import("../rendering").GlobalRenderingOptions} globalOptions
12
+ */
13
+ constructor(globalOptions) {
14
+ super(globalOptions);
15
+ /** @type {import("../../utils/layout/rectangle").default} */
16
+ this.coords = undefined;
17
+
18
+ /** @type {Set<import("../view").default>} */
19
+ this.views = new Set();
20
+ }
21
+
22
+ /**
23
+ * Must be called when a view's render() method is entered
24
+ *
25
+ * @param {View} view
26
+ * @param {import("../../utils/layout/rectangle").default} coords View coordinates
27
+ * inside the padding.
28
+ */
29
+ pushView(view, coords) {
30
+ if (!this.views.has(view)) {
31
+ // Ensure that the method is called only once, even when rendering facets.
32
+ view.onBeforeRender();
33
+ this.views.add(view);
34
+ }
35
+
36
+ this.coords = coords;
37
+ }
38
+
39
+ /**
40
+ * Must be called when a view's render() method is being exited
41
+ *
42
+ * @param {View} view
43
+ */
44
+ popView(view) {
45
+ //
46
+ }
47
+
48
+ /**
49
+ *
50
+ * @param {import("../../marks/mark").default} mark
51
+ * @param {import("../view").RenderingOptions} options
52
+ */
53
+ renderMark(mark, options) {
54
+ if (this.globalOptions.picking && !mark.isPickingParticipant()) {
55
+ return;
56
+ }
57
+
58
+ mark.prepareRender(this.globalOptions);
59
+ mark.setViewport(this.coords, options.clipRect);
60
+ mark.render(options)();
61
+ }
62
+ }
@@ -0,0 +1,121 @@
1
+ import { peek } from "../../utils/arrayUtils";
2
+ import ViewRenderingContext from "./viewRenderingContext";
3
+
4
+ /**
5
+ * A trivial proof-of-concept SVG rendering context. Doesn't render any
6
+ * marks at this point, only placeholders.
7
+ *
8
+ * @typedef {import("../view").default} View
9
+ */
10
+ export default class SvgViewRenderingContext extends ViewRenderingContext {
11
+ constructor() {
12
+ super();
13
+
14
+ /** @type {import("../../utils/layout/rectangle").default} */
15
+ this.coords = undefined;
16
+
17
+ this.svg = document.createElementNS(
18
+ "http://www.w3.org/2000/svg",
19
+ "svg"
20
+ );
21
+
22
+ /** @type {SVGElement[]} */
23
+ this.nodeStack = [this.svg];
24
+ }
25
+
26
+ /**
27
+ * Must be called when a view's render() method is entered
28
+ *
29
+ * @param {View} view
30
+ * @param {import("../../utils/layout/rectangle").default} coords View coordinates
31
+ * inside the padding.
32
+ */
33
+ pushView(view, coords) {
34
+ view.onBeforeRender();
35
+ this.coords = coords;
36
+
37
+ if (this._currentNode === this.svg) {
38
+ const viewBox = coords.expand(view.getPadding());
39
+ this.svg.setAttributeNS(
40
+ null,
41
+ "viewBox",
42
+ ["x", "y", "width", "height"].map((a) => viewBox[a]).join(" ")
43
+ );
44
+ }
45
+
46
+ const group = createNode("g");
47
+ const title = createNode("title");
48
+ title.textContent = view.name;
49
+ group.appendChild(title);
50
+
51
+ this._currentNode.appendChild(group);
52
+ this.nodeStack.push(group);
53
+ }
54
+
55
+ /**
56
+ * Must be called when a view's render() method is being exited
57
+ *
58
+ * @param {View} view
59
+ */
60
+ popView(view) {
61
+ this.nodeStack.pop();
62
+ }
63
+
64
+ /**
65
+ *
66
+ * @param {import("../../marks/mark").default} mark
67
+ * @param {import("../view").RenderingOptions} options
68
+ */
69
+ renderMark(mark, options) {
70
+ const current = this._currentNode;
71
+
72
+ const rect = createNode("rect", {
73
+ x: this.coords.x,
74
+ y: this.coords.y,
75
+ width: this.coords.width,
76
+ height: this.coords.height,
77
+ fill: "transparent",
78
+ stroke: "black",
79
+ "stroke-width": 1,
80
+ });
81
+
82
+ const name = createNode("text", {
83
+ x: this.coords.x + this.coords.width / 2,
84
+ y: this.coords.y + this.coords.height / 2,
85
+ "dominant-baseline": "middle",
86
+ "text-anchor": "middle",
87
+ });
88
+
89
+ name.textContent = mark.getType();
90
+
91
+ current.appendChild(rect);
92
+ current.appendChild(name);
93
+ }
94
+
95
+ getSvg() {
96
+ return this.svg;
97
+ }
98
+
99
+ get _currentNode() {
100
+ return peek(this.nodeStack);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Adapted from: https://stackoverflow.com/a/37411738/1547896
106
+ *
107
+ * @param {string} name
108
+ * @param {Record<string, any>} [attributes]
109
+ */
110
+ function createNode(name, attributes) {
111
+ const element = document.createElementNS(
112
+ "http://www.w3.org/2000/svg",
113
+ name
114
+ );
115
+ if (attributes) {
116
+ for (const [k, v] of Object.entries(attributes)) {
117
+ element.setAttributeNS(null, k, v);
118
+ }
119
+ }
120
+ return element;
121
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @typedef {import("../view").default} View
3
+ */
4
+ export default class ViewRenderingContext {
5
+ /**
6
+ *
7
+ * @param {import("../rendering").GlobalRenderingOptions} globalOptions
8
+ */
9
+ constructor(globalOptions) {
10
+ this.globalOptions = globalOptions;
11
+ }
12
+
13
+ /**
14
+ * Must be called when a view's render() method is entered
15
+ *
16
+ * @param {View} view
17
+ * @param {import("../../utils/layout/rectangle").default} coords View coordinates
18
+ * inside the padding.
19
+ */
20
+ pushView(view, coords) {
21
+ //
22
+ }
23
+
24
+ /**
25
+ * Must be called when a view's render() method is being exited
26
+ *
27
+ * @param {View} view
28
+ */
29
+ popView(view) {
30
+ //
31
+ }
32
+
33
+ /**
34
+ *
35
+ * @param {import("../../marks/mark").default} mark
36
+ * @param {import("../view").RenderingOptions} options
37
+ */
38
+ renderMark(mark, options) {
39
+ //
40
+ }
41
+ }