@genome-spy/core 0.55.0 → 0.56.1

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/schema.json CHANGED
@@ -4512,6 +4512,10 @@
4512
4512
  ],
4513
4513
  "type": "string"
4514
4514
  },
4515
+ "toggle": {
4516
+ "description": "Controls whether data values should be toggled (inserted or removed from a point selection) when clicking with the shift key pressed.\n\n- `true` -- additional values can be selected by shift-clicking.\n- `false` -- only a single value can be selected at a time.\n\n__Default value:__ `true`",
4517
+ "type": "boolean"
4518
+ },
4515
4519
  "type": {
4516
4520
  "const": "point",
4517
4521
  "description": "Determines the default event processing and data query for the selection. Vega-Lite currently supports two selection types:\n\n- `\"point\"` -- to select multiple discrete data values; the first value is selected on `click` and additional values toggled on shift-click.\n- `\"interval\"` -- to select a continuous range of data values on `drag`.",
@@ -141,6 +141,7 @@ export const SCALE_FUNCTION_PREFIX: "scale_";
141
141
  export const SCALED_FUNCTION_PREFIX: "getScaled_";
142
142
  export const RANGE_TEXTURE_PREFIX: "uRangeTexture_";
143
143
  export const PARAM_PREFIX: "uParam_";
144
+ export const SELECTION_CHECKER_PREFIX: "checkSelection_";
144
145
  export function getRangeForGlsl(scale: any, channel: Channel): number[];
145
146
  export type Channel = import("../spec/channel.js").Channel;
146
147
  export type AccessorParts = {
@@ -1 +1 @@
1
- {"version":3,"file":"glslScaleGenerator.d.ts","sourceRoot":"","sources":["../../../src/gl/glslScaleGenerator.js"],"names":[],"mappings":";AAqDA;;;;;GAKG;AACH,kDAJW,OAAO,mBACP,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,mDALW,OAAO,mBACP,MAAM,SACN,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,GAClC,aAAa,CA4CzB;AAED;;;;;;;GAOG;AACH,4DAJW,OAAO,mBACP,MAAM,GACJ,aAAa,CAkCzB;AAED;;;;;;GAMG;AACH,0CANW,OAAO,SACP,GAAG,mBACH,MAAM,+BACN,OAAO,EAAE,GACP,aAAa,CA+BzB;AACD;;;;;GAKG;AACH,qDALW,OAAO,SACP,GAAG,mBACH,MAAM,GACJ,aAAa,CAyBzB;AAED;;;;;GAKG;AAEH,2CALW,OAAO,SACP,GAAG,cACH,OAAO,oBAAoB,EAAE,UAAU;;;;;;EAwQjD;AAED;;;;GAIG;AACH,wDAHW,OAAO,aACP,OAAO,qBAAqB,EAAE,QAAQ,EAAE,UAuClD;AAmED;;GAEG;AACH,qDAFW,OAAO,6BAQjB;AA+CD;;;;GAIG;AACH,iDAHW,OAAO,qBAAqB,EAAE,SAAS,WACvC,OAAO,oBAAoB,EAAE,OAAO;mBAQjB,MAAM;sBAAoB,sBAAsB,GAAG,sBAAsB,GAAG,uBAAuB;;;;;;EAkBhI;AAED;;;;GAIG;AACH,2CAFW,MAAM,WAIhB;AAED;;;;GAIG;AACH,sCAFW,MAAM,EAAE,WAIlB;AAMD;;;GAGG;AACH,sCAHW,MAAM,QACN,MAAM,EAAE,YAYlB;AAED;;;GAGG;AACH,2CAHW,MAAM,QACN,MAAM,EAAE,YAUlB;AAYD;;GAEG;AACH,qDAFW,MAAM,EAAE,YAIlB;AAED;;GAEG;AAEH;;;;;GAKG;AACH,+CAFW,QAAQ,OAAO,OAAO,oBAAoB,EAAE,OAAO,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CAAC,+DA4BtG;AAED;;GAEG;AACH,2CAFW,OAAO,oBAAoB,EAAE,OAAO,GAAG,OAAO,oBAAoB,EAAE,OAAO,EAAE,UAIvF;AAwBD;;;;GAIG;AACH,uCAJW,MAAM,EAAE,cACR,MAAM,EAAE,GACN,MAAM,CAgClB;AAr1BD,uCAAwC;AACxC,uCAAwC;AACxC,oCAAqC;AACrC,mDAAoD;AACpD,6CAA8C;AAC9C,kDAAmD;AACnD,oDAAqD;AACrD,qCAAsC;AAmyB/B,uCAJI,GAAG,WACH,OAAO,GACL,MAAM,EAAE,CAQF;sBAnyBN,OAAO,oBAAoB,EAAE,OAAO;;aA+BvC,OAAO;kBACP,MAAM;0BACN,MAAM;oBACN,MAAM;oBACN,MAAM;kBACN,MAAM;kBACN,MAAM;eACN,CAAC,CAAC,EAAE,GAAG,KAAK,GAAG;;;;;8BA6fZ,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;uBAgMhD,CAAC,MAAM,EAAE,OAAO,CAAC;0BApvBJ,WAAW"}
1
+ {"version":3,"file":"glslScaleGenerator.d.ts","sourceRoot":"","sources":["../../../src/gl/glslScaleGenerator.js"],"names":[],"mappings":";AAsDA;;;;;GAKG;AACH,kDAJW,OAAO,mBACP,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,mDALW,OAAO,mBACP,MAAM,SACN,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,GAClC,aAAa,CA4CzB;AAED;;;;;;;GAOG;AACH,4DAJW,OAAO,mBACP,MAAM,GACJ,aAAa,CAkCzB;AAED;;;;;;GAMG;AACH,0CANW,OAAO,SACP,GAAG,mBACH,MAAM,+BACN,OAAO,EAAE,GACP,aAAa,CA+BzB;AACD;;;;;GAKG;AACH,qDALW,OAAO,SACP,GAAG,mBACH,MAAM,GACJ,aAAa,CAyBzB;AAED;;;;;GAKG;AAEH,2CALW,OAAO,SACP,GAAG,cACH,OAAO,oBAAoB,EAAE,UAAU;;;;;;EAwQjD;AAED;;;;GAIG;AACH,wDAHW,OAAO,aACP,OAAO,qBAAqB,EAAE,QAAQ,EAAE,UA+BlD;AAmED;;GAEG;AACH,qDAFW,OAAO,6BAQjB;AA+CD;;;;GAIG;AACH,iDAHW,OAAO,qBAAqB,EAAE,SAAS,WACvC,OAAO,oBAAoB,EAAE,OAAO;mBAQjB,MAAM;sBAAoB,sBAAsB,GAAG,sBAAsB,GAAG,uBAAuB;;;;;;EAkBhI;AAED;;;;GAIG;AACH,2CAFW,MAAM,WAIhB;AAED;;;;GAIG;AACH,sCAFW,MAAM,EAAE,WAIlB;AAMD;;;GAGG;AACH,sCAHW,MAAM,QACN,MAAM,EAAE,YAYlB;AAED;;;GAGG;AACH,2CAHW,MAAM,QACN,MAAM,EAAE,YAUlB;AAYD;;GAEG;AACH,qDAFW,MAAM,EAAE,YAIlB;AAED;;GAEG;AAEH;;;;;GAKG;AACH,+CAFW,QAAQ,OAAO,OAAO,oBAAoB,EAAE,OAAO,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CAAC,+DA4BtG;AAED;;GAEG;AACH,2CAFW,OAAO,oBAAoB,EAAE,OAAO,GAAG,OAAO,oBAAoB,EAAE,OAAO,EAAE,UAIvF;AAwBD;;;;GAIG;AACH,uCAJW,MAAM,EAAE,cACR,MAAM,EAAE,GACN,MAAM,CAgClB;AA90BD,uCAAwC;AACxC,uCAAwC;AACxC,oCAAqC;AACrC,mDAAoD;AACpD,6CAA8C;AAC9C,kDAAmD;AACnD,oDAAqD;AACrD,qCAAsC;AACtC,yDAA0D;AA2xBnD,uCAJI,GAAG,WACH,OAAO,GACL,MAAM,EAAE,CAQF;sBA3xBN,OAAO,oBAAoB,EAAE,OAAO;;aA+BvC,OAAO;kBACP,MAAM;0BACN,MAAM;oBACN,MAAM;oBACN,MAAM;kBACN,MAAM;kBACN,MAAM;eACN,CAAC,CAAC,EAAE,GAAG,KAAK,GAAG;;;;;8BAqfZ,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;uBAgMhD,CAAC,MAAM,EAAE,OAAO,CAAC;0BA7uBJ,WAAW"}
@@ -16,7 +16,7 @@ import {
16
16
  } from "../encoder/encoder.js";
17
17
  import { asArray, peek } from "../utils/arrayUtils.js";
18
18
  import { InternMap } from "internmap";
19
- import { isExprRef, validateParameterName } from "../view/paramMediator.js";
19
+ import { isExprRef } from "../view/paramMediator.js";
20
20
  import scaleNull from "../utils/scaleNull.js";
21
21
 
22
22
  export const ATTRIBUTE_PREFIX = "attr_";
@@ -27,6 +27,7 @@ export const SCALE_FUNCTION_PREFIX = "scale_";
27
27
  export const SCALED_FUNCTION_PREFIX = "getScaled_";
28
28
  export const RANGE_TEXTURE_PREFIX = "uRangeTexture_";
29
29
  export const PARAM_PREFIX = "uParam_";
30
+ export const SELECTION_CHECKER_PREFIX = "checkSelection_";
30
31
 
31
32
  // https://stackoverflow.com/a/47543127
32
33
  const FLT_MAX = 3.402823466e38;
@@ -523,16 +524,8 @@ export function generateConditionalEncoderGlsl(channel, accessors) {
523
524
  const accessorFunctionName = makeAccessorFunctionName(channel, i);
524
525
  const { param, empty } = accessor.predicate;
525
526
 
526
- const paramUniform = PARAM_PREFIX + validateParameterName(param);
527
- const idAttribute = ATTRIBUTE_PREFIX + "uniqueId";
528
-
529
- // Hardcoded condition for single point selection ... for now.
530
527
  conditions.push(
531
- param
532
- ? `${idAttribute} == ${paramUniform}${
533
- empty ? ` || ${paramUniform} == uint(0)` : ""
534
- }`
535
- : null
528
+ param ? `${SELECTION_CHECKER_PREFIX}${param}(${!!empty})` : null
536
529
  );
537
530
 
538
531
  statements.push(
@@ -1,2 +1,2 @@
1
- const shader = "#define PI 3.141593\nuniform View{mediump vec2 uViewOffset;mediump vec2 uViewScale;mediump vec2 uViewportSize;lowp float uDevicePixelRatio;lowp float uViewOpacity;bool uPickingEnabled;};/***Maps a coordinate on the unit scale to a normalized device coordinate.*(0,0)is at the bottom left corner.*/vec4 unitToNdc(vec2 coord){return vec4((coord*uViewScale+uViewOffset)*2.0-1.0,0.0,1.0);}vec4 unitToNdc(float x,float y){return unitToNdc(vec2(x,y));}vec4 pixelsToNdc(vec2 coord){return unitToNdc(coord/uViewportSize);}vec4 pixelsToNdc(float x,float y){return pixelsToNdc(vec2(x,y));}float linearstep(float edge0,float edge1,float x){return clamp((x-edge0)/(edge1-edge0),0.0,1.0);}/***Calculates a gamma for antialiasing opacity based on the color.*/float getGammaForColor(vec3 rgb){return mix(1.25,0.75,smoothstep(0.0,1.0,dot(rgb,vec3(0.299,0.587,0.114))));}/***Specialized linearstep for doing antialiasing*/float distanceToRatio(float d){return clamp(d*uDevicePixelRatio+0.5,0.0,1.0);}vec4 distanceToColor(float d,vec4 fill,vec4 stroke,float halfStrokeWidth){if(halfStrokeWidth>0.0){float sd=abs(d)-halfStrokeWidth;return mix(stroke,d<=0.0 ? fill : vec4(0.0),distanceToRatio(sd));}else{return fill*distanceToRatio(-d);}}";
1
+ const shader = "#define PI 3.141593\nuniform View{mediump vec2 uViewOffset;mediump vec2 uViewScale;mediump vec2 uViewportSize;lowp float uDevicePixelRatio;lowp float uViewOpacity;bool uPickingEnabled;};/***Maps a coordinate on the unit scale to a normalized device coordinate.*(0,0)is at the bottom left corner.*/vec4 unitToNdc(vec2 coord){return vec4((coord*uViewScale+uViewOffset)*2.0-1.0,0.0,1.0);}vec4 unitToNdc(float x,float y){return unitToNdc(vec2(x,y));}vec4 pixelsToNdc(vec2 coord){return unitToNdc(coord/uViewportSize);}vec4 pixelsToNdc(float x,float y){return pixelsToNdc(vec2(x,y));}float linearstep(float edge0,float edge1,float x){return clamp((x-edge0)/(edge1-edge0),0.0,1.0);}bool isEmptyBinarySearchTexture(highp usampler2D s){return textureSize(s,0).x==1&&texelFetch(s,ivec2(0,0),0).r==0u;}bool binarySearchTexture(highp usampler2D s,uint value){int texSize=textureSize(s,0).x;int left=0;int right=texSize-1;while(left<=right){int mid=left+(right-left)/2;uint midValue=texelFetch(s,ivec2(mid,0),0).r;if(midValue==value){return true;}if(midValue<value){left=mid+1;}else{right=mid-1;}}return false;}/***Calculates a gamma for antialiasing opacity based on the color.*/float getGammaForColor(vec3 rgb){return mix(1.25,0.75,smoothstep(0.0,1.0,dot(rgb,vec3(0.299,0.587,0.114))));}/***Specialized linearstep for doing antialiasing*/float distanceToRatio(float d){return clamp(d*uDevicePixelRatio+0.5,0.0,1.0);}vec4 distanceToColor(float d,vec4 fill,vec4 stroke,float halfStrokeWidth){if(halfStrokeWidth>0.0){float sd=abs(d)-halfStrokeWidth;return mix(stroke,d<=0.0 ? fill : vec4(0.0),distanceToRatio(sd));}else{return fill*distanceToRatio(-d);}}";
2
2
  export default shader;
@@ -40,6 +40,10 @@ export default class WebGLHelper {
40
40
  _shaderCache: Map<string, WebGLShader>;
41
41
  /** @type {WeakMap<import("../view/scaleResolution.js").default, WebGLTexture>} */
42
42
  rangeTextures: WeakMap<import("../view/scaleResolution.js").default, WebGLTexture>;
43
+ /**
44
+ * @type {WeakMap<import("../types/selectionTypes.js").MultiPointSelection, WebGLTexture>}
45
+ */
46
+ selectionTextures: WeakMap<import("../types/selectionTypes.js").MultiPointSelection, WebGLTexture>;
43
47
  canvas: HTMLCanvasElement;
44
48
  gl: WebGL2RenderingContext;
45
49
  /** @type {import("twgl.js").AttachmentOptions[]} */
@@ -100,5 +104,9 @@ export default class WebGLHelper {
100
104
  * @param {boolean} update Update the texture if it exists already.
101
105
  */
102
106
  createRangeTexture(resolution: import("../view/scaleResolution.js").default, update?: boolean): void;
107
+ /**
108
+ * @param {import("../types/selectionTypes.js").MultiPointSelection} selection
109
+ */
110
+ createSelectionTexture(selection: import("../types/selectionTypes.js").MultiPointSelection, update?: boolean): void;
103
111
  }
104
112
  //# sourceMappingURL=webGLHelper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webGLHelper.d.ts","sourceRoot":"","sources":["../../../src/gl/webGLHelper.js"],"names":[],"mappings":"AAmaA;;;;GAIG;AACH,kCAJW,sBAAsB,gBACtB,WAAW,kBACX,WAAW;;;;;;EA8CrB;AAED;;;;;GAKG;AACH,0CALW,qBAAqB,WACrB,KAAK,OAAO,SAAS,EAAE,cAAc,EAAE,KAAK,CAAC,OAC7C,MAAM,EAAE,GAAG,eAAe,YAC1B,YAAY,gBAYtB;AAxcD;IACI;;;;;;;;OAQG;IACH,uBAPW,WAAW,eACX,MAAM;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,eAGrC,MAAM,2BACN,sBAAsB,EAyFhC;IAjFG,wBAA2B;IAC3B;;;MAKO;IAEP,uCAAuC;IACvC,cADW,IAAI,MAAM,EAAE,WAAW,CAAC,CACN;IAE7B,kFAAkF;IAClF,eADW,QAAQ,OAAO,4BAA4B,EAAE,OAAO,EAAE,YAAY,CAAC,CAC5C;IAwClC,0BAAoB;IACpB,2BAAY;IAGZ,oDAAoD;IACpD,2BADW,OAAO,SAAS,EAAE,iBAAiB,EAAE,CAQ/C;IACD,sDAGC;IAOD,+CAA+C;IAC/C,aADW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CACZ;IAOnC,uBAIC;IAHG;;;MAAmC;IAKvC,mBAEC;IADG,YAAkC;IAGtC;;;;;OAKG;IACH,oBAHW,MAAM,QACN,MAAM,GAAG,MAAM,EAAE,eA2B3B;IAED,iBAcC;IAED,iBAEC;IAED;;;;OAIG;IACH,oCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;MAQ3C;IAED;;OAEG;IACH;;;MAuBC;IAED;;;;OAIG;IACH,oBAHW,MAAM,KACN,MAAM,cAwBhB;IAED,iBAOC;IAED;;;;;;;;;OASG;IACH,+BAHW,OAAO,4BAA4B,EAAE,OAAO,WAC5C,OAAO,QA0GjB;CACJ"}
1
+ {"version":3,"file":"webGLHelper.d.ts","sourceRoot":"","sources":["../../../src/gl/webGLHelper.js"],"names":[],"mappings":"AA2cA;;;;GAIG;AACH,kCAJW,sBAAsB,gBACtB,WAAW,kBACX,WAAW;;;;;;EA8CrB;AAED;;;;;GAKG;AACH,0CALW,qBAAqB,WACrB,KAAK,OAAO,SAAS,EAAE,cAAc,EAAE,KAAK,CAAC,OAC7C,MAAM,EAAE,GAAG,eAAe,YAC1B,YAAY,gBAYtB;AA/eD;IACI;;;;;;;;OAQG;IACH,uBAPW,WAAW,eACX,MAAM;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,eAGrC,MAAM,2BACN,sBAAsB,EA8FhC;IAtFG,wBAA2B;IAC3B;;;MAKO;IAEP,uCAAuC;IACvC,cADW,IAAI,MAAM,EAAE,WAAW,CAAC,CACN;IAE7B,kFAAkF;IAClF,eADW,QAAQ,OAAO,4BAA4B,EAAE,OAAO,EAAE,YAAY,CAAC,CAC5C;IAElC;;OAEG;IACH,mBAFU,QAAQ,OAAO,4BAA4B,EAAE,mBAAmB,EAAE,YAAY,CAAC,CAEnD;IAwCtC,0BAAoB;IACpB,2BAAY;IAGZ,oDAAoD;IACpD,2BADW,OAAO,SAAS,EAAE,iBAAiB,EAAE,CAQ/C;IACD,sDAGC;IAOD,+CAA+C;IAC/C,aADW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CACZ;IAOnC,uBAIC;IAHG;;;MAAmC;IAKvC,mBAEC;IADG,YAAkC;IAGtC;;;;;OAKG;IACH,oBAHW,MAAM,QACN,MAAM,GAAG,MAAM,EAAE,eA2B3B;IAED,iBAcC;IAED,iBAEC;IAED;;;;OAIG;IACH,oCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;MAQ3C;IAED;;OAEG;IACH;;;MAuBC;IAED;;;;OAIG;IACH,oBAHW,MAAM,KACN,MAAM,cAwBhB;IAED,iBAOC;IAED;;;;;;;;;OASG;IACH,+BAHW,OAAO,4BAA4B,EAAE,OAAO,WAC5C,OAAO,QA0GjB;IAED;;OAEG;IACH,kCAFW,OAAO,4BAA4B,EAAE,mBAAmB,0BA+BlE;CACJ"}
@@ -27,6 +27,7 @@ import {
27
27
  isDiscreteChannel,
28
28
  } from "../encoder/encoder.js";
29
29
  import { color } from "d3-color";
30
+ import { isMultiPointSelection } from "../selection/selection.js";
30
31
 
31
32
  export default class WebGLHelper {
32
33
  /**
@@ -58,6 +59,11 @@ export default class WebGLHelper {
58
59
  /** @type {WeakMap<import("../view/scaleResolution.js").default, WebGLTexture>} */
59
60
  this.rangeTextures = new WeakMap();
60
61
 
62
+ /**
63
+ * @type {WeakMap<import("../types/selectionTypes.js").MultiPointSelection, WebGLTexture>}
64
+ */
65
+ this.selectionTextures = new WeakMap();
66
+
61
67
  // --------------------------------------------------------
62
68
 
63
69
  const canvas = document.createElement("canvas");
@@ -384,6 +390,40 @@ export default class WebGLHelper {
384
390
  }
385
391
  }
386
392
  }
393
+
394
+ /**
395
+ * @param {import("../types/selectionTypes.js").MultiPointSelection} selection
396
+ */
397
+ createSelectionTexture(selection, update = true) {
398
+ if (!isMultiPointSelection(selection)) {
399
+ throw new Error(
400
+ "Not a multi-point selection, cannot create texture"
401
+ );
402
+ }
403
+
404
+ const keys = Array.from(selection.data.keys());
405
+ // Zero is a special value for no selection. The minimum texture size is 1.
406
+ const uniqueIds = keys.length > 0 ? keys.sort((a, b) => a - b) : [0];
407
+
408
+ const existingTexture = this.selectionTextures.get(selection);
409
+
410
+ const gl = this.gl;
411
+ const texture = createOrUpdateTexture(
412
+ this.gl,
413
+ {
414
+ level: 0,
415
+ minMag: gl.NEAREST,
416
+ format: gl.RED_INTEGER,
417
+ internalFormat: gl.R32UI,
418
+ height: 1,
419
+ width: uniqueIds.length,
420
+ },
421
+ new Uint32Array(uniqueIds),
422
+ update ? existingTexture : false
423
+ );
424
+
425
+ this.selectionTextures.set(selection, texture);
426
+ }
387
427
  }
388
428
 
389
429
  /**
@@ -1,6 +1,7 @@
1
1
  /// <reference types="external-typings/internmap.js" />
2
2
  export const SAMPLE_FACET_UNIFORM: "SAMPLE_FACET_UNIFORM";
3
3
  export const SAMPLE_FACET_TEXTURE: "SAMPLE_FACET_TEXTURE";
4
+ export const SELECTION_TEXTURE_PREFIX: "uSelectionTexture_";
4
5
  /**
5
6
  *
6
7
  * @typedef {import("../types/rendering.js").RenderingOptions} RenderingOptions
@@ -69,6 +70,13 @@ export default class Mark<P extends import("../spec/mark.js").MarkProps = import
69
70
  * @protected
70
71
  */
71
72
  protected markUniformsAltered: boolean;
73
+ /**
74
+ * Functions that set textures for multi-selection parameters.
75
+ *
76
+ * @type {(() => void)[]}
77
+ * @private
78
+ */
79
+ private selectionTextureOps;
72
80
  /** @type {RangeMap<any>} keep track of facet locations within the vertex array */
73
81
  rangeMap: RangeMap<any>;
74
82
  defaultProperties: P;
@@ -1 +1 @@
1
- {"version":3,"file":"mark.d.ts","sourceRoot":"","sources":["../../../src/marks/mark.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mark.d.ts","sourceRoot":"","sources":["../../../src/marks/mark.js"],"names":[],"mappings":";AAwDA,0DAA2D;AAC3D,0DAA2D;AAE3D,4DAA6D;AAE7D;;;;;;;;;;;GAWG;AAEH;;GAEG;AACH;IAgBI;;OAEG;IACH,sBAFW,OAAO,qBAAqB,EAAE,OAAO,EA4G/C;IAzGG,gDAAwB;IAExB,8EAA8E;IAC9E,uGAAyB;IAIzB;;;OAGG;IACH,sBAHU,OAAO,SAAS,EAAE,UAAU,GAAG;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,CAG5C;IAE3B;;;;;;;OAOG;IACH,2BAHU,IAAI,MAAM,EAAE,MAAM,CAAC,CAGG;IAEhC;;;OAGG;IACH,uBAHU,OAAO,SAAS,EAAE,WAAW,CAGX;IAE5B;;;OAGG;IACH,2BAHU,OAAO,SAAS,EAAE,eAAe,CAGX;IAEhC;;;OAGG;IACH,2BAHU,OAAO,SAAS,EAAE,gBAAgB,CAGZ;IAEhC;;;;;OAKG;IACH,2BAHU,OAAO,SAAS,EAAE,gBAAgB,CAGZ;IAEhC;;;;;OAKG;IACH,uCAA+B;IAE/B;;;;;OAKG;IACH,4BAA6B;IAE7B,kFAAkF;IAClF,UADW,SAAS,GAAG,CAAC,CACM;IAG9B,qBAqBE;IAEF;;;;;;OAMG;IACH,qBAHU,CAAC,CAQV;IAGL;;;OAGG;IACH,0CAHW,QAAQ,CAAC,CAAC,QAQpB;IAED,sBAEC;IAED;;;;;OAKG;IACH,2BAHa,OAAO,oBAAoB,EAAE,OAAO,EAAE,CAMlD;IAED;;OAEG;IACH,+DAWC;IAED;;OAEG;IACH,4DAcC;IAED;;;;;OAKG;IACH,oGAEC;IAED;;;;;;OAMG;IACH,oDAHW,CAAC,MAAM,CAAC,CAAC,EAAE,QAqCrB;IAED;;;;OAIG;IACH,sDAiDC;IAED,wDAEC;IAED,uDAEC;IAED,uBAEC;IAED;;;OAGG;IACH,2BAEC;IAED;;OAEG;IACH,oCAEC;IAED;;OAEG;IACH,2BAEC;IAED,sEAeC;IAED;;;;;;OAMG;IAEH,6CANW,MAAM,kBACN,MAAM,iBACN,MAAM,EAAE,QAiYlB;IALG;;;;;;MAIC;IAGL;;;;;;OAMG;IACH,uCA6CC;IAED;;;;;;OAMG;IACH,+CAHW,MAAM,UACK,GAAG,KAAE,IAAI,CAe9B;IAED;;;;;;;;;;OAUG;IACH,mDAJW,MAAM,sFAEsB,GAAG,QA6BzC;IAED;;OAEG;IACH,2BAwBC;IAED;;;OAGG;IACH,6BAFW,GAAG,QAyCb;IAED,yBAAyB;IACzB,uDAEC;IAED,yBAAyB;IACzB,iCAEC;IAED,gCAEC;IAED,+BAEC;IAED,yCAEC;IAED;;OAEG;IACH,gCAgBC;IAED;;OAEG;IACH,4CAOC;IAED;;;;;;;;OAQG;IAEH,uBAJW,OAAO,uBAAuB,EAAE,sBAAsB,GACpD,CAAC,MAAM,IAAI,CAAC,EAAE,CAwF1B;IAED;;;;;;OAMG;IACH,qCAJW,oBAAoB,GAClB,OAAO,CAqCnB;IAED;;;;;;;OAOG;IACH,gBAJW,oBAAoB,SACP,IAAI,CAM3B;IAED;;;;OAIG;IACH,2BAJW,YAAY,WACZ,OAAO,WAAW,EAAE,oBAAoB,SAC3B,IAAI,CAmE3B;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;+BAl1CY,OAAO,uBAAuB,EAAE,gBAAgB;;;;;;wBAEnD,OAAO;;mCAEJ,gBAAgB,GAAG,qBAAqB;oCAG1C,MAAM,SACN,MAAM;AA40CjB;;;GAGG;AACH;IACI,cAEC;IAkBD;;;OAGG;IACH,2BAFW,IAAI,CAAC,EAAE,OAAO,yBAAyB,EAAE,UAAU,CAAC,QAiB9D;CACJ;0BAj5CyB,WAAW"}
@@ -34,6 +34,7 @@ import {
34
34
  generateConditionalEncoderGlsl,
35
35
  PARAM_PREFIX,
36
36
  ATTRIBUTE_PREFIX,
37
+ SELECTION_CHECKER_PREFIX,
37
38
  } from "../gl/glslScaleGenerator.js";
38
39
  import GLSL_COMMON from "../gl/includes/common.glsl.js";
39
40
  import GLSL_SCALES from "../gl/includes/scales.glsl.js";
@@ -48,11 +49,16 @@ import { InternMap } from "internmap";
48
49
  import ViewError from "../view/viewError.js";
49
50
  import { isExprRef, validateParameterName } from "../view/paramMediator.js";
50
51
  import { UNIQUE_ID_KEY } from "../data/transforms/identifier.js";
51
- import { isSinglePointSelection } from "../selection/selection.js";
52
+ import {
53
+ isMultiPointSelection,
54
+ isSinglePointSelection,
55
+ } from "../selection/selection.js";
52
56
 
53
57
  export const SAMPLE_FACET_UNIFORM = "SAMPLE_FACET_UNIFORM";
54
58
  export const SAMPLE_FACET_TEXTURE = "SAMPLE_FACET_TEXTURE";
55
59
 
60
+ export const SELECTION_TEXTURE_PREFIX = "uSelectionTexture_";
61
+
56
62
  /**
57
63
  *
58
64
  * @typedef {import("../types/rendering.js").RenderingOptions} RenderingOptions
@@ -146,6 +152,14 @@ export default class Mark {
146
152
  */
147
153
  this.markUniformsAltered = true;
148
154
 
155
+ /**
156
+ * Functions that set textures for multi-selection parameters.
157
+ *
158
+ * @type {(() => void)[]}
159
+ * @private
160
+ */
161
+ this.selectionTextureOps = [];
162
+
149
163
  /** @type {RangeMap<any>} keep track of facet locations within the vertex array */
150
164
  this.rangeMap = new RangeMap();
151
165
 
@@ -456,7 +470,8 @@ export default class Mark {
456
470
 
457
471
  for (const predicate of paramPredicates) {
458
472
  const param = predicate.param;
459
- const selection = this.unitView.paramMediator.getValue(param);
473
+ const paramMediator = this.unitView.paramMediator;
474
+ const selection = paramMediator.getValue(param);
460
475
 
461
476
  // The selection is supposed to have an empty value at this point
462
477
  // so that we can figure out the type of the selection.
@@ -466,6 +481,8 @@ export default class Mark {
466
481
  );
467
482
  }
468
483
 
484
+ const uniqueIdAttr = ATTRIBUTE_PREFIX + "uniqueId";
485
+
469
486
  if (isSinglePointSelection(selection)) {
470
487
  // Register a mark uniform for each param. The uniform will have
471
488
  // the value of uniqueId of the selected datum.
@@ -487,13 +504,63 @@ export default class Mark {
487
504
  ) => selection.uniqueId ?? 0
488
505
  );
489
506
  });
507
+ scaleCode.push(
508
+ `bool ${SELECTION_CHECKER_PREFIX}${param}(bool empty) {\n` +
509
+ ` return ${PARAM_PREFIX}${param} == ${uniqueIdAttr} || (empty && ${PARAM_PREFIX}${param} == 0u);\n` +
510
+ `}`
511
+ );
512
+ }
513
+ } else if (isMultiPointSelection(selection)) {
514
+ // We need a texture for each multi-selection parameter.
515
+ // The texture contains the uniqueIds of the selected data objects sorted
516
+ // in ascending order, which allows for binary search.
517
+ if (!selectionParameterUniforms.has(param)) {
518
+ selectionParameterUniforms.set(param, "multi");
519
+
520
+ const uniformName =
521
+ SELECTION_TEXTURE_PREFIX + validateParameterName(param);
522
+ scaleCode.push(
523
+ `// Selection texture\nuniform highp usampler2D ${uniformName};`
524
+ );
525
+
526
+ const glHelper = this.getContext().glHelper;
527
+ const selectionTextures = glHelper.selectionTextures;
528
+
529
+ this.selectionTextureOps.push(() => {
530
+ // Texture is set in the prepareRender method
531
+ const selection = paramMediator.getValue(param);
532
+ const texture = selectionTextures.get(selection);
533
+ if (!texture) {
534
+ throw new Error(
535
+ `Bug: no selection texture found for "${param}"!`
536
+ );
537
+ }
538
+
539
+ setUniforms(this.programInfo, {
540
+ [uniformName]: texture,
541
+ });
542
+ });
543
+
544
+ const texName = SELECTION_TEXTURE_PREFIX + param;
545
+ scaleCode.push(
546
+ `bool ${SELECTION_CHECKER_PREFIX}${param}(bool empty) {\n` +
547
+ ` return binarySearchTexture(${texName}, ${uniqueIdAttr}) || (empty && isEmptyBinarySearchTexture(${texName}));\n` +
548
+ `}`
549
+ );
550
+
551
+ // Create the initial texture
552
+ glHelper.createSelectionTexture(selection);
553
+
554
+ const fn = paramMediator.createExpression(param);
555
+ fn.addListener(() => {
556
+ const selection =
557
+ /** @type {import("../types/selectionTypes.js").MultiPointSelection} */ (
558
+ fn(null)
559
+ );
560
+ glHelper.createSelectionTexture(selection);
561
+ this.getContext().animator.requestRender();
562
+ });
490
563
  }
491
- } else {
492
- throw new Error(
493
- `Unsupported selection (${param}) in condition: ${JSON.stringify(
494
- selection
495
- )}`
496
- );
497
564
  }
498
565
  }
