@genome-spy/core 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/index.js +46 -119
  2. package/dist/schema.json +213 -25
  3. package/package.json +4 -3
  4. package/src/data/collector.test.js +2 -0
  5. package/src/data/dataFlow.test.js +2 -0
  6. package/src/data/flow.test.js +1 -0
  7. package/src/data/flowNode.test.js +1 -0
  8. package/src/data/flowOptimizer.test.js +1 -0
  9. package/src/data/formats/fasta.test.js +1 -0
  10. package/src/data/sources/inlineSource.test.js +1 -0
  11. package/src/data/sources/sequenceSource.test.js +1 -0
  12. package/src/data/transforms/clone.test.js +1 -0
  13. package/src/data/transforms/coverage.test.js +1 -0
  14. package/src/data/transforms/filter.test.js +1 -0
  15. package/src/data/transforms/flattenDelimited.test.js +1 -0
  16. package/src/data/transforms/flattenSequence.test.js +1 -0
  17. package/src/data/transforms/formula.test.js +1 -0
  18. package/src/data/transforms/identifier.test.js +1 -0
  19. package/src/data/transforms/pileup.test.js +1 -0
  20. package/src/data/transforms/project.test.js +1 -0
  21. package/src/data/transforms/regexExtract.test.js +1 -0
  22. package/src/data/transforms/regexFold.test.js +1 -0
  23. package/src/data/transforms/sample.test.js +1 -0
  24. package/src/data/transforms/stack.test.js +1 -0
  25. package/src/encoder/accessor.test.js +1 -0
  26. package/src/encoder/encoder.test.js +1 -0
  27. package/src/genome/genome.test.js +1 -0
  28. package/src/genome/scaleIndex.js +3 -2
  29. package/src/genome/scaleIndex.test.js +23 -6
  30. package/src/genome/scaleLocus.test.js +1 -0
  31. package/src/genomeSpy.js +16 -11
  32. package/src/gl/dataToVertices.js +52 -52
  33. package/src/gl/includes/common.glsl +12 -12
  34. package/src/gl/includes/picking.fragment.glsl +0 -2
  35. package/src/gl/includes/picking.vertex.glsl +0 -2
  36. package/src/gl/includes/scales.glsl +33 -2
  37. package/src/gl/point.vertex.glsl +0 -2
  38. package/src/gl/rule.vertex.glsl +1 -1
  39. package/src/gl/webGLHelper.js +0 -3
  40. package/src/marks/link.js +32 -39
  41. package/src/marks/mark.js +176 -106
  42. package/src/marks/pointMark.js +28 -59
  43. package/src/marks/rectMark.js +38 -33
  44. package/src/marks/rule.js +31 -21
  45. package/src/marks/text.js +18 -14
  46. package/src/scale/glslScaleGenerator.js +56 -17
  47. package/src/scale/scale.test.js +1 -0
  48. package/src/scale/ticks.test.js +1 -0
  49. package/src/spec/mark.d.ts +0 -3
  50. package/src/spec/scale.d.ts +0 -9
  51. package/src/spec/title.d.ts +102 -0
  52. package/src/spec/view.d.ts +6 -4
  53. package/src/tooltip/dataTooltipHandler.js +3 -2
  54. package/src/utils/addBaseUrl.test.js +1 -0
  55. package/src/utils/binnedIndex.js +147 -0
  56. package/src/utils/binnedIndex.test.js +73 -0
  57. package/src/utils/cloner.test.js +1 -0
  58. package/src/utils/coalesce.test.js +1 -0
  59. package/src/utils/concatIterables.test.js +1 -0
  60. package/src/utils/domainArray.test.js +1 -0
  61. package/src/utils/indexer.test.js +1 -0
  62. package/src/utils/iterateNestedMaps.test.js +1 -0
  63. package/src/utils/kWayMerge.test.js +1 -0
  64. package/src/utils/layout/flexLayout.js +35 -3
  65. package/src/utils/layout/flexLayout.test.js +15 -0
  66. package/src/utils/layout/grid.js +95 -0
  67. package/src/utils/layout/grid.test.js +71 -0
  68. package/src/utils/layout/padding.js +13 -0
  69. package/src/utils/layout/rectangle.js +6 -0
  70. package/src/utils/layout/rectangle.test.js +1 -0
  71. package/src/utils/mergeObjects.test.js +1 -0
  72. package/src/utils/numberExtractor.test.js +1 -0
  73. package/src/utils/propertyCacher.test.js +1 -0
  74. package/src/utils/propertyCoalescer.test.js +1 -0
  75. package/src/utils/reservationMap.test.js +1 -0
  76. package/src/utils/topK.test.js +1 -0
  77. package/src/utils/variableTools.test.js +1 -0
  78. package/src/view/axisResolution.test.js +1 -0
  79. package/src/view/axisView.js +3 -5
  80. package/src/view/concatView.js +24 -275
  81. package/src/view/flowBuilder.test.js +1 -0
  82. package/src/view/gridView.js +774 -0
  83. package/src/view/implicitRootView.js +14 -0
  84. package/src/view/layerView.js +15 -1
  85. package/src/view/renderingContext/deferredViewRenderingContext.js +3 -1
  86. package/src/view/renderingContext/simpleViewRenderingContext.js +3 -1
  87. package/src/view/scaleResolution.js +5 -11
  88. package/src/view/scaleResolution.test.js +1 -0
  89. package/src/view/title.js +165 -0
  90. package/src/view/unitView.js +9 -5
  91. package/src/view/view.js +35 -14
  92. package/src/view/view.test.js +1 -0
  93. package/src/view/viewContext.d.ts +6 -1
  94. package/src/view/viewFactory.test.js +1 -0
  95. package/src/view/viewUtils.js +1 -93
  96. package/src/view/zoom.js +89 -0
  97. package/src/gl/includes/fp64-arithmetic.glsl +0 -187
  98. package/src/gl/includes/fp64-utils.js +0 -142
  99. package/src/gl/includes/scales_fp64.glsl +0 -30
  100. package/src/utils/binnedRangeIndex.js +0 -83
  101. package/src/view/decoratorView.js +0 -513
