@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.
- package/dist/index.js +224 -0
- package/dist/style.css +1 -0
- package/package.json +54 -0
- package/src/data/collector.js +178 -0
- package/src/data/collector.test.js +82 -0
- package/src/data/dataFlow.js +109 -0
- package/src/data/dataFlow.test.js +3 -0
- package/src/data/facetNode.js +17 -0
- package/src/data/flow.test.js +71 -0
- package/src/data/flowBatch.d.ts +40 -0
- package/src/data/flowNode.js +283 -0
- package/src/data/flowNode.test.js +49 -0
- package/src/data/flowOptimizer.js +117 -0
- package/src/data/flowOptimizer.test.js +192 -0
- package/src/data/flowTestUtils.js +63 -0
- package/src/data/formats/fasta.js +32 -0
- package/src/data/formats/fasta.test.js +26 -0
- package/src/data/sources/dataSource.js +22 -0
- package/src/data/sources/dataSourceFactory.js +24 -0
- package/src/data/sources/dataUtils.js +31 -0
- package/src/data/sources/dynamicCallbackSource.js +56 -0
- package/src/data/sources/dynamicSource.js +36 -0
- package/src/data/sources/inlineSource.js +69 -0
- package/src/data/sources/inlineSource.test.js +55 -0
- package/src/data/sources/namedSource.js +74 -0
- package/src/data/sources/sequenceSource.js +46 -0
- package/src/data/sources/sequenceSource.test.js +45 -0
- package/src/data/sources/urlSource.js +74 -0
- package/src/data/transforms/aggregate.js +69 -0
- package/src/data/transforms/clone.js +40 -0
- package/src/data/transforms/clone.test.js +10 -0
- package/src/data/transforms/coverage.js +187 -0
- package/src/data/transforms/coverage.test.js +122 -0
- package/src/data/transforms/filter.js +37 -0
- package/src/data/transforms/filter.test.js +17 -0
- package/src/data/transforms/filterScoredLabels.js +134 -0
- package/src/data/transforms/flattenCompressedExons.js +57 -0
- package/src/data/transforms/flattenDelimited.js +68 -0
- package/src/data/transforms/flattenDelimited.test.js +86 -0
- package/src/data/transforms/flattenSequence.js +39 -0
- package/src/data/transforms/flattenSequence.test.js +33 -0
- package/src/data/transforms/formula.js +39 -0
- package/src/data/transforms/formula.test.js +18 -0
- package/src/data/transforms/identifier.js +108 -0
- package/src/data/transforms/identifier.test.js +82 -0
- package/src/data/transforms/linearizeGenomicCoordinate.js +101 -0
- package/src/data/transforms/measureText.js +44 -0
- package/src/data/transforms/pileup.js +128 -0
- package/src/data/transforms/pileup.test.js +69 -0
- package/src/data/transforms/project.js +41 -0
- package/src/data/transforms/project.test.js +31 -0
- package/src/data/transforms/regexExtract.js +61 -0
- package/src/data/transforms/regexExtract.test.js +66 -0
- package/src/data/transforms/regexFold.js +141 -0
- package/src/data/transforms/regexFold.test.js +159 -0
- package/src/data/transforms/sample.js +101 -0
- package/src/data/transforms/sample.test.js +37 -0
- package/src/data/transforms/stack.js +137 -0
- package/src/data/transforms/stack.test.js +90 -0
- package/src/data/transforms/transformFactory.js +60 -0
- package/src/encoder/accessor.js +82 -0
- package/src/encoder/accessor.test.js +46 -0
- package/src/encoder/encoder.js +369 -0
- package/src/encoder/encoder.test.js +97 -0
- package/src/fonts/Lato-Regular.json +1267 -0
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +93 -0
- package/src/fonts/README.md +3 -0
- package/src/fonts/bmFont.d.ts +58 -0
- package/src/fonts/bmFontManager.js +357 -0
- package/src/fonts/bmFontMetrics.js +108 -0
- package/src/genome/genome.js +305 -0
- package/src/genome/genome.test.js +152 -0
- package/src/genome/genomeStore.js +54 -0
- package/src/genome/locusFormat.js +31 -0
- package/src/genome/scaleIndex.js +199 -0
- package/src/genome/scaleIndex.test.js +61 -0
- package/src/genome/scaleLocus.js +112 -0
- package/src/genome/scaleLocus.test.js +3 -0
- package/src/genomeSpy.js +753 -0
- package/src/gl/arrayBuilder.js +199 -0
- package/src/gl/dataToVertices.js +621 -0
- package/src/gl/includes/common.glsl +63 -0
- package/src/gl/includes/fp64-arithmetic.glsl +187 -0
- package/src/gl/includes/fp64-utils.js +132 -0
- package/src/gl/includes/picking.fragment.glsl +3 -0
- package/src/gl/includes/picking.vertex.glsl +29 -0
- package/src/gl/includes/sampleFacet.glsl +107 -0
- package/src/gl/includes/scales.glsl +79 -0
- package/src/gl/includes/scales_fp64.glsl +30 -0
- package/src/gl/link.fragment.glsl +18 -0
- package/src/gl/link.vertex.glsl +111 -0
- package/src/gl/point.fragment.glsl +123 -0
- package/src/gl/point.vertex.glsl +128 -0
- package/src/gl/rect.fragment.glsl +51 -0
- package/src/gl/rect.vertex.glsl +114 -0
- package/src/gl/rule.fragment.glsl +52 -0
- package/src/gl/rule.vertex.glsl +89 -0
- package/src/gl/text.fragment.glsl +31 -0
- package/src/gl/text.vertex.glsl +246 -0
- package/src/gl/webGLHelper.js +490 -0
- package/src/img/bowtie.svg +1 -0
- package/src/img/genomespy-favicon.svg +34 -0
- package/src/index.html +11 -0
- package/src/index.js +151 -0
- package/src/marks/link.js +189 -0
- package/src/marks/mark.js +867 -0
- package/src/marks/markUtils.js +109 -0
- package/src/marks/pointMark.js +279 -0
- package/src/marks/rectMark.js +236 -0
- package/src/marks/rule.js +231 -0
- package/src/marks/text.js +274 -0
- package/src/options.d.ts +9 -0
- package/src/scale/colorUtils.js +184 -0
- package/src/scale/glslScaleGenerator.js +462 -0
- package/src/scale/scale.js +441 -0
- package/src/scale/scale.test.js +323 -0
- package/src/scale/ticks.js +198 -0
- package/src/scale/ticks.test.js +39 -0
- package/src/singlePageApp.js +13 -0
- package/src/spec/axis.d.ts +296 -0
- package/src/spec/channel.d.ts +127 -0
- package/src/spec/data.d.ts +185 -0
- package/src/spec/font.d.ts +15 -0
- package/src/spec/genome.d.ts +35 -0
- package/src/spec/mark.d.ts +432 -0
- package/src/spec/root.d.ts +22 -0
- package/src/spec/scale.d.ts +265 -0
- package/src/spec/tooltip.d.ts +9 -0
- package/src/spec/transform.d.ts +479 -0
- package/src/spec/view.d.ts +215 -0
- package/src/styles/genome-spy.scss +153 -0
- package/src/tooltip/dataTooltipHandler.js +59 -0
- package/src/tooltip/refseqGeneTooltipHandler.js +77 -0
- package/src/tooltip/tooltipHandler.ts +12 -0
- package/src/types/filetypes.d.ts +4 -0
- package/src/types/flatqueue.d.ts +53 -0
- package/src/types/glsl.d.ts +4 -0
- package/src/types/object.d.ts +21 -0
- package/src/types/vega-scale.d.ts +60 -0
- package/src/utils/animator.js +83 -0
- package/src/utils/arrayUtils.js +55 -0
- package/src/utils/binnedRangeIndex.js +83 -0
- package/src/utils/clamp.js +8 -0
- package/src/utils/cloner.js +32 -0
- package/src/utils/cloner.test.js +23 -0
- package/src/utils/coalesce.js +11 -0
- package/src/utils/coalesce.test.js +15 -0
- package/src/utils/concatIterables.js +26 -0
- package/src/utils/concatIterables.test.js +7 -0
- package/src/utils/debounce.js +37 -0
- package/src/utils/domainArray.js +224 -0
- package/src/utils/domainArray.test.js +129 -0
- package/src/utils/eerp.js +13 -0
- package/src/utils/expression.js +32 -0
- package/src/utils/field.js +28 -0
- package/src/utils/fisheye.js +60 -0
- package/src/utils/formatObject.js +31 -0
- package/src/utils/html.js +23 -0
- package/src/utils/html.test.js +13 -0
- package/src/utils/indexer.js +43 -0
- package/src/utils/indexer.test.js +46 -0
- package/src/utils/inertia.js +124 -0
- package/src/utils/interactionEvent.js +33 -0
- package/src/utils/iterateNestedMaps.js +21 -0
- package/src/utils/iterateNestedMaps.test.js +32 -0
- package/src/utils/kWayMerge.js +42 -0
- package/src/utils/kWayMerge.test.js +25 -0
- package/src/utils/layout/flexLayout.js +336 -0
- package/src/utils/layout/flexLayout.test.js +296 -0
- package/src/utils/layout/padding.js +107 -0
- package/src/utils/layout/point.js +23 -0
- package/src/utils/layout/rectangle.js +282 -0
- package/src/utils/layout/rectangle.test.js +171 -0
- package/src/utils/mergeObjects.js +99 -0
- package/src/utils/mergeObjects.test.js +41 -0
- package/src/utils/numberExtractor.js +24 -0
- package/src/utils/numberExtractor.test.js +5 -0
- package/src/utils/point.js +14 -0
- package/src/utils/propertyCacher.js +70 -0
- package/src/utils/propertyCacher.test.js +84 -0
- package/src/utils/propertyCoalescer.js +37 -0
- package/src/utils/propertyCoalescer.test.js +21 -0
- package/src/utils/reservationMap.js +103 -0
- package/src/utils/reservationMap.test.js +19 -0
- package/src/utils/scaleNull.js +19 -0
- package/src/utils/setOperations.js +75 -0
- package/src/utils/smoothstep.js +10 -0
- package/src/utils/throttle.js +34 -0
- package/src/utils/topK.js +76 -0
- package/src/utils/topK.test.js +63 -0
- package/src/utils/transition.js +74 -0
- package/src/utils/ui/tooltip.js +189 -0
- package/src/utils/url.js +22 -0
- package/src/utils/variableTools.js +24 -0
- package/src/utils/variableTools.test.js +12 -0
- package/src/view/axisResolution.js +135 -0
- package/src/view/axisResolution.test.js +200 -0
- package/src/view/axisView.js +746 -0
- package/src/view/channel.js +5 -0
- package/src/view/concatView.js +296 -0
- package/src/view/containerView.js +141 -0
- package/src/view/decoratorView.js +510 -0
- package/src/view/facetView.js +488 -0
- package/src/view/flowBuilder.js +362 -0
- package/src/view/flowBuilder.test.js +124 -0
- package/src/view/importView.js +19 -0
- package/src/view/layerView.js +60 -0
- package/src/view/rendering.d.ts +44 -0
- package/src/view/renderingContext/compositeViewRenderingContext.js +51 -0
- package/src/view/renderingContext/deferredViewRenderingContext.js +174 -0
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +128 -0
- package/src/view/renderingContext/simpleViewRenderingContext.js +62 -0
- package/src/view/renderingContext/svgViewRenderingContext.js +121 -0
- package/src/view/renderingContext/viewRenderingContext.js +41 -0
- package/src/view/scaleResolution.js +756 -0
- package/src/view/scaleResolution.test.js +571 -0
- package/src/view/scaleResolutionApi.d.ts +40 -0
- package/src/view/testUtils.js +48 -0
- package/src/view/unitView.js +368 -0
- package/src/view/view.js +589 -0
- package/src/view/view.test.js +213 -0
- package/src/view/viewContext.d.ts +57 -0
- package/src/view/viewFactory.js +179 -0
- package/src/view/viewFactory.test.js +16 -0
- 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
|
+
}
|