499
566
 
@@ -686,18 +753,16 @@ export default class Mark {
686
753
  }
687
754
 
688
755
  // Generate a function that checks if the datum is subject to any point selection
689
- const conditions = [...selectionParameterUniforms.entries()]
690
- .filter(([, v]) => v == "single")
691
- .map(
692
- ([param]) =>
693
- `${PARAM_PREFIX}${param} == ${ATTRIBUTE_PREFIX}uniqueId`
694
- );
756
+ const conditions = [...selectionParameterUniforms.keys()].map(
757
+ (param) => `${SELECTION_CHECKER_PREFIX}${param}(false)`
758
+ );
759
+
695
760
  scaleCode.push(
696
- "bool isPointSelected() {",
697
- this.encoders.uniqueId && conditions.length > 0
698
- ? ` return ${conditions.join(" || ")};`
699
- : " return false;",
700
- "}"
761
+ "bool isPointSelected() {\n" +
762
+ (this.encoders.uniqueId && conditions.length > 0
763
+ ? ` return ${conditions.join(" || ")};`
764
+ : " return false;") +
765
+ "\n}"
701
766
  );
702
767
 
703
768
  const vertexPrecision = "precision highp float;\nprecision highp int;";
@@ -1046,6 +1111,8 @@ export default class Mark {
1046
1111
  }
