From 104921604987b2144d0e6e16622f5862eb6185d0 Mon Sep 17 00:00:00 2001 From: metacube Date: Thu, 4 Jun 2026 14:16:55 +0200 Subject: [PATCH] Label finance 3D axes --- .../Components/Pages/ManagementCockpit.razor | 4 ++ TrafagSalesExporter/wwwroot/js/finance3d.js | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor b/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor index 8c89724..cd1452f 100644 --- a/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor +++ b/TrafagSalesExporter/Components/Pages/ManagementCockpit.razor @@ -1345,6 +1345,10 @@ indicator = _finance3dIndicator, title = ResolveFinance3dIndicatorLabel(_finance3dIndicator), chartType = _finance3dChartType, + xAxis = T("X: Land", "X: country"), + yAxis = T("Y: Wert / Indikator", "Y: value / indicator"), + zAxis = T("Z: Jahr / Zeit", "Z: year / time"), + pieAxis = T("Kreis: Laenderanteile", "Pie: country shares"), scenarioFactor = Finance3dScenarioAffectsValue ? _finance3dScenarioFactor : 1d }); } diff --git a/TrafagSalesExporter/wwwroot/js/finance3d.js b/TrafagSalesExporter/wwwroot/js/finance3d.js index 41b713d..b6a91fe 100644 --- a/TrafagSalesExporter/wwwroot/js/finance3d.js +++ b/TrafagSalesExporter/wwwroot/js/finance3d.js @@ -49,6 +49,7 @@ gridPoints.push(new THREE.Vector3(xStart - 1, 0, z), new THREE.Vector3(xStart + Math.max(1, axes.countries.length - 1) * (xStep || 2) + 1, 0, z)); } root.add(new THREE.LineSegments(new THREE.BufferGeometry().setFromPoints(gridPoints), gridMaterial)); + addAxisGuides(scene, THREE, layoutFromAxes(axes, xStep, zStep, xStart, zStart), options || {}, chartType); const scalables = []; const layout = { axes, xStep, zStep, xStart, zStart }; @@ -91,6 +92,18 @@ resizeAndRender(canvas); } + function layoutFromAxes(axes, xStep, zStep, xStart, zStart) { + return { + axes, + xStep, + zStep, + xStart, + zStart, + xEnd: xStart + Math.max(1, axes.countries.length - 1) * (xStep || 2), + zEnd: zStart + Math.max(1, axes.years.length - 1) * (zStep || 2) + }; + } + function normalizeFactor(value) { const factor = Number(value); if (!Number.isFinite(factor)) return 1; @@ -102,6 +115,39 @@ return ["bar", "line", "surface", "pie"].includes(text) ? text : "bar"; } + function addAxisGuides(scene, THREE, layout, options, chartType) { + if (chartType === "pie") { + addCanvasLabel(scene, THREE, options.pieAxis || "Pie: country shares", -8.4, 8.2, -7.6, 0.85); + addCanvasLabel(scene, THREE, options.yAxis || "Y: value / indicator", 6.2, 1.4, 6.8, 0.62); + return; + } + + const xEnd = layout.xEnd + 1.2; + const zEnd = layout.zEnd + 1.2; + const axisYOffset = 0.04; + addAxisLine(scene, THREE, new THREE.Vector3(layout.xStart - 1.2, axisYOffset, layout.zStart - 1.0), new THREE.Vector3(xEnd, axisYOffset, layout.zStart - 1.0), 0x2869a6); + addAxisLine(scene, THREE, new THREE.Vector3(layout.xStart - 1.1, axisYOffset, layout.zStart - 1.0), new THREE.Vector3(layout.xStart - 1.1, axisYOffset, zEnd), 0x7a8f2a); + addAxisLine(scene, THREE, new THREE.Vector3(layout.xStart - 1.1, 0, layout.zStart - 1.0), new THREE.Vector3(layout.xStart - 1.1, 8.8, layout.zStart - 1.0), 0xb84f3a); + + addCanvasLabel(scene, THREE, options.xAxis || "X: country", xEnd, 0.45, layout.zStart - 1.2, 0.66); + addCanvasLabel(scene, THREE, options.zAxis || "Z: year / time", layout.xStart - 1.4, 0.45, zEnd, 0.66); + addCanvasLabel(scene, THREE, options.yAxis || "Y: value / indicator", layout.xStart - 1.6, 9.3, layout.zStart - 1.0, 0.66); + } + + function addAxisLine(scene, THREE, from, to, color) { + const material = new THREE.LineBasicMaterial({ color, linewidth: 3 }); + const geometry = new THREE.BufferGeometry().setFromPoints([from, to]); + scene.add(new THREE.Line(geometry, material)); + + const direction = new THREE.Vector3().subVectors(to, from).normalize(); + const cone = new THREE.Mesh( + new THREE.ConeGeometry(0.22, 0.55, 18), + new THREE.MeshBasicMaterial({ color })); + cone.position.copy(to); + cone.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); + scene.add(cone); + } + function rowPosition(row, layout) { const countryIndex = Math.max(0, layout.axes.countries.indexOf(String(row.country || "-"))); const yearIndex = Math.max(0, layout.axes.years.indexOf(Number(row.year || 0)));