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