@chartts/gl 0.1.3 → 0.1.5

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 (66) hide show
  1. package/README.md +55 -0
  2. package/dist/bar3d.cjs +4 -4
  3. package/dist/bar3d.d.cts +2 -2
  4. package/dist/bar3d.d.ts +2 -2
  5. package/dist/bar3d.js +1 -1
  6. package/dist/{chunk-M24XMYGG.js → chunk-AGG2KSBO.js} +418 -83
  7. package/dist/chunk-AGG2KSBO.js.map +1 -0
  8. package/dist/{chunk-Q4JAQOV3.cjs → chunk-OTQKQDB6.cjs} +419 -82
  9. package/dist/chunk-OTQKQDB6.cjs.map +1 -0
  10. package/dist/{factory-Cp9Kr7aa.d.cts → factory-jQSzXhK4.d.cts} +4 -1
  11. package/dist/{factory-Cp9Kr7aa.d.ts → factory-jQSzXhK4.d.ts} +4 -1
  12. package/dist/flow-gl.cjs +4 -4
  13. package/dist/flow-gl.d.cts +2 -2
  14. package/dist/flow-gl.d.ts +2 -2
  15. package/dist/flow-gl.js +1 -1
  16. package/dist/globe3d.cjs +4 -4
  17. package/dist/globe3d.d.cts +4 -3
  18. package/dist/globe3d.d.ts +4 -3
  19. package/dist/globe3d.js +1 -1
  20. package/dist/graph-gl.cjs +4 -4
  21. package/dist/graph-gl.d.cts +2 -2
  22. package/dist/graph-gl.d.ts +2 -2
  23. package/dist/graph-gl.js +1 -1
  24. package/dist/index.cjs +60 -52
  25. package/dist/index.d.cts +3 -2
  26. package/dist/index.d.ts +3 -2
  27. package/dist/index.js +1 -1
  28. package/dist/line3d.cjs +4 -4
  29. package/dist/line3d.d.cts +2 -2
  30. package/dist/line3d.d.ts +2 -2
  31. package/dist/line3d.js +1 -1
  32. package/dist/lines-gl.cjs +4 -4
  33. package/dist/lines-gl.d.cts +2 -2
  34. package/dist/lines-gl.d.ts +2 -2
  35. package/dist/lines-gl.js +1 -1
  36. package/dist/lines3d.cjs +4 -4
  37. package/dist/lines3d.d.cts +2 -2
  38. package/dist/lines3d.d.ts +2 -2
  39. package/dist/lines3d.js +1 -1
  40. package/dist/map3d.cjs +4 -4
  41. package/dist/map3d.d.cts +2 -2
  42. package/dist/map3d.d.ts +2 -2
  43. package/dist/map3d.js +1 -1
  44. package/dist/scatter-gl.cjs +4 -4
  45. package/dist/scatter-gl.d.cts +2 -2
  46. package/dist/scatter-gl.d.ts +2 -2
  47. package/dist/scatter-gl.js +1 -1
  48. package/dist/scatter3d.cjs +4 -4
  49. package/dist/scatter3d.d.cts +2 -2
  50. package/dist/scatter3d.d.ts +2 -2
  51. package/dist/scatter3d.js +1 -1
  52. package/dist/surface3d.cjs +4 -4
  53. package/dist/surface3d.d.cts +2 -2
  54. package/dist/surface3d.d.ts +2 -2
  55. package/dist/surface3d.js +1 -1
  56. package/dist/torus3d-NpDo5Nb5.d.ts +15 -0
  57. package/dist/torus3d-fgZpxxQZ.d.cts +15 -0
  58. package/dist/torus3d.cjs +20 -0
  59. package/dist/torus3d.cjs.map +1 -0
  60. package/dist/torus3d.d.cts +2 -0
  61. package/dist/torus3d.d.ts +2 -0
  62. package/dist/torus3d.js +3 -0
  63. package/dist/torus3d.js.map +1 -0
  64. package/package.json +10 -4
  65. package/dist/chunk-M24XMYGG.js.map +0 -1
  66. package/dist/chunk-Q4JAQOV3.cjs.map +0 -1
@@ -1813,20 +1813,37 @@ function createSurface3DPlugin() {
1813
1813
  }
1814
1814
 
1815
1815
  // src/charts/globe3d/globe3d-type.ts
