support for multiple datasets added

This commit is contained in:
Axel Müller
2023-12-27 12:13:30 +01:00
parent 60280361b0
commit a36fa7cd2f
5 changed files with 128 additions and 83 deletions

View File

@@ -52,7 +52,7 @@
line [+1.3, .6, 2.4, -.34]
</pre>
<h1>XY Charts Bar with multiple category</h1>
<h1>XY Charts bar with single dataset</h1>
<pre class="mermaid">
xychart-beta
title "Basic xychart with many categories"
@@ -61,7 +61,25 @@
bar "sample bar" [52, 96, 35, 10, 87, 34, 67, 99]
</pre>
<h1>XY Charts line with multiple category</h1>
<h1>XY Charts bar with multiple datasets</h1>
<pre class="mermaid">
xychart-beta
title "Basic xychart with multiple datasets"
x-axis "Relevant categories" [category1, "category 2", category3, category4, category5, category6, category7]
y-axis Animals 10 --> 200
bar [["dogs" [52, 96, 35, 10, 87, 34, 67, 99]],["cats" [15, 7, 23, 55, 11, 41, 26, 3]]]
</pre>
<h1>XY Charts bar horizontal with multiple datasets</h1>
<pre class="mermaid">
xychart-beta horizontal
title "Basic xychart with multiple datasets"
x-axis "Relevant categories" [category1, "category 2", category3, category4, category5, category6, category7]
y-axis Animals 10 --> 200
bar [["dogs" [52, 96, 35, 10, 87, 34, 67, 99]],["cats" [15, 7, 23, 55, 11, 41, 26, 3]]]
</pre>
<h1>XY Charts line single dataset</h1>
<pre class="mermaid">
xychart-beta
title "Line chart with many category"
@@ -70,7 +88,7 @@
line "sample line" [52, 96, 35, 10, 87, 34, 67, 99]
</pre>
<h1>XY Charts category with large text</h1>
<h1>XY Charts bar with large text</h1>
<pre class="mermaid">
xychart-beta
title "Basic xychart with many categories with category overlap"

View File