1047
1112
  }
1048
1113
 
1114
+ ops.push(...this.selectionTextureOps);
1115
+
1049
1116
  if (this.getSampleFacetMode() == SAMPLE_FACET_TEXTURE) {
1050
1117
  ops.push(() => {
1051
1118
  /** @type {WebGLTexture} */
@@ -1 +1 @@
1
- {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../src/marks/rule.js"],"names":[],"mappings":"AAcA;;GAEG;AACH;IACI;;OAEG;IACH,sBAFW,OAAO,qBAAqB,EAAE,OAAO,EAoB/C;IAfG,wBAAwB;IA4GpB,0BAOE;CAgGb;iBA3OgB,WAAW"}
1
+ {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../src/marks/rule.js"],"names":[],"mappings":"AAcA;;GAEG;AACH;IACI;;OAEG;IACH,sBAFW,OAAO,qBAAqB,EAAE,OAAO,EAoB/C;IAfG,wBAAwB;IAyGxB,0BAQE;CA+FT;iBAxOgB,WAAW"}
@@ -125,21 +125,18 @@ export default class RuleMark extends Mark {
125
125
  async initializeGraphics() {
126
126
  await super.initializeGraphics();
127
127
 
128
- if (this.properties.strokeDash) {
129
- const gl = this.gl;
130
- const textureData = createDashTextureArray(
131
- this.properties.strokeDash
132
- );
133
- this.dashTexture = createTexture(gl, {
134
- mag: gl.NEAREST,
135
- min: gl.NEAREST,
136
- internalFormat: gl.R8,
137
- format: gl.RED,
138
- src: textureData,
139
- height: 1,
140
- });
141
- this.dashTextureSize = textureData.length; // Not needed with WebGL2
142
- }
128
+ const gl = this.gl;
129
+ const textureData = createDashTextureArray(this.properties.strokeDash);
130
+ this.dashTexture = createTexture(gl, {
131
+ level: 0,
132
+ mag: gl.NEAREST,
133
+ min: gl.NEAREST,
134
+ internalFormat: gl.R8,
135
+ format: gl.RED,
136
+ src: textureData,
137
+ height: 1,
138
+ });
139
+ this.dashTextureSize = textureData.length; // Not needed with WebGL2
143
140
 
144
141
  this.createAndLinkShaders(VERTEX_SHADER, FRAGMENT_SHADER, [
145
142
  COMMON_SHADER,
@@ -196,13 +193,13 @@ export default class RuleMark extends Mark {
196
193
 
197
194
  ops.push(() => this.bindOrSetMarkUniformBlock());
198
195
 
199
- if (this.dashTexture) {
200
- ops.push(() =>
201
- setUniforms(this.programInfo, {
202
- uDashTexture: this.dashTexture,
203
- })
204
- );
205
- }
196
+ ops.push(() =>
197
+ // Dash texture must be set always. Otherwise the texture unit may have
198
+ // an incompatible texture from an earlier program.
199
+ setUniforms(this.programInfo, {
200
+ uDashTexture: this.dashTexture,
201
+ })
202
+ );
206
203
 
207
204
  ops.push(() =>
208
205
  setBuffersAndAttributes(
@@ -240,6 +237,10 @@ export default class RuleMark extends Mark {
240
237
  * @param {number[]} pattern
241
238
  */
242
239
  function createDashTextureArray(pattern) {
240
+ if (!pattern) {
241
+ return new Uint8Array(0);
242
+ }
243
+
243
244
  if (
244
245
  pattern.length == 0 ||
245
246
  pattern.length % 2 ||
@@ -3,6 +3,20 @@
3
3
  * @returns {import("../types/selectionTypes.js").SinglePointSelection}
4
4
  */
5
5
  export function createSinglePointSelection(datum: import("../data/flowNode.js").Datum): import("../types/selectionTypes.js").SinglePointSelection;
6
+ /**
7
+ * @param {import("../data/flowNode.js").Datum[]} [data]
8
+ * @returns {import("../types/selectionTypes.js").MultiPointSelection}
9
+ */
10
+ export function createMultiPointSelection(data?: import("../data/flowNode.js").Datum[]): import("../types/selectionTypes.js").MultiPointSelection;
11
+ /**
12
+ * Updates the backing data and returns a new instance of the selection object.
13
+ * A new instance is required to trigger reactivity in parameters.
14
+ *
15
+ * @param {import("../types/selectionTypes.js").MultiPointSelection} selection
16
+ * @param {Partial<Record<"add" | "remove" | "toggle", Iterable<import("../data/flowNode.js").Datum>>>} update
17
+ * @returns {import("../types/selectionTypes.js").MultiPointSelection}
18
+ */
19
+ export function updateMultiPointSelection(selection: import("../types/selectionTypes.js").MultiPointSelection, { add, remove, toggle }: Partial<Record<"add" | "remove" | "toggle", Iterable<import("../data/flowNode.js").Datum>>>): import("../types/selectionTypes.js").MultiPointSelection;
6
20
  /**
7
21
  * @param {import("../types/selectionTypes.js").Selection} selection
8
22
  * @param {import("../data/flowNode.js").Datum} datum
@@ -36,4 +50,14 @@ export function isMultiPointSelection(selection: import("../types/selectionTypes
36
50
  * @returns {selection is import("../types/selectionTypes.js").ProjectedSelection}
37
51
  */
38
52
  export function isProjectedSelection(selection: import("../types/selectionTypes.js").Selection): selection is import("../types/selectionTypes.js").ProjectedSelection;
53
+ /**
54
+ * @param {import("../spec/parameter.js").SelectionTypeOrConfig} typeOrConfig
55
+ * @returns {import("../spec/parameter.js").SelectionConfig}
56
+ */
57
+ export function asSelectionConfig(typeOrConfig: import("../spec/parameter.js").SelectionTypeOrConfig): import("../spec/parameter.js").SelectionConfig;
58
+ /**
59
+ * @param {import("../spec/parameter.js").SelectionConfig} config
60
+ * @returns {config is import("../spec/parameter.js").PointSelectionConfig}
61
+ */
62
+ export function isPointSelectionConfig(config: import("../spec/parameter.js").SelectionConfig): config is import("../spec/parameter.js").PointSelectionConfig;
39
63
  //# sourceMappingURL=selection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../../src/selection/selection.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH,kDAHW,OAAO,qBAAqB,EAAE,KAAK,GACjC,OAAO,4BAA4B,EAAE,oBAAoB,CAQrE;AAED;;;;GAIG;AACH,yCAJW,OAAO,4BAA4B,EAAE,SAAS,SAC9C,OAAO,qBAAqB,EAAE,KAAK,UACnC,OAAO,WAkBjB;AAED;;GAEG;AACH,oDAFW;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,UAM1C;AAED;;;GAGG;AACH,4CAHW,OAAO,4BAA4B,EAAE,SAAS,oEAKxD;AAED;;;GAGG;AACH,kDAHW,OAAO,4BAA4B,EAAE,SAAS,0EAKxD;AAED;;;GAGG;AACH,iDAHW,OAAO,4BAA4B,EAAE,SAAS,yEAKxD;AAED;;;GAGG;AACH,gDAHW,OAAO,4BAA4B,EAAE,SAAS,wEAKxD"}
1
+ {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../../src/selection/selection.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH,kDAHW,OAAO,qBAAqB,EAAE,KAAK,GACjC,OAAO,4BAA4B,EAAE,oBAAoB,CAQrE;AAED;;;GAGG;AACH,iDAHW,OAAO,qBAAqB,EAAE,KAAK,EAAE,GACnC,OAAO,4BAA4B,EAAE,mBAAmB,CAQpE;AAED;;;;;;;GAOG;AACH,qDAJW,OAAO,4BAA4B,EAAE,mBAAmB,2BACxD,QAAQ,OAAO,KAAK,GAAG,QAAQ,GAAG,QAAQ,EAAE,SAAS,OAAO,qBAAqB,EAAE,KAAK,CAAC,CAAC,CAAC,GACzF,OAAO,4BAA4B,EAAE,mBAAmB,CA2BpE;AAED;;;;GAIG;AACH,yCAJW,OAAO,4BAA4B,EAAE,SAAS,SAC9C,OAAO,qBAAqB,EAAE,KAAK,UACnC,OAAO,WAkBjB;AAED;;GAEG;AACH,oDAFW;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,UAM1C;AAED;;;GAGG;AACH,4CAHW,OAAO,4BAA4B,EAAE,SAAS,oEAKxD;AAED;;;GAGG;AACH,kDAHW,OAAO,4BAA4B,EAAE,SAAS,0EAKxD;AAED;;;GAGG;AACH,iDAHW,OAAO,4BAA4B,EAAE,SAAS,yEAKxD;AAED;;;GAGG;AACH,gDAHW,OAAO,4BAA4B,EAAE,SAAS,wEAKxD;AAED;;;GAGG;AACH,gDAHW,OAAO,sBAAsB,EAAE,qBAAqB,GAClD,OAAO,sBAAsB,EAAE,eAAe,CAiB1D;AAED;;;GAGG;AACH,+CAHW,OAAO,sBAAsB,EAAE,eAAe,iEAKxD"}
@@ -13,6 +13,53 @@ export function createSinglePointSelection(datum) {
13
13
  };
14
14
  }
15
15
 
16
+ /**
17
+ * @param {import("../data/flowNode.js").Datum[]} [data]
18
+ * @returns {import("../types/selectionTypes.js").MultiPointSelection}
19
+ */
20
+ export function createMultiPointSelection(data) {
21
+ data ??= [];
22
+ return {
23
+ type: "multi",
24
+ data: new Map(data.map((d) => [d[UNIQUE_ID_KEY], d])),
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Updates the backing data and returns a new instance of the selection object.
30
+ * A new instance is required to trigger reactivity in parameters.
31
+ *
32
+ * @param {import("../types/selectionTypes.js").MultiPointSelection} selection
33
+ * @param {Partial<Record<"add" | "remove" | "toggle", Iterable<import("../data/flowNode.js").Datum>>>} update
34
+ * @returns {import("../types/selectionTypes.js").MultiPointSelection}
35
+ */
36
+ export function updateMultiPointSelection(selection, { add, remove, toggle }) {
37
+ const data = selection.data;
38
+
39
+ for (const d of add ?? []) {
40
+ data.set(d[UNIQUE_ID_KEY], d);
41
+ }
42
+
43
+ for (const d of remove ?? []) {
44
+ data.delete(d[UNIQUE_ID_KEY]);
45
+ }
46
+
47
+ for (const d of toggle ?? []) {
48
+ const id = d[UNIQUE_ID_KEY];
49
+ if (data.has(id)) {
50
+ data.delete(id);
51
+ } else {
52
+ data.set(id, d);
53
+ }
54
+ }
55
+
56
+ return {
57
+ type: "multi",
58
+ // Note, the data map is reused for performance reasons.
59
+ data,
60
+ };
61
+ }
62
+
16
63
  /**
17
64
  * @param {import("../types/selectionTypes.js").Selection} selection
18
65
  * @param {import("../data/flowNode.js").Datum} datum
@@ -28,9 +75,9 @@ export function selectionTest(selection, datum, empty = true) {
28
75
  ? empty
29
76
  : selection.uniqueId === datum[UNIQUE_ID_KEY];
30
77
  } else if (isMultiPointSelection(selection)) {
31
- return selection.uniqueIds.size == 0
78
+ return selection.data.size == 0
32
79
  ? empty
33
- : selection.uniqueIds.has(datum[UNIQUE_ID_KEY]);
80
+ : selection.data.has(datum[UNIQUE_ID_KEY]); // TODO: Binary search
34
81
  } else {
35
82
  throw new Error("Not a selection: " + JSON.stringify(selection));
36
83
  }
@@ -76,3 +123,32 @@ export function isMultiPointSelection(selection) {
76
123
  export function isProjectedSelection(selection) {
77
124
  return selection.type === "projected";
78
125
  }
126
+
127
+ /**
128
+ * @param {import("../spec/parameter.js").SelectionTypeOrConfig} typeOrConfig
129
+ * @returns {import("../spec/parameter.js").SelectionConfig}
130
+ */
131
+ export function asSelectionConfig(typeOrConfig) {
132
+ const config =
133
+ typeof typeOrConfig === "string"
134
+ ? { type: typeOrConfig }
135
+ : typeOrConfig;
136
+
137
+ // Set some default
138
+ if (isPointSelectionConfig(config)) {
139
+ config.on ??= "click";
140
+ if (config.on === "click") {
141
+ config.toggle = true;
142
+ }
143
+ }
144
+
145
+ return config;
146
+ }
147
+
148
+ /**
149
+ * @param {import("../spec/parameter.js").SelectionConfig} config
150
+ * @returns {config is import("../spec/parameter.js").PointSelectionConfig}
151
+ */
152
+ export function isPointSelectionConfig(config) {
153
+ return config && config.type == "point";
154
+ }
@@ -196,6 +196,8 @@ export interface BaseSelectionConfig<T extends SelectionType = SelectionType> {
196
196
  }
197
197
 
198
198
  export interface PointSelectionConfig extends BaseSelectionConfig<"point"> {
199
+ type: "point";
200
+
199
201
  /**
200
202
  * Controls whether data values should be toggled (inserted or removed from a point selection)
201
203
  * when clicking with the shift key pressed.
@@ -205,8 +207,7 @@ export interface PointSelectionConfig extends BaseSelectionConfig<"point"> {
205
207
  *
206
208
  * __Default value:__ `true`
207
209
  */
208
- // TODO TODO TODO TODO TODO TODO TODO TODO
209
- //toggle?: boolean;
210
+ toggle?: boolean;
210
211
  /**
211
212
  * A set of fields that uniquely identify a tuple. Used for bookmarking point selections
212
213
  * in the GenomeSpy App. Still work in progress.
@@ -252,4 +253,7 @@ export interface SelectionParameter<T extends SelectionType = SelectionType>
252
253
  */
253
254
  }
254
255
 
256
+ export type SelectionConfig = PointSelectionConfig | IntervalSelectionConfig;
257
+ export type SelectionTypeOrConfig = SelectionType | SelectionConfig;
258
+
255
259
  export type Parameter = VariableParameter | SelectionParameter;
@@ -33,8 +33,8 @@ export interface SinglePointSelection extends SelectionBase {
33
33
  export interface MultiPointSelection extends SelectionBase {
34
34
  type: "multi";
35
35
 
36
- data: Datum[];
37
- uniqueIds: Set<number>;
36
+ /** Maps unique id to datum */
37
+ data: Map<number, Datum>;
38
38
  }
39
39
 
40
40
  export type Selection =
@@ -1 +1 @@
1
- {"version":3,"file":"paramMediator.d.ts","sourceRoot":"","sources":["../../../src/view/paramMediator.js"],"names":[],"mappings":"AAoUA;;;GAGG;AACH,6BAHW,GAAG,+CAKb;AAED;;;;;;;GAOG;AACH,oFASC;AAED;;;GAGG;AACH,gJAEC;AAED;;;GAGG;AACH,gMAEC;AAED;;;;;;;;;;GAUG;AACH,mFANW,aAAa,+CAEW,IAAI,KAwCtC;AAED;;;;;;GAMG;AACH,4CAHW,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;GAOG;AACH,2CAHW,GAAG,GACD,eAAe,CAW3B;AA9bD;;;;;;;;GAQG;AACH;IA2BI;;;;;OAKG;IACH,2BALW,MAAM,aAAa,EAU7B;IA7BD;;;OAGG;IACH,0BAHU,IAAI,MAAM,EAAE,IAAI,MAAM,IAAI,CAAC,CAAC,CAGvB;IA2Bf;;;OAGG;IACH,wEAzCqB,GAAG,KAAK,IAAI,CAmGhC;IAED;;;;;;;OAOG;IACH,6BANW,MAAM,6BAEN,OAAO,iBACS,IAAI,CAgC9B;IAED;;;OAGG;IACH,qBAFW,MAAM,WAlIc,GAAG,KAAK,IAAI,CA0I1C;IAED;;;OAGG;IACH,oBAFW,MAAM,OAIhB;IAED;;;OAGG;IACH,qBAFW,MAAM,OAKhB;IAED;;OAEG;IACH,kFAIC;IAED;;;;OAIG;IACH,gCAHW,MAAM,GACJ,aAAa,CAQzB;IAID;;;;OAIG;IACH,uBAFW,MAAM,mBA4EhB;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAKhB;IAED;;;;;OAKG;IACH,sBAFa,OAAO,CAiBnB;;CACJ;;;;;;;;8BAvTY,OAAO,wBAAwB,EAAE,kBAAkB,GAAG;IAAE,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,MAAM,CAAA;CAAC"}
1
+ {"version":3,"file":"paramMediator.d.ts","sourceRoot":"","sources":["../../../src/view/paramMediator.js"],"names":[],"mappings":"AA0UA;;;GAGG;AACH,6BAHW,GAAG,+CAKb;AAED;;;;;;;GAOG;AACH,oFASC;AAED;;;GAGG;AACH,gJAEC;AAED;;;GAGG;AACH,gMAEC;AAED;;;;;;;;;;GAUG;AACH,mFANW,aAAa,+CAEW,IAAI,KAwCtC;AAED;;;;;;GAMG;AACH,4CAHW,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;GAOG;AACH,2CAHW,GAAG,GACD,eAAe,CAW3B;AA/bD;;;;;;;;GAQG;AACH;IA2BI;;;;;OAKG;IACH,2BALW,MAAM,aAAa,EAU7B;IA7BD;;;OAGG;IACH,0BAHU,IAAI,MAAM,EAAE,IAAI,MAAM,IAAI,CAAC,CAAC,CAGvB;IA2Bf;;;OAGG;IACH,wEAzCqB,GAAG,KAAK,IAAI,CAoGhC;IAED;;;;;;;OAOG;IACH,6BANW,MAAM,6BAEN,OAAO,iBACS,IAAI,CAgC9B;IAED;;;OAGG;IACH,qBAFW,MAAM,WAnIc,GAAG,KAAK,IAAI,CA2I1C;IAED;;;OAGG;IACH,oBAFW,MAAM,OAIhB;IAED;;;OAGG;IACH,qBAFW,MAAM,OAKhB;IAED;;OAEG;IACH,kFAIC;IAED;;;;OAIG;IACH,gCAHW,MAAM,GACJ,aAAa,CAQzB;IAID;;;;OAIG;IACH,uBAFW,MAAM,mBA4EhB;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAKhB;IAED;;;;;OAKG;IACH,sBAFa,OAAO,CAiBnB;;CACJ;;;;;;;;8BAxTY,OAAO,wBAAwB,EAAE,kBAAkB,GAAG;IAAE,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,MAAM,CAAA;CAAC"}