Fix finance 3D scenario scaling

This commit is contained in:
2026-06-04 13:48:27 +02:00
parent 13a7331f3d
commit 9409174a07
2 changed files with 42 additions and 8 deletions
@@ -1296,20 +1296,20 @@
if (double.TryParse(Convert.ToString(args.Value, CultureInfo.InvariantCulture), NumberStyles.Number, CultureInfo.InvariantCulture, out var value)) if (double.TryParse(Convert.ToString(args.Value, CultureInfo.InvariantCulture), NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
{ {
_finance3dScenarioFactor = Math.Clamp(value, 0.5d, 1.5d); _finance3dScenarioFactor = Math.Clamp(value, 0.5d, 1.5d);
await RenderFinance3dAsync(); await UpdateFinance3dScenarioFactorAsync();
} }
} }
private async Task ResetFinance3dScenarioFactor() private async Task ResetFinance3dScenarioFactor()
{ {
_finance3dScenarioFactor = 1d; _finance3dScenarioFactor = 1d;
await RenderFinance3dAsync(); await UpdateFinance3dScenarioFactorAsync();
} }
private async Task SetFinance3dScenarioFactorPreset(double value) private async Task SetFinance3dScenarioFactorPreset(double value)
{ {
_finance3dScenarioFactor = Math.Clamp(value, 0.5d, 1.5d); _finance3dScenarioFactor = Math.Clamp(value, 0.5d, 1.5d);
await RenderFinance3dAsync(); await UpdateFinance3dScenarioFactorAsync();
} }
private async Task RenderFinance3dAsync() private async Task RenderFinance3dAsync()
@@ -1321,10 +1321,19 @@
await JsRuntime.InvokeVoidAsync("trafagFinance3d.render", _finance3dCanvas, rows, new await JsRuntime.InvokeVoidAsync("trafagFinance3d.render", _finance3dCanvas, rows, new
{ {
indicator = _finance3dIndicator, indicator = _finance3dIndicator,
title = ResolveFinance3dIndicatorLabel(_finance3dIndicator) title = ResolveFinance3dIndicatorLabel(_finance3dIndicator),
scenarioFactor = Finance3dScenarioAffectsValue ? _finance3dScenarioFactor : 1d
}); });
} }
private async Task UpdateFinance3dScenarioFactorAsync()
{
await JsRuntime.InvokeVoidAsync(
"trafagFinance3d.updateFactor",
_finance3dCanvas,
Finance3dScenarioAffectsValue ? _finance3dScenarioFactor : 1d);
}
private IReadOnlyList<object> BuildFinance3dRows() private IReadOnlyList<object> BuildFinance3dRows()
{ {
if (_financeResult is null) if (_financeResult is null)
@@ -1351,8 +1360,8 @@
{ {
Finance3dIndicators.IncludedRows => row.IncludedRows, Finance3dIndicators.IncludedRows => row.IncludedRows,
Finance3dIndicators.ExcludedRows => row.ExcludedRows, Finance3dIndicators.ExcludedRows => row.ExcludedRows,
Finance3dIndicators.Deviation => deviation * (decimal)_finance3dScenarioFactor, Finance3dIndicators.Deviation => deviation,
_ => Math.Abs(row.NetSalesActual) * (decimal)_finance3dScenarioFactor _ => Math.Abs(row.NetSalesActual)
}; };
return new return new
{ {
+27 -2
View File
@@ -14,6 +14,7 @@
function createThreeScene(canvas, rows, options) { function createThreeScene(canvas, rows, options) {
const THREE = window.THREE; const THREE = window.THREE;
const factor = normalizeFactor(options && options.scenarioFactor);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: false }); const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: false });
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.setClearColor(0xf7f9fb, 1); renderer.setClearColor(0xf7f9fb, 1);
@@ -49,6 +50,7 @@
root.add(new THREE.LineSegments(new THREE.BufferGeometry().setFromPoints(gridPoints), gridMaterial)); root.add(new THREE.LineSegments(new THREE.BufferGeometry().setFromPoints(gridPoints), gridMaterial));
const barGeometry = new THREE.BoxGeometry(0.68, 1, 0.68); const barGeometry = new THREE.BoxGeometry(0.68, 1, 0.68);
const bars = [];
rows.forEach(row => { rows.forEach(row => {
const countryIndex = Math.max(0, axes.countries.indexOf(String(row.country || "-"))); const countryIndex = Math.max(0, axes.countries.indexOf(String(row.country || "-")));
const yearIndex = Math.max(0, axes.years.indexOf(Number(row.year || 0))); const yearIndex = Math.max(0, axes.years.indexOf(Number(row.year || 0)));
@@ -60,8 +62,10 @@
metalness: 0.05 metalness: 0.05
}); });
const bar = new THREE.Mesh(barGeometry, material); const bar = new THREE.Mesh(barGeometry, material);
bar.scale.y = height; bar.userData.baseHeight = height;
bar.position.set(xStart + countryIndex * (xStep || 2), height / 2, zStart + yearIndex * (zStep || 2)); bar.position.set(xStart + countryIndex * (xStep || 2), 0, zStart + yearIndex * (zStep || 2));
applyBarFactor(bar, factor);
bars.push(bar);
root.add(bar); root.add(bar);
}); });
@@ -81,6 +85,8 @@
targetX: previous ? previous.targetX : 0, targetX: previous ? previous.targetX : 0,
targetY: previous ? previous.targetY : 2.8, targetY: previous ? previous.targetY : 2.8,
targetZ: previous ? previous.targetZ : 0, targetZ: previous ? previous.targetZ : 0,
factor,
bars,
dragging: false, dragging: false,
dragMode: "rotate", dragMode: "rotate",
lastX: 0, lastX: 0,
@@ -91,6 +97,18 @@
resizeAndRender(canvas); resizeAndRender(canvas);
} }
function normalizeFactor(value) {
const factor = Number(value);
if (!Number.isFinite(factor)) return 1;
return Math.max(0.5, Math.min(1.5, factor));
}
function applyBarFactor(bar, factor) {
const height = Math.max(0.02, Number(bar.userData.baseHeight || 0.08) * factor);
bar.scale.y = height;
bar.position.y = height / 2;
}
function addCanvasLabel(scene, THREE, text, x, y, z, scale) { function addCanvasLabel(scene, THREE, text, x, y, z, scale) {
const labelCanvas = document.createElement("canvas"); const labelCanvas = document.createElement("canvas");
labelCanvas.width = 512; labelCanvas.width = 512;
@@ -247,6 +265,13 @@
renderFallback(canvas, normalizedRows, options || {}); renderFallback(canvas, normalizedRows, options || {});
} }
}, },
updateFactor: function (canvas, factor) {
const state = stateByCanvas.get(canvas);
if (!state || !state.bars) return;
state.factor = normalizeFactor(factor);
state.bars.forEach(bar => applyBarFactor(bar, state.factor));
renderState(state, canvas);
},
resize: resizeAndRender, resize: resizeAndRender,
pixelProbe: function (canvas) { pixelProbe: function (canvas) {
const ctx = canvas && canvas.getContext ? canvas.getContext("2d", { willReadFrequently: true }) : null; const ctx = canvas && canvas.getContext ? canvas.getContext("2d", { willReadFrequently: true }) : null;