@genome-spy/core 0.44.0 → 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.
Files changed (96) hide show
  1. package/dist/bundle/index.es.js +6123 -5482
  2. package/dist/bundle/index.js +211 -148
  3. package/dist/schema.json +329 -79
  4. package/dist/src/data/collector.d.ts.map +1 -1
  5. package/dist/src/data/collector.js +4 -2
  6. package/dist/src/data/flowOptimizer.test.js +12 -3
  7. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  8. package/dist/src/data/sources/dataUtils.js +3 -1
  9. package/dist/src/data/sources/lazy/bigBedSource.d.ts +1 -1
  10. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  11. package/dist/src/data/sources/lazy/bigBedSource.js +52 -20
  12. package/dist/src/data/sources/lazy/bigWigSource.d.ts +0 -1
  13. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  14. package/dist/src/data/sources/lazy/bigWigSource.js +30 -4
  15. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  16. package/dist/src/data/sources/lazy/singleAxisLazySource.js +0 -2
  17. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts +6 -2
  18. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  19. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +38 -20
  20. package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
  21. package/dist/src/data/sources/sequenceSource.js +14 -5
  22. package/dist/src/data/sources/sequenceSource.test.js +23 -5
  23. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  24. package/dist/src/data/sources/urlSource.js +15 -2
  25. package/dist/src/data/transforms/aggregate.d.ts.map +1 -1
  26. package/dist/src/data/transforms/aggregate.js +5 -2
  27. package/dist/src/encoder/encoder.d.ts +2 -4
  28. package/dist/src/encoder/encoder.d.ts.map +1 -1
  29. package/dist/src/encoder/encoder.js +8 -8
  30. package/dist/src/encoder/encoder.test.js +3 -0
  31. package/dist/src/genomeSpy.d.ts +7 -5
  32. package/dist/src/genomeSpy.d.ts.map +1 -1
  33. package/dist/src/genomeSpy.js +109 -132
  34. package/dist/src/gl/glslScaleGenerator.js +1 -1
  35. package/dist/src/index.d.ts +1 -1
  36. package/dist/src/index.d.ts.map +1 -1
  37. package/dist/src/index.js +1 -1
  38. package/dist/src/marks/mark.d.ts +8 -5
  39. package/dist/src/marks/mark.d.ts.map +1 -1
  40. package/dist/src/marks/mark.js +56 -12
  41. package/dist/src/marks/point.common.glsl.js +1 -1
  42. package/dist/src/marks/point.d.ts +1 -4
  43. package/dist/src/marks/point.d.ts.map +1 -1
  44. package/dist/src/marks/point.js +31 -23
  45. package/dist/src/marks/point.vertex.glsl.js +1 -1
  46. package/dist/src/marks/text.d.ts.map +1 -1
  47. package/dist/src/marks/text.js +15 -7
  48. package/dist/src/spec/data.d.ts +11 -10
  49. package/dist/src/spec/mark.d.ts +11 -21
  50. package/dist/src/spec/parameter.d.ts +11 -7
  51. package/dist/src/spec/root.d.ts +0 -8
  52. package/dist/src/spec/title.d.ts +5 -4
  53. package/dist/src/spec/view.d.ts +19 -4
  54. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  55. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  56. package/dist/src/styles/genome-spy.css.js +52 -5
  57. package/dist/src/styles/genome-spy.scss +63 -10
  58. package/dist/src/styles/update.sh +6 -0
  59. package/dist/src/tooltip/dataTooltipHandler.js +1 -1
  60. package/dist/src/tooltip/refseqGeneTooltipHandler.js +1 -1
  61. package/dist/src/tooltip/tooltipHandler.d.ts +1 -1
  62. package/dist/src/tooltip/tooltipHandler.d.ts.map +1 -1
  63. package/dist/src/tooltip/tooltipHandler.ts +1 -1
  64. package/dist/src/types/embedApi.d.ts +6 -0
  65. package/dist/src/types/viewContext.d.ts +2 -3
  66. package/dist/src/utils/debounce.d.ts +2 -2
  67. package/dist/src/utils/debounce.d.ts.map +1 -1
  68. package/dist/src/utils/debounce.js +5 -2
  69. package/dist/src/utils/expression.d.ts +2 -2
  70. package/dist/src/utils/expression.d.ts.map +1 -1
  71. package/dist/src/utils/expression.js +1 -1
  72. package/dist/src/utils/formatObject.d.ts +2 -2
  73. package/dist/src/utils/formatObject.d.ts.map +1 -1
  74. package/dist/src/utils/formatObject.js +2 -2
  75. package/dist/src/utils/inputBinding.d.ts +5 -0
  76. package/dist/src/utils/inputBinding.d.ts.map +1 -0
  77. package/dist/src/utils/inputBinding.js +115 -0
  78. package/dist/src/utils/ui/tooltip.js +1 -1
  79. package/dist/src/view/paramMediator.d.ts +108 -0
  80. package/dist/src/view/paramMediator.d.ts.map +1 -0
  81. package/dist/src/view/paramMediator.js +337 -0
  82. package/dist/src/view/paramMediator.test.js +211 -0
  83. package/dist/src/view/scaleResolution.d.ts.map +1 -1
  84. package/dist/src/view/scaleResolution.js +11 -6
  85. package/dist/src/view/view.d.ts +4 -1
  86. package/dist/src/view/view.d.ts.map +1 -1
  87. package/dist/src/view/view.js +19 -5
  88. package/dist/src/view/viewFactory.d.ts.map +1 -1
  89. package/dist/src/view/viewFactory.js +45 -0
  90. package/dist/src/view/viewUtils.d.ts +5 -1
  91. package/dist/src/view/viewUtils.d.ts.map +1 -1
  92. package/dist/src/view/viewUtils.js +9 -4
  93. package/package.json +16 -17
  94. package/dist/src/paramBroker.d.ts +0 -46
  95. package/dist/src/paramBroker.d.ts.map +0 -1
  96. package/dist/src/paramBroker.js +0 -118
