hyper-scatter 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -47
- package/dist-lib/core/types.d.ts +20 -1
- package/dist-lib/core/types.d.ts.map +1 -1
- package/dist-lib/core/types.js.map +1 -1
- package/dist-lib/core/types3d.d.ts +84 -0
- package/dist-lib/core/types3d.d.ts.map +1 -0
- package/dist-lib/core/types3d.js +21 -0
- package/dist-lib/core/types3d.js.map +1 -0
- package/dist-lib/impl_candidate/webgl_candidate.d.ts +11 -1
- package/dist-lib/impl_candidate/webgl_candidate.d.ts.map +1 -1
- package/dist-lib/impl_candidate/webgl_candidate.js +250 -50
- package/dist-lib/impl_candidate/webgl_candidate.js.map +1 -1
- package/dist-lib/impl_candidate/webgl_candidate_3d.d.ts +147 -0
- package/dist-lib/impl_candidate/webgl_candidate_3d.d.ts.map +1 -0
- package/dist-lib/impl_candidate/webgl_candidate_3d.js +1404 -0
- package/dist-lib/impl_candidate/webgl_candidate_3d.js.map +1 -0
- package/dist-lib/index.d.ts +6 -1
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +29 -0
- package/dist-lib/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* is called, because the demo/harness may re-initialize renderers and we
|
|
40
40
|
* want `init()` to remain side-effect-light.
|
|
41
41
|
*/
|
|
42
|
-
import { DEFAULT_COLORS, SELECTION_COLOR, HOVER_COLOR, createIndicesSelectionResult,
|
|
42
|
+
import { DEFAULT_COLORS, SELECTION_COLOR, HOVER_COLOR, createIndicesSelectionResult, } from '../core/types.js';
|
|
43
43
|
import { createEuclideanView, projectEuclidean, unprojectEuclidean, panEuclidean, zoomEuclidean, } from '../core/math/euclidean.js';
|
|
44
44
|
import { createHyperbolicView, projectPoincare, unprojectPoincare, panPoincare, zoomPoincare, } from '../core/math/poincare.js';
|
|
45
45
|
import { UniformGridIndex, } from './spatial_index.js';
|
|
@@ -463,6 +463,14 @@ class WebGLRendererBase {
|
|
|
463
463
|
pointRadiusCss = 3;
|
|
464
464
|
colors = DEFAULT_COLORS;
|
|
465
465
|
backgroundColor = '#0a0a0a';
|
|
466
|
+
categoryVisibilityMask = new Uint8Array(0);
|
|
467
|
+
hasCategoryVisibilityMask = false;
|
|
468
|
+
categoryAlpha = 1;
|
|
469
|
+
interactionStyle = {
|
|
470
|
+
selectionColor: SELECTION_COLOR,
|
|
471
|
+
hoverColor: HOVER_COLOR,
|
|
472
|
+
hoverFillColor: null,
|
|
473
|
+
};
|
|
466
474
|
// Hyperbolic backdrop styling (Poincaré disk). Neutral grayscale defaults.
|
|
467
475
|
// Override per app via InitOptions as needed.
|
|
468
476
|
poincareDiskFillColor = '#141414';
|
|
@@ -747,6 +755,74 @@ class WebGLRendererBase {
|
|
|
747
755
|
// Dataset changes don't affect the backdrop, but point DPR might have changed.
|
|
748
756
|
this.markBackdropDirty();
|
|
749
757
|
}
|
|
758
|
+
setPalette(colors) {
|
|
759
|
+
this.colors = colors;
|
|
760
|
+
this.paletteDirty = true;
|
|
761
|
+
if (this.gl) {
|
|
762
|
+
this.uploadPaletteUniforms();
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
setCategoryVisibility(mask) {
|
|
766
|
+
if (mask == null) {
|
|
767
|
+
this.categoryVisibilityMask = new Uint8Array(0);
|
|
768
|
+
this.hasCategoryVisibilityMask = false;
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
const n = mask.length >>> 0;
|
|
772
|
+
const next = new Uint8Array(n);
|
|
773
|
+
for (let i = 0; i < n; i++) {
|
|
774
|
+
next[i] = mask[i] ? 1 : 0;
|
|
775
|
+
}
|
|
776
|
+
this.categoryVisibilityMask = next;
|
|
777
|
+
this.hasCategoryVisibilityMask = true;
|
|
778
|
+
}
|
|
779
|
+
if (this.hoveredIndex >= 0 && !this.isPointVisibleByCategory(this.hoveredIndex)) {
|
|
780
|
+
this.hoveredIndex = -1;
|
|
781
|
+
}
|
|
782
|
+
this.paletteDirty = true;
|
|
783
|
+
this.selectionDirty = true;
|
|
784
|
+
this.hoverDirty = true;
|
|
785
|
+
if (this.gl) {
|
|
786
|
+
this.uploadPaletteUniforms();
|
|
787
|
+
this.uploadSelectionToGPU();
|
|
788
|
+
this.uploadHoverToGPU();
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
setCategoryAlpha(alpha) {
|
|
792
|
+
const next = Number.isFinite(alpha) ? Math.max(0, Math.min(1, alpha)) : 1;
|
|
793
|
+
if (Math.abs(next - this.categoryAlpha) <= 1e-12)
|
|
794
|
+
return;
|
|
795
|
+
this.categoryAlpha = next;
|
|
796
|
+
this.paletteDirty = true;
|
|
797
|
+
if (this.gl) {
|
|
798
|
+
this.uploadPaletteUniforms();
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
setInteractionStyle(style) {
|
|
802
|
+
if (typeof style.selectionColor === 'string' && style.selectionColor.length > 0) {
|
|
803
|
+
this.interactionStyle.selectionColor = style.selectionColor;
|
|
804
|
+
}
|
|
805
|
+
if (typeof style.hoverColor === 'string' && style.hoverColor.length > 0) {
|
|
806
|
+
this.interactionStyle.hoverColor = style.hoverColor;
|
|
807
|
+
}
|
|
808
|
+
if (Object.prototype.hasOwnProperty.call(style, 'hoverFillColor')) {
|
|
809
|
+
this.interactionStyle.hoverFillColor = style.hoverFillColor ?? null;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
isCategoryVisible(category) {
|
|
813
|
+
if (!this.hasCategoryVisibilityMask)
|
|
814
|
+
return true;
|
|
815
|
+
const mask = this.categoryVisibilityMask;
|
|
816
|
+
if (category < 0 || category >= mask.length)
|
|
817
|
+
return true;
|
|
818
|
+
return mask[category] !== 0;
|
|
819
|
+
}
|
|
820
|
+
isPointVisibleByCategory(index) {
|
|
821
|
+
const ds = this.dataset;
|
|
822
|
+
if (!ds || index < 0 || index >= ds.n)
|
|
823
|
+
return false;
|
|
824
|
+
return this.isCategoryVisible(ds.labels[index]);
|
|
825
|
+
}
|
|
750
826
|
resize(width, height) {
|
|
751
827
|
this.width = width;
|
|
752
828
|
this.height = height;
|
|
@@ -774,7 +850,12 @@ class WebGLRendererBase {
|
|
|
774
850
|
return this.selection.size <= 200_000 ? new Set(this.selection) : this.selection;
|
|
775
851
|
}
|
|
776
852
|
setHovered(index) {
|
|
777
|
-
this.
|
|
853
|
+
if (index >= 0 && !this.isPointVisibleByCategory(index)) {
|
|
854
|
+
this.hoveredIndex = -1;
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
this.hoveredIndex = index;
|
|
858
|
+
}
|
|
778
859
|
this.hoverDirty = true;
|
|
779
860
|
if (this.gl) {
|
|
780
861
|
this.uploadHoverToGPU();
|
|
@@ -894,16 +975,19 @@ class WebGLRendererBase {
|
|
|
894
975
|
this.paletteBytes[0] = 255;
|
|
895
976
|
this.paletteBytes[1] = 255;
|
|
896
977
|
this.paletteBytes[2] = 255;
|
|
897
|
-
|
|
978
|
+
const visible = this.isCategoryVisible(0);
|
|
979
|
+
this.paletteBytes[3] = visible ? Math.round(255 * this.categoryAlpha) : 0;
|
|
898
980
|
}
|
|
899
981
|
else {
|
|
900
982
|
for (let i = 0; i < size; i++) {
|
|
901
983
|
const [r, g, b, a] = parseHexColorBytes(this.colors[i]);
|
|
984
|
+
const visible = this.isCategoryVisible(i);
|
|
985
|
+
const alpha = visible ? Math.round(a * this.categoryAlpha) : 0;
|
|
902
986
|
const o = i * 4;
|
|
903
987
|
this.paletteBytes[o + 0] = r;
|
|
904
988
|
this.paletteBytes[o + 1] = g;
|
|
905
989
|
this.paletteBytes[o + 2] = b;
|
|
906
|
-
this.paletteBytes[o + 3] =
|
|
990
|
+
this.paletteBytes[o + 3] = alpha;
|
|
907
991
|
}
|
|
908
992
|
}
|
|
909
993
|
if (!this.paletteTex) {
|
|
@@ -959,8 +1043,14 @@ class WebGLRendererBase {
|
|
|
959
1043
|
const idx = this.dataIndex;
|
|
960
1044
|
if (!ds || !idx)
|
|
961
1045
|
return 0;
|
|
962
|
-
if (result.indices)
|
|
963
|
-
|
|
1046
|
+
if (result.indices) {
|
|
1047
|
+
let visibleCount = 0;
|
|
1048
|
+
for (const i of result.indices) {
|
|
1049
|
+
if (this.isPointVisibleByCategory(i))
|
|
1050
|
+
visibleCount++;
|
|
1051
|
+
}
|
|
1052
|
+
return visibleCount;
|
|
1053
|
+
}
|
|
964
1054
|
if (result.kind !== 'geometry' || !result.geometry)
|
|
965
1055
|
return 0;
|
|
966
1056
|
const polygon = result.geometry.coords;
|
|
@@ -1026,6 +1116,8 @@ class WebGLRendererBase {
|
|
|
1026
1116
|
const end = offsets[cell + 1];
|
|
1027
1117
|
for (let k = start; k < end; k++) {
|
|
1028
1118
|
const i = ids[k];
|
|
1119
|
+
if (!this.isCategoryVisible(ds.labels[i]))
|
|
1120
|
+
continue;
|
|
1029
1121
|
const x = positions[i * 2];
|
|
1030
1122
|
const y = positions[i * 2 + 1];
|
|
1031
1123
|
// Tight AABB prefilter (cells overlap bounds).
|
|
@@ -1432,6 +1524,8 @@ class WebGLRendererBase {
|
|
|
1432
1524
|
const lab = new Uint16Array(renderCount);
|
|
1433
1525
|
let k = 0;
|
|
1434
1526
|
for (const i of this.selection) {
|
|
1527
|
+
if (!this.isPointVisibleByCategory(i))
|
|
1528
|
+
continue;
|
|
1435
1529
|
pos[k * 2] = ds.positions[i * 2];
|
|
1436
1530
|
pos[k * 2 + 1] = ds.positions[i * 2 + 1];
|
|
1437
1531
|
lab[k] = ds.labels[i];
|
|
@@ -1461,6 +1555,8 @@ class WebGLRendererBase {
|
|
|
1461
1555
|
const indices = new Uint32Array(renderCount);
|
|
1462
1556
|
let k = 0;
|
|
1463
1557
|
for (const i of this.selection) {
|
|
1558
|
+
if (!this.isPointVisibleByCategory(i))
|
|
1559
|
+
continue;
|
|
1464
1560
|
indices[k++] = i;
|
|
1465
1561
|
if (k >= renderCount)
|
|
1466
1562
|
break;
|
|
@@ -1479,7 +1575,11 @@ class WebGLRendererBase {
|
|
|
1479
1575
|
const ds = this.dataset;
|
|
1480
1576
|
if (!ds || !this.hoverVao || !this.hoverPosBuffer || !this.hoverLabelBuffer)
|
|
1481
1577
|
return;
|
|
1482
|
-
const i = (this.hoveredIndex >= 0 &&
|
|
1578
|
+
const i = (this.hoveredIndex >= 0 &&
|
|
1579
|
+
this.hoveredIndex < ds.n &&
|
|
1580
|
+
this.isPointVisibleByCategory(this.hoveredIndex))
|
|
1581
|
+
? this.hoveredIndex
|
|
1582
|
+
: -1;
|
|
1483
1583
|
const pos = this.hoverPosScratch;
|
|
1484
1584
|
const lab = this.hoverLabScratch;
|
|
1485
1585
|
if (i >= 0) {
|
|
@@ -1502,7 +1602,9 @@ class WebGLRendererBase {
|
|
|
1502
1602
|
this.hoverDirty = false;
|
|
1503
1603
|
return;
|
|
1504
1604
|
}
|
|
1505
|
-
const idx = this.hoveredIndex >= 0
|
|
1605
|
+
const idx = this.hoveredIndex >= 0 && this.isPointVisibleByCategory(this.hoveredIndex)
|
|
1606
|
+
? this.hoveredIndex
|
|
1607
|
+
: 0;
|
|
1506
1608
|
const indices = this.hoverIndexScratch;
|
|
1507
1609
|
indices[0] = idx;
|
|
1508
1610
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.hoverEbo);
|
|
@@ -1645,7 +1747,7 @@ class WebGLRendererBase {
|
|
|
1645
1747
|
fragmentBudget: this.policy.fragmentBudget,
|
|
1646
1748
|
isInteracting,
|
|
1647
1749
|
};
|
|
1648
|
-
// Selection overlay (still into points buffer)
|
|
1750
|
+
// Selection overlay (ring + label-colored fill, still into points buffer)
|
|
1649
1751
|
if (!isInteracting && this.selection.size > 0) {
|
|
1650
1752
|
gl.useProgram(this.programSolid);
|
|
1651
1753
|
this.bindViewUniformsForProgram(this.programSolid);
|
|
@@ -1656,13 +1758,13 @@ class WebGLRendererBase {
|
|
|
1656
1758
|
if (this.uPointRadiusSolid)
|
|
1657
1759
|
gl.uniform1f(this.uPointRadiusSolid, this.pointRadiusCss + 1);
|
|
1658
1760
|
if (this.uSolidColor) {
|
|
1659
|
-
const [r, g, b, a] = parseHexColor(
|
|
1761
|
+
const [r, g, b, a] = parseHexColor(this.interactionStyle.selectionColor);
|
|
1660
1762
|
gl.uniform4f(this.uSolidColor, r, g, b, a);
|
|
1661
1763
|
}
|
|
1662
1764
|
if (this.uSolidRingMode)
|
|
1663
|
-
gl.uniform1i(this.uSolidRingMode,
|
|
1765
|
+
gl.uniform1i(this.uSolidRingMode, 1);
|
|
1664
1766
|
if (this.uSolidRingThicknessPx)
|
|
1665
|
-
gl.uniform1f(this.uSolidRingThicknessPx,
|
|
1767
|
+
gl.uniform1f(this.uSolidRingThicknessPx, 2);
|
|
1666
1768
|
if (this.uSolidPointSizePx)
|
|
1667
1769
|
gl.uniform1f(this.uSolidPointSizePx, (this.pointRadiusCss + 1) * 2 * this.dpr);
|
|
1668
1770
|
if (this.gpuUsesFullDataset) {
|
|
@@ -1675,9 +1777,33 @@ class WebGLRendererBase {
|
|
|
1675
1777
|
gl.drawArrays(gl.POINTS, 0, this.selectionOverlayCount);
|
|
1676
1778
|
gl.bindVertexArray(this.vao);
|
|
1677
1779
|
}
|
|
1780
|
+
const circlePoints = this.pointsCircle;
|
|
1781
|
+
if (circlePoints) {
|
|
1782
|
+
gl.useProgram(circlePoints.program);
|
|
1783
|
+
this.bindViewUniformsForProgram(circlePoints.program);
|
|
1784
|
+
if (this.paletteDirty)
|
|
1785
|
+
this.uploadPaletteUniforms();
|
|
1786
|
+
this.bindPaletteTexture();
|
|
1787
|
+
if (circlePoints.uCssSize)
|
|
1788
|
+
gl.uniform2f(circlePoints.uCssSize, this.width, this.height);
|
|
1789
|
+
if (circlePoints.uDpr)
|
|
1790
|
+
gl.uniform1f(circlePoints.uDpr, this.dpr);
|
|
1791
|
+
if (circlePoints.uPointRadius)
|
|
1792
|
+
gl.uniform1f(circlePoints.uPointRadius, this.pointRadiusCss);
|
|
1793
|
+
if (this.gpuUsesFullDataset) {
|
|
1794
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.selectionEbo);
|
|
1795
|
+
gl.drawElements(gl.POINTS, this.selectionOverlayCount, gl.UNSIGNED_INT, 0);
|
|
1796
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
|
1797
|
+
}
|
|
1798
|
+
else if (this.selectionVao && this.selectionOverlayCount > 0) {
|
|
1799
|
+
gl.bindVertexArray(this.selectionVao);
|
|
1800
|
+
gl.drawArrays(gl.POINTS, 0, this.selectionOverlayCount);
|
|
1801
|
+
gl.bindVertexArray(this.vao);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1678
1804
|
}
|
|
1679
1805
|
// Hover overlay (still into points buffer)
|
|
1680
|
-
if (!isInteracting && this.hoveredIndex >= 0 && this.hoveredIndex < ds.n) {
|
|
1806
|
+
if (!isInteracting && this.hoveredIndex >= 0 && this.hoveredIndex < ds.n && this.isPointVisibleByCategory(this.hoveredIndex)) {
|
|
1681
1807
|
// Ring
|
|
1682
1808
|
gl.useProgram(this.programSolid);
|
|
1683
1809
|
this.bindViewUniformsForProgram(this.programSolid);
|
|
@@ -1690,7 +1816,7 @@ class WebGLRendererBase {
|
|
|
1690
1816
|
if (this.uPointRadiusSolid)
|
|
1691
1817
|
gl.uniform1f(this.uPointRadiusSolid, ringRadius);
|
|
1692
1818
|
if (this.uSolidColor) {
|
|
1693
|
-
const [r, g, b, a] = parseHexColor(
|
|
1819
|
+
const [r, g, b, a] = parseHexColor(this.interactionStyle.hoverColor);
|
|
1694
1820
|
gl.uniform4f(this.uSolidColor, r, g, b, a);
|
|
1695
1821
|
}
|
|
1696
1822
|
if (this.uSolidRingMode)
|
|
@@ -1708,20 +1834,19 @@ class WebGLRendererBase {
|
|
|
1708
1834
|
gl.drawArrays(gl.POINTS, 0, 1);
|
|
1709
1835
|
gl.bindVertexArray(this.vao);
|
|
1710
1836
|
}
|
|
1711
|
-
// Fill pass (selection
|
|
1837
|
+
// Fill pass (selection ring if selected else fill)
|
|
1712
1838
|
const fillRadius = this.pointRadiusCss + 1;
|
|
1713
1839
|
if (this.selection.has(this.hoveredIndex)) {
|
|
1714
|
-
// Solid red
|
|
1715
1840
|
if (this.uPointRadiusSolid)
|
|
1716
1841
|
gl.uniform1f(this.uPointRadiusSolid, fillRadius);
|
|
1717
1842
|
if (this.uSolidColor) {
|
|
1718
|
-
const [r, g, b, a] = parseHexColor(
|
|
1843
|
+
const [r, g, b, a] = parseHexColor(this.interactionStyle.selectionColor);
|
|
1719
1844
|
gl.uniform4f(this.uSolidColor, r, g, b, a);
|
|
1720
1845
|
}
|
|
1721
1846
|
if (this.uSolidRingMode)
|
|
1722
|
-
gl.uniform1i(this.uSolidRingMode,
|
|
1847
|
+
gl.uniform1i(this.uSolidRingMode, 1);
|
|
1723
1848
|
if (this.uSolidRingThicknessPx)
|
|
1724
|
-
gl.uniform1f(this.uSolidRingThicknessPx,
|
|
1849
|
+
gl.uniform1f(this.uSolidRingThicknessPx, 2);
|
|
1725
1850
|
if (this.uSolidPointSizePx)
|
|
1726
1851
|
gl.uniform1f(this.uSolidPointSizePx, fillRadius * 2 * this.dpr);
|
|
1727
1852
|
if (this.gpuUsesFullDataset) {
|
|
@@ -1732,30 +1857,79 @@ class WebGLRendererBase {
|
|
|
1732
1857
|
gl.drawArrays(gl.POINTS, 0, 1);
|
|
1733
1858
|
gl.bindVertexArray(this.vao);
|
|
1734
1859
|
}
|
|
1860
|
+
const circlePoints = this.pointsCircle;
|
|
1861
|
+
if (circlePoints) {
|
|
1862
|
+
gl.useProgram(circlePoints.program);
|
|
1863
|
+
this.bindViewUniformsForProgram(circlePoints.program);
|
|
1864
|
+
if (this.paletteDirty)
|
|
1865
|
+
this.uploadPaletteUniforms();
|
|
1866
|
+
this.bindPaletteTexture();
|
|
1867
|
+
if (circlePoints.uCssSize)
|
|
1868
|
+
gl.uniform2f(circlePoints.uCssSize, this.width, this.height);
|
|
1869
|
+
if (circlePoints.uDpr)
|
|
1870
|
+
gl.uniform1f(circlePoints.uDpr, this.dpr);
|
|
1871
|
+
if (circlePoints.uPointRadius)
|
|
1872
|
+
gl.uniform1f(circlePoints.uPointRadius, this.pointRadiusCss);
|
|
1873
|
+
if (this.gpuUsesFullDataset) {
|
|
1874
|
+
gl.drawElements(gl.POINTS, 1, gl.UNSIGNED_INT, 0);
|
|
1875
|
+
}
|
|
1876
|
+
else if (this.hoverVao) {
|
|
1877
|
+
gl.bindVertexArray(this.hoverVao);
|
|
1878
|
+
gl.drawArrays(gl.POINTS, 0, 1);
|
|
1879
|
+
gl.bindVertexArray(this.vao);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1735
1882
|
}
|
|
1736
1883
|
else {
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1884
|
+
const hoverFillColor = this.interactionStyle.hoverFillColor;
|
|
1885
|
+
if (hoverFillColor) {
|
|
1886
|
+
gl.useProgram(this.programSolid);
|
|
1887
|
+
this.bindViewUniformsForProgram(this.programSolid);
|
|
1888
|
+
if (this.uPointRadiusSolid)
|
|
1889
|
+
gl.uniform1f(this.uPointRadiusSolid, fillRadius);
|
|
1890
|
+
if (this.uSolidColor) {
|
|
1891
|
+
const [r, g, b, a] = parseHexColor(hoverFillColor);
|
|
1892
|
+
gl.uniform4f(this.uSolidColor, r, g, b, a);
|
|
1893
|
+
}
|
|
1894
|
+
if (this.uSolidRingMode)
|
|
1895
|
+
gl.uniform1i(this.uSolidRingMode, 0);
|
|
1896
|
+
if (this.uSolidRingThicknessPx)
|
|
1897
|
+
gl.uniform1f(this.uSolidRingThicknessPx, 0);
|
|
1898
|
+
if (this.uSolidPointSizePx)
|
|
1899
|
+
gl.uniform1f(this.uSolidPointSizePx, fillRadius * 2 * this.dpr);
|
|
1900
|
+
if (this.gpuUsesFullDataset) {
|
|
1901
|
+
gl.drawElements(gl.POINTS, 1, gl.UNSIGNED_INT, 0);
|
|
1902
|
+
}
|
|
1903
|
+
else if (this.hoverVao) {
|
|
1904
|
+
gl.bindVertexArray(this.hoverVao);
|
|
1905
|
+
gl.drawArrays(gl.POINTS, 0, 1);
|
|
1906
|
+
gl.bindVertexArray(this.vao);
|
|
1907
|
+
}
|
|
1754
1908
|
}
|
|
1755
|
-
else
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1909
|
+
else {
|
|
1910
|
+
// Use palette program for category color when no hover fill override is set.
|
|
1911
|
+
const circlePoints = this.pointsCircle;
|
|
1912
|
+
if (!circlePoints)
|
|
1913
|
+
return;
|
|
1914
|
+
gl.useProgram(circlePoints.program);
|
|
1915
|
+
this.bindViewUniformsForProgram(circlePoints.program);
|
|
1916
|
+
if (this.paletteDirty)
|
|
1917
|
+
this.uploadPaletteUniforms();
|
|
1918
|
+
this.bindPaletteTexture();
|
|
1919
|
+
if (circlePoints.uCssSize)
|
|
1920
|
+
gl.uniform2f(circlePoints.uCssSize, this.width, this.height);
|
|
1921
|
+
if (circlePoints.uDpr)
|
|
1922
|
+
gl.uniform1f(circlePoints.uDpr, this.dpr);
|
|
1923
|
+
if (circlePoints.uPointRadius)
|
|
1924
|
+
gl.uniform1f(circlePoints.uPointRadius, fillRadius);
|
|
1925
|
+
if (this.gpuUsesFullDataset) {
|
|
1926
|
+
gl.drawElements(gl.POINTS, 1, gl.UNSIGNED_INT, 0);
|
|
1927
|
+
}
|
|
1928
|
+
else if (this.hoverVao) {
|
|
1929
|
+
gl.bindVertexArray(this.hoverVao);
|
|
1930
|
+
gl.drawArrays(gl.POINTS, 0, 1);
|
|
1931
|
+
gl.bindVertexArray(this.vao);
|
|
1932
|
+
}
|
|
1759
1933
|
}
|
|
1760
1934
|
}
|
|
1761
1935
|
if (this.gpuUsesFullDataset) {
|
|
@@ -1883,6 +2057,8 @@ export class EuclideanWebGLCandidate extends WebGLRendererBase {
|
|
|
1883
2057
|
// Avoid building a potentially large candidates array (push-heavy).
|
|
1884
2058
|
// Instead iterate overlapping grid cells directly.
|
|
1885
2059
|
idx.forEachInAABB(dataPt.x - dataRadius, dataPt.y - dataRadius, dataPt.x + dataRadius, dataPt.y + dataRadius, (i) => {
|
|
2060
|
+
if (!this.isCategoryVisible(ds.labels[i]))
|
|
2061
|
+
return;
|
|
1886
2062
|
const dataX = ds.positions[i * 2];
|
|
1887
2063
|
const dataY = ds.positions[i * 2 + 1];
|
|
1888
2064
|
// Fast reject in data space (equivalent up to scale).
|
|
@@ -1951,11 +2127,22 @@ export class EuclideanWebGLCandidate extends WebGLRendererBase {
|
|
|
1951
2127
|
const bounds = { xMin: minX, yMin: minY, xMax: maxX, yMax: maxY };
|
|
1952
2128
|
const geometry = { type: 'polygon', coords: dataPolyline, bounds };
|
|
1953
2129
|
const computeTimeMs = performance.now() - startTime;
|
|
1954
|
-
return
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
2130
|
+
return {
|
|
2131
|
+
kind: 'geometry',
|
|
2132
|
+
geometry,
|
|
2133
|
+
computeTimeMs,
|
|
2134
|
+
has: (index) => {
|
|
2135
|
+
if (index < 0 || index >= ds.n)
|
|
2136
|
+
return false;
|
|
2137
|
+
if (!this.isCategoryVisible(ds.labels[index]))
|
|
2138
|
+
return false;
|
|
2139
|
+
const px = ds.positions[index * 2];
|
|
2140
|
+
const py = ds.positions[index * 2 + 1];
|
|
2141
|
+
if (px < bounds.xMin || px > bounds.xMax || py < bounds.yMin || py > bounds.yMax)
|
|
2142
|
+
return false;
|
|
2143
|
+
return pointInPolygon(px, py, dataPolyline);
|
|
2144
|
+
},
|
|
2145
|
+
};
|
|
1959
2146
|
}
|
|
1960
2147
|
projectToScreen(dataX, dataY) {
|
|
1961
2148
|
return projectEuclidean(dataX, dataY, this.view, this.width, this.height);
|
|
@@ -2135,6 +2322,8 @@ export class HyperbolicWebGLCandidate extends WebGLRendererBase {
|
|
|
2135
2322
|
const ay = view.ay;
|
|
2136
2323
|
// Avoid building a candidates array; iterate overlapping cells directly.
|
|
2137
2324
|
idx.forEachInAABB(dataPt.x - queryRadius, dataPt.y - queryRadius, dataPt.x + queryRadius, dataPt.y + queryRadius, (i) => {
|
|
2325
|
+
if (!this.isCategoryVisible(ds.labels[i]))
|
|
2326
|
+
return;
|
|
2138
2327
|
const dataX = ds.positions[i * 2];
|
|
2139
2328
|
const dataY = ds.positions[i * 2 + 1];
|
|
2140
2329
|
// mobiusTransform(z) = (z - a) / (1 - conj(a) * z)
|
|
@@ -2260,11 +2449,22 @@ export class HyperbolicWebGLCandidate extends WebGLRendererBase {
|
|
|
2260
2449
|
const bounds = { xMin: minX, yMin: minY, xMax: maxX, yMax: maxY };
|
|
2261
2450
|
const geometry = { type: 'polygon', coords: dataPolyline, bounds };
|
|
2262
2451
|
const computeTimeMs = performance.now() - startTime;
|
|
2263
|
-
return
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2452
|
+
return {
|
|
2453
|
+
kind: 'geometry',
|
|
2454
|
+
geometry,
|
|
2455
|
+
computeTimeMs,
|
|
2456
|
+
has: (index) => {
|
|
2457
|
+
if (index < 0 || index >= ds.n)
|
|
2458
|
+
return false;
|
|
2459
|
+
if (!this.isCategoryVisible(ds.labels[index]))
|
|
2460
|
+
return false;
|
|
2461
|
+
const px = ds.positions[index * 2];
|
|
2462
|
+
const py = ds.positions[index * 2 + 1];
|
|
2463
|
+
if (px < bounds.xMin || px > bounds.xMax || py < bounds.yMin || py > bounds.yMax)
|
|
2464
|
+
return false;
|
|
2465
|
+
return pointInPolygon(px, py, dataPolyline);
|
|
2466
|
+
},
|
|
2467
|
+
};
|
|
2268
2468
|
}
|
|
2269
2469
|
projectToScreen(dataX, dataY) {
|
|
2270
2470
|
return projectPoincare(dataX, dataY, this.view, this.width, this.height);
|