package/src/marks/mark.js CHANGED
@@ -4,11 +4,11 @@ import {
4
4
  createUniformBlockInfo,
5
5
  createVertexArrayInfo,
6
6
  setAttribInfoBufferFromArray,
7
+ setBlockUniforms,
7
8
  setUniformBlock,
8
9
  setUniforms,
9
10
  } from "twgl.js";
10
- import { isDiscrete } from "vega-scale";
11
- import { fp64ify } from "../gl/includes/fp64-utils";
11
+ import { isContinuous, isDiscrete } from "vega-scale";
12
12
  import createEncoders, {
13
13
  isChannelDefWithScale,
14
14
  isDatumDef,
@@ -20,11 +20,12 @@ import {
20
20
  generateScaleGlsl,
21
21
  RANGE_TEXTURE_PREFIX,
22
22
  ATTRIBUTE_PREFIX,
23
+ isHighPrecisionScale,
24
+ toHighPrecisionDomainUniform,
25
+ splitHighPrecision,
23
26
  } from "../scale/glslScaleGenerator";
24
- import FP64 from "../gl/includes/fp64-arithmetic.glsl";
25
27
  import GLSL_COMMON from "../gl/includes/common.glsl";
26
28
  import GLSL_SCALES from "../gl/includes/scales.glsl";
27
- import GLSL_SCALES_FP64 from "../gl/includes/scales_fp64.glsl";
28
29
  import GLSL_SAMPLE_FACET from "../gl/includes/sampleFacet.glsl";
29
30
  import GLSL_PICKING_VERTEX from "../gl/includes/picking.vertex.glsl";
30
31
  import GLSL_PICKING_FRAGMENT from "../gl/includes/picking.fragment.glsl";
@@ -32,6 +33,7 @@ import { getCachedOrCall } from "../utils/propertyCacher";
32
33
  import { createProgram } from "../gl/webGLHelper";
33
34
  import coalesceProperties from "../utils/propertyCoalescer";
34
35
  import { isScalar } from "../utils/variableTools";
36
+ import { InternMap } from "internmap";
35
37
 
36
38
  export const SAMPLE_FACET_UNIFORM = "SAMPLE_FACET_UNIFORM";
37
39
  export const SAMPLE_FACET_TEXTURE = "SAMPLE_FACET_TEXTURE";
@@ -77,6 +79,12 @@ export default class Mark {
77
79
  /** @type {import("twgl.js").UniformBlockInfo} WebGL buffers */
78
80
  this.domainUniformInfo = undefined;
79
81
 
82
+ /** @type {import("twgl.js").UniformBlockInfo} WebGL buffers */
83
+ this.viewUniformInfo = undefined;
84
+
85
+ /** @type {RangeMap<any>} keep track of facet locations within the vertex array */
86
+ this.rangeMap = new RangeMap();
87
+
80
88
  // TODO: Implement https://vega.github.io/vega-lite/docs/config.html
81
89
  /** @type {MarkConfig} */
82
90
  this.defaultProperties = {
@@ -334,7 +342,10 @@ export default class Mark {
334
342
  "};\n\n"
335
343
  : "";
336
344
 
345
+ const vertexPrecision = "precision highp float;\n";
346
+
337
347
  const vertexParts = [
348
+ vertexPrecision,
338
349
  ...extraHeaders,
339
350
  GLSL_COMMON,
340
351
  GLSL_SCALES,
@@ -345,11 +356,6 @@ export default class Mark {
345
356
  vertexShader,
346
357
  ];
347
358
 
348
- if (vertexParts.some((code) => /[Ff]p64/.test(code))) {
349
- vertexParts.unshift(GLSL_SCALES_FP64);
350
- vertexParts.unshift(FP64);
351
- }
352
-
353
359
  const fragmentParts = [
354
360
  ...extraHeaders,
355
361
  GLSL_COMMON,
@@ -401,9 +407,21 @@ export default class Mark {
401
407
  );
402
408
  }
403
409
 
410
+ this.viewUniformInfo = createUniformBlockInfo(
411
+ this.gl,
412
+ this.programInfo,
413
+ "View"
414
+ );
415
+
404
416
  this.gl.useProgram(this.programInfo.program);
405
417
 
406
418
  this._setDatums();
419
+
420
+ setUniforms(this.programInfo, {
421
+ // left pos, left height, right pos, right height
422
+ uSampleFacet: [0, 1, 0, 1],
423
+ uTransitionOffset: 0.0,
424
+ });
407
425
  }
408
426
 
409
427
  _setDatums() {
@@ -413,8 +431,8 @@ export default class Mark {
413
431
 
414
432
  const datum = encoder.indexer
415
433
  ? encoder.indexer(channelDef.datum)
416
- : encoder.scale.fp64
417
- ? fp64ify(+channelDef.datum)
434
+ : isHighPrecisionScale(encoder.scale.type)
435
+ ? splitHighPrecision(+channelDef.datum)
418
436
  : +channelDef.datum;
419
437
 
420
438
  setUniforms(this.programInfo, {
@@ -537,21 +555,27 @@ export default class Mark {
537
555
  * and scales) and buffers.
538
556
  *
539
557
  * @param {import("../view/rendering").GlobalRenderingOptions} options
558
+ * @returns {(() => void)[]}
540
559
  */
541
560
  // eslint-disable-next-line complexity
542
561
  prepareRender(options) {
543
562
  const glHelper = this.glHelper;
544
563
  const gl = this.gl;
545
564
 
546
- if (!this.vertexArrayInfo) {
547
- this.vertexArrayInfo = createVertexArrayInfo(
548
- this.gl,
549
- this.programInfo,
550
- this.bufferInfo
551
- );
552
- }
565
+ /** @type {(() => void)[]} */
566
+ const ops = [];
553
567
 
554
- gl.useProgram(this.programInfo.program);
568
+ ops.push(() => {
569
+ if (!this.vertexArrayInfo) {
570
+ this.vertexArrayInfo = createVertexArrayInfo(
571
+ this.gl,
572
+ this.programInfo,
573
+ this.bufferInfo
574
+ );
575
+ }
576
+
577
+ gl.useProgram(this.programInfo.program);
578
+ });
555
579
 
556
580
  if (this.domainUniformInfo) {
557
581
  // TODO: Only update the domains that have changed
@@ -574,19 +598,24 @@ export default class Mark {
574
598
 
575
599
  if (resolution) {
576
600
  const scale = resolution.getScale();
577
- const domain = isDiscrete(scale.type)
578
- ? [0, scale.domain().length]
579
- : scale.domain();
580
-
581
- setter(
582
- scale.fp64
583
- ? domain.map((x) => fp64ify(x)).flat()
584
- : domain
585
- );
601
+
602
+ ops.push(() => {
603
+ const domain = isDiscrete(scale.type)
604
+ ? [0, scale.domain().length]
605
+ : scale.domain();
606
+
607
+ setter(
608
+ isHighPrecisionScale(scale.type)
609
+ ? toHighPrecisionDomainUniform(domain)
610
+ : domain
611
+ );
612
+ });
586
613
  }
587
614
  }
588
615
 
589
- setUniformBlock(gl, this.programInfo, this.domainUniformInfo);
616
+ ops.push(() =>
617
+ setUniformBlock(gl, this.programInfo, this.domainUniformInfo)
618
+ );
590
619
  }
591
620
 
592
621
  for (const [channel, channelDef] of Object.entries(this.encoding)) {
@@ -601,49 +630,56 @@ export default class Mark {
601
630
 
602
631
  const texture = glHelper.rangeTextures.get(resolution);
603
632
  if (texture) {
604
- setUniforms(this.programInfo, {
605
- [RANGE_TEXTURE_PREFIX + channel]: texture,
606
- });
633
+ ops.push(() =>
634
+ setUniforms(this.programInfo, {
635
+ [RANGE_TEXTURE_PREFIX + channel]: texture,
636
+ })
637
+ );
607
638
  }
608
639
  }
609
640
  }
610
641
 
611
642
  if (this.getSampleFacetMode() == SAMPLE_FACET_TEXTURE) {
612
- /** @type {WebGLTexture} */
613
- let facetTexture;
614
- for (const view of this.unitView.getAncestors()) {
615
- facetTexture = view.getSampleFacetTexture();
616
- if (facetTexture) {
617
- break;
643
+ ops.push(() => {
644
+ /** @type {WebGLTexture} */
645
+ let facetTexture;
646
+ for (const view of this.unitView.getAncestors()) {
647
+ facetTexture = view.getSampleFacetTexture();
648
+ if (facetTexture) {
649
+ break;
650
+ }
618
651
  }
619
- }
620
652
 
621
- setUniforms(this.programInfo, {
622
- uSampleFacetTexture: facetTexture,
653
+ if (!facetTexture) {
654
+ throw new Error("No facet texture available. This is bug.");
655
+ }
656
+
657
+ setUniforms(this.programInfo, {
658
+ uSampleFacetTexture: facetTexture,
659
+ });
623
660
  });
624
661
  }
625
662
 
626
- setUniforms(this.programInfo, {
627
- ONE: 1.0, // a hack needed by emulated 64 bit floats
628
- uDevicePixelRatio: this.glHelper.dpr,
629
- uViewOpacity: this.unitView.getEffectiveOpacity(),
630
- // TODO: Rendering of the mark should be completely skipped if it doesn't
631
- // participate picking
632
- uPickingEnabled:
633
- (options.picking ?? false) && this.isPickingParticipant(),
634
- });
635
-
636
- setUniforms(this.programInfo, {
637
- // left pos, left height, right pos, right height
638
- uSampleFacet: [0, 1, 0, 1],
639
- uTransitionOffset: 0.0,
640
- });
663
+ // TODO: Rendering of the mark should be completely skipped if it doesn't
664
+ // participate picking
665
+ const picking =
666
+ (options.picking ?? false) && this.isPickingParticipant();
667
+
668
+ // Note: the block is sent to GPU in setViewport(), which is repeated for each facet
669
+ ops.push(() =>
670
+ setBlockUniforms(this.viewUniformInfo, {
671
+ uViewOpacity: this.unitView.getEffectiveOpacity(),
672
+ uPickingEnabled: picking,
673
+ })
674
+ );
641
675
 
642
676
  if (this.opaque || options.picking) {
643
- gl.disable(gl.BLEND);
677
+ ops.push(() => gl.disable(gl.BLEND));
644
678
  } else {
645
- gl.enable(gl.BLEND);
679
+ ops.push(() => gl.enable(gl.BLEND));
646
680
  }
681
+
682
+ return ops;
647
683
  }
648
684
 
649
685
  /**
@@ -705,58 +741,56 @@ export default class Mark {
705
741
  /**
706
742
  * @param {DrawFunction} draw A function that draws a range of vertices
707
743
  * @param {import("./Mark").MarkRenderingOptions} options
708
- * @param {function():Map<string, import("../gl/dataToVertices").RangeEntry>} rangeMapSource
709
744
  */
710
- createRenderCallback(draw, options, rangeMapSource) {
745
+ createRenderCallback(draw, options) {
711
746
  // eslint-disable-next-line consistent-this
712
747
  const self = this;
713
748
 
714
749
  /** @type {function(import("../gl/dataToVertices").RangeEntry):void} rangeEntry */
715
750
  let drawWithRangeEntry;
716
751
 
717
- if (this.properties.buildIndex) {
718
- const scale = this.unitView.getScaleResolution("x")?.getScale();
719
-
720
- drawWithRangeEntry = (rangeEntry) => {
721
- if (scale && rangeEntry.xIndex) {
722
- const domain = scale.domain();
723
- const vertexIndices = rangeEntry.xIndex(
724
- domain[0],
725
- domain[1]
726
- );
727
- const offset = vertexIndices[0];
728
- const count = vertexIndices[1] - offset;
729
- if (count > 0) {
730
- draw(offset, count);
731
- }
732
- } else {
733
- draw(rangeEntry.offset, rangeEntry.count);
752
+ const scale = this.unitView.getScaleResolution("x")?.getScale();
753
+ const continuous = scale && isContinuous(scale.type);
754
+ const domainStartOffset = ["index", "locus"].includes(scale?.type)
755
+ ? -1
756
+ : 0;
757
+
758
+ /** @type {[number, number]} Recycle to ease garbage collector's work */
759
+ const arr = [0, 0];
760
+
761
+ drawWithRangeEntry = (rangeEntry) => {
762
+ if (continuous && rangeEntry.xIndex) {
763
+ const domain = scale.domain();
764
+ const vertexIndices = rangeEntry.xIndex(
765
+ domain[0] + domainStartOffset,
766
+ domain[1],
767
+ arr
768
+ );
769
+ const offset = vertexIndices[0];
770
+ const count = vertexIndices[1] - offset;
771
+ if (count > 0) {
772
+ draw(offset, count);
734
773
  }
735
- };
736
- } else {
737
- drawWithRangeEntry = (rangeEntry) =>
774
+ } else {
738
775
  draw(rangeEntry.offset, rangeEntry.count);
739
- }
740
-
741
- if (this.properties.dynamicData) {
742
- return function renderDynamic() {
743
- const rangeEntry = rangeMapSource().get(options.facetId);
744
- if (rangeEntry && rangeEntry.count) {
745
- if (self.prepareSampleFacetRendering(options)) {
746
- drawWithRangeEntry(rangeEntry);
747
- }
748
- }
749
- };
750
- } else {
751
- const rangeEntry = rangeMapSource().get(options.facetId);
752
- if (rangeEntry && rangeEntry.count) {
753
- return function renderStatic() {
754
- if (self.prepareSampleFacetRendering(options)) {
755
- drawWithRangeEntry(rangeEntry);
756
- }
757
- };
758
776
  }
759
- }
777
+ };
778
+
779
+ const rangeEntry = this.rangeMap.get(options.facetId);
780
+
781
+ return options.sampleFacetRenderingOptions
782
+ ? function renderSampleFacetRange() {
783
+ if (rangeEntry.count) {
784
+ if (self.prepareSampleFacetRendering(options)) {
785
+ drawWithRangeEntry(rangeEntry);
786
+ }
787
+ }
788
+ }
789
+ : function renderRange() {
790
+ if (rangeEntry.count) {
791
+ drawWithRangeEntry(rangeEntry);
792
+ }
793
+ };
760
794
  }
761
795
 
762
796
  /**
@@ -838,7 +872,7 @@ export default class Mark {
838
872
  uViewScale,
839
873
  };
840
874
  } else {
841
- // Viewport comprises of the full canvas
875
+ // Viewport comprises the full canvas
842
876
  gl.viewport(
843
877
  0,
844
878
  0,
@@ -861,15 +895,15 @@ export default class Mark {
861
895
  };
862
896
  }
863
897
 
864
- // TODO: Optimization: Use uniform buffer object
865
- setUniforms(this.programInfo, uniforms);
866
-
867
- setUniforms(this.programInfo, {
898
+ setBlockUniforms(this.viewUniformInfo, {
899
+ ...uniforms,
868
900
  uViewportSize: [coords.width, coords.height],
901
+ uDevicePixelRatio: this.glHelper.dpr,
869
902
  });
870
903
 
871
- // TODO: Optimize: don't set viewport and stuff if rect is outside clipRect or screen
904
+ setUniformBlock(this.gl, this.programInfo, this.viewUniformInfo);
872
905
 
906
+ // TODO: Optimize: don't set viewport and stuff if rect is outside clipRect or screen
873
907
  return clippedCoords.height > 0 && clippedCoords.width > 0;
874
908
  }
875
909
 
@@ -887,3 +921,39 @@ export default class Mark {
887
921
  // override
888
922
  }
889
923
  }
924
+
925
+ /**
926
+ * @augments {InternMap<K, import("../gl/dataToVertices").RangeEntry>}
927
+ * @template K
928
+ */
929
+ class RangeMap extends InternMap {
930
+ constructor() {
931
+ super([], JSON.stringify);
932
+ }
933
+
934
+ /**
935
+ * @param {K} key
936
+ */
937
+ get(key) {
938
+ let value = super.get(key);
939
+ if (value === undefined) {
940
+ value = {
941
+ offset: 0,
942
+ count: 0,
943
+ xIndex: undefined,
944
+ };
945
+ super.set(key, value);
946
+ }
947
+ return value;
948
+ }
949
+
950
+ /**
951
+ *
952
+ * @param {Map<K, import("../gl/dataToVertices").RangeEntry>} anotherMap
953
+ */
954
+ migrateEntries(anotherMap) {
955
+ for (const [key, value] of anotherMap.entries()) {
956
+ Object.assign(this.get(key), value);
957
+ }
958
+ }
959
+ }
@@ -1,6 +1,5 @@
1
1
  import { drawBufferInfo, setBuffersAndAttributes, setUniforms } from "twgl.js";
2
- import { bisector, quantileSorted } from "d3-array";
3
- import { zoomLinear } from "vega-util";
2
+ import { quantileSorted } from "d3-array";
4
3
  import { PointVertexBuilder } from "../gl/dataToVertices";
5
4
  import VERTEX_SHADER from "../gl/point.vertex.glsl";
6
5
  import FRAGMENT_SHADER from "../gl/point.fragment.glsl";
@@ -156,7 +155,7 @@ export default class PointMark extends Mark {
156
155
  builder.addBatches(collector.facetBatches);
157
156
 
158
157
  const vertexData = builder.toArrays();
159
- this.rangeMap = vertexData.rangeMap;
158
+ this.rangeMap.migrateEntries(vertexData.rangeMap);
160
159
  this.updateBufferInfo(vertexData);
161
160
  }
162
161
 
@@ -210,44 +209,25 @@ export default class PointMark extends Mark {
210
209
  * @param {import("../view/rendering").GlobalRenderingOptions} options
211
210
  */
212
211
  prepareRender(options) {
213
- super.prepareRender(options);
212
+ const ops = super.prepareRender(options);
214
213
 
215
- setUniforms(this.programInfo, {
216
- uMaxPointSize: this._getMaxPointSize(),
217
- uScaleFactor: this._getGeometricScaleFactor(),
218
- uSemanticThreshold: this.getSemanticThreshold(),
219
- });
214
+ ops.push(() =>
215
+ setUniforms(this.programInfo, {
216
+ uMaxPointSize: this._getMaxPointSize(),
217
+ uScaleFactor: this._getGeometricScaleFactor(),
218
+ uSemanticThreshold: this.getSemanticThreshold(),
219
+ })
220
+ );
220
221
 
221
- setBuffersAndAttributes(
222
- this.gl,
223
- this.programInfo,
224
- this.vertexArrayInfo
222
+ ops.push(() =>
223
+ setBuffersAndAttributes(
224
+ this.gl,
225
+ this.programInfo,
226
+ this.vertexArrayInfo
227
+ )
225
228
  );
226
229
 
227
- // Setup bisector that allows for searching the points that reside within the viewport.
228
- const xEncoder = this.encoders.x;
229
- if (xEncoder && !xEncoder.constant) {
230
- const bisect = bisector(xEncoder.accessor).left;
231
- const visibleDomain = this.unitView
232
- .getScaleResolution("x")
233
- .getScale()
234
- .domain();
235
-
236
- // A hack to include points that are just beyond the borders. TODO: Compute based on maxPointSize
237
- const paddedDomain = zoomLinear(visibleDomain, null, 1.01);
238
-
239
- /** @param {any[]} facetId */
240
- this._findIndices = (facetId) => {
241
- const data = this.unitView
242
- .getCollector()
243
- .facetBatches.get(facetId);
244
-
245
- return [
246
- bisect(data, paddedDomain[0]),
247
- bisect(data, paddedDomain[paddedDomain.length - 1]),
248
- ];
249
- };
250
- }
230
+ return ops;
251
231
  }
252
232
 
253
233
  /**
@@ -256,27 +236,16 @@ export default class PointMark extends Mark {
256
236
  render(options) {
257
237
  const gl = this.gl;
258
238
 
259
- return this.createRenderCallback(
260
- (offset, count) => {
261
- // TODO: findIndices is rather slow. Consider a more coarse-grained, "tiled" solution.
262
- const [lower, upper] = this._findIndices
263
- ? this._findIndices(options.facetId)
264
- : [0, count];
265
-
266
- const length = upper - lower;
267
-
268
- if (length) {
269
- drawBufferInfo(
270
- gl,
271
- this.vertexArrayInfo,
272
- gl.POINTS,
273
- length,
274
- offset + lower
275
- );
276
- }
277
- },
278
- options,
279
- () => this.rangeMap
280
- );
239
+ return this.createRenderCallback((offset, count) => {
240
+ if (count) {
241
+ drawBufferInfo(
242
+ gl,
243
+ this.vertexArrayInfo,
244
+ gl.POINTS,
245
+ count,
246
+ offset
247
+ );
248
+ }
249
+ }, options);
281
250
  }
282
251
  }
@@ -146,6 +146,25 @@ export default class RectMark extends Mark {
146
146
  );
147
147
  }
148
148
 
149
+ finalizeGraphicsInitialization() {
150
+ super.finalizeGraphicsInitialization();
151
+
152
+ this.gl.useProgram(this.programInfo.program);
153
+
154
+ const props = this.properties;
155
+
156
+ setUniforms(this.programInfo, {
157
+ uMinSize: [props.minWidth, props.minHeight], // in pixels
158
+ uMinOpacity: props.minOpacity,
159
+ uCornerRadii: [
160
+ props.cornerRadiusTopRight ?? props.cornerRadius,
161
+ props.cornerRadiusBottomRight ?? props.cornerRadius,
162
+ props.cornerRadiusTopLeft ?? props.cornerRadius,
163
+ props.cornerRadiusBottomLeft ?? props.cornerRadius,
164
+ ],
165
+ });
166
+ }
167
+
149
168
  updateGraphicsData() {
150
169
  const collector = this.unitView.getCollector();
151
170
  const numItems = collector.getItemCount();
@@ -155,13 +174,12 @@ export default class RectMark extends Mark {
155
174
  encoders: this.encoders,
156
175
  attributes: this.getAttributes(),
157
176
  numItems,
158
- buildXIndex: this.properties.buildIndex,
159
177
  });
160
178
 
161
179
  builder.addBatches(collector.facetBatches);
162
180
 
163
181
  const vertexData = builder.toArrays();
164
- this.rangeMap = vertexData.rangeMap;
182
+ this.rangeMap.migrateEntries(vertexData.rangeMap);
165
183
  this.updateBufferInfo(vertexData);
166
184
  }
167
185
 
@@ -169,26 +187,17 @@ export default class RectMark extends Mark {
169
187
  * @param {import("../view/rendering").GlobalRenderingOptions} options
170
188
  */
171
189
  prepareRender(options) {
172
- super.prepareRender(options);
190
+ const ops = super.prepareRender(options);
173
191
 
174
- const props = this.properties;
175
-
176
- setUniforms(this.programInfo, {
177
- uMinSize: [props.minWidth, props.minHeight], // in pixels
178
- uMinOpacity: props.minOpacity,
179
- uCornerRadii: [
180
- props.cornerRadiusTopRight ?? props.cornerRadius,
181
- props.cornerRadiusBottomRight ?? props.cornerRadius,
182
- props.cornerRadiusTopLeft ?? props.cornerRadius,
183
- props.cornerRadiusBottomLeft ?? props.cornerRadius,
184
- ],
185
- });
186
-
187
- setBuffersAndAttributes(
188
- this.gl,
189
- this.programInfo,
190
- this.vertexArrayInfo
192
+ ops.push(() =>
193
+ setBuffersAndAttributes(
194
+ this.gl,
195
+ this.programInfo,
196
+ this.vertexArrayInfo
197
+ )
191
198
  );
199
+
200
+ return ops;
192
201
  }
193
202
 
194
203
  /**
@@ -197,19 +206,15 @@ export default class RectMark extends Mark {
197
206
  render(options) {
198
207
  const gl = this.gl;
199
208
 
200
- return this.createRenderCallback(
201
- (offset, count) => {
202
- drawBufferInfo(
203
- gl,
204
- this.vertexArrayInfo,
205
- gl.TRIANGLE_STRIP,
206
- count,
207
- offset
208
- );
209
- },
210
- options,
211
- () => this.rangeMap
212
- );
209
+ return this.createRenderCallback((offset, count) => {
210
+ drawBufferInfo(
211
+ gl,
212
+ this.vertexArrayInfo,
213
+ gl.TRIANGLE_STRIP,
214
+ count,
215
+ offset
216
+ );
217
+ }, options);
213
218
  }
214
219
 
215
220
  /**