1816
+ var GLOBE_RADIUS = 3;
1817
+ var SEGMENTS = 128;
1818
+ var RINGS = 64;
1816
1819
  function latLngToXYZ(lat, lng, radius) {
1817
1820
  const phi = (90 - lat) * Math.PI / 180;
1818
1821
  const theta = (lng + 180) * Math.PI / 180;
1819
- return [-radius * Math.sin(phi) * Math.cos(theta), radius * Math.cos(phi), radius * Math.sin(phi) * Math.sin(theta)];
1822
+ return [
1823
+ -radius * Math.sin(phi) * Math.cos(theta),
1824
+ radius * Math.cos(phi),
1825
+ radius * Math.sin(phi) * Math.sin(theta)
1826
+ ];
1827
+ }
1828
+ function angularDistance(lat1, lng1, lat2, lng2) {
1829
+ const p1 = lat1 * Math.PI / 180, p2 = lat2 * Math.PI / 180;
1830
+ const dl = (lng2 - lng1) * Math.PI / 180;
1831
+ return Math.acos(
1832
+ Math.min(1, Math.max(
1833
+ -1,
1834
+ Math.sin(p1) * Math.sin(p2) + Math.cos(p1) * Math.cos(p2) * Math.cos(dl)
1835
+ ))
1836
+ );
1820
1837
  }
