@genome-spy/core 0.40.0 → 0.41.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 (47) hide show
  1. package/dist/bundle/index.es.js +3615 -3460
  2. package/dist/bundle/index.js +69 -76
  3. package/dist/schema.json +196 -46
  4. package/dist/src/encoder/accessor.js +4 -2
  5. package/dist/src/genomeSpy.d.ts +2 -0
  6. package/dist/src/genomeSpy.d.ts.map +1 -1
  7. package/dist/src/genomeSpy.js +5 -0
  8. package/dist/src/gl/link.vertex.glsl.js +1 -1
  9. package/dist/src/gl/point.common.glsl.js +2 -0
  10. package/dist/src/gl/point.fragment.glsl.js +1 -1
  11. package/dist/src/gl/point.vertex.glsl.js +1 -1
  12. package/dist/src/gl/rect.vertex.glsl.js +1 -1
  13. package/dist/src/gl/rule.common.glsl.js +2 -0
  14. package/dist/src/gl/rule.fragment.glsl.js +1 -1
  15. package/dist/src/gl/rule.vertex.glsl.js +1 -1
  16. package/dist/src/gl/text.common.glsl.js +2 -0
  17. package/dist/src/gl/text.fragment.glsl.js +1 -1
  18. package/dist/src/gl/text.vertex.glsl.js +1 -1
  19. package/dist/src/marks/link.d.ts.map +1 -1
  20. package/dist/src/marks/link.js +28 -14
  21. package/dist/src/marks/mark.d.ts +34 -0
  22. package/dist/src/marks/mark.d.ts.map +1 -1
  23. package/dist/src/marks/mark.js +83 -1
  24. package/dist/src/marks/pointMark.d.ts.map +1 -1
  25. package/dist/src/marks/pointMark.js +21 -9
  26. package/dist/src/marks/rectMark.d.ts +1 -2
  27. package/dist/src/marks/rectMark.d.ts.map +1 -1
  28. package/dist/src/marks/rectMark.js +28 -17
  29. package/dist/src/marks/rule.d.ts.map +1 -1
  30. package/dist/src/marks/rule.js +17 -6
  31. package/dist/src/marks/text.d.ts.map +1 -1
  32. package/dist/src/marks/text.js +22 -7
  33. package/dist/src/paramBroker.d.ts +30 -0
  34. package/dist/src/paramBroker.d.ts.map +1 -0
  35. package/dist/src/paramBroker.js +102 -0
  36. package/dist/src/spec/mark.d.ts +27 -18
  37. package/dist/src/spec/view.d.ts +2 -1
  38. package/dist/src/types/viewContext.d.ts +2 -0
  39. package/dist/src/utils/expression.d.ts +12 -2
  40. package/dist/src/utils/expression.d.ts.map +1 -1
  41. package/dist/src/utils/expression.js +68 -9
  42. package/dist/src/utils/linearstep.d.ts +7 -0
  43. package/dist/src/utils/linearstep.d.ts.map +1 -0
  44. package/dist/src/utils/linearstep.js +10 -0
  45. package/dist/src/view/view.d.ts.map +1 -1
  46. package/dist/src/view/view.js +6 -0
  47. package/package.json +2 -2
@@ -1,4 +1,9 @@
1
1
  /// <reference types="external-typings/internmap.js" />
2
+ /**
3
+ * @param {any} x
4
+ * @returns {x is import("../spec/mark.js").ExprRef}
5
+ */
6
+ export function isExprRef(x: any): x is import("../spec/mark.js").ExprRef;
2
7
  export const SAMPLE_FACET_UNIFORM: "SAMPLE_FACET_UNIFORM";
3
8
  export const SAMPLE_FACET_TEXTURE: "SAMPLE_FACET_TEXTURE";
4
9
  /**
@@ -20,6 +25,7 @@ export default class Mark {
20
25
  * @typedef {import("../spec/channel.js").Channel} Channel
21
26
  * @typedef {import("../spec/channel.js").Encoding} Encoding
22
27
  * @typedef {import("../spec/channel.js").ValueDef} ValueDef
28
+ * @typedef {import("../spec/mark.js").ExprRef} ExprRef
23
29
  */
24
30
  /**
25
31
  * @param {import("../view/unitView.js").default} unitView
@@ -40,6 +46,18 @@ export default class Mark {
40
46
  domainUniformInfo: import("twgl.js").UniformBlockInfo;
41
47
  /** @type {import("twgl.js").UniformBlockInfo} WebGL buffers */
42
48
  viewUniformInfo: import("twgl.js").UniformBlockInfo;
