Add finance 3D chart modes
This commit is contained in:
@@ -662,6 +662,14 @@
|
|||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
<MudItem xs="12" md="2">
|
||||||
|
<MudSelect T="string" Value="_finance3dChartType" ValueChanged="SetFinance3dChartType" Label="@T("Grafik", "Chart")" Dense>
|
||||||
|
@foreach (var option in _finance3dChartTypeOptions)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@option.Key">@T(option.GermanLabel, option.EnglishLabel)</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
<MudItem xs="12" md="4">
|
<MudItem xs="12" md="4">
|
||||||
<MudText Typo="Typo.caption">@T("Szenario-Faktor / Wechselkurs", "Scenario factor / exchange rate")</MudText>
|
<MudText Typo="Typo.caption">@T("Szenario-Faktor / Wechselkurs", "Scenario factor / exchange rate")</MudText>
|
||||||
<div class="finance-3d-range-row">
|
<div class="finance-3d-range-row">
|
||||||
@@ -696,10 +704,10 @@
|
|||||||
</MudText>
|
</MudText>
|
||||||
}
|
}
|
||||||
</MudItem>
|
</MudItem>
|
||||||
<MudItem xs="12" md="5">
|
<MudItem xs="12" md="3">
|
||||||
<MudText Typo="Typo.body2">
|
<MudText Typo="Typo.body2">
|
||||||
@T("Linke Maustaste drehen, Mausrad zoomen, Shift+Ziehen oder rechte Maustaste verschieben. X-Achse = Land, Z-Achse = Jahr, Hoehe = gewaehlter Indikator.",
|
@T("Linke Maustaste drehen, Mausrad zoomen, Shift+Ziehen oder rechte Maustaste verschieben. Balken/Linie/Flaeche zeigen Land-Jahr-Verlauf, Kreis zeigt Laenderanteile.",
|
||||||
"Left mouse button rotates, mouse wheel zooms, Shift+drag or right mouse button pans. X axis = country, Z axis = year, height = selected indicator.")
|
"Left mouse button rotates, mouse wheel zooms, Shift+drag or right mouse button pans. Bar/line/surface show country-year trend, pie shows country shares.")
|
||||||
</MudText>
|
</MudText>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
@@ -1083,6 +1091,13 @@
|
|||||||
new(Finance3dIndicators.ExcludedRows, "Ausgeschlossene Zeilen", "Excluded rows"),
|
new(Finance3dIndicators.ExcludedRows, "Ausgeschlossene Zeilen", "Excluded rows"),
|
||||||
new(Finance3dIndicators.Deviation, "Soll/Ist Differenz Filterjahr", "Actual/reference difference filter year")
|
new(Finance3dIndicators.Deviation, "Soll/Ist Differenz Filterjahr", "Actual/reference difference filter year")
|
||||||
];
|
];
|
||||||
|
private readonly List<Finance3dChartTypeOption> _finance3dChartTypeOptions =
|
||||||
|
[
|
||||||
|
new(Finance3dChartTypes.Bar, "Balken", "Bar"),
|
||||||
|
new(Finance3dChartTypes.Line, "Linie", "Line"),
|
||||||
|
new(Finance3dChartTypes.Surface, "Flaeche", "Surface"),
|
||||||
|
new(Finance3dChartTypes.Pie, "Kreis / Anteil", "Pie / share")
|
||||||
|
];
|
||||||
private string? _selectedFilePath;
|
private string? _selectedFilePath;
|
||||||
private ManagementCockpitResult? _result;
|
private ManagementCockpitResult? _result;
|
||||||
private ManagementCockpitCentralResult? _centralResult;
|
private ManagementCockpitCentralResult? _centralResult;
|
||||||
@@ -1109,6 +1124,7 @@
|
|||||||
private string _productFinanceGroupLevel = ProductFinanceGroupLevels.Hierarchy;
|
private string _productFinanceGroupLevel = ProductFinanceGroupLevels.Hierarchy;
|
||||||
private bool _limitProductFinanceTop10;
|
private bool _limitProductFinanceTop10;
|
||||||
private string _finance3dIndicator = Finance3dIndicators.Actual;
|
private string _finance3dIndicator = Finance3dIndicators.Actual;
|
||||||
|
private string _finance3dChartType = Finance3dChartTypes.Bar;
|
||||||
private double _finance3dScenarioFactor = 1d;
|
private double _finance3dScenarioFactor = 1d;
|
||||||
private ElementReference _finance3dCanvas;
|
private ElementReference _finance3dCanvas;
|
||||||
private bool _finance3dNeedsRender;
|
private bool _finance3dNeedsRender;
|
||||||
@@ -1291,6 +1307,12 @@
|
|||||||
await RenderFinance3dAsync();
|
await RenderFinance3dAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SetFinance3dChartType(string value)
|
||||||
|
{
|
||||||
|
_finance3dChartType = string.IsNullOrWhiteSpace(value) ? Finance3dChartTypes.Bar : value;
|
||||||
|
await RenderFinance3dAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SetFinance3dScenarioFactor(ChangeEventArgs args)
|
private async Task SetFinance3dScenarioFactor(ChangeEventArgs args)
|
||||||
{
|
{
|
||||||
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))
|
||||||
@@ -1322,6 +1344,7 @@
|
|||||||
{
|
{
|
||||||
indicator = _finance3dIndicator,
|
indicator = _finance3dIndicator,
|
||||||
title = ResolveFinance3dIndicatorLabel(_finance3dIndicator),
|
title = ResolveFinance3dIndicatorLabel(_finance3dIndicator),
|
||||||
|
chartType = _finance3dChartType,
|
||||||
scenarioFactor = Finance3dScenarioAffectsValue ? _finance3dScenarioFactor : 1d
|
scenarioFactor = Finance3dScenarioAffectsValue ? _finance3dScenarioFactor : 1d
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1718,8 +1741,17 @@
|
|||||||
public const string Deviation = "deviation";
|
public const string Deviation = "deviation";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Finance3dChartTypes
|
||||||
|
{
|
||||||
|
public const string Bar = "bar";
|
||||||
|
public const string Line = "line";
|
||||||
|
public const string Surface = "surface";
|
||||||
|
public const string Pie = "pie";
|
||||||
|
}
|
||||||
|
|
||||||
private sealed record ProductFinanceGroupingOption(string Key, string GermanLabel, string EnglishLabel);
|
private sealed record ProductFinanceGroupingOption(string Key, string GermanLabel, string EnglishLabel);
|
||||||
private sealed record Finance3dIndicatorOption(string Key, string GermanLabel, string EnglishLabel);
|
private sealed record Finance3dIndicatorOption(string Key, string GermanLabel, string EnglishLabel);
|
||||||
|
private sealed record Finance3dChartTypeOption(string Key, string GermanLabel, string EnglishLabel);
|
||||||
|
|
||||||
private sealed record ProductFinanceGroupKey(
|
private sealed record ProductFinanceGroupKey(
|
||||||
string ProductDivisionCode,
|
string ProductDivisionCode,
|
||||||
|
|||||||
@@ -15,6 +15,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 factor = normalizeFactor(options && options.scenarioFactor);
|
||||||
|
const chartType = normalizeChartType(options && options.chartType);
|
||||||
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,25 +50,18 @@
|
|||||||
}
|
}
|
||||||
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 scalables = [];
|
||||||
const bars = [];
|
const layout = { axes, xStep, zStep, xStart, zStart };
|
||||||
rows.forEach(row => {
|
if (chartType === "line") {
|
||||||
const countryIndex = Math.max(0, axes.countries.indexOf(String(row.country || "-")));
|
createLineChart(THREE, root, rows, layout, scalables);
|
||||||
const yearIndex = Math.max(0, axes.years.indexOf(Number(row.year || 0)));
|
} else if (chartType === "surface") {
|
||||||
const rawValue = Math.abs(Number(row.value || 0));
|
createSurfaceChart(THREE, root, rows, layout, scalables);
|
||||||
const height = Math.max(0.08, rawValue / axes.maxValue * 8);
|
} else if (chartType === "pie") {
|
||||||
const material = new THREE.MeshStandardMaterial({
|
createPieChart(THREE, root, rows, layout, scalables);
|
||||||
color: colorForValue(rawValue / axes.maxValue),
|
} else {
|
||||||
roughness: 0.58,
|
createBarChart(THREE, root, rows, layout, scalables);
|
||||||
metalness: 0.05
|
}
|
||||||
});
|
applyFactorToScalables(scalables, factor);
|
||||||
const bar = new THREE.Mesh(barGeometry, material);
|
|
||||||
bar.userData.baseHeight = height;
|
|
||||||
bar.position.set(xStart + countryIndex * (xStep || 2), 0, zStart + yearIndex * (zStep || 2));
|
|
||||||
applyBarFactor(bar, factor);
|
|
||||||
bars.push(bar);
|
|
||||||
root.add(bar);
|
|
||||||
});
|
|
||||||
|
|
||||||
addCanvasLabel(scene, THREE, options.title || "", -8.8, 9.2, -7.8, 1.05);
|
addCanvasLabel(scene, THREE, options.title || "", -8.8, 9.2, -7.8, 1.05);
|
||||||
axes.countries.forEach((country, index) => addCanvasLabel(scene, THREE, country, xStart + index * (xStep || 2), -0.15, zStart - 1.3, 0.58));
|
axes.countries.forEach((country, index) => addCanvasLabel(scene, THREE, country, xStart + index * (xStep || 2), -0.15, zStart - 1.3, 0.58));
|
||||||
@@ -86,7 +80,7 @@
|
|||||||
targetY: previous ? previous.targetY : 2.8,
|
targetY: previous ? previous.targetY : 2.8,
|
||||||
targetZ: previous ? previous.targetZ : 0,
|
targetZ: previous ? previous.targetZ : 0,
|
||||||
factor,
|
factor,
|
||||||
bars,
|
scalables,
|
||||||
dragging: false,
|
dragging: false,
|
||||||
dragMode: "rotate",
|
dragMode: "rotate",
|
||||||
lastX: 0,
|
lastX: 0,
|
||||||
@@ -103,12 +97,207 @@
|
|||||||
return Math.max(0.5, Math.min(1.5, factor));
|
return Math.max(0.5, Math.min(1.5, factor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeChartType(value) {
|
||||||
|
const text = String(value || "bar").toLowerCase();
|
||||||
|
return ["bar", "line", "surface", "pie"].includes(text) ? text : "bar";
|
||||||
|
}
|
||||||
|
|
||||||
|
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)));
|
||||||
|
const rawValue = Math.abs(Number(row.value || 0));
|
||||||
|
return {
|
||||||
|
x: layout.xStart + countryIndex * (layout.xStep || 2),
|
||||||
|
z: layout.zStart + yearIndex * (layout.zStep || 2),
|
||||||
|
baseHeight: Math.max(0.08, rawValue / layout.axes.maxValue * 8),
|
||||||
|
ratio: rawValue / layout.axes.maxValue,
|
||||||
|
country: String(row.country || "-"),
|
||||||
|
year: Number(row.year || 0),
|
||||||
|
value: rawValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBarChart(THREE, root, rows, layout, scalables) {
|
||||||
|
const barGeometry = new THREE.BoxGeometry(0.68, 1, 0.68);
|
||||||
|
rows.forEach(row => {
|
||||||
|
const p = rowPosition(row, layout);
|
||||||
|
const material = new THREE.MeshStandardMaterial({
|
||||||
|
color: colorForValue(p.ratio),
|
||||||
|
roughness: 0.58,
|
||||||
|
metalness: 0.05
|
||||||
|
});
|
||||||
|
const bar = new THREE.Mesh(barGeometry, material);
|
||||||
|
bar.userData.baseHeight = p.baseHeight;
|
||||||
|
bar.position.set(p.x, 0, p.z);
|
||||||
|
scalables.push({ type: "bar", object: bar });
|
||||||
|
root.add(bar);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLineChart(THREE, root, rows, layout, scalables) {
|
||||||
|
const pointGeometry = new THREE.SphereGeometry(0.22, 16, 12);
|
||||||
|
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x2f6f9f, linewidth: 2 });
|
||||||
|
const sortedGroups = groupRowsByCountry(rows, layout);
|
||||||
|
sortedGroups.forEach((points, groupIndex) => {
|
||||||
|
const material = new THREE.MeshStandardMaterial({ color: colorForSeries(groupIndex), roughness: 0.5 });
|
||||||
|
const linePositions = [];
|
||||||
|
points.forEach(point => {
|
||||||
|
const marker = new THREE.Mesh(pointGeometry, material);
|
||||||
|
marker.userData.baseHeight = point.baseHeight;
|
||||||
|
marker.userData.baseX = point.x;
|
||||||
|
marker.userData.baseZ = point.z;
|
||||||
|
scalables.push({ type: "point", object: marker });
|
||||||
|
root.add(marker);
|
||||||
|
linePositions.push(point.x, point.baseHeight, point.z);
|
||||||
|
});
|
||||||
|
if (linePositions.length >= 6) {
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
geometry.setAttribute("position", new THREE.Float32BufferAttribute(linePositions, 3));
|
||||||
|
const line = new THREE.Line(geometry, lineMaterial.clone());
|
||||||
|
line.userData.basePositions = linePositions.slice();
|
||||||
|
scalables.push({ type: "line", object: line });
|
||||||
|
root.add(line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSurfaceChart(THREE, root, rows, layout, scalables) {
|
||||||
|
const rowByKey = new Map(rows.map(row => [`${String(row.country || "-")}|${Number(row.year || 0)}`, rowPosition(row, layout)]));
|
||||||
|
const vertices = [];
|
||||||
|
const indices = [];
|
||||||
|
layout.axes.countries.forEach(country => {
|
||||||
|
layout.axes.years.forEach(year => {
|
||||||
|
const p = rowByKey.get(`${country}|${year}`) || {
|
||||||
|
x: layout.xStart + Math.max(0, layout.axes.countries.indexOf(country)) * (layout.xStep || 2),
|
||||||
|
z: layout.zStart + Math.max(0, layout.axes.years.indexOf(year)) * (layout.zStep || 2),
|
||||||
|
baseHeight: 0.04
|
||||||
|
};
|
||||||
|
vertices.push(p.x, p.baseHeight, p.z);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const yearCount = layout.axes.years.length;
|
||||||
|
for (let c = 0; c < layout.axes.countries.length - 1; c++) {
|
||||||
|
for (let y = 0; y < layout.axes.years.length - 1; y++) {
|
||||||
|
const a = c * yearCount + y;
|
||||||
|
const b = (c + 1) * yearCount + y;
|
||||||
|
const d = c * yearCount + y + 1;
|
||||||
|
const e = (c + 1) * yearCount + y + 1;
|
||||||
|
indices.push(a, b, d, b, e, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
|
||||||
|
geometry.setIndex(indices);
|
||||||
|
geometry.computeVertexNormals();
|
||||||
|
const material = new THREE.MeshStandardMaterial({
|
||||||
|
color: 0x4188a8,
|
||||||
|
roughness: 0.64,
|
||||||
|
metalness: 0.03,
|
||||||
|
opacity: 0.72,
|
||||||
|
transparent: true,
|
||||||
|
side: THREE.DoubleSide
|
||||||
|
});
|
||||||
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
|
mesh.userData.basePositions = vertices.slice();
|
||||||
|
scalables.push({ type: "surface", object: mesh });
|
||||||
|
root.add(mesh);
|
||||||
|
|
||||||
|
const wire = new THREE.LineSegments(new THREE.WireframeGeometry(geometry), new THREE.LineBasicMaterial({ color: 0x24485a, transparent: true, opacity: 0.42 }));
|
||||||
|
wire.userData.sourceGeometry = geometry;
|
||||||
|
scalables.push({ type: "wire", object: wire, source: mesh });
|
||||||
|
root.add(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPieChart(THREE, root, rows, layout, scalables) {
|
||||||
|
const totals = [...rows.reduce((map, row) => {
|
||||||
|
const country = String(row.country || "-");
|
||||||
|
map.set(country, (map.get(country) || 0) + Math.abs(Number(row.value || 0)));
|
||||||
|
return map;
|
||||||
|
}, new Map())]
|
||||||
|
.filter(([, value]) => value > 0)
|
||||||
|
.sort((a, b) => b[1] - a[1]);
|
||||||
|
const total = totals.reduce((sum, [, value]) => sum + value, 0) || 1;
|
||||||
|
let start = -Math.PI / 2;
|
||||||
|
totals.forEach(([country, value], index) => {
|
||||||
|
const angle = value / total * Math.PI * 2;
|
||||||
|
const shape = new THREE.Shape();
|
||||||
|
shape.moveTo(0, 0);
|
||||||
|
const steps = Math.max(8, Math.ceil(angle / (Math.PI / 20)));
|
||||||
|
for (let i = 0; i <= steps; i++) {
|
||||||
|
const a = start + angle * i / steps;
|
||||||
|
shape.lineTo(Math.cos(a) * 6, Math.sin(a) * 6);
|
||||||
|
}
|
||||||
|
shape.lineTo(0, 0);
|
||||||
|
const geometry = new THREE.ExtrudeGeometry(shape, { depth: 0.35, bevelEnabled: false });
|
||||||
|
const material = new THREE.MeshStandardMaterial({ color: colorForSeries(index), roughness: 0.55 });
|
||||||
|
const slice = new THREE.Mesh(geometry, material);
|
||||||
|
slice.rotation.x = -Math.PI / 2;
|
||||||
|
slice.position.y = 0.04;
|
||||||
|
slice.userData.baseHeight = 0.35;
|
||||||
|
scalables.push({ type: "pie", object: slice });
|
||||||
|
root.add(slice);
|
||||||
|
|
||||||
|
const labelAngle = start + angle / 2;
|
||||||
|
addCanvasLabel(root, THREE, country, Math.cos(labelAngle) * 7.1, 0.25, Math.sin(labelAngle) * 7.1, 0.5);
|
||||||
|
start += angle;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupRowsByCountry(rows, layout) {
|
||||||
|
const groups = new Map();
|
||||||
|
rows.forEach(row => {
|
||||||
|
const p = rowPosition(row, layout);
|
||||||
|
if (!groups.has(p.country)) groups.set(p.country, []);
|
||||||
|
groups.get(p.country).push(p);
|
||||||
|
});
|
||||||
|
return [...groups.values()].map(points => points.sort((a, b) => a.year - b.year));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyFactorToScalables(scalables, factor) {
|
||||||
|
scalables.forEach(item => {
|
||||||
|
if (item.type === "bar") {
|
||||||
|
applyBarFactor(item.object, factor);
|
||||||
|
} else if (item.type === "point") {
|
||||||
|
applyPointFactor(item.object, factor);
|
||||||
|
} else if (item.type === "line" || item.type === "surface") {
|
||||||
|
applyPositionFactor(item.object, factor);
|
||||||
|
} else if (item.type === "wire") {
|
||||||
|
item.object.geometry.dispose();
|
||||||
|
item.object.geometry = new window.THREE.WireframeGeometry(item.source.geometry);
|
||||||
|
} else if (item.type === "pie") {
|
||||||
|
item.object.scale.z = factor;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function applyBarFactor(bar, factor) {
|
function applyBarFactor(bar, factor) {
|
||||||
const height = Math.max(0.02, Number(bar.userData.baseHeight || 0.08) * factor);
|
const height = Math.max(0.02, Number(bar.userData.baseHeight || 0.08) * factor);
|
||||||
bar.scale.y = height;
|
bar.scale.y = height;
|
||||||
bar.position.y = height / 2;
|
bar.position.y = height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyPointFactor(point, factor) {
|
||||||
|
point.scale.set(1, 1, 1);
|
||||||
|
point.position.set(
|
||||||
|
Number(point.userData.baseX || 0),
|
||||||
|
Math.max(0.02, Number(point.userData.baseHeight || 0.08) * factor),
|
||||||
|
Number(point.userData.baseZ || 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyPositionFactor(object, factor) {
|
||||||
|
const base = object.userData.basePositions;
|
||||||
|
const attribute = object.geometry && object.geometry.attributes && object.geometry.attributes.position;
|
||||||
|
if (!base || !attribute) return;
|
||||||
|
for (let i = 0; i < base.length; i += 3) {
|
||||||
|
attribute.array[i] = base[i];
|
||||||
|
attribute.array[i + 1] = Math.max(0.02, base[i + 1] * factor);
|
||||||
|
attribute.array[i + 2] = base[i + 2];
|
||||||
|
}
|
||||||
|
attribute.needsUpdate = true;
|
||||||
|
object.geometry.computeBoundingSphere();
|
||||||
|
if (object.geometry.computeVertexNormals) object.geometry.computeVertexNormals();
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -136,6 +325,11 @@
|
|||||||
return (r << 16) + (g << 8) + b;
|
return (r << 16) + (g << 8) + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function colorForSeries(index) {
|
||||||
|
const colors = [0x2f6f9f, 0xc45a42, 0x6b8f3a, 0x8b6bb1, 0xd09b2c, 0x4d908e, 0x9d4edd, 0x577590];
|
||||||
|
return colors[index % colors.length];
|
||||||
|
}
|
||||||
|
|
||||||
function attachInteraction(canvas, state) {
|
function attachInteraction(canvas, state) {
|
||||||
canvas.onpointerdown = event => {
|
canvas.onpointerdown = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -267,9 +461,9 @@
|
|||||||
},
|
},
|
||||||
updateFactor: function (canvas, factor) {
|
updateFactor: function (canvas, factor) {
|
||||||
const state = stateByCanvas.get(canvas);
|
const state = stateByCanvas.get(canvas);
|
||||||
if (!state || !state.bars) return;
|
if (!state || !state.scalables) return;
|
||||||
state.factor = normalizeFactor(factor);
|
state.factor = normalizeFactor(factor);
|
||||||
state.bars.forEach(bar => applyBarFactor(bar, state.factor));
|
applyFactorToScalables(state.scalables, state.factor);
|
||||||
renderState(state, canvas);
|
renderState(state, canvas);
|
||||||
},
|
},
|
||||||
resize: resizeAndRender,
|
resize: resizeAndRender,
|
||||||
|
|||||||
Reference in New Issue
Block a user