@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.
- package/README.md +55 -0
- package/dist/bar3d.cjs +4 -4
- package/dist/bar3d.d.cts +2 -2
- package/dist/bar3d.d.ts +2 -2
- package/dist/bar3d.js +1 -1
- package/dist/{chunk-M24XMYGG.js → chunk-AGG2KSBO.js} +418 -83
- package/dist/chunk-AGG2KSBO.js.map +1 -0
- package/dist/{chunk-Q4JAQOV3.cjs → chunk-OTQKQDB6.cjs} +419 -82
- package/dist/chunk-OTQKQDB6.cjs.map +1 -0
- package/dist/{factory-Cp9Kr7aa.d.cts → factory-jQSzXhK4.d.cts} +4 -1
- package/dist/{factory-Cp9Kr7aa.d.ts → factory-jQSzXhK4.d.ts} +4 -1
- package/dist/flow-gl.cjs +4 -4
- package/dist/flow-gl.d.cts +2 -2
- package/dist/flow-gl.d.ts +2 -2
- package/dist/flow-gl.js +1 -1
- package/dist/globe3d.cjs +4 -4
- package/dist/globe3d.d.cts +4 -3
- package/dist/globe3d.d.ts +4 -3
- package/dist/globe3d.js +1 -1
- package/dist/graph-gl.cjs +4 -4
- package/dist/graph-gl.d.cts +2 -2
- package/dist/graph-gl.d.ts +2 -2
- package/dist/graph-gl.js +1 -1
- package/dist/index.cjs +60 -52
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/line3d.cjs +4 -4
- package/dist/line3d.d.cts +2 -2
- package/dist/line3d.d.ts +2 -2
- package/dist/line3d.js +1 -1
- package/dist/lines-gl.cjs +4 -4
- package/dist/lines-gl.d.cts +2 -2
- package/dist/lines-gl.d.ts +2 -2
- package/dist/lines-gl.js +1 -1
- package/dist/lines3d.cjs +4 -4
- package/dist/lines3d.d.cts +2 -2
- package/dist/lines3d.d.ts +2 -2
- package/dist/lines3d.js +1 -1
- package/dist/map3d.cjs +4 -4
- package/dist/map3d.d.cts +2 -2
- package/dist/map3d.d.ts +2 -2
- package/dist/map3d.js +1 -1
- package/dist/scatter-gl.cjs +4 -4
- package/dist/scatter-gl.d.cts +2 -2
- package/dist/scatter-gl.d.ts +2 -2
- package/dist/scatter-gl.js +1 -1
- package/dist/scatter3d.cjs +4 -4
- package/dist/scatter3d.d.cts +2 -2
- package/dist/scatter3d.d.ts +2 -2
- package/dist/scatter3d.js +1 -1
- package/dist/surface3d.cjs +4 -4
- package/dist/surface3d.d.cts +2 -2
- package/dist/surface3d.d.ts +2 -2
- package/dist/surface3d.js +1 -1
- package/dist/torus3d-NpDo5Nb5.d.ts +15 -0
- package/dist/torus3d-fgZpxxQZ.d.cts +15 -0
- package/dist/torus3d.cjs +20 -0
- package/dist/torus3d.cjs.map +1 -0
- package/dist/torus3d.d.cts +2 -0
- package/dist/torus3d.d.ts +2 -0
- package/dist/torus3d.js +3 -0
- package/dist/torus3d.js.map +1 -0
- package/package.json +10 -4
- package/dist/chunk-M24XMYGG.js.map +0 -1
- 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 [
|
|
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
|
|
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
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
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
|
|
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
|
|
1879
|
-
|
|
1880
|
-
|
|
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
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
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
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
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
|
-
|
|
1898
|
-
setLightUniforms(
|
|
1899
|
-
|
|
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
|
|
1903
|
-
{ location:
|
|
1904
|
-
{ location:
|
|
1905
|
-
{ location:
|
|
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,
|
|
1989
|
+
applyVertexLayout(gl, layout);
|
|
1908
1990
|
sphereIBO.bind();
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
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 =
|
|
1938
|
-
for (const
|
|
1939
|
-
const screen = projectToScreen(
|
|
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 = {
|
|
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
|
-
|
|
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-
|
|
3516
|
-
//# sourceMappingURL=chunk-
|
|
3852
|
+
//# sourceMappingURL=chunk-OTQKQDB6.cjs.map
|
|
3853
|
+
//# sourceMappingURL=chunk-OTQKQDB6.cjs.map
|