@genome-spy/core 0.43.3 → 0.45.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/bundle/index.es.js +5231 -4324
- package/dist/bundle/index.js +197 -85
- package/dist/schema.json +723 -104
- package/dist/src/data/collector.d.ts.map +1 -1
- package/dist/src/data/collector.js +4 -2
- package/dist/src/data/flowOptimizer.test.js +12 -3
- package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
- package/dist/src/data/sources/dataUtils.js +3 -1
- package/dist/src/data/sources/lazy/axisTickSource.d.ts +1 -1
- package/dist/src/data/sources/lazy/axisTickSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/axisTickSource.js +2 -2
- package/dist/src/data/sources/lazy/bigBedSource.d.ts +1 -1
- package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigBedSource.js +52 -20
- package/dist/src/data/sources/lazy/bigWigSource.d.ts +6 -1
- package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigWigSource.js +33 -9
- package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +1 -1
- package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/singleAxisLazySource.js +1 -3
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts +13 -14
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +70 -48
- package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
- package/dist/src/data/sources/sequenceSource.js +14 -5
- package/dist/src/data/sources/sequenceSource.test.js +23 -5
- package/dist/src/data/sources/urlSource.d.ts.map +1 -1
- package/dist/src/data/sources/urlSource.js +15 -2
- package/dist/src/data/transforms/aggregate.d.ts.map +1 -1
- package/dist/src/data/transforms/aggregate.js +5 -2
- package/dist/src/data/transforms/filterScoredLabels.js +1 -1
- package/dist/src/encoder/encoder.d.ts +2 -4
- package/dist/src/encoder/encoder.d.ts.map +1 -1
- package/dist/src/encoder/encoder.js +20 -10
- package/dist/src/encoder/encoder.test.js +3 -0
- package/dist/src/genomeSpy.d.ts +8 -5
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +121 -42
- package/dist/src/gl/glslScaleGenerator.d.ts +23 -3
- package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
- package/dist/src/gl/glslScaleGenerator.js +137 -42
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +5 -7
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/marks/link.common.glsl.js +2 -0
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +19 -9
- package/dist/src/marks/link.vertex.glsl.js +1 -1
- package/dist/src/marks/mark.d.ts +25 -20
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +234 -129
- package/dist/src/marks/point.common.glsl.js +1 -1
- package/dist/src/marks/point.d.ts +1 -4
- package/dist/src/marks/point.d.ts.map +1 -1
- package/dist/src/marks/point.js +31 -23
- package/dist/src/marks/point.vertex.glsl.js +1 -1
- package/dist/src/marks/rect.common.glsl.js +2 -0
- package/dist/src/marks/rect.d.ts.map +1 -1
- package/dist/src/marks/rect.js +12 -12
- package/dist/src/marks/rect.vertex.glsl.js +1 -1
- package/dist/src/marks/rule.common.glsl.js +1 -1
- package/dist/src/marks/rule.js +2 -2
- package/dist/src/marks/text.common.glsl.js +1 -1
- package/dist/src/marks/text.d.ts.map +1 -1
- package/dist/src/marks/text.js +17 -9
- package/dist/src/spec/channel.d.ts +4 -3
- package/dist/src/spec/data.d.ts +11 -10
- package/dist/src/spec/mark.d.ts +28 -46
- package/dist/src/spec/parameter.d.ts +127 -0
- package/dist/src/spec/root.d.ts +1 -0
- package/dist/src/spec/scale.d.ts +2 -1
- package/dist/src/spec/title.d.ts +5 -4
- package/dist/src/spec/view.d.ts +20 -5
- package/dist/src/styles/genome-spy.css.d.ts +1 -1
- package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
- package/dist/src/styles/genome-spy.css.js +52 -5
- package/dist/src/styles/genome-spy.scss +63 -10
- package/dist/src/styles/update.sh +6 -0
- package/dist/src/tooltip/dataTooltipHandler.js +1 -1
- package/dist/src/tooltip/refseqGeneTooltipHandler.js +1 -1
- package/dist/src/tooltip/tooltipHandler.d.ts +1 -1
- package/dist/src/tooltip/tooltipHandler.d.ts.map +1 -1
- package/dist/src/tooltip/tooltipHandler.ts +1 -1
- package/dist/src/types/embedApi.d.ts +6 -0
- package/dist/src/types/scaleResolutionApi.d.ts +7 -3
- package/dist/src/types/viewContext.d.ts +2 -3
- package/dist/src/utils/debounce.d.ts +2 -2
- package/dist/src/utils/debounce.d.ts.map +1 -1
- package/dist/src/utils/debounce.js +5 -2
- package/dist/src/utils/expression.d.ts +2 -2
- package/dist/src/utils/expression.d.ts.map +1 -1
- package/dist/src/utils/expression.js +3 -3
- package/dist/src/utils/formatObject.d.ts +2 -2
- package/dist/src/utils/formatObject.d.ts.map +1 -1
- package/dist/src/utils/formatObject.js +2 -2
- package/dist/src/utils/inputBinding.d.ts +5 -0
- package/dist/src/utils/inputBinding.d.ts.map +1 -0
- package/dist/src/utils/inputBinding.js +115 -0
- package/dist/src/utils/ui/tooltip.js +1 -1
- package/dist/src/view/axisView.js +3 -3
- package/dist/src/view/paramMediator.d.ts +108 -0
- package/dist/src/view/paramMediator.d.ts.map +1 -0
- package/dist/src/view/paramMediator.js +337 -0
- package/dist/src/view/paramMediator.test.js +211 -0
- package/dist/src/view/scaleResolution.d.ts +8 -18
- package/dist/src/view/scaleResolution.d.ts.map +1 -1
- package/dist/src/view/scaleResolution.js +225 -126
- package/dist/src/view/scaleResolution.test.js +7 -7
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +10 -3
- package/dist/src/view/view.d.ts +4 -1
- package/dist/src/view/view.d.ts.map +1 -1
- package/dist/src/view/view.js +21 -7
- package/dist/src/view/viewFactory.d.ts.map +1 -1
- package/dist/src/view/viewFactory.js +45 -0
- package/dist/src/view/viewUtils.d.ts +5 -1
- package/dist/src/view/viewUtils.d.ts.map +1 -1
- package/dist/src/view/viewUtils.js +9 -4
- package/package.json +16 -17
- package/dist/src/paramBroker.d.ts +0 -30
- package/dist/src/paramBroker.d.ts.map +0 -1
- package/dist/src/paramBroker.js +0 -102
package/dist/src/marks/mark.js
CHANGED
|
@@ -18,15 +18,16 @@ import createEncoders, {
|
|
|
18
18
|
isValueDef,
|
|
19
19
|
} from "../encoder/encoder.js";
|
|
20
20
|
import {
|
|
21
|
-
|
|
22
|
-
generateValueGlsl,
|
|
21
|
+
generateConstantValueGlsl,
|
|
23
22
|
generateScaleGlsl,
|
|
24
23
|
RANGE_TEXTURE_PREFIX,
|
|
25
|
-
ATTRIBUTE_PREFIX,
|
|
26
24
|
isHighPrecisionScale,
|
|
27
25
|
toHighPrecisionDomainUniform,
|
|
28
|
-
splitHighPrecision,
|
|
29
26
|
dedupeEncodingFields,
|
|
27
|
+
generateDynamicValueGlslAndUniform,
|
|
28
|
+
isLargeGenome,
|
|
29
|
+
splitLargeHighPrecision,
|
|
30
|
+
getRangeForGlsl,
|
|
30
31
|
} from "../gl/glslScaleGenerator.js";
|
|
31
32
|
import GLSL_COMMON from "../gl/includes/common.glsl.js";
|
|
32
33
|
import GLSL_SCALES from "../gl/includes/scales.glsl.js";
|
|
@@ -38,9 +39,8 @@ import { createProgram } from "../gl/webGLHelper.js";
|
|
|
38
39
|
import coalesceProperties from "../utils/propertyCoalescer.js";
|
|
39
40
|
import { isScalar } from "../utils/variableTools.js";
|
|
40
41
|
import { InternMap } from "internmap";
|
|
41
|
-
import scaleNull from "../utils/scaleNull.js";
|
|
42
42
|
import ViewError from "../view/viewError.js";
|
|
43
|
-
import {
|
|
43
|
+
import { isExprRef } from "../view/paramMediator.js";
|
|
44
44
|
|
|
45
45
|
export const SAMPLE_FACET_UNIFORM = "SAMPLE_FACET_UNIFORM";
|
|
46
46
|
export const SAMPLE_FACET_TEXTURE = "SAMPLE_FACET_TEXTURE";
|
|
@@ -60,13 +60,20 @@ export const SAMPLE_FACET_TEXTURE = "SAMPLE_FACET_TEXTURE";
|
|
|
60
60
|
*/
|
|
61
61
|
export default class Mark {
|
|
62
62
|
/**
|
|
63
|
-
* @typedef {import("../spec/mark.js").
|
|
63
|
+
* @typedef {import("../spec/mark.js").MarkProps} MarkProps
|
|
64
64
|
* @typedef {import("../spec/channel.js").Channel} Channel
|
|
65
65
|
* @typedef {import("../spec/channel.js").Encoding} Encoding
|
|
66
66
|
* @typedef {import("../spec/channel.js").ValueDef} ValueDef
|
|
67
|
-
* @typedef {import("../spec/
|
|
67
|
+
* @typedef {import("../spec/parameter.js").ExprRef} ExprRef
|
|
68
68
|
*/
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Only needed during initialization;
|
|
72
|
+
*
|
|
73
|
+
* @type {(() => void)[]}
|
|
74
|
+
*/
|
|
75
|
+
#callAfterShaderCompilation = [];
|
|
76
|
+
|
|
70
77
|
/**
|
|
71
78
|
* @param {import("../view/unitView.js").default} unitView
|
|
72
79
|
*/
|
|
@@ -111,7 +118,7 @@ export default class Mark {
|
|
|
111
118
|
this.rangeMap = new RangeMap();
|
|
112
119
|
|
|
113
120
|
// TODO: Implement https://vega.github.io/vega-lite/docs/config.html
|
|
114
|
-
/** @type {
|
|
121
|
+
/** @type {MarkProps} */
|
|
115
122
|
this.defaultProperties = {
|
|
116
123
|
get clip() {
|
|
117
124
|
// TODO: Cache once the scales have been resolved
|
|
@@ -141,13 +148,13 @@ export default class Mark {
|
|
|
141
148
|
*
|
|
142
149
|
* TODO: Proper and comprehensive typings for mark properties
|
|
143
150
|
*
|
|
144
|
-
* @type {Partial<
|
|
151
|
+
* @type {Partial<MarkProps>}
|
|
145
152
|
* @readonly
|
|
146
153
|
*/
|
|
147
154
|
this.properties = coalesceProperties(
|
|
148
155
|
typeof this.unitView.spec.mark == "object"
|
|
149
|
-
? () => /** @type {
|
|
150
|
-
: () => /** @type {
|
|
156
|
+
? () => /** @type {MarkProps} */ (this.unitView.spec.mark)
|
|
157
|
+
: () => /** @type {MarkProps} */ ({}),
|
|
151
158
|
() => this.defaultProperties
|
|
152
159
|
);
|
|
153
160
|
}
|
|
@@ -214,6 +221,49 @@ export default class Mark {
|
|
|
214
221
|
return encoding;
|
|
215
222
|
}
|
|
216
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Handles dynamic properties that are not bound to uniforms but need
|
|
226
|
+
* to trigger a graphics update, i.e., rebuild the vertex buffer.
|
|
227
|
+
*
|
|
228
|
+
* @param {(keyof MarkProps)[]} props
|
|
229
|
+
* @protected
|
|
230
|
+
*/
|
|
231
|
+
setupExprRefsNeedingGraphicsUpdate(props) {
|
|
232
|
+
const channels = this.getSupportedChannels();
|
|
233
|
+
/** @type {Partial<MarkProps>} */
|
|
234
|
+
const exprProps = {};
|
|
235
|
+
for (const key of props) {
|
|
236
|
+
const prop = this.properties[key];
|
|
237
|
+
if (prop && isExprRef(prop)) {
|
|
238
|
+
const fn = this.unitView.paramMediator.createExpression(
|
|
239
|
+
prop.expr
|
|
240
|
+
);
|
|
241
|
+
fn.addListener(() => {
|
|
242
|
+
this.updateGraphicsData();
|
|
243
|
+
this.unitView.context.animator.requestRender();
|
|
244
|
+
});
|
|
245
|
+
// @ts-ignore
|
|
246
|
+
if (!channels.includes(key)) {
|
|
247
|
+
Object.defineProperty(exprProps, key, {
|
|
248
|
+
get() {
|
|
249
|
+
return fn();
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
} else {
|
|
253
|
+
// Encoder takes care of evaluating the expression
|
|
254
|
+
// N.B.: There are now two expression instances for the
|
|
255
|
+
// same expression, which is no ideal
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const originalProperties = this.properties;
|
|
260
|
+
// @ts-ignore
|
|
261
|
+
this.properties = coalesceProperties(
|
|
262
|
+
() => exprProps,
|
|
263
|
+
() => originalProperties
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
217
267
|
/**
|
|
218
268
|
* Returns the encoding spec supplemented with mark's default encodings
|
|
219
269
|
*
|
|
@@ -227,20 +277,22 @@ export default class Mark {
|
|
|
227
277
|
/** @type {(property: string) => ValueDef} */
|
|
228
278
|
const propToValueDef = (property) => {
|
|
229
279
|
const value =
|
|
230
|
-
this.properties[/** @type {keyof
|
|
231
|
-
return isScalar(value)
|
|
280
|
+
this.properties[/** @type {keyof MarkProps} */ (property)];
|
|
281
|
+
return isScalar(value) || isExprRef(value)
|
|
282
|
+
? { value }
|
|
283
|
+
: undefined;
|
|
232
284
|
};
|
|
233
285
|
|
|
234
286
|
const propertyValues = Object.fromEntries(
|
|
235
287
|
this.getSupportedChannels()
|
|
236
288
|
.map(
|
|
237
289
|
(channel) =>
|
|
238
|
-
/** @type {[Channel, ValueDef]} */ ([
|
|
290
|
+
/** @type {[Channel, ValueDef] } */ ([
|
|
239
291
|
channel,
|
|
240
292
|
propToValueDef(channel),
|
|
241
293
|
])
|
|
242
294
|
)
|
|
243
|
-
.filter((entry) => entry[1]
|
|
295
|
+
.filter((entry) => isValueDef(entry[1]))
|
|
244
296
|
);
|
|
245
297
|
|
|
246
298
|
const encoding = this.fixEncoding({
|
|
@@ -326,16 +378,13 @@ export default class Mark {
|
|
|
326
378
|
* @param {string[]} [extraHeaders]
|
|
327
379
|
* @protected
|
|
328
380
|
*/
|
|
381
|
+
// eslint-disable-next-line complexity
|
|
329
382
|
createAndLinkShaders(vertexShader, fragmentShader, extraHeaders = []) {
|
|
330
383
|
const attributes = this.getAttributes();
|
|
331
384
|
|
|
332
385
|
// For debugging
|
|
333
386
|
const debugHeader = "// view: " + this.unitView.getPathString();
|
|
334
387
|
|
|
335
|
-
// TODO: This is a temporary variable, don't store it in the mark object
|
|
336
|
-
/** @type {string[]} */
|
|
337
|
-
this.domainUniforms = [];
|
|
338
|
-
|
|
339
388
|
/** @type {string[]} */
|
|
340
389
|
let scaleCode = [];
|
|
341
390
|
|
|
@@ -353,6 +402,9 @@ export default class Mark {
|
|
|
353
402
|
extraHeaders.push(`#define ${sampleFacetMode}`);
|
|
354
403
|
}
|
|
355
404
|
|
|
405
|
+
/** @type {string[]} */
|
|
406
|
+
const dynamicMarkUniforms = [];
|
|
407
|
+
|
|
356
408
|
for (const attribute of attributes) {
|
|
357
409
|
/** @type {Channel} */
|
|
358
410
|
let channel;
|
|
@@ -363,24 +415,40 @@ export default class Mark {
|
|
|
363
415
|
}
|
|
364
416
|
|
|
365
417
|
const channelDef = this.encoding[channel];
|
|
366
|
-
|
|
367
418
|
if (!channelDef) {
|
|
368
419
|
continue;
|
|
369
420
|
}
|
|
370
421
|
|
|
371
422
|
if (isValueDef(channelDef)) {
|
|
372
|
-
|
|
423
|
+
if (isExprRef(channelDef.value)) {
|
|
424
|
+
// An expression that evaluates to a value
|
|
425
|
+
const { uniformName, uniformGlsl, scaleGlsl, adjuster } =
|
|
426
|
+
generateDynamicValueGlslAndUniform(channel);
|
|
427
|
+
scaleCode.push(scaleGlsl);
|
|
428
|
+
dynamicMarkUniforms.push(uniformGlsl);
|
|
429
|
+
|
|
430
|
+
this.#callAfterShaderCompilation.push(() => {
|
|
431
|
+
this.registerMarkUniformValue(
|
|
432
|
+
uniformName,
|
|
433
|
+
channelDef.value,
|
|
434
|
+
adjuster
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
} else {
|
|
438
|
+
// A constant value
|
|
439
|
+
scaleCode.push(
|
|
440
|
+
generateConstantValueGlsl(channel, channelDef.value)
|
|
441
|
+
);
|
|
442
|
+
}
|
|
373
443
|
} else {
|
|
374
444
|
const resolutionChannel =
|
|
375
445
|
(isChannelDefWithScale(channelDef) &&
|
|
376
446
|
channelDef.resolutionChannel) ||
|
|
377
447
|
channel;
|
|
378
448
|
|
|
379
|
-
const
|
|
380
|
-
? this.unitView
|
|
381
|
-
|
|
382
|
-
.getScale()
|
|
383
|
-
: scaleNull();
|
|
449
|
+
const scaleResolution = isChannelWithScale(resolutionChannel)
|
|
450
|
+
? this.unitView.getScaleResolution(resolutionChannel)
|
|
451
|
+
: null;
|
|
384
452
|
|
|
385
453
|
// Channels that share the same quantitative field
|
|
386
454
|
// TODO: It should be ok to share a categorical field if the channels
|
|
@@ -391,7 +459,7 @@ export default class Mark {
|
|
|
391
459
|
|
|
392
460
|
const generated = generateScaleGlsl(
|
|
393
461
|
channel,
|
|
394
|
-
|
|
462
|
+
scaleResolution,
|
|
395
463
|
channelDef,
|
|
396
464
|
sharedChannels?.includes(channel)
|
|
397
465
|
? sharedChannels
|
|
@@ -399,22 +467,103 @@ export default class Mark {
|
|
|
399
467
|
);
|
|
400
468
|
|
|
401
469
|
scaleCode.push(generated.glsl);
|
|
402
|
-
|
|
403
|
-
|
|
470
|
+
dynamicMarkUniforms.push(generated.domainUniform);
|
|
471
|
+
dynamicMarkUniforms.push(generated.rangeUniform);
|
|
472
|
+
attributeCode.add(generated.attributeGlsl);
|
|
473
|
+
|
|
474
|
+
if (generated.rangeUniform) {
|
|
475
|
+
this.#callAfterShaderCompilation.push(() => {
|
|
476
|
+
const rangeSetter = this.createMarkUniformSetter(
|
|
477
|
+
generated.rangeName
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const set = () =>
|
|
481
|
+
rangeSetter(
|
|
482
|
+
getRangeForGlsl(scaleResolution.scale, channel)
|
|
483
|
+
);
|
|
484
|
+
scaleResolution.addEventListener("range", set);
|
|
485
|
+
|
|
486
|
+
// Initial value
|
|
487
|
+
set();
|
|
488
|
+
});
|
|
404
489
|
}
|
|
405
|
-
|
|
406
|
-
|
|
490
|
+
|
|
491
|
+
if (generated.markUniformGlsl) {
|
|
492
|
+
if (!isDatumDef(channelDef)) {
|
|
493
|
+
throw new Error("Bug!");
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const encoder = this.encoders[channel];
|
|
497
|
+
|
|
498
|
+
const indexer = encoder.indexer;
|
|
499
|
+
const hp = isHighPrecisionScale(encoder.scale.type);
|
|
500
|
+
const largeHp = hp && isLargeGenome(encoder.scale.domain());
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Discrete variables both numeric and strings must be "indexed",
|
|
504
|
+
* 64 bit floats must be converted to vec2.
|
|
505
|
+
* 32 bit continuous variables go to GPU as is.
|
|
506
|
+
*
|
|
507
|
+
* @type {function(import("../spec/channel.js").Scalar):(number | number[])}
|
|
508
|
+
*/
|
|
509
|
+
const adjuster = indexer
|
|
510
|
+
? indexer
|
|
511
|
+
: largeHp
|
|
512
|
+
? splitLargeHighPrecision
|
|
513
|
+
: (d) => +d;
|
|
514
|
+
|
|
515
|
+
dynamicMarkUniforms.push(generated.markUniformGlsl);
|
|
516
|
+
|
|
517
|
+
this.#callAfterShaderCompilation.push(() => {
|
|
518
|
+
this.registerMarkUniformValue(
|
|
519
|
+
generated.attributeName,
|
|
520
|
+
channelDef.datum,
|
|
521
|
+
adjuster
|
|
522
|
+
);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (generated.domainUniform) {
|
|
527
|
+
this.#callAfterShaderCompilation.push(() => {
|
|
528
|
+
const domainSetter = this.createMarkUniformSetter(
|
|
529
|
+
generated.domainUniformName
|
|
530
|
+
);
|
|
531
|
+
const scale = scaleResolution.scale;
|
|
532
|
+
const set = () => {
|
|
533
|
+
const domain = isDiscrete(scale.type)
|
|
534
|
+
? [0, scale.domain().length]
|
|
535
|
+
: scale.domain();
|
|
536
|
+
|
|
537
|
+
domainSetter(
|
|
538
|
+
isHighPrecisionScale(scale.type)
|
|
539
|
+
? toHighPrecisionDomainUniform(domain)
|
|
540
|
+
: domain
|
|
541
|
+
);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
scaleResolution.addEventListener("domain", set);
|
|
545
|
+
|
|
546
|
+
// Initial value
|
|
547
|
+
set();
|
|
548
|
+
});
|
|
407
549
|
}
|
|
408
550
|
}
|
|
409
551
|
}
|
|
410
552
|
|
|
411
|
-
const
|
|
412
|
-
? "layout(std140) uniform Domains {\n" +
|
|
413
|
-
this.domainUniforms.map((u) => ` ${u}\n`).join("") +
|
|
414
|
-
"};\n\n"
|
|
415
|
-
: "";
|
|
553
|
+
const vertexPrecision = "precision highp float;\nprecision highp int;";
|
|
416
554
|
|
|
417
|
-
|
|
555
|
+
/**
|
|
556
|
+
* @param {string} shaderCode
|
|
557
|
+
*/
|
|
558
|
+
const addDynamicMarkUniforms = (shaderCode) =>
|
|
559
|
+
shaderCode.replace(
|
|
560
|
+
"#pragma markUniforms",
|
|
561
|
+
dynamicMarkUniforms.join("\n")
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
extraHeaders = extraHeaders.map(addDynamicMarkUniforms);
|
|
565
|
+
vertexShader = addDynamicMarkUniforms(vertexShader);
|
|
566
|
+
fragmentShader = addDynamicMarkUniforms(fragmentShader);
|
|
418
567
|
|
|
419
568
|
const vertexParts = [
|
|
420
569
|
vertexPrecision,
|
|
@@ -422,7 +571,6 @@ export default class Mark {
|
|
|
422
571
|
...extraHeaders,
|
|
423
572
|
GLSL_COMMON,
|
|
424
573
|
GLSL_SCALES,
|
|
425
|
-
domainUniformBlock,
|
|
426
574
|
[...attributeCode].join("\n"),
|
|
427
575
|
...scaleCode,
|
|
428
576
|
GLSL_SAMPLE_FACET,
|
|
@@ -431,6 +579,7 @@ export default class Mark {
|
|
|
431
579
|
];
|
|
432
580
|
|
|
433
581
|
const fragmentParts = [
|
|
582
|
+
vertexPrecision,
|
|
434
583
|
debugHeader,
|
|
435
584
|
...extraHeaders,
|
|
436
585
|
GLSL_COMMON,
|
|
@@ -453,6 +602,9 @@ export default class Mark {
|
|
|
453
602
|
/**
|
|
454
603
|
* Check WebGL shader/program compilation/linking status and finalize
|
|
455
604
|
* initialization.
|
|
605
|
+
*
|
|
606
|
+
* This is done as a separate step after all shader compilations have been
|
|
607
|
+
* initiated. The idea is to allow for parallel background compilation.
|
|
456
608
|
*/
|
|
457
609
|
finalizeGraphicsInitialization() {
|
|
458
610
|
const error = this.programStatus.getProgramErrors();
|
|
@@ -474,14 +626,6 @@ export default class Mark {
|
|
|
474
626
|
);
|
|
475
627
|
delete this.programStatus;
|
|
476
628
|
|
|
477
|
-
if (this.domainUniforms.length) {
|
|
478
|
-
this.domainUniformInfo = createUniformBlockInfo(
|
|
479
|
-
this.gl,
|
|
480
|
-
this.programInfo,
|
|
481
|
-
"Domains"
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
629
|
this.viewUniformInfo = createUniformBlockInfo(
|
|
486
630
|
this.gl,
|
|
487
631
|
this.programInfo,
|
|
@@ -496,14 +640,39 @@ export default class Mark {
|
|
|
496
640
|
|
|
497
641
|
this.gl.useProgram(this.programInfo.program);
|
|
498
642
|
|
|
499
|
-
this._setDatums();
|
|
500
|
-
|
|
501
643
|
setUniforms(this.programInfo, {
|
|
502
644
|
// left pos, left height, right pos, right height
|
|
503
645
|
uSampleFacet: [0, 1, 0, 1],
|
|
504
646
|
uTransitionOffset: 0.0,
|
|
505
647
|
uZero: 0.0,
|
|
506
648
|
});
|
|
649
|
+
|
|
650
|
+
for (const fn of this.#callAfterShaderCompilation) {
|
|
651
|
+
fn();
|
|
652
|
+
}
|
|
653
|
+
this.#callAfterShaderCompilation = undefined;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Sets a uniform in the Mark block. Requests a render from the animator.
|
|
658
|
+
*
|
|
659
|
+
* @protected
|
|
660
|
+
* @param {string} uniformName
|
|
661
|
+
* @returns {function(any):void}
|
|
662
|
+
*/
|
|
663
|
+
createMarkUniformSetter(uniformName) {
|
|
664
|
+
const uniformSetter = this.markUniformInfo.setters[uniformName];
|
|
665
|
+
if (!uniformSetter) {
|
|
666
|
+
throw new Error(
|
|
667
|
+
`Uniform "${uniformName}" not found int the Mark block!`
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return (value) => {
|
|
672
|
+
uniformSetter(value);
|
|
673
|
+
this.markUniformsAltered = true;
|
|
674
|
+
this.unitView.context.animator.requestRender();
|
|
675
|
+
};
|
|
507
676
|
}
|
|
508
677
|
|
|
509
678
|
/**
|
|
@@ -517,46 +686,32 @@ export default class Mark {
|
|
|
517
686
|
* @param {T} propValue
|
|
518
687
|
* @param {(x: Exclude<T, ExprRef>) => any} adjuster
|
|
519
688
|
*/
|
|
520
|
-
|
|
521
|
-
const
|
|
689
|
+
registerMarkUniformValue(uniformName, propValue, adjuster = (x) => x) {
|
|
690
|
+
const rawSetter = this.createMarkUniformSetter(uniformName);
|
|
691
|
+
const setter = (/** @type {any} */ value) => {
|
|
692
|
+
if (value == null) {
|
|
693
|
+
throw new Error(
|
|
694
|
+
`Trying to set null/undefined value for uniform: ${uniformName}${
|
|
695
|
+
isExprRef(propValue) ? `Expr: ${propValue.expr}` : ""
|
|
696
|
+
}`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
rawSetter(value);
|
|
700
|
+
};
|
|
522
701
|
|
|
523
702
|
if (isExprRef(propValue)) {
|
|
524
|
-
const fn = this.unitView.
|
|
703
|
+
const fn = this.unitView.paramMediator.createExpression(
|
|
525
704
|
propValue.expr
|
|
526
705
|
);
|
|
527
706
|
|
|
528
|
-
const set = () =>
|
|
529
|
-
uniformSetter(adjuster(fn(null)));
|
|
530
|
-
this.markUniformsAltered = true;
|
|
531
|
-
};
|
|
707
|
+
const set = () => setter(adjuster(fn(null)));
|
|
532
708
|
|
|
533
709
|
// Register a listener ...
|
|
534
710
|
fn.addListener(set);
|
|
535
711
|
// ... and set the initial value
|
|
536
712
|
set();
|
|
537
713
|
} else {
|
|
538
|
-
|
|
539
|
-
adjuster(/** @type {Exclude<T, ExprRef>} */ (propValue))
|
|
540
|
-
);
|
|
541
|
-
this.markUniformsAltered = true;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
_setDatums() {
|
|
546
|
-
for (const [channel, channelDef] of Object.entries(this.encoding)) {
|
|
547
|
-
if (isDatumDef(channelDef)) {
|
|
548
|
-
const encoder = this.encoders[channel];
|
|
549
|
-
|
|
550
|
-
const datum = encoder.indexer
|
|
551
|
-
? encoder.indexer(channelDef.datum)
|
|
552
|
-
: isHighPrecisionScale(encoder.scale.type)
|
|
553
|
-
? splitHighPrecision(+channelDef.datum)
|
|
554
|
-
: +channelDef.datum;
|
|
555
|
-
|
|
556
|
-
setUniforms(this.programInfo, {
|
|
557
|
-
[ATTRIBUTE_PREFIX + channel]: datum,
|
|
558
|
-
});
|
|
559
|
-
}
|
|
714
|
+
setter(adjuster(/** @type {Exclude<T, ExprRef>} */ (propValue)));
|
|
560
715
|
}
|
|
561
716
|
}
|
|
562
717
|
|
|
@@ -707,47 +862,6 @@ export default class Mark {
|
|
|
707
862
|
gl.useProgram(this.programInfo.program);
|
|
708
863
|
});
|
|
709
864
|
|
|
710
|
-
if (this.domainUniformInfo) {
|
|
711
|
-
// TODO: Only update the domains that have changed
|
|
712
|
-
|
|
713
|
-
for (const [uniform, setter] of Object.entries(
|
|
714
|
-
this.domainUniformInfo.setters
|
|
715
|
-
)) {
|
|
716
|
-
// TODO: isChannel()
|
|
717
|
-
const channel = /** @type {Channel} */ (
|
|
718
|
-
uniform.substring(DOMAIN_PREFIX.length)
|
|
719
|
-
);
|
|
720
|
-
|
|
721
|
-
const channelDef = this.encoding[channel];
|
|
722
|
-
const resolutionChannel =
|
|
723
|
-
(isChannelDefWithScale(channelDef) &&
|
|
724
|
-
channelDef.resolutionChannel) ||
|
|
725
|
-
channel;
|
|
726
|
-
|
|
727
|
-
if (isChannelWithScale(resolutionChannel)) {
|
|
728
|
-
const scale = this.unitView
|
|
729
|
-
.getScaleResolution(resolutionChannel)
|
|
730
|
-
.getScale();
|
|
731
|
-
|
|
732
|
-
ops.push(() => {
|
|
733
|
-
const domain = isDiscrete(scale.type)
|
|
734
|
-
? [0, scale.domain().length]
|
|
735
|
-
: scale.domain();
|
|
736
|
-
|
|
737
|
-
setter(
|
|
738
|
-
isHighPrecisionScale(scale.type)
|
|
739
|
-
? toHighPrecisionDomainUniform(domain)
|
|
740
|
-
: domain
|
|
741
|
-
);
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
ops.push(() =>
|
|
747
|
-
setUniformBlock(gl, this.programInfo, this.domainUniformInfo)
|
|
748
|
-
);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
865
|
for (const [channel, channelDef] of Object.entries(this.encoding)) {
|
|
752
866
|
if (isChannelDefWithScale(channelDef)) {
|
|
753
867
|
const resolutionChannel =
|
|
@@ -888,7 +1002,7 @@ export default class Mark {
|
|
|
888
1002
|
/** @type {function(import("../gl/dataToVertices.js").RangeEntry):void} rangeEntry */
|
|
889
1003
|
let drawWithRangeEntry;
|
|
890
1004
|
|
|
891
|
-
const scale = this.unitView.getScaleResolution("x")?.
|
|
1005
|
+
const scale = this.unitView.getScaleResolution("x")?.scale;
|
|
892
1006
|
const continuous = scale && isContinuous(scale.type);
|
|
893
1007
|
const domainStartOffset = ["index", "locus"].includes(scale?.type)
|
|
894
1008
|
? -1
|
|
@@ -1115,12 +1229,3 @@ class RangeMap extends InternMap {
|
|
|
1115
1229
|
}
|
|
1116
1230
|
}
|
|
1117
1231
|
}
|
|
1118
|
-
|
|
1119
|
-
// TODO: Find a better place for this function
|
|
1120
|
-
/**
|
|
1121
|
-
* @param {any} x
|
|
1122
|
-
* @returns {x is import("../spec/mark.js").ExprRef}
|
|
1123
|
-
*/
|
|
1124
|
-
export function isExprRef(x) {
|
|
1125
|
-
return typeof x === "object" && "expr" in x && isString(x.expr);
|
|
1126
|
-
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const shader = "uniform Mark{/***The stroke should only grow inwards,e.g,the diameter/outline is not affected by the stroke width.*Thus,a point that has a zero size has no visible stroke. This allows strokes to be used with*geometric zoom,etc.*/uniform bool uInwardStroke;uniform
|
|
1
|
+
const shader = "layout(std140)uniform Mark{/***The stroke should only grow inwards,e.g,the diameter/outline is not affected by the stroke width.*Thus,a point that has a zero size has no visible stroke. This allows strokes to be used with*geometric zoom,etc.*/uniform bool uInwardStroke;uniform mediump float uScaleFactor;uniform mediump float uZoomLevel;uniform highp float uSemanticThreshold;uniform mediump float uGradientStrength;\n#pragma markUniforms\n};";
|
|
2
2
|
export default shader;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
export default class PointMark extends Mark {
|
|
2
2
|
sampledSemanticScores: Float32Array;
|
|
3
3
|
_getGeometricScaleFactor(): number;
|
|
4
|
-
/**
|
|
5
|
-
* Returns the maximum size of the points in the data, before any scaling
|
|
6
|
-
*/
|
|
7
|
-
_getMaxPointSize(): import("../spec/channel.js").Scalar;
|
|
8
4
|
getSemanticThreshold(): number;
|
|
5
|
+
#private;
|
|
9
6
|
}
|
|
10
7
|
import Mark from "./mark.js";
|
|
11
8
|
//# sourceMappingURL=point.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"point.d.ts","sourceRoot":"","sources":["../../../src/marks/point.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"point.d.ts","sourceRoot":"","sources":["../../../src/marks/point.js"],"names":[],"mappings":"AAmBA;IA0HY,oCAMC;IA+CT,mCAQC;IAED,+BAkBC;;CAgDJ;iBAnQgB,WAAW"}
|
package/dist/src/marks/point.js
CHANGED
|
@@ -10,6 +10,7 @@ import FRAGMENT_SHADER from "./point.fragment.glsl.js";
|
|
|
10
10
|
import COMMON_SHADER from "./point.common.glsl.js";
|
|
11
11
|
|
|
12
12
|
import Mark from "./mark.js";
|
|
13
|
+
import { isExprRef } from "../view/paramMediator.js";
|
|
13
14
|
import { sampleIterable } from "../data/transforms/sample.js";
|
|
14
15
|
import { fixFill, fixStroke } from "./markUtils.js";
|
|
15
16
|
|
|
@@ -17,6 +18,8 @@ import { fixFill, fixStroke } from "./markUtils.js";
|
|
|
17
18
|
const defaultEncoding = {};
|
|
18
19
|
|
|
19
20
|
export default class PointMark extends Mark {
|
|
21
|
+
#semanticZoomFraction = () => 0;
|
|
22
|
+
|
|
20
23
|
/**
|
|
21
24
|
* @param {import("../view/unitView.js").default} unitView
|
|
22
25
|
*/
|
|
@@ -45,6 +48,24 @@ export default class PointMark extends Mark {
|
|
|
45
48
|
semanticZoomFraction: 0.02,
|
|
46
49
|
})
|
|
47
50
|
);
|
|
51
|
+
|
|
52
|
+
// TODO: This mess should be simplified
|
|
53
|
+
// TODO: createExpression should accept constant values or ExprRefs and allow
|
|
54
|
+
// easy registration of requestRender listeners
|
|
55
|
+
const szf = this.properties.semanticZoomFraction;
|
|
56
|
+
if (szf != null) {
|
|
57
|
+
if (isExprRef(szf)) {
|
|
58
|
+
const fn = this.unitView.paramMediator.createExpression(
|
|
59
|
+
szf.expr
|
|
60
|
+
);
|
|
61
|
+
fn.addListener(() =>
|
|
62
|
+
this.getContext().animator.requestRender()
|
|
63
|
+
);
|
|
64
|
+
this.#semanticZoomFraction = fn;
|
|
65
|
+
} else {
|
|
66
|
+
this.#semanticZoomFraction = () => szf;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
48
69
|
}
|
|
49
70
|
|
|
50
71
|
getAttributes() {
|
|
@@ -143,11 +164,15 @@ export default class PointMark extends Mark {
|
|
|
143
164
|
|
|
144
165
|
const props = this.properties;
|
|
145
166
|
|
|
146
|
-
|
|
147
|
-
uInwardStroke
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
this.registerMarkUniformValue(
|
|
168
|
+
"uInwardStroke",
|
|
169
|
+
props.inwardStroke,
|
|
170
|
+
(x) => !!x
|
|
171
|
+
);
|
|
172
|
+
this.registerMarkUniformValue(
|
|
173
|
+
"uGradientStrength",
|
|
174
|
+
props.fillGradientStrength
|
|
175
|
+
);
|
|
151
176
|
}
|
|
152
177
|
|
|
153
178
|
updateGraphicsData() {
|
|
@@ -177,27 +202,11 @@ export default class PointMark extends Mark {
|
|
|
177
202
|
);
|
|
178
203
|
}
|
|
179
204
|
|
|
180
|
-
/**
|
|
181
|
-
* Returns the maximum size of the points in the data, before any scaling
|
|
182
|
-
*/
|
|
183
|
-
_getMaxPointSize() {
|
|
184
|
-
const e = this.encoders.size;
|
|
185
|
-
if (e.constant) {
|
|
186
|
-
return e(null);
|
|
187
|
-
} else {
|
|
188
|
-
return /** @type {number[]} */ (e.scale.range()).reduce((a, b) =>
|
|
189
|
-
Math.max(a, b)
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
205
|
getSemanticThreshold() {
|
|
195
206
|
if (this.sampledSemanticScores) {
|
|
196
207
|
const p = Math.max(
|
|
197
208
|
0,
|
|
198
|
-
1 -
|
|
199
|
-
this.properties.semanticZoomFraction *
|
|
200
|
-
this.unitView.getZoomLevel()
|
|
209
|
+
1 - this.#semanticZoomFraction() * this.unitView.getZoomLevel()
|
|
201
210
|
);
|
|
202
211
|
if (p <= 0) {
|
|
203
212
|
// The sampled scores may be missing the min/max values
|
|
@@ -222,7 +231,6 @@ export default class PointMark extends Mark {
|
|
|
222
231
|
ops.push(() => {
|
|
223
232
|
// TODO: Use bindUniformBlock if none of the uniform has changed
|
|
224
233
|
setBlockUniforms(this.markUniformInfo, {
|
|
225
|
-
uMaxPointSize: this._getMaxPointSize(),
|
|
226
234
|
uScaleFactor: this._getGeometricScaleFactor(),
|
|
227
235
|
uSemanticThreshold: this.getSemanticThreshold(),
|
|
228
236
|
});
|