From a36fa7cd2fa94d20461bc9d03fbc7ebdcb570d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20M=C3=BCller?= Date: Wed, 27 Dec 2023 12:13:30 +0100 Subject: [PATCH] support for multiple datasets added --- demos/xychart.html | 24 ++++- .../chartBuilder/components/plot/barPlot.ts | 95 ++++++++++--------- .../chartBuilder/components/plot/index.ts | 55 ++++++----- .../src/diagrams/xychart/parser/xychart.jison | 19 +++- .../mermaid/src/diagrams/xychart/xychartDb.ts | 18 ++-- 5 files changed, 128 insertions(+), 83 deletions(-) diff --git a/demos/xychart.html b/demos/xychart.html index 1d8bad78b..4f2711016 100644 --- a/demos/xychart.html +++ b/demos/xychart.html @@ -52,7 +52,7 @@ line [+1.3, .6, 2.4, -.34] -

XY Charts Bar with multiple category

+

XY Charts bar with single dataset

     xychart-beta
     title "Basic xychart with many categories"
@@ -61,7 +61,25 @@
     bar "sample bar" [52, 96, 35, 10, 87, 34, 67, 99]
     
-

XY Charts line with multiple category

+

XY Charts bar with multiple datasets

+
+      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]]]
+    
+ +

XY Charts bar horizontal with multiple datasets

+
+      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]]]
+    
+ +

XY Charts line single dataset

     xychart-beta
     title "Line chart with many category"
@@ -70,7 +88,7 @@
     line "sample line" [52, 96, 35, 10, 87, 34, 67, 99]
     
-

XY Charts category with large text

+

XY Charts bar with large text

     xychart-beta
     title "Basic xychart with many categories with category overlap"
diff --git a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts
index cf7d4e516..8d4c3aa1e 100644
--- a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts
+++ b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/barPlot.ts
@@ -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,
+            };
+          }),
+        };
+      });
   }
 }
diff --git a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/index.ts b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/index.ts
index 2a7b4a283..1eea56436 100644
--- a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/index.ts
+++ b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/plot/index.ts
@@ -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;
   }
diff --git a/packages/mermaid/src/diagrams/xychart/parser/xychart.jison b/packages/mermaid/src/diagrams/xychart/parser/xychart.jison
index 987132d17..d44dbd170 100644
--- a/packages/mermaid/src/diagrams/xychart/parser/xychart.jison
+++ b/packages/mermaid/src/diagrams/xychart/parser/xychart.jison
@@ -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 }
   ;
diff --git a/packages/mermaid/src/diagrams/xychart/xychartDb.ts b/packages/mermaid/src/diagrams/xychart/xychartDb.ts
index 637477f28..65b7d3b66 100644
--- a/packages/mermaid/src/diagrams/xychart/xychartDb.ts
+++ b/packages/mermaid/src/diagrams/xychart/xychartDb.ts
@@ -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[] {