@genome-spy/core 0.19.0 → 0.21.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 +46 -119
- package/dist/schema.json +213 -25
- package/package.json +4 -3
- package/src/data/collector.test.js +2 -0
- package/src/data/dataFlow.test.js +2 -0
- package/src/data/flow.test.js +1 -0
- package/src/data/flowNode.test.js +1 -0
- package/src/data/flowOptimizer.test.js +1 -0
- package/src/data/formats/fasta.test.js +1 -0
- package/src/data/sources/inlineSource.test.js +1 -0
- package/src/data/sources/sequenceSource.test.js +1 -0
- package/src/data/transforms/clone.test.js +1 -0
- package/src/data/transforms/coverage.test.js +1 -0
- package/src/data/transforms/filter.test.js +1 -0
- package/src/data/transforms/flattenDelimited.test.js +1 -0
- package/src/data/transforms/flattenSequence.test.js +1 -0
- package/src/data/transforms/formula.test.js +1 -0
- package/src/data/transforms/identifier.test.js +1 -0
- package/src/data/transforms/pileup.test.js +1 -0
- package/src/data/transforms/project.test.js +1 -0
- package/src/data/transforms/regexExtract.test.js +1 -0
- package/src/data/transforms/regexFold.test.js +1 -0
- package/src/data/transforms/sample.test.js +1 -0
- package/src/data/transforms/stack.test.js +1 -0
- package/src/encoder/accessor.test.js +1 -0
- package/src/encoder/encoder.test.js +1 -0
- package/src/genome/genome.test.js +1 -0
- package/src/genome/scaleIndex.js +3 -2
- package/src/genome/scaleIndex.test.js +23 -6
- package/src/genome/scaleLocus.test.js +1 -0
- package/src/genomeSpy.js +16 -11
- package/src/gl/dataToVertices.js +52 -52
- package/src/gl/includes/common.glsl +12 -12
- package/src/gl/includes/picking.fragment.glsl +0 -2
- package/src/gl/includes/picking.vertex.glsl +0 -2
- package/src/gl/includes/scales.glsl +33 -2
- package/src/gl/point.vertex.glsl +0 -2
- package/src/gl/rule.vertex.glsl +1 -1
- package/src/gl/webGLHelper.js +0 -3
- package/src/marks/link.js +32 -39
- package/src/marks/mark.js +176 -106
- package/src/marks/pointMark.js +28 -59
- package/src/marks/rectMark.js +38 -33
- package/src/marks/rule.js +31 -21
- package/src/marks/text.js +18 -14
- package/src/scale/glslScaleGenerator.js +56 -17
- package/src/scale/scale.test.js +1 -0
- package/src/scale/ticks.test.js +1 -0
- package/src/spec/mark.d.ts +0 -3
- package/src/spec/scale.d.ts +0 -9
- package/src/spec/title.d.ts +102 -0
- package/src/spec/view.d.ts +6 -4
- package/src/tooltip/dataTooltipHandler.js +3 -2
- package/src/utils/addBaseUrl.test.js +1 -0
- package/src/utils/binnedIndex.js +147 -0
- package/src/utils/binnedIndex.test.js +73 -0
- package/src/utils/cloner.test.js +1 -0
- package/src/utils/coalesce.test.js +1 -0
- package/src/utils/concatIterables.test.js +1 -0
- package/src/utils/domainArray.test.js +1 -0
- package/src/utils/indexer.test.js +1 -0
- package/src/utils/iterateNestedMaps.test.js +1 -0
- package/src/utils/kWayMerge.test.js +1 -0
- package/src/utils/layout/flexLayout.js +35 -3
- package/src/utils/layout/flexLayout.test.js +15 -0
- package/src/utils/layout/grid.js +95 -0
- package/src/utils/layout/grid.test.js +71 -0
- package/src/utils/layout/padding.js +13 -0
- package/src/utils/layout/rectangle.js +6 -0
- package/src/utils/layout/rectangle.test.js +1 -0
- package/src/utils/mergeObjects.test.js +1 -0
- package/src/utils/numberExtractor.test.js +1 -0
- package/src/utils/propertyCacher.test.js +1 -0
- package/src/utils/propertyCoalescer.test.js +1 -0
- package/src/utils/reservationMap.test.js +1 -0
- package/src/utils/topK.test.js +1 -0
- package/src/utils/variableTools.test.js +1 -0
- package/src/view/axisResolution.test.js +1 -0
- package/src/view/axisView.js +3 -5
- package/src/view/concatView.js +24 -275
- package/src/view/flowBuilder.test.js +1 -0
- package/src/view/gridView.js +774 -0
- package/src/view/implicitRootView.js +14 -0
- package/src/view/layerView.js +15 -1
- package/src/view/renderingContext/deferredViewRenderingContext.js +3 -1
- package/src/view/renderingContext/simpleViewRenderingContext.js +3 -1
- package/src/view/scaleResolution.js +5 -11
- package/src/view/scaleResolution.test.js +1 -0
- package/src/view/title.js +165 -0
- package/src/view/unitView.js +9 -5
- package/src/view/view.js +35 -14
- package/src/view/view.test.js +1 -0
- package/src/view/viewContext.d.ts +6 -1
- package/src/view/viewFactory.test.js +1 -0
- package/src/view/viewUtils.js +1 -93
- package/src/view/zoom.js +89 -0
- package/src/gl/includes/fp64-arithmetic.glsl +0 -187
- package/src/gl/includes/fp64-utils.js +0 -142
- package/src/gl/includes/scales_fp64.glsl +0 -30
- package/src/utils/binnedRangeIndex.js +0 -83
- package/src/view/decoratorView.js +0 -513
package/src/genomeSpy.js
CHANGED
|
@@ -8,7 +8,6 @@ import Tooltip from "./utils/ui/tooltip";
|
|
|
8
8
|
import AccessorFactory from "./encoder/accessor";
|
|
9
9
|
import {
|
|
10
10
|
resolveScalesAndAxes,
|
|
11
|
-
addDecorators,
|
|
12
11
|
processImports,
|
|
13
12
|
setImplicitScaleNames,
|
|
14
13
|
} from "./view/viewUtils";
|
|
@@ -17,7 +16,6 @@ import UnitView from "./view/unitView";
|
|
|
17
16
|
import WebGLHelper from "./gl/webGLHelper";
|
|
18
17
|
import Rectangle from "./utils/layout/rectangle";
|
|
19
18
|
import DeferredViewRenderingContext from "./view/renderingContext/deferredViewRenderingContext";
|
|
20
|
-
import LayoutRecorderViewRenderingContext from "./view/renderingContext/layoutRecorderViewRenderingContext";
|
|
21
19
|
import CompositeViewRenderingContext from "./view/renderingContext/compositeViewRenderingContext";
|
|
22
20
|
import InteractionEvent from "./utils/interactionEvent";
|
|
23
21
|
import Point from "./utils/layout/point";
|
|
@@ -36,6 +34,8 @@ import refseqGeneTooltipHandler from "./tooltip/refseqGeneTooltipHandler";
|
|
|
36
34
|
import dataTooltipHandler from "./tooltip/dataTooltipHandler";
|
|
37
35
|
import { invalidatePrefix } from "./utils/propertyCacher";
|
|
38
36
|
import { ViewFactory } from "./view/viewFactory";
|
|
37
|
+
import LayerView from "./view/layerView";
|
|
38
|
+
import ImplicitRootView from "./view/implicitRootView";
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* @typedef {import("./spec/view").UnitSpec} UnitSpec
|
|
@@ -166,7 +166,9 @@ export default class GenomeSpy {
|
|
|
166
166
|
|
|
167
167
|
this._glHelper = new WebGLHelper(this.container, () => {
|
|
168
168
|
if (this.viewRoot) {
|
|
169
|
-
const size = this.viewRoot
|
|
169
|
+
const size = this.viewRoot
|
|
170
|
+
.getSize()
|
|
171
|
+
.addPadding(this.viewRoot.getOverhang());
|
|
170
172
|
|
|
171
173
|
// If a dimension has an absolutely specified size (in pixels), use it for the canvas size.
|
|
172
174
|
// However, if the dimension has a growing component, the canvas should be fit to the
|
|
@@ -283,12 +285,19 @@ export default class GenomeSpy {
|
|
|
283
285
|
// Replace placeholder ImportViews with actual views.
|
|
284
286
|
await processImports(this.viewRoot);
|
|
285
287
|
|
|
288
|
+
if (
|
|
289
|
+
this.viewRoot instanceof UnitView ||
|
|
290
|
+
this.viewRoot instanceof LayerView
|
|
291
|
+
) {
|
|
292
|
+
this.viewRoot = new ImplicitRootView(context, this.viewRoot);
|
|
293
|
+
}
|
|
294
|
+
|
|
286
295
|
// Resolve scales, i.e., if possible, pull them towards the root
|
|
287
296
|
resolveScalesAndAxes(this.viewRoot);
|
|
288
297
|
setImplicitScaleNames(this.viewRoot);
|
|
289
298
|
|
|
290
299
|
// Wrap unit or layer views that need axes
|
|
291
|
-
this.viewRoot = addDecorators(this.viewRoot);
|
|
300
|
+
//this.viewRoot = addDecorators(this.viewRoot);
|
|
292
301
|
|
|
293
302
|
// We should now have a complete view hierarchy. Let's update the canvas size
|
|
294
303
|
// and ensure that the loading message is visible.
|
|
@@ -424,7 +433,7 @@ export default class GenomeSpy {
|
|
|
424
433
|
|
|
425
434
|
/** @param {Event} event */
|
|
426
435
|
const listener = (event) => {
|
|
427
|
-
if (
|
|
436
|
+
if (event instanceof MouseEvent) {
|
|
428
437
|
if (event.type == "mousemove") {
|
|
429
438
|
this.tooltip.handleMouseMove(event);
|
|
430
439
|
this._tooltipUpdateRequested = false;
|
|
@@ -445,7 +454,7 @@ export default class GenomeSpy {
|
|
|
445
454
|
* @param {MouseEvent} event
|
|
446
455
|
*/
|
|
447
456
|
const dispatchEvent = (event) => {
|
|
448
|
-
this.
|
|
457
|
+
this.viewRoot.propagateInteractionEvent(
|
|
449
458
|
new InteractionEvent(point, event)
|
|
450
459
|
);
|
|
451
460
|
|
|
@@ -677,20 +686,16 @@ export default class GenomeSpy {
|
|
|
677
686
|
},
|
|
678
687
|
this._glHelper
|
|
679
688
|
);
|
|
680
|
-
const layoutRecorder = new LayoutRecorderViewRenderingContext({});
|
|
681
689
|
|
|
682
690
|
root.render(
|
|
683
691
|
new CompositeViewRenderingContext(
|
|
684
692
|
this._renderingContext,
|
|
685
|
-
this._pickingContext
|
|
686
|
-
layoutRecorder
|
|
693
|
+
this._pickingContext
|
|
687
694
|
),
|
|
688
695
|
// Canvas should now be sized based on the root view or the container
|
|
689
696
|
Rectangle.create(0, 0, canvasSize.width, canvasSize.height)
|
|
690
697
|
);
|
|
691
698
|
|
|
692
|
-
this.layout = layoutRecorder.getLayout();
|
|
693
|
-
|
|
694
699
|
this.broadcast("layoutComputed");
|
|
695
700
|
}
|
|
696
701
|
|
package/src/gl/dataToVertices.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { InternMap } from "internmap";
|
|
2
2
|
import { format } from "d3-format";
|
|
3
3
|
import { isString } from "vega-util";
|
|
4
|
-
import { fp64ify } from "./includes/fp64-utils";
|
|
5
4
|
import ArrayBuilder from "./arrayBuilder";
|
|
6
5
|
import { SDF_PADDING } from "../fonts/bmFontMetrics";
|
|
7
|
-
import {
|
|
8
|
-
import createBinningRangeIndexer from "../utils/binnedRangeIndex";
|
|
6
|
+
import { createBinningRangeIndexer } from "../utils/binnedIndex";
|
|
9
7
|
import { isValueDef } from "../encoder/encoder";
|
|
8
|
+
import {
|
|
9
|
+
isHighPrecisionScale,
|
|
10
|
+
splitHighPrecision,
|
|
11
|
+
} from "../scale/glslScaleGenerator";
|
|
12
|
+
import { isContinuous } from "vega-scale";
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* @typedef {object} RangeEntry Represents a location of a vertex subset
|
|
13
16
|
* @prop {number} offset in vertices
|
|
14
17
|
* @prop {number} count in vertices
|
|
15
|
-
* @prop {import("../utils/
|
|
18
|
+
* @prop {import("../utils/binnedIndex").Lookup} xIndex
|
|
16
19
|
*
|
|
17
20
|
* @typedef {import("./arraybuilder").ConverterMetadata} Converter
|
|
18
21
|
* @typedef {import("../encoder/encoder").Encoder} Encoder
|
|
@@ -25,16 +28,9 @@ export class GeometryBuilder {
|
|
|
25
28
|
* @param {string[]} [object.attributes]
|
|
26
29
|
* @param {number} [object.numVertices] If the number of data items is known, a
|
|
27
30
|
* preallocated TypedArray is used
|
|
28
|
-
* @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
|
|
29
31
|
*/
|
|
30
|
-
constructor({
|
|
31
|
-
encoders,
|
|
32
|
-
numVertices = undefined,
|
|
33
|
-
attributes = [],
|
|
34
|
-
buildXIndex = false,
|
|
35
|
-
}) {
|
|
32
|
+
constructor({ encoders, numVertices = undefined, attributes = [] }) {
|
|
36
33
|
this.encoders = encoders;
|
|
37
|
-
this._buildXIndex = buildXIndex;
|
|
38
34
|
|
|
39
35
|
// Encoders for variable channels
|
|
40
36
|
this.variableEncoders = Object.fromEntries(
|
|
@@ -55,7 +51,7 @@ export class GeometryBuilder {
|
|
|
55
51
|
const accessor = ce.accessor;
|
|
56
52
|
|
|
57
53
|
const doubleArray = [0, 0];
|
|
58
|
-
const
|
|
54
|
+
const hp = isHighPrecisionScale(ce.scale.type);
|
|
59
55
|
|
|
60
56
|
const indexer = ce.indexer;
|
|
61
57
|
|
|
@@ -68,20 +64,20 @@ export class GeometryBuilder {
|
|
|
68
64
|
*/
|
|
69
65
|
const f = indexer
|
|
70
66
|
? (d) => indexer(accessor(d))
|
|
71
|
-
:
|
|
72
|
-
? (d) =>
|
|
67
|
+
: hp
|
|
68
|
+
? (d) => splitHighPrecision(accessor(d), doubleArray)
|
|
73
69
|
: accessor;
|
|
74
70
|
|
|
75
71
|
this.variableBuilder.addConverter(channel, {
|
|
76
72
|
f,
|
|
77
|
-
numComponents:
|
|
78
|
-
arrayReference:
|
|
73
|
+
numComponents: hp ? 2 : 1,
|
|
74
|
+
arrayReference: hp ? doubleArray : undefined,
|
|
79
75
|
});
|
|
80
76
|
}
|
|
81
77
|
|
|
82
78
|
this.lastOffset = 0;
|
|
83
79
|
|
|
84
|
-
/** @type {Map<any, RangeEntry>} keep track of
|
|
80
|
+
/** @type {Map<any, RangeEntry>} keep track of facet locations within the vertex array */
|
|
85
81
|
this.rangeMap = new InternMap([], JSON.stringify);
|
|
86
82
|
}
|
|
87
83
|
|
|
@@ -118,32 +114,50 @@ export class GeometryBuilder {
|
|
|
118
114
|
* @param {object[]} data
|
|
119
115
|
*/
|
|
120
116
|
addBatch(key, data, lo = 0, hi = data.length) {
|
|
117
|
+
this.prepareXIndexer(data, lo, hi);
|
|
118
|
+
|
|
121
119
|
for (let i = lo; i < hi; i++) {
|
|
122
|
-
|
|
120
|
+
const d = data[i];
|
|
121
|
+
this.variableBuilder.pushFromDatum(d);
|
|
122
|
+
this.addToXIndex(d);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
this.registerBatch(key);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
* @param {
|
|
129
|
+
* @param {import("../data/flowNode").Data} data Domain, but specified using datums
|
|
130
|
+
* @param {number} [lo]
|
|
131
|
+
* @param {number} [hi]
|
|
130
132
|
*/
|
|
131
|
-
prepareXIndexer(data) {
|
|
132
|
-
if (!
|
|
133
|
+
prepareXIndexer(data, lo = 0, hi = lo + data.length) {
|
|
134
|
+
if (!data.length || hi - lo < 0) {
|
|
135
|
+
/**
|
|
136
|
+
* @param {import("../data/flowNode").Datum} datum
|
|
137
|
+
*/
|
|
138
|
+
this.addToXIndex = (datum) => {
|
|
139
|
+
// nop
|
|
140
|
+
};
|
|
133
141
|
return;
|
|
134
142
|
}
|
|
135
143
|
|
|
136
|
-
|
|
137
|
-
const
|
|
144
|
+
/** @param {Encoder} encoder */
|
|
145
|
+
const getContinuousEncoder = (encoder) =>
|
|
146
|
+
encoder && isContinuous(encoder.scale?.type) && encoder;
|
|
147
|
+
|
|
148
|
+
const xe = getContinuousEncoder(this.variableEncoders.x);
|
|
149
|
+
const x2e = getContinuousEncoder(this.variableEncoders.x2);
|
|
138
150
|
|
|
139
|
-
if (xe
|
|
151
|
+
if (xe) {
|
|
140
152
|
const xa = xe.accessor;
|
|
141
|
-
const x2a = x2e.accessor;
|
|
153
|
+
const x2a = x2e ? x2e.accessor : xa;
|
|
142
154
|
|
|
143
|
-
this.xIndexer = createBinningRangeIndexer(
|
|
144
|
-
|
|
145
|
-
x2a(
|
|
146
|
-
|
|
155
|
+
this.xIndexer = createBinningRangeIndexer(
|
|
156
|
+
50,
|
|
157
|
+
[xa(data[lo]), x2a(data[hi - 1])],
|
|
158
|
+
xa,
|
|
159
|
+
x2a
|
|
160
|
+
);
|
|
147
161
|
|
|
148
162
|
let lastVertexCount = this.variableBuilder.vertexCount;
|
|
149
163
|
|
|
@@ -152,21 +166,16 @@ export class GeometryBuilder {
|
|
|
152
166
|
*/
|
|
153
167
|
this.addToXIndex = (datum) => {
|
|
154
168
|
let currentVertexCount = this.variableBuilder.vertexCount;
|
|
155
|
-
this.xIndexer(
|
|
156
|
-
xa(datum),
|
|
157
|
-
x2a(datum),
|
|
158
|
-
lastVertexCount,
|
|
159
|
-
currentVertexCount
|
|
160
|
-
);
|
|
169
|
+
this.xIndexer(datum, lastVertexCount, currentVertexCount);
|
|
161
170
|
lastVertexCount = currentVertexCount;
|
|
162
171
|
};
|
|
163
172
|
} else {
|
|
164
173
|
this.xIndexer = undefined;
|
|
165
174
|
/**
|
|
166
|
-
* @param {
|
|
175
|
+
* @param {import("../data/flowNode").Datum} datum
|
|
167
176
|
*/
|
|
168
177
|
this.addToXIndex = (datum) => {
|
|
169
|
-
//
|
|
178
|
+
// nop
|
|
170
179
|
};
|
|
171
180
|
}
|
|
172
181
|
}
|
|
@@ -175,7 +184,7 @@ export class GeometryBuilder {
|
|
|
175
184
|
* Add the datum to an index, which allows for efficient rendering of ranges
|
|
176
185
|
* on the x axis. Must be called after a datum has been pushed to the ArrayBuilder.
|
|
177
186
|
*
|
|
178
|
-
* @param {
|
|
187
|
+
* @param {import("../data/flowNode").Datum} datum
|
|
179
188
|
*/
|
|
180
189
|
addToXIndex(datum) {
|
|
181
190
|
//
|
|
@@ -204,7 +213,6 @@ export class RectVertexBuilder extends GeometryBuilder {
|
|
|
204
213
|
* If the rect is wider than the threshold, tessellate it into pieces
|
|
205
214
|
* @param {number[]} [object.visibleRange]
|
|
206
215
|
* @param {number} [object.numItems] Number of data items
|
|
207
|
-
* @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
|
|
208
216
|
*/
|
|
209
217
|
constructor({
|
|
210
218
|
encoders,
|
|
@@ -212,14 +220,12 @@ export class RectVertexBuilder extends GeometryBuilder {
|
|
|
212
220
|
tessellationThreshold = Infinity,
|
|
213
221
|
visibleRange = [-Infinity, Infinity],
|
|
214
222
|
numItems,
|
|
215
|
-
buildXIndex = false,
|
|
216
223
|
}) {
|
|
217
224
|
super({
|
|
218
225
|
encoders,
|
|
219
226
|
attributes,
|
|
220
227
|
numVertices:
|
|
221
228
|
tessellationThreshold == Infinity ? numItems * 6 : undefined,
|
|
222
|
-
buildXIndex,
|
|
223
229
|
});
|
|
224
230
|
|
|
225
231
|
this.visibleRange = visibleRange;
|
|
@@ -253,7 +259,7 @@ export class RectVertexBuilder extends GeometryBuilder {
|
|
|
253
259
|
const xAccessor = a(e.x);
|
|
254
260
|
const x2Accessor = a(e.x2);
|
|
255
261
|
|
|
256
|
-
this.prepareXIndexer(data);
|
|
262
|
+
this.prepareXIndexer(data, lo, hi);
|
|
257
263
|
|
|
258
264
|
const frac = [0, 0];
|
|
259
265
|
this.updateFrac(frac);
|
|
@@ -319,7 +325,6 @@ export class RuleVertexBuilder extends GeometryBuilder {
|
|
|
319
325
|
* If the rule is wider than the threshold, tessellate it into pieces
|
|
320
326
|
* @param {number[]} [object.visibleRange]
|
|
321
327
|
* @param {number} [object.numItems] Number of data items
|
|
322
|
-
* @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
|
|
323
328
|
*/
|
|
324
329
|
constructor({
|
|
325
330
|
encoders,
|
|
@@ -327,14 +332,12 @@ export class RuleVertexBuilder extends GeometryBuilder {
|
|
|
327
332
|
tessellationThreshold = Infinity,
|
|
328
333
|
visibleRange = [-Infinity, Infinity],
|
|
329
334
|
numItems,
|
|
330
|
-
buildXIndex,
|
|
331
335
|
}) {
|
|
332
336
|
super({
|
|
333
337
|
encoders,
|
|
334
338
|
attributes,
|
|
335
339
|
numVertices:
|
|
336
340
|
tessellationThreshold == Infinity ? numItems * 6 : undefined,
|
|
337
|
-
buildXIndex,
|
|
338
341
|
});
|
|
339
342
|
|
|
340
343
|
this.visibleRange = visibleRange;
|
|
@@ -354,7 +357,7 @@ export class RuleVertexBuilder extends GeometryBuilder {
|
|
|
354
357
|
addBatch(key, data, lo = 0, hi = data.length) {
|
|
355
358
|
//const [lower, upper] = this.visibleRange; // TODO
|
|
356
359
|
|
|
357
|
-
this.prepareXIndexer(data);
|
|
360
|
+
this.prepareXIndexer(data, lo, hi);
|
|
358
361
|
|
|
359
362
|
for (let i = lo; i < hi; i++) {
|
|
360
363
|
const d = data[i];
|
|
@@ -440,7 +443,6 @@ export class TextVertexBuilder extends GeometryBuilder {
|
|
|
440
443
|
* @param {import("../fonts/bmFontMetrics").BMFontMetrics} object.fontMetrics
|
|
441
444
|
* @param {Record<string, any>} object.properties
|
|
442
445
|
* @param {number} [object.numCharacters] number of characters
|
|
443
|
-
* @param {boolean} [object.buildXIndex] True if data are sorted by the field mapped to x channel and should be indexed
|
|
444
446
|
* @param {boolean} [object.logoLetters]
|
|
445
447
|
*/
|
|
446
448
|
constructor({
|
|
@@ -449,13 +451,11 @@ export class TextVertexBuilder extends GeometryBuilder {
|
|
|
449
451
|
fontMetrics,
|
|
450
452
|
properties,
|
|
451
453
|
numCharacters = undefined,
|
|
452
|
-
buildXIndex = false,
|
|
453
454
|
}) {
|
|
454
455
|
super({
|
|
455
456
|
encoders,
|
|
456
457
|
attributes,
|
|
457
458
|
numVertices: numCharacters * 6, // six vertices per quad (character)
|
|
458
|
-
buildXIndex,
|
|
459
459
|
});
|
|
460
460
|
|
|
461
461
|
this.metadata = fontMetrics;
|
|
@@ -521,7 +521,7 @@ export class TextVertexBuilder extends GeometryBuilder {
|
|
|
521
521
|
const textureCoord = [0, 0];
|
|
522
522
|
this.updateTextureCoord(textureCoord);
|
|
523
523
|
|
|
524
|
-
this.prepareXIndexer(data);
|
|
524
|
+
this.prepareXIndexer(data, lo, hi);
|
|
525
525
|
|
|
526
526
|
for (let i = lo; i < hi; i++) {
|
|
527
527
|
const d = data[i];
|
|
@@ -621,7 +621,7 @@ export class TextVertexBuilder extends GeometryBuilder {
|
|
|
621
621
|
x += advance;
|
|
622
622
|
}
|
|
623
623
|
|
|
624
|
-
this.addToXIndex(
|
|
624
|
+
this.addToXIndex(d);
|
|
625
625
|
}
|
|
626
626
|
|
|
627
627
|
this.registerBatch(key);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#define PI 3.141593
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
uniform View {
|
|
4
|
+
/** Offset in "unit" units */
|
|
5
|
+
mediump vec2 uViewOffset;
|
|
6
|
+
mediump vec2 uViewScale;
|
|
7
|
+
/** Size of the logical viewport in pixels, i.e., the view */
|
|
8
|
+
mediump vec2 uViewportSize;
|
|
9
|
+
lowp float uDevicePixelRatio;
|
|
10
|
+
// TODO: Views with opacity less than 1.0 should be rendered into a texture
|
|
11
|
+
// that is rendered with the specified opacity.
|
|
12
|
+
lowp float uViewOpacity;
|
|
13
|
+
bool uPickingEnabled;
|
|
14
|
+
};
|
|
5
15
|
|
|
6
|
-
uniform vec2 uViewScale;
|
|
7
|
-
|
|
8
|
-
/** Size of the logical viewport in pixels, i.e., the view */
|
|
9
|
-
uniform vec2 uViewportSize;
|
|
10
|
-
|
|
11
|
-
uniform lowp float uDevicePixelRatio;
|
|
12
|
-
|
|
13
|
-
// TODO: Views with opacity less than 1.0 should be rendered into a texture
|
|
14
|
-
// that is rendered with the specified opacity.
|
|
15
|
-
uniform lowp float uViewOpacity;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Maps a coordinate on the unit scale to a normalized device coordinate.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const float inf = 1.0 / 0.0;
|
|
2
|
+
|
|
1
3
|
// Utils ------------
|
|
2
4
|
|
|
3
5
|
vec3 getDiscreteColor(sampler2D s, int index) {
|
|
@@ -67,15 +69,44 @@ float scaleBand(float value, vec2 domainExtent, vec2 range,
|
|
|
67
69
|
// TODO: reverse
|
|
68
70
|
float start = range[0];
|
|
69
71
|
float stop = range[1];
|
|
72
|
+
float rangeSpan = stop - start;
|
|
70
73
|
|
|
71
74
|
float n = domainExtent[1] - domainExtent[0];
|
|
72
75
|
|
|
76
|
+
// This fix departs from Vega and d3: https://github.com/vega/vega/issues/3357#issuecomment-1063253596
|
|
73
77
|
paddingInner = int(n) > 1 ? paddingInner : 0.0;
|
|
74
78
|
|
|
75
79
|
// Adapted from: https://github.com/d3/d3-scale/blob/master/src/band.js
|
|
76
|
-
float step =
|
|
77
|
-
start += (
|
|
80
|
+
float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
|
|
81
|
+
start += (rangeSpan - step * (n - paddingInner)) * align;
|
|
78
82
|
float bandwidth = step * (1.0 - paddingInner);
|
|
79
83
|
|
|
80
84
|
return start + (value - domainExtent[0]) * step + bandwidth * band;
|
|
81
85
|
}
|
|
86
|
+
|
|
87
|
+
// High precision variant of scaleBand for index/locus scales
|
|
88
|
+
float scaleBandHp(vec2 value, vec3 domainExtent, vec2 range,
|
|
89
|
+
float paddingInner, float paddingOuter,
|
|
90
|
+
float align, float band) {
|
|
91
|
+
|
|
92
|
+
// TODO: reverse
|
|
93
|
+
float start = range[0];
|
|
94
|
+
float stop = range[1];
|
|
95
|
+
float rangeSpan = stop - start;
|
|
96
|
+
|
|
97
|
+
vec2 domainStart = domainExtent.xy;
|
|
98
|
+
float n = domainExtent[2];
|
|
99
|
+
|
|
100
|
+
// The following computation is identical for every vertex. Could be done on the JS side.
|
|
101
|
+
float step = rangeSpan / max(1.0, n - paddingInner + paddingOuter * 2.0);
|
|
102
|
+
start += (rangeSpan - step * (n - paddingInner)) * align;
|
|
103
|
+
float bandwidth = step * (1.0 - paddingInner);
|
|
104
|
+
|
|
105
|
+
// Using max to prevent the shader compiler from wrecking the precision.
|
|
106
|
+
// Othwewise the compiler could optimize the sum of the four terms into
|
|
107
|
+
// some equivalent form that does premature rounding.
|
|
108
|
+
float hi = max(value[0] - domainStart[0], -inf);
|
|
109
|
+
float lo = max(value[1] - domainStart[1], -inf);
|
|
110
|
+
|
|
111
|
+
return dot(vec4(start, hi, lo, bandwidth), vec4(1.0, step, step, band));
|
|
112
|
+
}
|
package/src/gl/point.vertex.glsl
CHANGED
package/src/gl/rule.vertex.glsl
CHANGED
package/src/gl/webGLHelper.js
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
setTextureFromArray,
|
|
9
9
|
} from "twgl.js";
|
|
10
10
|
import { isArray, isString } from "vega-util";
|
|
11
|
-
import { getPlatformShaderDefines } from "./includes/fp64-utils";
|
|
12
11
|
|
|
13
12
|
import { isDiscrete, isDiscretizing, isInterpolating } from "vega-scale";
|
|
14
13
|
import {
|
|
@@ -81,8 +80,6 @@ export default class WebGLHelper {
|
|
|
81
80
|
// Always use pre-multiplied alpha
|
|
82
81
|
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
83
82
|
|
|
84
|
-
this._shaderDefines = getPlatformShaderDefines(gl);
|
|
85
|
-
|
|
86
83
|
this.canvas = canvas;
|
|
87
84
|
this.gl = gl;
|
|
88
85
|
|
package/src/marks/link.js
CHANGED
|
@@ -104,7 +104,7 @@ export default class LinkMark extends Mark {
|
|
|
104
104
|
numComponents: 2,
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
this.rangeMap
|
|
107
|
+
this.rangeMap.migrateEntries(vertexData.rangeMap);
|
|
108
108
|
|
|
109
109
|
this.arrays = Object.fromEntries(
|
|
110
110
|
Object.entries(vertexData.arrays).map(([k, v]) => [
|
|
@@ -124,45 +124,38 @@ export default class LinkMark extends Mark {
|
|
|
124
124
|
|
|
125
125
|
// TODO: Vertical clipping in faceted view
|
|
126
126
|
|
|
127
|
-
return this.createRenderCallback(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.arrays[attribute].numComponents
|
|
147
|
-
) {
|
|
148
|
-
attribInfo.offset =
|
|
149
|
-
offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
|
|
150
|
-
}
|
|
127
|
+
return this.createRenderCallback((offset, count) => {
|
|
128
|
+
// We are using instanced drawing here.
|
|
129
|
+
// However, WebGL does not provide glDrawArraysInstancedBaseInstance and thus,
|
|
130
|
+
// we have to hack with offsets in vertexAttribPointer
|
|
131
|
+
// TODO: Use VAOs more intelligently to reduce WebGL calls
|
|
132
|
+
// TODO: Explore multiDrawArraysInstancedWEBGL
|
|
133
|
+
// There's also a promising extension draft:
|
|
134
|
+
// https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_instanced_base_vertex_base_instance/
|
|
135
|
+
// (and https://www.khronos.org/registry/webgl/extensions/WEBGL_multi_draw_instanced_base_vertex_base_instance/)
|
|
136
|
+
|
|
137
|
+
this.gl.bindVertexArray(this.vertexArrayInfo.vertexArrayObject);
|
|
138
|
+
|
|
139
|
+
for (const attribInfoObject of Object.entries(
|
|
140
|
+
this.bufferInfo.attribs
|
|
141
|
+
)) {
|
|
142
|
+
const [attribute, attribInfo] = attribInfoObject;
|
|
143
|
+
if (attribInfo.buffer && this.arrays[attribute].numComponents) {
|
|
144
|
+
attribInfo.offset =
|
|
145
|
+
offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
|
|
151
146
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
() => this.rangeMap
|
|
165
|
-
);
|
|
147
|
+
}
|
|
148
|
+
setBuffersAndAttributes(gl, this.programInfo, this.bufferInfo);
|
|
149
|
+
|
|
150
|
+
drawBufferInfo(
|
|
151
|
+
gl,
|
|
152
|
+
this.bufferInfo,
|
|
153
|
+
gl.TRIANGLE_STRIP,
|
|
154
|
+
(this.properties.segments + 1) * 2, // number of vertices in a triangle strip
|
|
155
|
+
0,
|
|
156
|
+
count
|
|
157
|
+
);
|
|
158
|
+
}, options);
|
|
166
159
|
}
|
|
167
160
|
}
|
|
168
161
|
|