1821
1838
  function createGlobe3DPlugin() {
1822
1839
  let sphereVBO = null;
1823
1840
  let sphereIBO = null;
1824
1841
  let sphereIndexCount = 0;
1825
- let pointVBO = null;
1826
- let pointCount = 0;
1827
1842
  const modelMatrix = mat4();
1843
+ let locationColors = /* @__PURE__ */ new Map();
1828
1844
  const normalMatrix = new Float32Array(9);
1829
- let globePoints = [];
1845
+ let screenPoints = [];
1846
+ let seriesInfo = [];
1830
1847
  return {
1831
1848
  type: "globe3d",
1832
1849
  prepare(ctx) {
@@ -1840,108 +1857,199 @@ function createGlobe3DPlugin() {
1840
1857
  [...MESH_VERT_UNIFORMS, ...MESH_FRAG_UNIFORMS],
1841
1858
  MESH_VERT_ATTRIBUTES
1842
1859
  );
1843
- renderer.registerProgram(
1844
- "point3d",
1845
- POINT_VERT,
1846
- POINT_FRAG,
1847
- [...POINT_VERT_UNIFORMS, ...POINT_FRAG_UNIFORMS],
1848
- POINT_VERT_ATTRIBUTES
1849
- );
1850
- const globeRadius = 3;
1851
- const segments = 64, rings = 32;
1852
- const sphereColor = [0.12, 0.22, 0.48];
1853
- const sv = [], si = [];
1854
- for (let ring = 0; ring <= rings; ring++) {
1855
- const phi = ring / rings * Math.PI;
1856
- for (let seg = 0; seg <= segments; seg++) {
1857
- const theta = seg / segments * Math.PI * 2;
1858
- const nx = Math.sin(phi) * Math.cos(theta), ny = Math.cos(phi), nz = Math.sin(phi) * Math.sin(theta);
1859
- sv.push(nx * globeRadius, ny * globeRadius, nz * globeRadius, nx, ny, nz, ...sphereColor);
1860
- }
1861
- }
1862
- for (let ring = 0; ring < rings; ring++) for (let seg = 0; seg < segments; seg++) {
1863
- const a = ring * (segments + 1) + seg, b = a + segments + 1;
1864
- si.push(a, b, a + 1, a + 1, b, b + 1);
1865
- }
1866
- if (sphereVBO) sphereVBO.update(new Float32Array(sv));
1867
- else sphereVBO = createVertexBuffer(gl, new Float32Array(sv), gl.STATIC_DRAW);
1868
- if (sphereIBO) sphereIBO.update(new Uint16Array(si));
1869
- else sphereIBO = createIndexBuffer(gl, new Uint16Array(si), gl.STATIC_DRAW);
1870
- sphereIndexCount = si.length;
1871
- globePoints = [];
1872
- const pv = [];
1860
+ const locationMap = /* @__PURE__ */ new Map();
1861
+ seriesInfo = [];
1862
+ screenPoints = [];
1873
1863
  for (let sIdx = 0; sIdx < series.length; sIdx++) {
1874
1864
  const s = series[sIdx];
1875
- const color = hexToRGB(s.color ?? theme.colors[sIdx % theme.colors.length]);
1865
+ const colorHex = s.color ?? theme.colors[sIdx % theme.colors.length];
1866
+ seriesInfo.push({ name: s.name, color: colorHex });
1876
1867
  for (let di = 0; di < s.values.length; di++) {
1877
1868
  const lat = s.y?.[di] ?? 0, lng = s.x?.[di] ?? 0, value = s.values[di];
1878
- const [wx, wy, wz] = latLngToXYZ(lat, lng, globeRadius * (1 + value * 5e-3));
1879
- pv.push(wx, wy, wz, color[0], color[1], color[2], Math.max(4, value * 0.3));
1880
- globePoints.push({ si: sIdx, di, lat, lng, wx, wy, wz, value, name: s.name });
1869
+ const label = data.categories?.[di] ?? "";
1870
+ const key = `${lat.toFixed(1)}_${lng.toFixed(1)}`;
1871
+ let loc = locationMap.get(key);
1872
+ if (!loc) {
1873
+ loc = { lat, lng, totalValue: 0, label, entries: [] };
1874
+ locationMap.set(key, loc);
1875
+ }
1876
+ loc.totalValue += Math.abs(value);
1877
+ loc.entries.push({ si: sIdx, di, value });
1878
+ const [wx, wy, wz] = latLngToXYZ(lat, lng, GLOBE_RADIUS);
1879
+ screenPoints.push({ si: sIdx, di, lat, lng, wx, wy, wz, value, name: s.name, label });
1880
+ }
1881
+ }
1882
+ const patches = [];
1883
+ locationColors = /* @__PURE__ */ new Map();
1884
+ let maxVal = 0;
1885
+ for (const loc of locationMap.values()) if (loc.totalValue > maxVal) maxVal = loc.totalValue;
1886
+ if (maxVal === 0) maxVal = 1;
1887
+ let locIdx = 0;
1888
+ for (const loc of locationMap.values()) {
1889
+ const colorHex = theme.colors[locIdx % theme.colors.length];
1890
+ locationColors.set(loc.label, colorHex);
1891
+ const color = hexToRGB(colorHex);
1892
+ const normValue = loc.totalValue / maxVal;
1893
+ patches.push({ lat: loc.lat, lng: loc.lng, value: loc.totalValue, normValue, color, si: 0, di: locIdx, name: loc.label, label: loc.label });
1894
+ locIdx++;
1895
+ }
1896
+ const baseColor = [0.03, 0.05, 0.12];
1897
+ const patchRadius = 0.8;
1898
+ const gratLatStep = 30, gratLngStep = 30;
1899
+ const gratWidth = 0.015;
1900
+ const gratColor = [0.08, 0.12, 0.22];
1901
+ const verts = [];
1902
+ const indices = [];
1903
+ for (let ring = 0; ring <= RINGS; ring++) {
1904
+ const phi = ring / RINGS * Math.PI;
1905
+ const lat = 90 - ring / RINGS * 180;
1906
+ for (let seg = 0; seg <= SEGMENTS; seg++) {
1907
+ const theta = seg / SEGMENTS * Math.PI * 2;
1908
+ const lng = seg / SEGMENTS * 360 - 180;
1909
+ const nx = Math.sin(phi) * Math.cos(theta);
1910
+ const ny = Math.cos(phi);
1911
+ const nz = Math.sin(phi) * Math.sin(theta);
1912
+ let r = baseColor[0], g = baseColor[1], b = baseColor[2];
1913
+ const latMod = (lat % gratLatStep + gratLatStep) % gratLatStep;
1914
+ const latDist = Math.min(latMod, gratLatStep - latMod) * Math.PI / 180;
1915
+ const lngMod = (lng % gratLngStep + gratLngStep) % gratLngStep;
1916
+ const lngDist = Math.min(lngMod, gratLngStep - lngMod) * Math.PI / 180;
1917
+ const gratDist = Math.min(latDist, lngDist);
1918
+ if (gratDist < gratWidth) {
1919
+ const gratT = 1 - gratDist / gratWidth;
1920
+ r = r + (gratColor[0] - r) * gratT;
1921
+ g = g + (gratColor[1] - g) * gratT;
1922
+ b = b + (gratColor[2] - b) * gratT;
1923
+ }
1924
+ let ar = 0, ag = 0, ab = 0;
1925
+ for (const patch of patches) {
1926
+ const dist = angularDistance(lat, lng, patch.lat, patch.lng);
1927
+ if (dist < patchRadius) {
1928
+ const t = 1 - dist / patchRadius;
1929
+ const influence = t * (0.4 + t * 0.6) * patch.normValue;
1930
+ ar += patch.color[0] * influence;
1931
+ ag += patch.color[1] * influence;
1932
+ ab += patch.color[2] * influence;
1933
+ }
1934
+ }
1935
+ if (ar > 0.01 || ag > 0.01 || ab > 0.01) {
1936
+ r = Math.min(1, r + ar);
1937
+ g = Math.min(1, g + ag);
1938
+ b = Math.min(1, b + ab);
1939
+ }
1940
+ verts.push(
1941
+ nx * GLOBE_RADIUS,
1942
+ ny * GLOBE_RADIUS,
1943
+ nz * GLOBE_RADIUS,
1944
+ nx,
1945
+ ny,
1946
+ nz,
1947
+ r,
1948
+ g,
1949
+ b
1950
+ );
1951
+ }
1952
+ }
1953
+ for (let ring = 0; ring < RINGS; ring++) {
1954
+ for (let seg = 0; seg < SEGMENTS; seg++) {
1955
+ const a = ring * (SEGMENTS + 1) + seg;
1956
+ const b = a + SEGMENTS + 1;
1957
+ indices.push(a, a + 1, b, a + 1, b + 1, b);
1881
1958
  }
1882
1959
  }
1883
- if (pointVBO) pointVBO.update(new Float32Array(pv));
1884
- else pointVBO = createVertexBuffer(gl, new Float32Array(pv), gl.DYNAMIC_DRAW);
1885
- pointCount = globePoints.length;
1960
+ const vertArr = new Float32Array(verts);
1961
+ const idxArr = new Uint16Array(indices);
1962
+ if (sphereVBO) sphereVBO.update(vertArr);
1963
+ else sphereVBO = createVertexBuffer(gl, vertArr, gl.DYNAMIC_DRAW);
1964
+ if (sphereIBO) sphereIBO.update(idxArr);
1965
+ else sphereIBO = createIndexBuffer(gl, idxArr, gl.DYNAMIC_DRAW);
1966
+ sphereIndexCount = indices.length;
1886
1967
  mat4Identity(modelMatrix);
1887
1968
  },
1888
1969
  render(ctx) {
1889
1970
  const { renderer, camera } = ctx;
1890
1971
  const gl = renderer.gl;
1891
1972
  const progress = ctx.animationProgress;
1892
- const meshProg = renderer.getProgram("mesh");
1893
- meshProg.use();
1894
- meshProg.setMat4("u_projView", camera.projViewMatrix);
1895
- meshProg.setMat4("u_model", modelMatrix);
1973
+ const prog = renderer.getProgram("mesh");
1974
+ prog.use();
1975
+ prog.setMat4("u_projView", camera.projViewMatrix);
1976
+ prog.setMat4("u_model", modelMatrix);
1896
1977
  mat3NormalFromMat4(normalMatrix, modelMatrix);
1897
- meshProg.setMat3("u_normalMatrix", normalMatrix);
1898
- setLightUniforms(meshProg, defaultLightConfig(), camera.position);
1899
- meshProg.setFloat("u_opacity", progress * 0.9);
1978
+ prog.setMat3("u_normalMatrix", normalMatrix);
1979
+ setLightUniforms(prog, defaultLightConfig(), camera.position);
1980
+ prog.setFloat("u_opacity", progress);
1900
1981
  if (sphereVBO && sphereIBO) {
1982
+ gl.disable(gl.CULL_FACE);
1901
1983
  sphereVBO.bind();
1902
- const ml = createVertexLayout([
1903
- { location: meshProg.attributes["a_position"], size: 3 },
1904
- { location: meshProg.attributes["a_normal"], size: 3 },
1905
- { location: meshProg.attributes["a_color"], size: 3 }
1984
+ const layout = createVertexLayout([
1985
+ { location: prog.attributes["a_position"], size: 3 },
1986
+ { location: prog.attributes["a_normal"], size: 3 },
1987
+ { location: prog.attributes["a_color"], size: 3 }
1906
1988
  ]);
1907
- applyVertexLayout(gl, ml);
1989
+ applyVertexLayout(gl, layout);
1908
1990
  sphereIBO.bind();
1909
- gl.drawElements(gl.TRIANGLES, sphereIndexCount, gl.UNSIGNED_SHORT, 0);
1910
- disableVertexLayout(gl, ml);
1911
- }
1912
- const pointProg = renderer.getProgram("point3d");
1913
- if (pointProg && pointVBO && pointCount > 0) {
1914
- pointProg.use();
1915
- pointProg.setMat4("u_projView", camera.projViewMatrix);
1916
- pointProg.setMat4("u_model", modelMatrix);
1917
- pointProg.setFloat("u_pixelRatio", renderer.pixelRatio);
1918
- pointProg.setFloat("u_sizeAttenuation", 30);
1919
- pointProg.setFloat("u_opacity", progress);
1920
- pointVBO.bind();
1921
- const pl = createVertexLayout([
1922
- { location: pointProg.attributes["a_position"], size: 3 },
1923
- { location: pointProg.attributes["a_color"], size: 3 },
1924
- { location: pointProg.attributes["a_size"], size: 1 }
1925
- ]);
1926
- applyVertexLayout(gl, pl);
1927
- gl.drawArrays(gl.POINTS, 0, pointCount);
1928
- disableVertexLayout(gl, pl);
1991
+ const indexType = gl.UNSIGNED_SHORT;
1992
+ gl.drawElements(gl.TRIANGLES, sphereIndexCount, indexType, 0);
1993
+ disableVertexLayout(gl, layout);
1994
+ gl.enable(gl.CULL_FACE);
1929
1995
  }
1930
1996
  },
1997
+ renderOverlay(ctx, ctx2d) {
1998
+ const { camera, width, height, theme, animationProgress } = ctx;
1999
+ if (animationProgress < 0.5) return;
2000
+ ctx2d.save();
2001
+ const seenLabels = /* @__PURE__ */ new Set();
2002
+ ctx2d.font = `${theme.fontSize - 1}px ${theme.fontFamily}`;
2003
+ ctx2d.textBaseline = "middle";
2004
+ for (const sp of screenPoints) {
2005
+ if (!sp.label || seenLabels.has(sp.label)) continue;
2006
+ const screen = projectToScreen(
2007
+ new Float32Array([sp.wx, sp.wy, sp.wz]),
2008
+ camera.projViewMatrix,
2009
+ width,
2010
+ height
2011
+ );
2012
+ if (!screen || screen.z < -1 || screen.z > 1 || screen.z > 0.97) continue;
2013
+ seenLabels.add(sp.label);
2014
+ const locColor = locationColors.get(sp.label) ?? theme.textColor;
2015
+ ctx2d.fillStyle = locColor;
2016
+ ctx2d.globalAlpha = 0.9;
2017
+ ctx2d.beginPath();
2018
+ ctx2d.arc(screen.x, screen.y, 3, 0, Math.PI * 2);
2019
+ ctx2d.fill();
2020
+ ctx2d.fillStyle = theme.textColor;
2021
+ ctx2d.globalAlpha = 0.85;
2022
+ ctx2d.textAlign = "left";
2023
+ ctx2d.fillText(sp.label, screen.x + 8, screen.y);
2024
+ }
2025
+ ctx2d.restore();
2026
+ },
1931
2027
  needsLoop() {
1932
2028
  return false;
1933
2029
  },
1934
2030
  hitTest(ctx, x, y) {
1935
2031
  const { camera, width, height } = ctx;
1936
2032
  let closest = null;
1937
- let closestDist = 20;
1938
- for (const gp of globePoints) {
1939
- const screen = projectToScreen(new Float32Array([gp.wx, gp.wy, gp.wz]), camera.projViewMatrix, width, height);
2033
+ let closestDist = 25;
2034
+ for (const sp of screenPoints) {
2035
+ const screen = projectToScreen(
2036
+ new Float32Array([sp.wx, sp.wy, sp.wz]),
2037
+ camera.projViewMatrix,
2038
+ width,
2039
+ height
2040
+ );
1940
2041
  if (!screen || screen.z < -1 || screen.z > 1) continue;
1941
2042
  const dist = Math.sqrt((screen.x - x) ** 2 + (screen.y - y) ** 2);
1942
2043
  if (dist < closestDist) {
1943
2044
  closestDist = dist;
1944
- closest = { seriesIndex: gp.si, dataIndex: gp.di, value: gp.value, x: gp.lng, y: gp.lat, seriesName: gp.name };
2045
+ closest = {
2046
+ seriesIndex: sp.si,
2047
+ dataIndex: sp.di,
2048
+ value: sp.value,
2049
+ x: sp.lng,
2050
+ y: sp.lat,
2051
+ seriesName: sp.label || sp.name
2052
+ };
1945
2053
  }
1946
2054
  }
1947
2055
  return closest;
@@ -1951,9 +2059,7 @@ function createGlobe3DPlugin() {
1951
2059
  sphereVBO = null;
1952
2060
  sphereIBO?.destroy();
1953
2061
  sphereIBO = null;
1954
- pointVBO?.destroy();
1955
- pointVBO = null;
1956
- globePoints = [];
2062
+ screenPoints = [];
1957
2063
  }
1958
2064
  };
1959
2065
  }
@@ -3384,6 +3490,230 @@ function createGraphGLPlugin() {
3384
3490
  };
3385
3491
  }
3386
3492
 
3493
+ // src/charts/torus3d/torus3d-type.ts
3494
+ var RADIAL_SEGMENTS = 64;
3495
+ var HEIGHT_STEPS = 200;
3496
+ var TOTAL_HEIGHT = 6;
3497
+ var MIN_RADIUS = 0.6;
3498
+ var MAX_RADIUS = 2;
3499
+ function createTorus3DPlugin() {
3500
+ let vbo = null;
3501
+ let ibo = null;
3502
+ let indexCount = 0;
3503
+ let use32bit = false;
3504
+ const modelMatrix = mat4();
3505
+ const normalMatrix = new Float32Array(9);
3506
+ let labelPoints = [];
3507
+ return {
3508
+ type: "torus3d",
3509
+ prepare(ctx) {
3510
+ const { renderer, data, options, theme } = ctx;
3511
+ const gl = renderer.gl;
3512
+ const series = data.series;
3513
+ renderer.registerProgram(
3514
+ "mesh",
3515
+ MESH_VERT,
3516
+ MESH_FRAG,
3517
+ [...MESH_VERT_UNIFORMS, ...MESH_FRAG_UNIFORMS],
3518
+ MESH_VERT_ATTRIBUTES
3519
+ );
3520
+ const s = series[0];
3521
+ if (!s || s.values.length === 0) return;
3522
+ const opts = options;
3523
+ const intensity = opts.intensity ?? 1;
3524
+ const numRings = s.values.length;
3525
+ let maxVal = 0;
3526
+ for (const v of s.values) if (Math.abs(v) > maxVal) maxVal = Math.abs(v);
3527
+ if (maxVal === 0) maxVal = 1;
3528
+ const ringRadii = [];
3529
+ for (let i = 0; i < numRings; i++) {
3530
+ const norm = Math.abs(s.values[i]) / maxVal;
3531
+ ringRadii.push((MIN_RADIUS + norm * (MAX_RADIUS - MIN_RADIUS)) * intensity);
3532
+ }
3533
+ const ringColors = [];
3534
+ const ringHexColors = [];
3535
+ for (let i = 0; i < numRings; i++) {
3536
+ const hex = theme.colors[i % theme.colors.length];
3537
+ ringHexColors.push(hex);
3538
+ ringColors.push(hexToRGB(hex));
3539
+ }
3540
+ function catmullRom(p0, p1, p2, p3, t) {
3541
+ return 0.5 * (2 * p1 + (-p0 + p2) * t + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t + (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t);
3542
+ }
3543
+ function radiusAt(t) {
3544
+ const f = t * (numRings - 1);
3545
+ const i = Math.max(0, Math.min(Math.floor(f), numRings - 2));
3546
+ const frac = f - i;
3547
+ const p0 = ringRadii[Math.max(0, i - 1)];
3548
+ const p1 = ringRadii[i];
3549
+ const p2 = ringRadii[Math.min(numRings - 1, i + 1)];
3550
+ const p3 = ringRadii[Math.min(numRings - 1, i + 2)];
3551
+ return Math.max(MIN_RADIUS * 0.5, catmullRom(p0, p1, p2, p3, frac));
3552
+ }
3553
+ function colorAt(t) {
3554
+ const f = t * (numRings - 1);
3555
+ const i = Math.max(0, Math.min(Math.floor(f), numRings - 2));
3556
+ const frac = f - i;
3557
+ const c0 = ringColors[Math.max(0, i - 1)];
3558
+ const c1 = ringColors[i];
3559
+ const c2 = ringColors[Math.min(numRings - 1, i + 1)];
3560
+ const c3 = ringColors[Math.min(numRings - 1, i + 2)];
3561
+ return [
3562
+ Math.max(0, Math.min(1, catmullRom(c0[0], c1[0], c2[0], c3[0], frac))),
3563
+ Math.max(0, Math.min(1, catmullRom(c0[1], c1[1], c2[1], c3[1], frac))),
3564
+ Math.max(0, Math.min(1, catmullRom(c0[2], c1[2], c2[2], c3[2], frac)))
3565
+ ];
3566
+ }
3567
+ const verts = [];
3568
+ const indices = [];
3569
+ labelPoints = [];
3570
+ for (let h = 0; h <= HEIGHT_STEPS; h++) {
3571
+ const t = h / HEIGHT_STEPS;
3572
+ const y = (t - 0.5) * TOTAL_HEIGHT;
3573
+ const radius = radiusAt(t);
3574
+ const color = colorAt(t);
3575
+ const eps = 2e-3;
3576
+ const rPrev = radiusAt(Math.max(0, t - eps));
3577
+ const rNext = radiusAt(Math.min(1, t + eps));
3578
+ const drdy = (rNext - rPrev) / (2 * eps * TOTAL_HEIGHT);
3579
+ for (let seg = 0; seg <= RADIAL_SEGMENTS; seg++) {
3580
+ const theta = seg / RADIAL_SEGMENTS * Math.PI * 2;
3581
+ const cosT = Math.cos(theta);
3582
+ const sinT = Math.sin(theta);
3583
+ const nx = cosT;
3584
+ const ny = -drdy;
3585
+ const nz = sinT;
3586
+ const nlen = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
3587
+ verts.push(
3588
+ radius * cosT,
3589
+ y,
3590
+ radius * sinT,
3591
+ nx / nlen,
3592
+ ny / nlen,
3593
+ nz / nlen,
3594
+ color[0],
3595
+ color[1],
3596
+ color[2]
3597
+ );
3598
+ }
3599
+ }
3600
+ for (let i = 0; i < numRings; i++) {
3601
+ const t = (i + 0.5) / numRings;
3602
+ const y = (t - 0.5) * TOTAL_HEIGHT;
3603
+ const r = ringRadii[i];
3604
+ labelPoints.push({
3605
+ si: 0,
3606
+ di: i,
3607
+ wx: r + 0.4,
3608
+ wy: y,
3609
+ wz: 0,
3610
+ value: s.values[i],
3611
+ label: data.categories?.[i] ?? `${i + 1}`,
3612
+ color: ringHexColors[i]
3613
+ });
3614
+ }
3615
+ for (let h = 0; h < HEIGHT_STEPS; h++) {
3616
+ for (let seg = 0; seg < RADIAL_SEGMENTS; seg++) {
3617
+ const a = h * (RADIAL_SEGMENTS + 1) + seg;
3618
+ const b = a + RADIAL_SEGMENTS + 1;
3619
+ indices.push(a, a + 1, b, a + 1, b + 1, b);
3620
+ }
3621
+ }
3622
+ const capStart = verts.length / 9;
3623
+ const bottomColor = colorAt(0);
3624
+ verts.push(0, -0.5 * TOTAL_HEIGHT, 0, 0, -1, 0, bottomColor[0], bottomColor[1], bottomColor[2]);
3625
+ const topColor = colorAt(1);
3626
+ verts.push(0, 0.5 * TOTAL_HEIGHT, 0, 0, 1, 0, topColor[0], topColor[1], topColor[2]);
3627
+ for (let seg = 0; seg < RADIAL_SEGMENTS; seg++) {
3628
+ indices.push(capStart, seg + 1, seg);
3629
+ const topRow = HEIGHT_STEPS * (RADIAL_SEGMENTS + 1);
3630
+ indices.push(capStart + 1, topRow + seg, topRow + seg + 1);
3631
+ }
3632
+ const vertArr = new Float32Array(verts);
3633
+ use32bit = verts.length / 9 > 65535;
3634
+ const idxArr = use32bit ? new Uint32Array(indices) : new Uint16Array(indices);
3635
+ if (vbo) vbo.update(vertArr);
3636
+ else vbo = createVertexBuffer(gl, vertArr, gl.DYNAMIC_DRAW);
3637
+ if (ibo) ibo.update(idxArr);
3638
+ else ibo = createIndexBuffer(gl, idxArr, gl.DYNAMIC_DRAW);
3639
+ indexCount = indices.length;
3640
+ mat4Identity(modelMatrix);
3641
+ },
3642
+ render(ctx) {
3643
+ const { renderer, camera } = ctx;
3644
+ const gl = renderer.gl;
3645
+ const prog = renderer.getProgram("mesh");
3646
+ prog.use();
3647
+ prog.setMat4("u_projView", camera.projViewMatrix);
3648
+ prog.setMat4("u_model", modelMatrix);
3649
+ mat3NormalFromMat4(normalMatrix, modelMatrix);
3650
+ prog.setMat3("u_normalMatrix", normalMatrix);
3651
+ setLightUniforms(prog, defaultLightConfig(), camera.position);
3652
+ prog.setFloat("u_opacity", ctx.animationProgress);
3653
+ if (vbo && ibo) {
3654
+ gl.disable(gl.CULL_FACE);
3655
+ vbo.bind();
3656
+ const layout = createVertexLayout([
3657
+ { location: prog.attributes["a_position"], size: 3 },
3658
+ { location: prog.attributes["a_normal"], size: 3 },
3659
+ { location: prog.attributes["a_color"], size: 3 }
3660
+ ]);
3661
+ applyVertexLayout(gl, layout);
3662
+ ibo.bind();
3663
+ gl.drawElements(gl.TRIANGLES, indexCount, use32bit ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT, 0);
3664
+ disableVertexLayout(gl, layout);
3665
+ gl.enable(gl.CULL_FACE);
3666
+ }
3667
+ },
3668
+ renderOverlay(ctx, ctx2d) {
3669
+ const { camera, width, height, theme, animationProgress } = ctx;
3670
+ if (animationProgress < 0.3) return;
3671
+ ctx2d.save();
3672
+ ctx2d.font = `bold ${theme.fontSize}px ${theme.fontFamily}`;
3673
+ ctx2d.textBaseline = "middle";
3674
+ for (const lp of labelPoints) {
3675
+ const screen = projectToScreen(new Float32Array([lp.wx, lp.wy, lp.wz]), camera.projViewMatrix, width, height);
3676
+ if (!screen || screen.z < -1 || screen.z > 1) continue;
3677
+ ctx2d.fillStyle = lp.color;
3678
+ ctx2d.globalAlpha = 0.9;
3679
+ ctx2d.beginPath();
3680
+ ctx2d.arc(screen.x, screen.y, 4, 0, Math.PI * 2);
3681
+ ctx2d.fill();
3682
+ ctx2d.fillStyle = theme.textColor;
3683
+ ctx2d.globalAlpha = 0.85;
3684
+ ctx2d.textAlign = "left";
3685
+ ctx2d.fillText(`${lp.label}: ${lp.value}`, screen.x + 10, screen.y);
3686
+ }
3687
+ ctx2d.restore();
3688
+ },
3689
+ needsLoop() {
3690
+ return false;
3691
+ },
3692
+ hitTest(ctx, x, y) {
3693
+ const { camera, width, height } = ctx;
3694
+ let closest = null;
3695
+ let closestDist = 30;
3696
+ for (const lp of labelPoints) {
3697
+ const screen = projectToScreen(new Float32Array([lp.wx, lp.wy, lp.wz]), camera.projViewMatrix, width, height);
3698
+ if (!screen || screen.z < -1 || screen.z > 1) continue;
3699
+ const dist = Math.sqrt((screen.x - x) ** 2 + (screen.y - y) ** 2);
3700
+ if (dist < closestDist) {
3701
+ closestDist = dist;
3702
+ closest = { seriesIndex: lp.si, dataIndex: lp.di, value: lp.value, x: screen.x, y: screen.y, seriesName: lp.label };
3703
+ }
3704
+ }
3705
+ return closest;
3706
+ },
3707
+ dispose() {
3708
+ vbo?.destroy();
3709
+ vbo = null;
3710
+ ibo?.destroy();
3711
+ ibo = null;
3712
+ labelPoints = [];
3713
+ }
3714
+ };
3715
+ }
3716
+
3387
3717
  // src/api/factory.ts
3388
3718
  function Scatter3D(container, opts) {
3389
3719
  const { data, ...options } = opts;
@@ -3460,6 +3790,11 @@ function GraphGL(container, opts) {
3460
3790
  const { data, ...options } = opts;
3461
3791
  return createGLChart(container, createGraphGLPlugin(), data, options);
3462
3792
  }
3793
+ function Torus3D(container, opts) {
3794
+ const { data, ...options } = opts;
3795
+ if (!options.camera) options.camera = { position: [4, 2, 6], target: [0, 0, 0] };
3796
+ return createGLChart(container, createTorus3DPlugin(), data, options);
3797
+ }
3463
3798
 
3464
3799
  exports.Bar3D = Bar3D;
3465
3800
  exports.DEFAULT_GL_THEME = DEFAULT_GL_THEME;
@@ -3474,6 +3809,7 @@ exports.Map3D = Map3D;
3474
3809
  exports.Scatter3D = Scatter3D;
3475
3810
  exports.ScatterGL = ScatterGL;
3476
3811
  exports.Surface3D = Surface3D;
3812
+ exports.Torus3D = Torus3D;
3477
3813
  exports.applyVertexLayout = applyVertexLayout;
3478
3814
  exports.compileShader = compileShader;
3479
3815
  exports.createBar3DPlugin = createBar3DPlugin;
@@ -3495,6 +3831,7 @@ exports.createScatter3DPlugin = createScatter3DPlugin;
3495
3831
  exports.createScatterGLPlugin = createScatterGLPlugin;
3496
3832
  exports.createShaderProgram = createShaderProgram;
3497
3833
  exports.createSurface3DPlugin = createSurface3DPlugin;
3834
+ exports.createTorus3DPlugin = createTorus3DPlugin;
3498
3835
  exports.createVertexBuffer = createVertexBuffer;
3499
3836
  exports.createVertexLayout = createVertexLayout;
3500
3837
  exports.defaultLightConfig = defaultLightConfig;
@@ -3512,5 +3849,5 @@ exports.toRad = toRad;
3512
3849
  exports.updateCamera = updateCamera;
3513
3850
  exports.updateOrbitControls = updateOrbitControls;
3514
3851
  exports.vec3 = vec3;
3515
- //# sourceMappingURL=chunk-Q4JAQOV3.cjs.map
3516
- //# sourceMappingURL=chunk-Q4JAQOV3.cjs.map
3852
+ //# sourceMappingURL=chunk-OTQKQDB6.cjs.map
3853
+ //# sourceMappingURL=chunk-OTQKQDB6.cjs.map