49
+ /**
50
+ * Uniforms related to the specific mark type.
51
+ * @type {import("twgl.js").UniformBlockInfo}
52
+ */
53
+ markUniformInfo: import("twgl.js").UniformBlockInfo;
54
+ /**
55
+ * Indicates whether the mark's uniforms have been altered since the last rendering.
56
+ * If set to true, the uniforms will be sent to the GPU before rendering the next frame.
57
+
58
+ * @protected
59
+ */
60
+ protected markUniformsAltered: boolean;
43
61
  /** @type {RangeMap<any>} keep track of facet locations within the vertex array */
44
62
  rangeMap: RangeMap<any>;
45
63
  /** @type {MarkConfig} */
@@ -123,6 +141,18 @@ export default class Mark {
123
141
  * initialization.
124
142
  */
125
143
  finalizeGraphicsInitialization(): void;
144
+ /**
145
+ * Set a uniform based on a mark property. If the property is an expression,
146
+ * register a listener to update the uniform when the params referenced by the
147
+ * expression change.
148
+ *
149
+ * @protected
150
+ * @template T
151
+ * @param {string} uniformName
152
+ * @param {T} propValue
153
+ * @param {(x: Exclude<T, ExprRef>) => any} adjuster
154
+ */
155
+ protected registerMarkUniform<T>(uniformName: string, propValue: T, adjuster?: (x: Exclude<T, import("../spec/mark.js").ExprRef>) => any): void;
126
156
  _setDatums(): void;
127
157
  /**
128
158
  * Delete WebGL buffers etc.
@@ -147,6 +177,10 @@ export default class Mark {
147
177
  * TODO: Check if selection (when it's implemented) is enabled
148
178
  */
149
179
  isPickingParticipant(): boolean;
180
+ /**
181
+ * @protected
182
+ */
183
+ protected bindOrSetMarkUniformBlock(): void;
150
184
  /**
151
185
  * Configures the WebGL state for rendering the mark instances.
152
186
  * A separate preparation stage allows for efficient rendering of faceted
@@ -1 +1 @@
1
- {"version":3,"file":"mark.d.ts","sourceRoot":"","sources":["../../../src/marks/mark.js"],"names":[],"mappings":";AAwCA,0DAA2D;AAC3D,0DAA2D;AAE3D;;;;;;;;;;;;GAYG;AACH;IACI;;;;;OAKG;IAEH;;OAEG;IACH,sBAFW,OAAO,qBAAqB,EAAE,OAAO,EAoE/C;IAjEG,gDAAwB;IAExB,oEAAoE;IACpE,UADW,OAAO,MAAM,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CACvC;IAIzB,0FAA0F;IAC1F,YADW,OAAO,SAAS,EAAE,UAAU,GAAG;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,CAC7C;IAE3B,0DAA0D;IAC1D,aADW,OAAO,SAAS,EAAE,WAAW,CACZ;IAE5B,8DAA8D;IAC9D,iBADW,OAAO,SAAS,EAAE,eAAe,CACZ;IAEhC,+DAA+D;IAC/D,mBADW,OAAO,SAAS,EAAE,gBAAgB,CACX;IAElC,+DAA+D;IAC/D,iBADW,OAAO,SAAS,EAAE,gBAAgB,CACb;IAEhC,kFAAkF;IAClF,UADW,SAAS,GAAG,CAAC,CACM;IAG9B,yBAAyB;IACzB,wDAqBC;IAED;;;;;;;;OAQG;IACH,mEAKC;IAGL,sBAEC;IAED;;;;;;;OAOG;IACH,iBAFa,MAAM,EAAE,CAKpB;IAED;;OAEG;IACH,+DAWC;IAED;;OAEG;IACH,oEAcC;IAED;;;;;OAKG;IACH,oHAEC;IAED;;;;OAIG;IACH,8DAyCC;IAED,wDAEC;IAED,8CAEC;IAED,uBAEC;IAED;;;OAGG;IACH,2BAEC;IAED;;OAEG;IACH,oCAEC;IAED;;OAEG;IACH,2BAEC;IAED,sEAeC;IAED;;;;;OAKG;IACH,mCAJW,MAAM,kBACN,MAAM,iBACN,MAAM,EAAE,QA+FlB;IAtFG,uBAAuB;IACvB,gBADW,MAAM,EAAE,CACK;IAgFxB;;;;;;MAIC;IAGL;;;OAGG;IACH,uCA4CC;IAED,mBAgBC;IAED;;OAEG;IACH,2BAiBC;IAED;;;OAGG;IACH,6BAFW,GAAG,QAoCb;IAED,yBAAyB;IACzB,uDAEC;IAED,yBAAyB;IACzB,iCAEC;IAED,gCAEC;IAED,+BAEC;IAED,yCAEC;IAED;;;;;OAKG;IACH,gCAcC;IAED;;;;;;;;OAQG;IAEH,uBAJW,OAAO,uBAAuB,EAAE,sBAAsB,GACpD,CAAC,MAAM,IAAI,CAAC,EAAE,CA+H1B;IAED;;;;;;OAMG;IACH,qCAJW,oBAAoB,GAClB,OAAO,CAqCnB;IAED;;;;;;;OAOG;IACH,gBAJW,oBAAoB,SACP,IAAI,CAM3B;IAED;;;OAGG;IACH,2BAHW,YAAY,WACZ,OAAO,WAAW,EAAE,oBAAoB,cAiElD;IAED;;;;;;OAMG;IACH,oBAJW,OAAO,6BAA6B,EAAE,OAAO,aAC7C,OAAO,6BAA6B,EAAE,OAAO,GAC3C,OAAO,CAuHnB;IAED;;;;;;;;;OASG;IACH,qBAJW,MAAM,KACN,OAAO,oBAAoB,EAAE,MAAM,GACjC,GAAG,CAIf;CACJ;+BA95BY,OAAO,uBAAuB,EAAE,gBAAgB;;;;;;wBAEnD,OAAO;;mCAEJ,gBAAgB,GAAG,qBAAqB;oCAG1C,MAAM,SACN,MAAM;AAw5BjB;;;GAGG;AACH;IACI,cAEC;IAkBD;;;OAGG;IACH,2BAFW,IAAI,CAAC,EAAE,OAAO,yBAAyB,EAAE,UAAU,CAAC,QAM9D;CACJ;0BA38ByB,WAAW"}
1
+ {"version":3,"file":"mark.d.ts","sourceRoot":"","sources":["../../../src/marks/mark.js"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import {
2
+ bindUniformBlock,
2
3
  createBufferInfoFromArrays,
3
4
  createProgramInfoFromProgram,
4
5
  createUniformBlockInfo,
@@ -37,6 +38,7 @@ import { isScalar } from "../utils/variableTools.js";
37
38
  import { InternMap } from "internmap";
38
39
  import scaleNull from "../utils/scaleNull.js";
39
40
  import ViewError from "../view/viewError.js";
41
+ import { isString } from "vega-util";
40
42
 
41
43
  export const SAMPLE_FACET_UNIFORM = "SAMPLE_FACET_UNIFORM";
42
44
  export const SAMPLE_FACET_TEXTURE = "SAMPLE_FACET_TEXTURE";
@@ -60,6 +62,7 @@ export default class Mark {
60
62
  * @typedef {import("../spec/channel.js").Channel} Channel
61
63
  * @typedef {import("../spec/channel.js").Encoding} Encoding
62
64
  * @typedef {import("../spec/channel.js").ValueDef} ValueDef
65
+ * @typedef {import("../spec/mark.js").ExprRef} ExprRef
63
66
  */
64
67
 
65
68
  /**
@@ -88,6 +91,20 @@ export default class Mark {
88
91
  /** @type {import("twgl.js").UniformBlockInfo} WebGL buffers */
89
92
  this.viewUniformInfo = undefined;
90
93
 
94
+ /**
95
+ * Uniforms related to the specific mark type.
96
+ * @type {import("twgl.js").UniformBlockInfo}
97
+ */
98
+ this.markUniformInfo = undefined;
99
+
100
+ /**
101
+ * Indicates whether the mark's uniforms have been altered since the last rendering.
102
+ * If set to true, the uniforms will be sent to the GPU before rendering the next frame.
103
+
104
+ * @protected
105
+ */
106
+ this.markUniformsAltered = true;
107
+
91
108
  /** @type {RangeMap<any>} keep track of facet locations within the vertex array */
92
109
  this.rangeMap = new RangeMap();
93
110
 
@@ -304,7 +321,7 @@ export default class Mark {
304
321
  const attributes = this.getAttributes();
305
322
 
306
323
  // For debugging
307
- extraHeaders.push("// view: " + this.unitView.getPathString());
324
+ const debugHeader = "// view: " + this.unitView.getPathString();
308
325
 
309
326
  // TODO: This is a temporary variable, don't store it in the mark object
310
327
  /** @type {string[]} */
@@ -366,6 +383,7 @@ export default class Mark {
366
383
 
367
384
  const vertexParts = [
368
385
  vertexPrecision,
386
+ debugHeader,
369
387
  ...extraHeaders,
370
388
  GLSL_COMMON,
371
389
  GLSL_SCALES,
@@ -377,6 +395,7 @@ export default class Mark {
377
395
  ];
378
396
 
379
397
  const fragmentParts = [
398
+ debugHeader,
380
399
  ...extraHeaders,
381
400
  GLSL_COMMON,
382
401
  GLSL_PICKING_FRAGMENT,
@@ -433,6 +452,12 @@ export default class Mark {
433
452
  "View"
434
453
  );
435
454
 
455
+ this.markUniformInfo = createUniformBlockInfo(
456
+ this.gl,
457
+ this.programInfo,
458
+ "Mark"
459
+ );
460
+
436
461
  this.gl.useProgram(this.programInfo.program);
437
462
 
438
463
  this._setDatums();
@@ -445,6 +470,42 @@ export default class Mark {
445
470
  });
446
471
  }
447
472
 
473
+ /**
474
+ * Set a uniform based on a mark property. If the property is an expression,
475
+ * register a listener to update the uniform when the params referenced by the
476
+ * expression change.
477
+ *
478
+ * @protected
479
+ * @template T
480
+ * @param {string} uniformName
481
+ * @param {T} propValue
482
+ * @param {(x: Exclude<T, ExprRef>) => any} adjuster
483
+ */
484
+ registerMarkUniform(uniformName, propValue, adjuster = (x) => x) {
485
+ const uniformSetter = this.markUniformInfo.setters[uniformName];
486
+
487
+ if (isExprRef(propValue)) {
488
+ const fn = this.unitView.context.paramBroker.createExpression(
489
+ propValue.expr
490
+ );
491
+
492
+ const set = () => {
493
+ uniformSetter(adjuster(fn(null)));
494
+ this.markUniformsAltered = true;
495
+ };
496
+
497
+ // Register a listener ...
498
+ fn.addListener(set);
499
+ // ... and set the initial value
500
+ set();
501
+ } else {
502
+ uniformSetter(
503
+ adjuster(/** @type {Exclude<T, ExprRef>} */ (propValue))
504
+ );
505
+ this.markUniformsAltered = true;
506
+ }
507
+ }
508
+
448
509
  _setDatums() {
449
510
  for (const [channel, channelDef] of Object.entries(this.encoding)) {
450
511
  if (isDatumDef(channelDef)) {
@@ -569,6 +630,18 @@ export default class Mark {
569
630
  return true;
570
631
  }
571
632
 
633
+ /**
634
+ * @protected
635
+ */
636
+ bindOrSetMarkUniformBlock() {
637
+ if (this.markUniformsAltered) {
638
+ setUniformBlock(this.gl, this.programInfo, this.markUniformInfo);
639
+ this.markUniformsAltered = false;
640
+ } else {
641
+ bindUniformBlock(this.gl, this.programInfo, this.markUniformInfo);
642
+ }
643
+ }
644
+
572
645
  /**
573
646
  * Configures the WebGL state for rendering the mark instances.
574
647
  * A separate preparation stage allows for efficient rendering of faceted
@@ -1006,3 +1079,12 @@ class RangeMap extends InternMap {
1006
1079
  }
1007
1080
  }
1008
1081
  }
1082
+
1083
+ // TODO: Find a better place for this function
1084
+ /**
1085
+ * @param {any} x
1086
+ * @returns {x is import("../spec/mark.js").ExprRef}
1087
+ */
1088
+ export function isExprRef(x) {
1089
+ return typeof x === "object" && "expr" in x && isString(x.expr);
1090
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"pointMark.d.ts","sourceRoot":"","sources":["../../../src/marks/pointMark.js"],"names":[],"mappings":"AAaA;IAsGY,oCAMC;IAwCT,mCAQC;IAED;;OAEG;IACH,wDASC;IAED,+BAoBC;CA6CJ;iBApPgB,WAAW"}
1
+ {"version":3,"file":"pointMark.d.ts","sourceRoot":"","sources":["../../../src/marks/pointMark.js"],"names":[],"mappings":"AAkBA;IAsGY,oCAMC;IA2CT,mCAQC;IAED;;OAEG;IACH,wDASC;IAED,+BAoBC;CAiDJ;iBA3PgB,WAAW"}
@@ -1,8 +1,13 @@
1
- import { drawBufferInfo, setBuffersAndAttributes, setUniforms } from "twgl.js";
1
+ import {
2
+ drawBufferInfo,
3
+ setBlockUniforms,
4
+ setBuffersAndAttributes,
5
+ } from "twgl.js";
2
6
  import { quantileSorted } from "d3-array";
3
7
  import { PointVertexBuilder } from "../gl/dataToVertices.js";
4
8
  import VERTEX_SHADER from "../gl/point.vertex.glsl.js";
5
9
  import FRAGMENT_SHADER from "../gl/point.fragment.glsl.js";
10
+ import COMMON_SHADER from "../gl/point.common.glsl.js";
6
11
 
7
12
  import Mark from "./mark.js";
8
13
  import { sampleIterable } from "../data/transforms/sample.js";
@@ -126,7 +131,9 @@ export default class PointMark extends Mark {
126
131
 
127
132
  async initializeGraphics() {
128
133
  await super.initializeGraphics();
129
- this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER);
134
+ this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER, [
135
+ COMMON_SHADER,
136
+ ]);
130
137
  }
131
138
 
132
139
  finalizeGraphicsInitialization() {
@@ -135,9 +142,10 @@ export default class PointMark extends Mark {
135
142
  this.gl.useProgram(this.programInfo.program);
136
143
 
137
144
  const props = this.properties;
138
- setUniforms(this.programInfo, {
139
- uInwardStroke: props.inwardStroke,
140
- uGradientStrength: props.fillGradientStrength,
145
+
146
+ setBlockUniforms(this.markUniformInfo, {
147
+ uInwardStroke: !!props.inwardStroke,
148
+ uGradientStrength: +props.fillGradientStrength,
141
149
  uMaxRelativePointDiameter: 1 - 2 * props.sampleFacetPadding,
142
150
  });
143
151
  }
@@ -211,13 +219,17 @@ export default class PointMark extends Mark {
211
219
  prepareRender(options) {
212
220
  const ops = super.prepareRender(options);
213
221
 
214
- ops.push(() =>
215
- setUniforms(this.programInfo, {
222
+ ops.push(() => {
223
+ // TODO: Use bindUniformBlock if none of the uniform has changed
224
+ setBlockUniforms(this.markUniformInfo, {
216
225
  uMaxPointSize: this._getMaxPointSize(),
217
226
  uScaleFactor: this._getGeometricScaleFactor(),
218
227
  uSemanticThreshold: this.getSemanticThreshold(),
219
- })
220
- );
228
+ });
229
+ this.markUniformsAltered = true;
230
+ });
231
+
232
+ ops.push(() => this.bindOrSetMarkUniformBlock());
221
233
 
222
234
  ops.push(() =>
223
235
  setBuffersAndAttributes(
@@ -1,6 +1,4 @@
1
1
  export default class RectMark extends Mark {
2
- _isRoundedCorners(): number;
3
- _isStroked(): boolean;
4
2
  /**
5
3
  * Finds a datum that overlaps the given value on the x domain.
6
4
  * The result is unspecified if multiple data are found.
@@ -13,6 +11,7 @@ export default class RectMark extends Mark {
13
11
  * @override
14
12
  */
15
13
  override findDatumAt(facetId: any, x: import("../spec/channel.js").Scalar): any;
14
+ #private;
16
15
  }
17
16
  import Mark from "./mark.js";
18
17
  //# sourceMappingURL=rectMark.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rectMark.d.ts","sourceRoot":"","sources":["../../../src/marks/rectMark.js"],"names":[],"mappings":"AAYA;IAsGI,4BASC;IAED,sBAGC;IA4FD;;;;;;;;;;OAUG;IACH,8BALW,GAAG,KACH,OAAO,oBAAoB,EAAE,MAAM,GACjC,GAAG,CAyBf;CACJ;iBAzPgB,WAAW"}
1
+ {"version":3,"file":"rectMark.d.ts","sourceRoot":"","sources":["../../../src/marks/rectMark.js"],"names":[],"mappings":"AAYA;IA2NI;;;;;;;;;;OAUG;IACH,8BALW,GAAG,KACH,OAAO,oBAAoB,EAAE,MAAM,GACjC,GAAG,CAyBf;;CACJ;iBApQgB,WAAW"}
@@ -1,4 +1,4 @@
1
- import { drawBufferInfo, setBuffersAndAttributes, setUniforms } from "twgl.js";
1
+ import { drawBufferInfo, setBuffersAndAttributes } from "twgl.js";
2
2
  import VERTEX_SHADER from "../gl/rect.vertex.glsl.js";
3
3
  import FRAGMENT_SHADER from "../gl/rect.fragment.glsl.js";
4
4
  import { RectVertexBuilder } from "../gl/dataToVertices.js";
@@ -76,8 +76,8 @@ export default class RectMark extends Mark {
76
76
  this,
77
77
  "opaque",
78
78
  () =>
79
- !this._isRoundedCorners() &&
80
- !this._isStroked() &&
79
+ !this.#isRoundedCorners() &&
80
+ !this.#isStroked() &&
81
81
  isValueDef(this.encoding.fillOpacity) &&
82
82
  this.encoding.fillOpacity.value == 1.0 &&
83
83
  this.properties.minOpacity == 1.0
@@ -112,7 +112,7 @@ export default class RectMark extends Mark {
112
112
  // TODO: Pop the previous buffers
113
113
  }
114
114
 
115
- _isRoundedCorners() {
115
+ #isRoundedCorners() {
116
116
  const p = this.properties;
117
117
  return (
118
118
  p.cornerRadius ||
@@ -123,7 +123,7 @@ export default class RectMark extends Mark {
123
123
  );
124
124
  }
125
125
 
126
- _isStroked() {
126
+ #isStroked() {
127
127
  const sw = this.encoding.strokeWidth;
128
128
  return !(isValueDef(sw) && !sw.value);
129
129
  }
@@ -133,10 +133,10 @@ export default class RectMark extends Mark {
133
133
 
134
134
  /** @type {string[]} */
135
135
  const defines = [];
136
- if (this._isRoundedCorners()) {
136
+ if (this.#isRoundedCorners()) {
137
137
  defines.push("ROUNDED_CORNERS");
138
138
  }
139
- if (this._isStroked()) {
139
+ if (this.#isStroked()) {
140
140
  defines.push("STROKED");
141
141
  }
142
142
 
@@ -154,16 +154,25 @@ export default class RectMark extends Mark {
154
154
 
155
155
  const props = this.properties;
156
156
 
157
- setUniforms(this.programInfo, {
158
- uMinSize: [props.minWidth, props.minHeight], // in pixels
159
- uMinOpacity: props.minOpacity,
160
- uCornerRadii: [
161
- props.cornerRadiusTopRight ?? props.cornerRadius,
162
- props.cornerRadiusBottomRight ?? props.cornerRadius,
163
- props.cornerRadiusTopLeft ?? props.cornerRadius,
164
- props.cornerRadiusBottomLeft ?? props.cornerRadius,
165
- ],
166
- });
157
+ this.registerMarkUniform("uMinWidth", props.minWidth);
158
+ this.registerMarkUniform("uMinHeight", props.minHeight);
159
+ this.registerMarkUniform("uMinOpacity", props.minOpacity);
160
+ this.registerMarkUniform(
161
+ "uCornerRadiusTopRight",
162
+ props.cornerRadiusTopRight ?? props.cornerRadius ?? 0
163
+ );
164
+ this.registerMarkUniform(
165
+ "uCornerRadiusBottomRight",
166
+ props.cornerRadiusBottomRight ?? props.cornerRadius ?? 0
167
+ );
168
+ this.registerMarkUniform(
169
+ "uCornerRadiusTopLeft",
170
+ props.cornerRadiusTopLeft ?? props.cornerRadius ?? 0
171
+ );
172
+ this.registerMarkUniform(
173
+ "uCornerRadiusBottomLeft",
174
+ props.cornerRadiusBottomLeft ?? props.cornerRadius ?? 0
175
+ );
167
176
  }
168
177
 
169
178
  updateGraphicsData() {
@@ -190,6 +199,8 @@ export default class RectMark extends Mark {
190
199
  prepareRender(options) {
191
200
  const ops = super.prepareRender(options);
192
201
 
202
+ ops.push(() => this.bindOrSetMarkUniformBlock());
203
+
193
204
  ops.push(() =>
194
205
  setBuffersAndAttributes(
195
206
  this.gl,
@@ -1 +1 @@
1
- {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../src/marks/rule.js"],"names":[],"mappings":"AAYA;IAOQ,wBAAwB;IA4GpB,0BAOE;CAmFb;iBAzNgB,WAAW"}
1
+ {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../src/marks/rule.js"],"names":[],"mappings":"AAcA;IAOQ,wBAAwB;IA4GpB,0BAOE;CA4Fb;iBApOgB,WAAW"}
@@ -2,11 +2,13 @@ import Mark from "./mark.js";
2
2
  import {
3
3
  createTexture,
4
4
  drawBufferInfo,
5
+ setBlockUniforms,
5
6
  setBuffersAndAttributes,
6
7
  setUniforms,
7
8
  } from "twgl.js";
8
9
  import VERTEX_SHADER from "../gl/rule.vertex.glsl.js";
9
10
  import FRAGMENT_SHADER from "../gl/rule.fragment.glsl.js";
11
+ import COMMON_SHADER from "../gl/rule.common.glsl.js";
10
12
  import { RuleVertexBuilder } from "../gl/dataToVertices.js";
11
13
  import { isChannelDefWithScale } from "../encoder/encoder.js";
12
14
 
@@ -136,7 +138,9 @@ export default class RuleMark extends Mark {
136
138
  this.dashTextureSize = textureData.length; // Not needed with WebGL2
137
139
  }
138
140
 
139
- this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER);
141
+ this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER, [
142
+ COMMON_SHADER,
143
+ ]);
140
144
  }
141
145
 
142
146
  finalizeGraphicsInitialization() {
@@ -146,11 +150,17 @@ export default class RuleMark extends Mark {
146
150
 
147
151
  const props = this.properties;
148
152
 
149
- setUniforms(this.programInfo, {
150
- uMinLength: props.minLength,
151
- uDashTextureSize: this.dashTextureSize,
152
- uStrokeCap: ["butt", "square", "round"].indexOf(props.strokeCap),
153
+ this.registerMarkUniform("uMinLength", props.minLength);
154
+ this.registerMarkUniform(
155
+ "uStrokeCap",
156
+ props.strokeCap ?? "butt",
157
+ (cap) => ["butt", "square", "round"].indexOf(cap)
158
+ );
159
+
160
+ setBlockUniforms(this.markUniformInfo, {
161
+ uDashTextureSize: +this.dashTextureSize,
153
162
  });
163
+ this.markUniformsAltered = true;
154
164
  }
155
165
 
156
166
  updateGraphicsData() {
@@ -177,11 +187,12 @@ export default class RuleMark extends Mark {
177
187
  prepareRender(options) {
178
188
  const ops = super.prepareRender(options);
179
189
 
190
+ ops.push(() => this.bindOrSetMarkUniformBlock());
191
+
180
192
  if (this.dashTexture) {
181
193
  ops.push(() =>
182
194
  setUniforms(this.programInfo, {
183
195
  uDashTexture: this.dashTexture,
184
- uStrokeDashOffset: this.properties.strokeDashOffset,
185
196
  })
186
197
  );
187
198
  }
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/marks/text.js"],"names":[],"mappings":"AA0BA;;;;;;;GAOG;AACH;IAsDQ,oDAMmD;CAwL1D;iBA/QgB,WAAW"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/marks/text.js"],"names":[],"mappings":"AAgCA;;;;;;;GAOG;AACH;IAsDQ,oDAMmD;CAiM1D;iBAxRgB,WAAW"}
@@ -1,8 +1,14 @@
1
1
  import { isString } from "vega-util";
2
2
  import { format } from "d3-format";
3
- import { drawBufferInfo, setBuffersAndAttributes, setUniforms } from "twgl.js";
3
+ import {
4
+ drawBufferInfo,
5
+ setBlockUniforms,
6
+ setBuffersAndAttributes,
7
+ setUniforms,
8
+ } from "twgl.js";
4
9
  import VERTEX_SHADER from "../gl/text.vertex.glsl.js";
5
10
  import FRAGMENT_SHADER from "../gl/text.fragment.glsl.js";
11
+ import COMMON_SHADER from "../gl/text.common.glsl.js";
6
12
  import { TextVertexBuilder } from "../gl/dataToVertices.js";
7
13
 
8
14
  import Mark from "./mark.js";
@@ -142,7 +148,9 @@ export default class TextMark extends Mark {
142
148
 
143
149
  async initializeGraphics() {
144
150
  await super.initializeGraphics();
145
- this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER);
151
+ this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER, [
152
+ COMMON_SHADER,
153
+ ]);
146
154
  }
147
155
 
148
156
  finalizeGraphicsInitialization() {
@@ -153,7 +161,7 @@ export default class TextMark extends Mark {
153
161
  const props = this.properties;
154
162
 
155
163
  // TODO: Use uniform block.
156
- setUniforms(this.programInfo, {
164
+ setBlockUniforms(this.markUniformInfo, {
157
165
  uPaddingX: props.paddingX,
158
166
  uPaddingY: props.paddingY,
159
167
  uFlushX: !!props.flushX,
@@ -240,12 +248,19 @@ export default class TextMark extends Mark {
240
248
  this.font.metrics.common.base /
241
249
  (this.unitView.context.devicePixelRatio / q);
242
250
 
243
- ops.push(() =>
251
+ ops.push(() => {
252
+ // TODO: only set if dpr changed
253
+ setBlockUniforms(this.markUniformInfo, {
254
+ uSdfNumerator,
255
+ });
256
+ this.markUniformsAltered = true;
257
+
244
258
  setUniforms(this.programInfo, {
245
259
  uTexture: this.font.texture,
246
- uSdfNumerator,
247
- })
248
- );
260
+ });
261
+ });
262
+
263
+ ops.push(() => this.bindOrSetMarkUniformBlock());
249
264
 
250
265
  ops.push(() =>
251
266
  setBuffersAndAttributes(
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A class that manages parameters and expressions. Still a work in progress.
3
+ *
4
+ * TODO: Write tests for this class.
5
+ *
6
+ * This should eventually handle the following:
7
+ * - Parameter registration
8
+ * - Dependency tracking
9
+ * - Calling observers when a parameter changes
10
+ * - Somehow saving parameter "state" (in bookmarks)
11
+ * - Maybe something else
12
+ */
13
+ export default class ParamBroker {
14
+ /**
15
+ *
16
+ * @param {string} paramName
17
+ * @returns {(value: any) => void}
18
+ */
19
+ allocateSetter(paramName: string): (value: any) => void;
20
+ /**
21
+ * Parse expr and return a function that returns the value of the parameter.
22
+ *
23
+ * @param {string} expr
24
+ */
25
+ createExpression(expr: string): ((x: object) => any) & import("./utils/expression.js").ExpressionProps & {
26
+ addListener: (listener: () => void) => void;
27
+ };
28
+ #private;
29
+ }
30
+ //# sourceMappingURL=paramBroker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paramBroker.d.ts","sourceRoot":"","sources":["../../src/paramBroker.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH;IAyBI;;;;OAIG;IACH,0BAHW,MAAM,WACI,GAAG,KAAK,IAAI,CAqBhC;IAID;;;;OAIG;IACH,uBAFW,MAAM;gCAG6E,MAAM,IAAI,KAAK,IAAI;MA0BhH;;CACJ"}
@@ -0,0 +1,102 @@
1
+ import { isString } from "vega-util";
2
+ import createFunction from "./utils/expression.js";
3
+
4
+ /**
5
+ * A class that manages parameters and expressions. Still a work in progress.
6
+ *
7
+ * TODO: Write tests for this class.
8
+ *
9
+ * This should eventually handle the following:
10
+ * - Parameter registration
11
+ * - Dependency tracking
12
+ * - Calling observers when a parameter changes
13
+ * - Somehow saving parameter "state" (in bookmarks)
14
+ * - Maybe something else
15
+ */
16
+ export default class ParamBroker {
17
+ /** @type {Map<string, any>} */
18
+ #params;
19
+
20
+ /** @type {Set<string>} */
21
+ #allocatedSetters;
22
+
23
+ /** @type {Record<string, any>} */
24
+ #proxy;
25
+
26
+ /** @type {Map<string, Set<() => void>>} */
27
+ #paramListeners;
28
+
29
+ constructor() {
30
+ this.#params = new Map();
31
+ this.#allocatedSetters = new Set();
32
+ this.#paramListeners = new Map();
33
+
34
+ this.#proxy = new Proxy(this.#params, {
35
+ get(target, prop) {
36
+ return isString(prop) ? target.get(prop) : undefined;
37
+ },
38
+ });
39
+ }
40
+
41
+ /**
42
+ *
43
+ * @param {string} paramName
44
+ * @returns {(value: any) => void}
45
+ */
46
+ allocateSetter(paramName) {
47
+ if (this.#allocatedSetters.has(paramName)) {
48
+ throw new Error(
49
+ "Setter already allocated for parameter: " + paramName
50
+ );
51
+ }
52
+
53
+ this.#allocatedSetters.add(paramName);
54
+
55
+ return (value) => {
56
+ this.#params.set(paramName, value);
57
+
58
+ const listeners = this.#paramListeners.get(paramName);
59
+ if (listeners) {
60
+ for (const listener of listeners) {
61
+ listener();
62
+ }
63
+ }
64
+ };
65
+ }
66
+
67
+ // TODO: deallocateSetter
68
+
69
+ /**
70
+ * Parse expr and return a function that returns the value of the parameter.
71
+ *
72
+ * @param {string} expr
73
+ */
74
+ createExpression(expr) {
75
+ /** @type {import("./utils/expression.js").ExpressionFunction & { addListener: (listener: () => void) => void}} */
76
+ const fn = /** @type {any} */ (createFunction(expr, this.#proxy));
77
+
78
+ for (const g of fn.globals) {
79
+ if (!this.#allocatedSetters.has(g)) {
80
+ throw new Error(
81
+ `Unknown variable "${g}" in expression: ${expr}`
82
+ );
83
+ }
84
+ }
85
+
86
+ /**
87
+ *
88
+ * @param {() => void} listener
89
+ */
90
+ fn.addListener = (listener) => {
91
+ for (const g of fn.globals) {
92
+ const listeners = this.#paramListeners.get(g) ?? new Set();
93
+ this.#paramListeners.set(g, listeners);
94
+ listeners.add(listener);
95
+ }
96
+ };
97
+
98
+ // TODO: remove listener
99
+
100
+ return fn;
101
+ }
102
+ }