@@ -40,7 +40,7 @@ import coalesceProperties from "../utils/propertyCoalescer.js";
40
40
  import { isScalar } from "../utils/variableTools.js";
41
41
  import { InternMap } from "internmap";
42
42
  import ViewError from "../view/viewError.js";
43
- import { isString } from "vega-util";
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";
@@ -221,6 +221,49 @@ export default class Mark {
221
221
  return encoding;
222
222
  }
223
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
+
224
267
  /**
225
268
  * Returns the encoding spec supplemented with mark's default encodings
226
269
  *
@@ -644,10 +687,20 @@ export default class Mark {
644
687
  * @param {(x: Exclude<T, ExprRef>) => any} adjuster
645
688
  */
646
689
  registerMarkUniformValue(uniformName, propValue, adjuster = (x) => x) {
647
- const setter = this.createMarkUniformSetter(uniformName);
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
+ };
648
701
 
649
702
  if (isExprRef(propValue)) {
650
- const fn = this.unitView.context.paramBroker.createExpression(
703
+ const fn = this.unitView.paramMediator.createExpression(
651
704
  propValue.expr
652
705
  );
653
706
 
@@ -1176,12 +1229,3 @@ class RangeMap extends InternMap {
1176
1229
  }
1177
1230
  }
1178
1231
  }
1179
-
1180
- // TODO: Find a better place for this function
1181
- /**
1182
- * @param {any} x
1183
- * @returns {x is import("../spec/parameter.js").ExprRef}
1184
- */
1185
- export function isExprRef(x) {
1186
- return typeof x == "object" && x != null && "expr" in x && isString(x.expr);
1187
- }
@@ -1,2 +1,2 @@
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 lowp float uMaxRelativePointDiameter;uniform mediump float uScaleFactor;uniform mediump float uMaxPointSize;uniform mediump float uZoomLevel;uniform highp float uSemanticThreshold;uniform mediump float uGradientStrength;\n#pragma markUniforms\n};";
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":"AAkBA;IAsGY,oCAMC;IA2CT,mCAQC;IAED;;OAEG;IACH,wDASC;IAED,+BAoBC;CAiDJ;iBA3PgB,WAAW"}
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"}
@@ -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
- setBlockUniforms(this.markUniformInfo, {
147
- uInwardStroke: !!props.inwardStroke,
148
- uGradientStrength: +props.fillGradientStrength,
149
- uMaxRelativePointDiameter: 1 - 2 * props.sampleFacetPadding,
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
  });