@@ -3,7 +3,7 @@ import type { Axis } from '../axis/index.js';
export class BarPlot {
constructor(
private barData: BarPlotData,
private barData: BarPlotData[],
private boundingRect: BoundingRect,
private xAxis: Axis,
private yAxis: Axis,
@@ -12,49 +12,58 @@ export class BarPlot {
) {}
getDrawableElement(): DrawableElem[] {
const finalData: [number, number][] = this.barData.data.map((d) => [
this.xAxis.getScaleValue(d[0]),
this.yAxis.getScaleValue(d[1]),
]);
const offset = new Array(this.barData[0].data.length).fill(0);
return this.barData.map((barData, dataIndex) => {
const finalData: [number, number][] = barData.data.map((d) => [
this.xAxis.getScaleValue(d[0]),
this.yAxis.getScaleValue(d[1]),
]);
const barPaddingPercent = 0.05;
const barPaddingPercent = 0.05;
const barWidth =
Math.min(this.xAxis.getAxisOuterPadding() * 2, this.xAxis.getTickDistance()) *
(1 - barPaddingPercent);
const barWidthHalf = barWidth / 2;
if (this.orientation === 'horizontal') {
return [
{
groupTexts: ['plot', `bar-plot-${this.plotIndex}`],
const barWidth =
Math.min(this.xAxis.getAxisOuterPadding() * 2, this.xAxis.getTickDistance()) *
(1 - barPaddingPercent);
const barWidthHalf = barWidth / 2;
if (this.orientation === 'horizontal') {
return {
groupTexts: ['plot', `bar-plot-${this.plotIndex}-${dataIndex}`],
type: 'rect',
data: finalData.map((data, index) => {
const x = offset[index] + this.boundingRect.x;
const width = data[1] - this.boundingRect.x;
offset[index] += width;
return {
x,
y: data[0] - barWidthHalf,
height: barWidth,
width,
fill: barData.fill,
strokeWidth: 0,
strokeFill: barData.fill,
}
}),
};
}
return {
groupTexts: ['plot', `bar-plot-${this.plotIndex}-${dataIndex}`],
type: 'rect',
data: finalData.map((data) => ({
x: this.boundingRect.x,
y: data[0] - barWidthHalf,
height: barWidth,
width: data[1] - this.boundingRect.x,
fill: this.barData.fill,
strokeWidth: 0,
strokeFill: this.barData.fill,
})),
},
];
}
return [
{
groupTexts: ['plot', `bar-plot-${this.plotIndex}`],
type: 'rect',
data: finalData.map((data) => ({
x: data[0] - barWidthHalf,
y: data[1],
width: barWidth,
height: this.boundingRect.y + this.boundingRect.height - data[1],
fill: this.barData.fill,
strokeWidth: 0,
strokeFill: this.barData.fill,
})),
},
];
data: finalData.map((data, index) => {
const y = data[1] - offset[index];
const height = this.boundingRect.y + this.boundingRect.height - data[1];
offset[index] += height;
return {
x: data[0] - barWidthHalf,
y,
width: barWidth,
height,
fill: barData.fill,
strokeWidth: 0,
strokeFill: barData.fill,
};
}),
};
});
}
}

View File

@@ -6,6 +6,8 @@ import type {
Point,
XYChartThemeConfig,
XYChartConfig,
BarPlotData,
LinePlotData,
} from '../../interfaces.js';
import type { Axis } from '../axis/index.js';
import type { ChartComponent } from '../../interfaces.js';
@@ -55,34 +57,31 @@ export class BasePlot implements Plot {
throw Error('Axes must be passed to render Plots');
}
const drawableElem: DrawableElem[] = [];
for (const [i, plot] of this.chartData.plots.entries()) {
switch (plot.type) {
case 'line':
{
const linePlot = new LinePlot(
plot,
this.xAxis,
this.yAxis,
this.chartConfig.chartOrientation,
i
);
drawableElem.push(...linePlot.getDrawableElement());
}
break;
case 'bar':
{
const barPlot = new BarPlot(
plot,
this.boundingRect,
this.xAxis,
this.yAxis,
this.chartConfig.chartOrientation,
i
);
drawableElem.push(...barPlot.getDrawableElement());
}
break;
}
const linePlots = this.chartData.plots.filter(plot => plot.type === 'line') as LinePlotData[];
const barPlots = this.chartData.plots.filter(plot => plot.type === 'bar') as BarPlotData[];
let plotIndex = 0;
if(linePlots.length) {
const linePlot = new LinePlot(
linePlots[0],
this.xAxis,
this.yAxis,
this.chartConfig.chartOrientation,
plotIndex
);
drawableElem.push(...linePlot.getDrawableElement());
}
if(barPlots.length) {
const barPlot = new BarPlot(
barPlots,
this.boundingRect,
this.xAxis,
this.yAxis,
this.chartConfig.chartOrientation,
plotIndex
);
drawableElem.push(...barPlot.getDrawableElement());
plotIndex++;
}
return drawableElem;
}

View File

@@ -102,13 +102,28 @@ statement
| Y_AXIS parseYAxis
| LINE plotData { yy.setLineData({text: '', type: 'text'}, $plotData); }
| LINE text plotData { yy.setLineData($text, $plotData); }
| BAR plotData { yy.setBarData({text: '', type: 'text'}, $plotData); }
| BAR text plotData { yy.setBarData($text, $plotData); }
| BAR datasets { yy.setBarData($datasets); }
| acc_title acc_title_value { $$=$acc_title_value.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$acc_descr_value.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$acc_descr_multiline_value.trim();yy.setAccDescription($$); }
;
datasets
: SQUARE_BRACES_START datasetBraced COMMA datasets SQUARE_BRACES_END { $$ = [$datasetBraced, ...$datasets] }
| SQUARE_BRACES_START datasetBraced SQUARE_BRACES_END { $$ = [$datasetBraced] }
| datasetBraced { $$ = [$datasetBraced] }
| dataset { $$ = [$dataset] }
;
datasetBraced
: SQUARE_BRACES_START dataset SQUARE_BRACES_END { $$ = $dataset }
;
dataset
: plotData { $$ = ['', $plotData] }
| text plotData { $$ = [$text, $plotData] }
;
plotData
: SQUARE_BRACES_START commaSeparatedNumbers SQUARE_BRACES_END { $$ = $commaSeparatedNumbers }
;

View File

@@ -169,14 +169,18 @@ function setLineData(title: NormalTextType, data: number[]) {
plotIndex++;
}
function setBarData(title: NormalTextType, data: number[]) {
const plotData = transformDataWithoutCategory(data);
xyChartData.plots.push({
type: 'bar',
fill: getPlotColorFromPalette(plotIndex),
data: plotData,
type NamedDataset = [title: NormalTextType, data: number[]];
function setBarData(datasets: NamedDataset[]) {
datasets.forEach(dataset => {
const plotData = transformDataWithoutCategory(dataset[1]);
xyChartData.plots.push({
type: 'bar',
fill: getPlotColorFromPalette(plotIndex),
data: plotData,
});
plotIndex++;
});
plotIndex++;
}
function getDrawableElem(): DrawableElem[] {