@@ -1,2 +1,2 @@
1
- const shader = "out float vRadius;out float vRadiusWithPadding;out lowp vec4 vFillColor;out lowp vec4 vStrokeColor;out lowp float vShape;out lowp float vHalfStrokeWidth;out mat2 vRotationMatrix;const float CIRCLE=0.0;const float SQUARE=1.0;const float CROSS=2.0;const float DIAMOND=3.0;const float TRIANGLE_UP=4.0;const float TRIANGLE_RIGHT=5.0;const float TRIANGLE_DOWN=6.0;const float TRIANGLE_LEFT=7.0;const float TICK_UP=8.0;const float TICK_RIGHT=9.0;const float TICK_DOWN=10.0;const float TICK_LEFT=11.0;float computeSemanticThresholdFactor(){return getScaled_semanticScore()>=uSemanticThreshold ? 1.0 : 0.0;}/***Computes a scaling factor for the points in a sample-faceted view.*/float getDownscaleFactor(vec2 pos){if(!isFacetedSamples()){return 1.0;}float sampleFacetHeight=getSampleFacetHeight(pos);float maxPointDiameter=sqrt(uMaxPointSize);float factor=sampleFacetHeight*uViewportSize.y*uMaxRelativePointDiameter;return clamp(0.0,maxPointDiameter,factor)/maxPointDiameter;}vec2 getDxDy(){\n#if defined(dx_DEFINED) || defined(dy_DEFINED)\nreturn vec2(getScaled_dx(),getScaled_dy())/uViewportSize;\n#else\nreturn vec2(0.0,0.0);\n#endif\n}void main(void){float shapeAngle=0.0;float semanticThresholdFactor=computeSemanticThresholdFactor();if(semanticThresholdFactor<=0.0){gl_PointSize=0.0;gl_Position=vec4(100.0,0.0,0.0,0.0);return;}float size=getScaled_size();vec2 pos=vec2(getScaled_x(),getScaled_y())+getDxDy();gl_Position=unitToNdc(applySampleFacet(pos));float strokeWidth=getScaled_strokeWidth();float diameter=sqrt(size)*uScaleFactor*semanticThresholdFactor*getDownscaleFactor(pos);float opacity=uViewOpacity;if(strokeWidth<=0.0||uInwardStroke){float minDiameter=1.0/uDevicePixelRatio;if(diameter<minDiameter){opacity*=pow(diameter/minDiameter,2.5);diameter=minDiameter;}}float fillOpa=getScaled_fillOpacity()*opacity;float strokeOpa=getScaled_strokeOpacity()*opacity;vShape=getScaled_shape();bool circle=vShape==0.0;if(vShape>TICK_UP&&vShape<=TICK_LEFT){shapeAngle=(vShape-TICK_UP)*90.0;vShape=TICK_UP;}else if(vShape>TRIANGLE_UP&&vShape<=TRIANGLE_LEFT){shapeAngle=(vShape-TRIANGLE_UP)*90.0;vShape=TRIANGLE_UP;}float angleInDegrees=getScaled_angle();float angle=-(shapeAngle+angleInDegrees)*PI/180.0;float sinTheta=sin(angle);float cosTheta=cos(angle);vRotationMatrix=mat2(cosTheta,sinTheta,-sinTheta,cosTheta);float roomForRotation=circle ? 1.0 : sin(mod(angle,PI/2.0)+PI/4.0)/sin(PI/4.0);float aaPadding=1.0/uDevicePixelRatio;float rotationPadding=(diameter*roomForRotation)-diameter;float strokePadding=uInwardStroke ? 0.0 : strokeWidth*(circle ? 1.0 : sqrt(3.0));float padding=rotationPadding+strokePadding+aaPadding;gl_PointSize=(diameter+padding)*uDevicePixelRatio;vRadius=diameter/2.0;vRadiusWithPadding=vRadius+padding/2.0;vHalfStrokeWidth=strokeWidth/2.0;vFillColor=vec4(getScaled_fill()*fillOpa,fillOpa);vStrokeColor=vec4(getScaled_stroke()*strokeOpa,strokeOpa);setupPicking();}";
1
+ const shader = "out float vRadius;out float vRadiusWithPadding;out lowp vec4 vFillColor;out lowp vec4 vStrokeColor;out lowp float vShape;out lowp float vHalfStrokeWidth;out mat2 vRotationMatrix;const float CIRCLE=0.0;const float SQUARE=1.0;const float CROSS=2.0;const float DIAMOND=3.0;const float TRIANGLE_UP=4.0;const float TRIANGLE_RIGHT=5.0;const float TRIANGLE_DOWN=6.0;const float TRIANGLE_LEFT=7.0;const float TICK_UP=8.0;const float TICK_RIGHT=9.0;const float TICK_DOWN=10.0;const float TICK_LEFT=11.0;float computeSemanticThresholdFactor(){return getScaled_semanticScore()>=uSemanticThreshold ? 1.0 : 0.0;}vec2 getDxDy(){\n#if defined(dx_DEFINED) || defined(dy_DEFINED)\nreturn vec2(getScaled_dx(),getScaled_dy())/uViewportSize;\n#else\nreturn vec2(0.0,0.0);\n#endif\n}void main(void){float shapeAngle=0.0;float semanticThresholdFactor=computeSemanticThresholdFactor();if(semanticThresholdFactor<=0.0){gl_PointSize=0.0;gl_Position=vec4(100.0,0.0,0.0,0.0);return;}float size=getScaled_size();vec2 pos=vec2(getScaled_x(),getScaled_y())+getDxDy();gl_Position=unitToNdc(applySampleFacet(pos));float strokeWidth=getScaled_strokeWidth();float diameter=sqrt(size)*uScaleFactor*semanticThresholdFactor;float opacity=uViewOpacity;if(strokeWidth<=0.0||uInwardStroke){float minDiameter=1.0/uDevicePixelRatio;if(diameter<minDiameter){opacity*=pow(diameter/minDiameter,2.5);diameter=minDiameter;}}float fillOpa=getScaled_fillOpacity()*opacity;float strokeOpa=getScaled_strokeOpacity()*opacity;vShape=getScaled_shape();bool circle=vShape==0.0;if(vShape>TICK_UP&&vShape<=TICK_LEFT){shapeAngle=(vShape-TICK_UP)*90.0;vShape=TICK_UP;}else if(vShape>TRIANGLE_UP&&vShape<=TRIANGLE_LEFT){shapeAngle=(vShape-TRIANGLE_UP)*90.0;vShape=TRIANGLE_UP;}float angleInDegrees=getScaled_angle();float angle=-(shapeAngle+angleInDegrees)*PI/180.0;float sinTheta=sin(angle);float cosTheta=cos(angle);vRotationMatrix=mat2(cosTheta,sinTheta,-sinTheta,cosTheta);float roomForRotation=circle ? 1.0 : sin(mod(angle,PI/2.0)+PI/4.0)/sin(PI/4.0);float aaPadding=1.0/uDevicePixelRatio;float rotationPadding=(diameter*roomForRotation)-diameter;float strokePadding=uInwardStroke ? 0.0 : strokeWidth*(circle ? 1.0 : sqrt(3.0));float padding=rotationPadding+strokePadding+aaPadding;gl_PointSize=(diameter+padding)*uDevicePixelRatio;vRadius=diameter/2.0;vRadiusWithPadding=vRadius+padding/2.0;vHalfStrokeWidth=strokeWidth/2.0;vFillColor=vec4(getScaled_fill()*fillOpa,fillOpa);vStrokeColor=vec4(getScaled_stroke()*strokeOpa,strokeOpa);setupPicking();}";
2
2
  export default shader;
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/marks/text.js"],"names":[],"mappings":"AAgCA;;;;;;;GAOG;AACH;IAsDQ,oDAMmD;CAgM1D;iBAvRgB,WAAW"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/marks/text.js"],"names":[],"mappings":"AAgCA;;;;;;;GAOG;AACH;IAyDQ,oDAMmD;CAqM1D;iBA/RgB,WAAW"}
@@ -39,6 +39,9 @@ const baselines = {
39
39
  * - Google's web fonts as SDFs: https://github.com/etiennepinchon/aframe-fonts
40
40
  */
41
41
  export default class TextMark extends Mark {
42
+ /**
43
+ * @typedef {import("../spec/mark.js").MarkProps} MarkProps
44
+ */
42
45
  /**
43
46
  * @param {import("../view/unitView.js").default} unitView
44
47
  */
@@ -99,6 +102,12 @@ export default class TextMark extends Mark {
99
102
  this.properties.fontWeight
100
103
  )
101
104
  : unitView.context.fontManager.getDefaultFont();
105
+
106
+ this.setupExprRefsNeedingGraphicsUpdate([
107
+ "text",
108
+ "fitToBand",
109
+ "logoLetters",
110
+ ]);
102
111
  }
103
112
 
104
113
  getAttributes() {
@@ -176,19 +185,18 @@ export default class TextMark extends Mark {
176
185
  }
177
186
  );
178
187
 
179
- // TODO: Use uniform block.
180
- setBlockUniforms(this.markUniformInfo, {
181
- uPaddingX: props.paddingX,
182
- uPaddingY: props.paddingY,
183
- uFlushX: !!props.flushX,
184
- uFlushY: !!props.flushY,
188
+ this.registerMarkUniformValue("uPaddingX", props.paddingX);
189
+ this.registerMarkUniformValue("uPaddingY", props.paddingY);
190
+ this.registerMarkUniformValue("uFlushX", props.flushX, (x) => !!x);
191
+ this.registerMarkUniformValue("uFlushY", props.flushY, (x) => !!x);
192
+ this.registerMarkUniformValue("uSqueeze", props.squeeze, (x) => !!x);
185
193
 
194
+ setBlockUniforms(this.markUniformInfo, {
186
195
  uAlign: [alignments[props.align], baselines[props.baseline]],
187
196
 
188
197
  uD: [props.dx, -props.dy],
189
198
 
190
199
  uLogoLetter: !!props.logoLetters,
191
- uSqueeze: !!props.squeeze,
192
200
 
193
201
  uViewportEdgeFadeWidth: [
194
202
  props.viewportEdgeFadeWidthTop,
@@ -13,6 +13,7 @@
13
13
  */
14
14
  import { Axis } from "./axis.js";
15
15
  import { FieldName, PrimaryPositionalChannel } from "./channel.js";
16
+ import { ExprRef } from "./parameter.js";
16
17
 
17
18
  export type ParseValue =
18
19
  | null
@@ -123,7 +124,7 @@ export interface UrlData extends DataBase {
123
124
  * An URL or an array of URLs from which to load the data set.
124
125
  * Use the `format.type` property to ensure the loaded data is correctly parsed.
125
126
  */
126
- url: string | string[];
127
+ url: string | string[] | ExprRef;
127
128
  }
128
129
 
129
130
  export interface InlineData extends DataBase {
@@ -169,24 +170,24 @@ export interface SequenceParams {
169
170
  /**
170
171
  * The starting value of the sequence (inclusive).
171
172
  */
172
- start: number;
173
+ start: number | ExprRef;
173
174
  /**
174
175
  * The ending value of the sequence (exclusive).
175
176
  */
176
- stop: number;
177
+ stop: number | ExprRef;
177
178
  /**
178
179
  * The step value between sequence entries.
179
180
  *
180
181
  * __Default value:__ `1`
181
182
  */
182
- step?: number;
183
+ step?: number | ExprRef;
183
184
 
184
185
  /**
185
186
  * The name of the generated sequence field.
186
187
  *
187
188
  * __Default value:__ `"data"`
188
189
  */
189
- as?: FieldName;
190
+ as?: FieldName | ExprRef;
190
191
  }
191
192
 
192
193
  export interface LazyData {
@@ -209,7 +210,7 @@ export interface DebouncedData {
209
210
  *
210
211
  * __Default value:__ `200`
211
212
  */
212
- debounce?: number;
213
+ debounce?: number | ExprRef;
213
214
 
214
215
  /**
215
216
  * The debounce mode for data updates. If set to `"domain"`, domain change
@@ -286,14 +287,14 @@ export interface BigWigData extends DebouncedData {
286
287
  /**
287
288
  * URL of the BigWig file.
288
289
  */
289
- url: string;
290
+ url: string | ExprRef;
290
291
 
291
292
  /**
292
293
  * The approximate minimum width of each data bin, in pixels.
293
294
  *
294
295
  * __Default value:__ `2`
295
296
  */
296
- pixelsPerBin?: number;
297
+ pixelsPerBin?: number | ExprRef;
297
298
  }
298
299
 
299
300
  export interface BigBedData extends DebouncedData {
@@ -309,7 +310,7 @@ export interface BigBedData extends DebouncedData {
309
310
  /**
310
311
  * URL of the BigBed file.
311
312
  */
312
- url: string;
313
+ url: string | ExprRef;
313
314
 
314
315
  /**
315
316
  * Size of each chunk when fetching the BigBed file. Data is only fetched
@@ -317,7 +318,7 @@ export interface BigBedData extends DebouncedData {
317
318
  *
318
319
  * __Default value:__ `1000000`
319
320
  */
320
- windowSize?: number;
321
+ windowSize?: number | ExprRef;
321
322
  }
322
323
 
323
324
  export interface BamData extends DebouncedData {
@@ -162,7 +162,7 @@ export interface TextProps
162
162
  *
163
163
  * **Default value:** `""`
164
164
  */
165
- text?: Scalar;
165
+ text?: Scalar | ExprRef;
166
166
 
167
167
  /**
168
168
  * The font size in pixels.
@@ -234,33 +234,33 @@ export interface TextProps
234
234
  *
235
235
  * **Default value:** `false`
236
236
  */
237
- fitToBand?: boolean;
237
+ fitToBand?: boolean | ExprRef;
238
238
 
239
239
  /**
240
240
  * The horizontal padding, in pixels, when the `x2` channel is used for ranged text.
241
241
  *
242
242
  * **Default value:** `0`
243
243
  */
244
- paddingX?: number;
244
+ paddingX?: number | ExprRef;
245
245
 
246
246
  /**
247
247
  * The vertical padding, in pixels, when the `y2` channel is used for ranged text.
248
248
  *
249
249
  * **Default value:** `0`
250
250
  */
251
- paddingY?: number;
251
+ paddingY?: number | ExprRef;
252
252
 
253
253
  /**
254
254
  * If true, the text is kept inside the viewport when the range of `x` and `x2`
255
255
  * intersect the viewport.
256
256
  */
257
- flushX?: boolean;
257
+ flushX?: boolean | ExprRef;
258
258
 
259
259
  /**
260
260
  * If true, the text is kept inside the viewport when the range of `y` and `y2`
261
261
  * intersect the viewport.
262
262
  */
263
- flushY?: boolean;
263
+ flushY?: boolean | ExprRef;
264
264
 
265
265
  /**
266
266
  * If the `squeeze` property is true and secondary positional channels (`x2` and/or `y2`)
@@ -268,12 +268,12 @@ export interface TextProps
268
268
  *
269
269
  * **Default value:** `true`
270
270
  */
271
- squeeze?: boolean;
271
+ squeeze?: boolean | ExprRef;
272
272
 
273
273
  /**
274
274
  * Stretch letters so that they can be used with [sequence logos](https://en.wikipedia.org/wiki/Sequence_logo), etc...
275
275
  */
276
- logoLetters?: boolean;
276
+ logoLetters?: boolean | ExprRef;
277
277
  }
278
278
 
279
279
  export interface PointProps extends AngleProps {
@@ -292,7 +292,7 @@ export interface PointProps extends AngleProps {
292
292
  *
293
293
  * **Default value:** `false`
294
294
  */
295
- inwardStroke?: boolean;
295
+ inwardStroke?: boolean | ExprRef;
296
296
 
297
297
  /**
298
298
  * Gradient strength controls the amount of the gradient eye-candy effect in the fill color.
@@ -300,24 +300,14 @@ export interface PointProps extends AngleProps {
300
300
  *
301
301
  * **Default value:** `0`
302
302
  */
303
- fillGradientStrength?: number;
304
-
305
- /**
306
- * Padding between sample facet's upper/lower edge and the maximum point size. This property
307
- * controls how tightly points are squeezed when facet's height is smaller than the maximum
308
- * point size. The unit is a proportion of facet's height. The value must be between `0`
309
- * and `0.5`. This property has no effect when sample faceting is not used.
310
- *
311
- * **Default value:** `0.1`
312
- */
313
- sampleFacetPadding?: number;
303
+ fillGradientStrength?: number | ExprRef;
314
304
 
315
305
  /**
316
306
  * TODO
317
307
  *
318
308
  * **Default value:** `0.02`
319
309
  */
320
- semanticZoomFraction?: number;
310
+ semanticZoomFraction?: number | ExprRef;
321
311
 
322
312
  /**
323
313
  * Enables geometric zooming. The value is the base two logarithmic zoom level where the maximum
@@ -23,6 +23,12 @@ export interface VariableParameter {
23
23
  */
24
24
  value?: any;
25
25
 
26
+ /**
27
+ * An expression for the value of the parameter. This expression may include other parameters,
28
+ * in which case the parameter will automatically update in response to upstream parameter changes.
29
+ */
30
+ expr?: string;
31
+
26
32
  /**
27
33
  * Binds the parameter to an external input element such as a slider, selection list or radio button group.
28
34
  */
@@ -35,13 +41,6 @@ export interface VariableParameter {
35
41
  export type Element = string;
36
42
 
37
43
  export interface BindBase {
38
- /**
39
- * An optional CSS selector string indicating the parent element to which
40
- * the input element should be added. By default, all input elements are
41
- * added within the parent container of the Vega view.
42
- */
43
- element?: Element;
44
-
45
44
  /**
46
45
  * If defined, delays event handling until the specified milliseconds have
47
46
  * elapsed since the last event was fired.
@@ -54,6 +53,11 @@ export interface BindBase {
54
53
  * label for the bound parameter.
55
54
  */
56
55
  name?: string;
56
+
57
+ /**
58
+ * An optional description or help text that is shown below the input element.
59
+ */
60
+ description?: string;
57
61
  }
58
62
 
59
63
  export interface BindCheckbox extends BindBase {
@@ -14,14 +14,6 @@ interface RootConfig {
14
14
  */
15
15
  background?: string;
16
16
 
17
- /**
18
- * Dynamic variables that parameterize a visualization.
19
- *
20
- * For now, these are only supported in mark properties, i.e.,
21
- * they are not supported in the filter and formula transforms (yet).
22
- */
23
- params?: VariableParameter[];
24
-
25
17
  /**
26
18
  * https://vega.github.io/vega-lite/docs/data.html#datasets
27
19
  */
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { Align, Baseline, FontStyle, FontWeight } from "./font.js";
12
+ import { ExprRef } from "./parameter.js";
12
13
 
13
14
  export type TitleOrient = "none" | "left" | "right" | "top" | "bottom";
14
15
  export type TitleAnchor = null | "start" | "middle" | "end";
@@ -18,7 +19,7 @@ export interface Title {
18
19
  /**
19
20
  * The title text.
20
21
  */
21
- text: string;
22
+ text: string | ExprRef;
22
23
 
23
24
  /**
24
25
  * A mark style property to apply to the title text mark. If not specified, a default style of `"group-title"` is applied.
@@ -54,7 +55,7 @@ export interface Title {
54
55
  /**
55
56
  * Angle in degrees of title and subtitle text.
56
57
  */
57
- angle?: number;
58
+ angle?: number | ExprRef;
58
59
 
59
60
  /**
60
61
  * Vertical text baseline for title and subtitle text. One of `"alphabetic"` (default), `"top"`, `"middle"`, or `"bottom"`.
@@ -75,7 +76,7 @@ export interface Title {
75
76
  /**
76
77
  * Text color for title text.
77
78
  */
78
- color?: string;
79
+ color?: string | ExprRef;
79
80
 
80
81
  /**
81
82
  * Font name for title text.
@@ -87,7 +88,7 @@ export interface Title {
87
88
  *
88
89
  * @minimum 0
89
90
  */
90
- fontSize?: number;
91
+ fontSize?: number | ExprRef;
91
92
 
92
93
  /**
93
94
  * Font style for title text.
@@ -15,6 +15,7 @@ import {
15
15
  import { ExprRef } from "./parameter.js";
16
16
  import { Title } from "./title.js";
17
17
  import { SampleSpec } from "./sampleView.js";
18
+ import { VariableParameter } from "./parameter.js";
18
19
 
19
20
  export interface SizeDef {
20
21
  /** Size in pixels */
@@ -108,6 +109,11 @@ export interface ViewSpecBase extends ResolveSpec {
108
109
  */
109
110
  padding?: PaddingConfig;
110
111
 
112
+ /**
113
+ * Dynamic variables that parameterize a visualization.
114
+ */
115
+ params?: VariableParameter[];
116
+
111
117
  data?: Data;
112
118
  transform?: TransformParams[];
113
119
  encoding?: Encoding;
@@ -212,14 +218,23 @@ export type ViewSpec =
212
218
  | ConcatSpec
213
219
  | SampleSpec;
214
220
 
215
- export interface ImportConfig {
216
- name?: string;
221
+ export interface ImportParams {
217
222
  url?: string;
218
- params?: object;
223
+
224
+ /**
225
+ * Name for the imported view. Overrides the name defined in the imported spec.
226
+ */
227
+ name?: string;
228
+
229
+ /**
230
+ * Dynamic variables that parameterize a visualization. Parameters defined here
231
+ * override the parameters defined in the imported spec.
232
+ */
233
+ params?: VariableParameter[] | Record<string, any>;
219
234
  }
220
235
 
221
236
  export interface ImportSpec {
222
- import: ImportConfig;
237
+ import: ImportParams;
223
238
  }
224
239
 
225
240
  export interface ConcatBase extends ViewSpecBase {
@@ -1,3 +1,3 @@
1
1
  export default css;
2
- declare const css: ".genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy.loading canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy.loading .loading-message .message {\n opacity: 1;\n}\n.genome-spy.loading .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .loading-indicators {\n position: absolute;\n inset: 0;\n user-select: none;\n pointer-events: none;\n}\n.genome-spy .loading-indicators div {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-indicators div > div {\n font-size: 11px;\n transition: opacity 0.2s;\n background: white;\n padding: 2px 5px;\n display: flex;\n border-radius: 3px;\n gap: 0.5em;\n opacity: 0;\n}\n.genome-spy .loading-indicators div > div.loading {\n opacity: 0.5;\n}\n.genome-spy .loading-indicators div > div > * {\n display: block;\n}\n.genome-spy .loading-indicators div > div img {\n width: 1.5em;\n height: 1.5em;\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}";
2
+ declare const css: "\n.genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n display: flex;\n flex-direction: column;\n}\n.genome-spy .canvas-wrapper {\n position: relative;\n flex-grow: 1;\n overflow: hidden;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy .loading > canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy .loading > .loading-message .message {\n opacity: 1;\n}\n.genome-spy .loading > .loading-message .message .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .loading-indicators {\n position: absolute;\n inset: 0;\n user-select: none;\n pointer-events: none;\n}\n.genome-spy .loading-indicators div {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-indicators div > div {\n font-size: 11px;\n transition: opacity 0.2s;\n background: white;\n padding: 2px 5px;\n display: flex;\n border-radius: 3px;\n gap: 0.5em;\n opacity: 0;\n}\n.genome-spy .loading-indicators div > div.loading {\n opacity: 0.5;\n}\n.genome-spy .loading-indicators div > div > * {\n display: block;\n}\n.genome-spy .loading-indicators div > div img {\n width: 1.5em;\n height: 1.5em;\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}\n\n.gs-input-binding {\n display: grid;\n grid-template-columns: max-content max-content;\n column-gap: 1em;\n row-gap: 0.3em;\n justify-items: start;\n}\n.gs-input-binding > select,\n.gs-input-binding > input:not([type=checkbox]) {\n width: 100%;\n}\n.gs-input-binding input[type=range] + span {\n display: inline-block;\n margin-left: 0.3em;\n min-width: 2.2em;\n font-variant-numeric: tabular-nums;\n}\n.gs-input-binding input[type=range],\n.gs-input-binding input[type=radio] {\n vertical-align: text-bottom;\n}\n.gs-input-binding .radio-group {\n display: flex;\n align-items: center;\n}\n.gs-input-binding .description {\n max-width: 26em;\n grid-column: 1/-1;\n color: #777;\n font-size: 90%;\n margin-top: -0.5em;\n}\n\n.gs-input-bindings {\n flex-basis: content;\n font-size: 14px;\n padding: 10px;\n}\n";
3
3
  //# sourceMappingURL=genome-spy.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,0jGA4IG"}
1
+ {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,6iIA4LE"}