diff --git a/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr b/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr
index 5dc2a3749..14a0fae72 100644
--- a/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr
+++ b/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr
@@ -5,7 +5,7 @@ program write_chart_demo;
uses
SysUtils, fpspreadsheet, fpstypes, fpschart, xlsxooxml, fpsopendocument;
const
- SERIES_CLASS: TsChartSeriesClass = TsScatterSeries;
+ SERIES_CLASS: TsChartSeriesClass = TsBubbleSeries; //TsScatterSeries;
r1 = 1;
r2 = 8;
var
@@ -21,18 +21,21 @@ begin
sheet1 := b.AddWorksheet('test1');
sheet1.WriteText(0, 1, '1+sin(x)');
sheet1.WriteText(0, 2, '1+sin(x/2)');
+ sheet1.WriteText(0, 3, 'Bubble');
for i := r1 to r2-1 do
begin
sheet1.WriteNumber(i, 0, i-1);
sheet1.WriteNumber(i, 1, 1+sin(i-1));
sheet1.WriteNumber(i, 2, 1+sin((i-1)/2));
+ sheet1.WriteNumber(i, 3, i*i); // Bubble series radii
end;
sheet1.WriteNumber(r2, 0, 9);
sheet1.WriteNumber(r2, 1, 2);
sheet1.WriteNumber(r2, 2, 2.5);
+ sheet1.WriteNumber(r2, 3, r2*r2);
// Create chart
- ch := b.AddChart(sheet1, 4, 4, 160, 100);
+ ch := b.AddChart(sheet1, 4, 6, 160, 100);
// Add first series (type depending on SERIES_CLASS)
ser := SERIES_CLASS.Create(ch);
@@ -47,16 +50,25 @@ begin
TsLineSeries(ser).ShowSymbols := true;
TsLineSeries(ser).Symbol := cssCircle;
end;
+ if (ser is TsBubbleSeries) then
+ begin
+ TsBubbleSeries(ser).SetXRange(r1, 0, r2, 0);
+ TsBubbleSeries(ser).SetYRange(r1, 2, r2, 2);
+ TsBubbleSeries(ser).SetBubbleRange(r1, 3, r2, 3);
+ end;
- // Add second series
- ser := SERIES_CLASS.Create(ch);
-// ser := TsBarSeries.Create(ch);
- ser.SetTitleAddr(0, 2);
- ser.SetLabelRange(r1, 0, r2, 0);
- ser.SetXRange(r1, 0, r2, 0);
- ser.SetYRange(r1, 2, r2, 2);
- ser.Line.Color := scRed;
- ser.Fill.FgColor := scRed;
+ if SERIES_CLASS <> TsBubbleSeries then
+ begin
+ // Add second series
+ ser := SERIES_CLASS.Create(ch);
+ // ser := TsBarSeries.Create(ch);
+ ser.SetTitleAddr(0, 2);
+ ser.SetLabelRange(r1, 0, r2, 0);
+ ser.SetXRange(r1, 0, r2, 0);
+ ser.SetYRange(r1, 2, r2, 2);
+ ser.Line.Color := scRed;
+ ser.Fill.FgColor := scRed;
+ end;
{$IFDEF DARK_MODE}
ch.Background.FgColor := scBlack;
@@ -71,7 +83,7 @@ begin
ch.Background.Style := fsSolidFill;
ch.Border.Style := clsSolid;
ch.PlotArea.Background.Style := fsSolidFill;
- //ch.RotatedAxes := true;
+ ch.RotatedAxes := true;
ch.StackMode := csmStackedPercentage;
//ch.Interpolation := ciCubicSpline;
diff --git a/components/fpspreadsheet/source/common/fpschart.pas b/components/fpspreadsheet/source/common/fpschart.pas
index c7cf1cb9a..97a840be8 100644
--- a/components/fpspreadsheet/source/common/fpschart.pas
+++ b/components/fpspreadsheet/source/common/fpschart.pas
@@ -111,7 +111,7 @@ type
end;
TsChartAxisPosition = (capStart, capEnd, capValue);
- TsChartType = (ctEmpty, ctBar, ctLine, ctArea, ctBarLine, ctScatter);
+ TsChartType = (ctEmpty, ctBar, ctLine, ctArea, ctBarLine, ctScatter, ctBubble);
TsChartAxis = class(TsChartFillElement)
private
@@ -239,6 +239,15 @@ type
constructor Create(AChart: TsChart); override;
end;
+ TsBubbleSeries = class(TsChartSeries)
+ private
+ FBubbleRange: TsCellRange;
+ public
+ constructor Create(AChart: TsChart); override;
+ procedure SetBubbleRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
+ property BubbleRange: TsCellRange read FBubbleRange;
+ end;
+
TsChartSeriesSymbol = (
cssRect, cssDiamond, cssTriangle, cssTriangleDown, cssTriangleLeft,
cssTriangleRight, cssCircle, cssStar, cssX, cssPlus, cssAsterisk
@@ -368,7 +377,7 @@ type
{ Connecting line between data points (for line and scatter series) }
property Interpolation: TsChartInterpolation read FInterpolation write FInterpolation;
- { x and y axes exchanged (for bar series) }
+ { x and y axes exchanged (mainly for bar series, but works also for scatter and bubble series) }
property RotatedAxes: Boolean read FRotatedAxes write FRotatedAxes;
{ Stacking of series (for bar and area series ) }
property StackMode: TsChartStackMode read FStackMode write FStackMode;
@@ -716,6 +725,23 @@ begin
FChartType := ctBar;
end;
+{ TsBubbleSeries }
+
+constructor TsBubbleSeries.Create(AChart: TsChart);
+begin
+ inherited;
+ FChartType := ctBubble;
+end;
+
+procedure TsBubbleSeries.SetBubbleRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
+begin
+ if (ARow1 <> ARow2) and (ACol1 <> ACol2) then
+ raise Exception.Create('Series bubble values can only be located in a single column or row.');
+ FBubbleRange.Row1 := ARow1;
+ FBubbleRange.Col1 := ACol1;
+ FBubbleRange.Row2 := ARow2;
+ FBubbleRange.Col2 := ACol2;
+end;
{ TsLineSeries }
diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas
index 51598d5cd..20f8fbf6e 100644
--- a/components/fpspreadsheet/source/common/fpsopendocument.pas
+++ b/components/fpspreadsheet/source/common/fpsopendocument.pas
@@ -538,7 +538,7 @@ const
);
CHART_TYPE_NAMES: array[TsChartType] of string = (
- '', 'bar', 'line', 'area', 'barLine', 'scatter'
+ '', 'bar', 'line', 'area', 'barLine', 'scatter', 'bubble'
);
CHART_SYMBOL_NAMES: array[TsChartSeriesSymbol] of String = (
@@ -7142,7 +7142,9 @@ var
sheet: TsWorksheet;
series: TsChartSeries;
valuesRange: String;
- domainRange: String = '';
+ domainRangeX: String = '';
+ domainRangeY: String = '';
+ rangeStr: String = '';
titleAddr: String;
count: Integer;
begin
@@ -7151,10 +7153,10 @@ begin
series := AChart.Series[ASeriesIndex];
sheet := TsWorkbook(FWorkbook).GetWorksheetByIndex(AChart.sheetIndex);
- // These are the x values of a scatter plot.
- if series is TsScatterSeries then
+ // These are the x values of a scatter or bubble plot.
+ if (series is TsScatterSeries) or (series is TsBubbleSeries) then
begin
- domainRange := GetSheetCellRangeString_ODS(
+ domainRangeX := GetSheetCellRangeString_ODS(
sheet.Name, sheet.Name,
series.XRange.Row1, series.XRange.Col1,
series.XRange.Row2, series.XRange.Col2,
@@ -7162,20 +7164,37 @@ begin
);
end;
- // These are the y values
- valuesRange := GetSheetCellRangeString_ODS(
- sheet.Name, sheet.Name,
- series.YRange.Row1, series.YRange.Col1,
- series.YRange.Row2, series.YRange.Col2,
- rfAllRel, false
- );
+ if series is TsBubbleSeries then
+ begin
+ domainRangeY := GetSheetCellRangeString_ODS(
+ sheet.Name, sheet.Name,
+ series.YRange.Row1, series.YRange.Col1,
+ series.YRange.Row2, series.YRange.Col2,
+ rfAllRel, false
+ );
+ // These are the bubble radii
+ valuesRange := GetSheetCellRangeString_ODS(
+ sheet.Name, sheet.Name,
+ TsBubbleSeries(series).BubbleRange.Row1, TsBubbleSeries(series).BubbleRange.Col1,
+ TsBubbleSeries(series).BubbleRange.Row2, TsBubbleSeries(series).BubbleRange.Col2,
+ rfAllRel, false
+ );
+ end else
+ // These are the y values of the non-bubble series
+ valuesRange := GetSheetCellRangeString_ODS(
+ sheet.Name, sheet.Name,
+ series.YRange.Row1, series.YRange.Col1,
+ series.YRange.Row2, series.YRange.Col2,
+ rfAllRel, false
+ );
// And these are the data point labels.
titleAddr := GetSheetCellRangeString_ODS(
sheet.Name, sheet.Name,
series.TitleAddr.Row, series.TitleAddr.Col,
series.TitleAddr.Row, series.TitleAddr.Col,
- rfAllRel, false);
+ rfAllRel, false
+ );
count := series.YRange.Row2 - series.YRange.Row1 + 1;
// Store the series properties
@@ -7186,11 +7205,17 @@ begin
'chart:class="chart:%s">' + LE,
[ AStyleID, valuesRange, titleAddr, CHART_TYPE_NAMES[series.ChartType] ]
));
- if domainRange <> '' then
+ if domainRangeY <> '' then
AppendToStream(AChartStream, Format(
indent + '' + LE,
- [ domainRange ]
+ [ domainRangeY ]
));
+ if domainRangeX <> '' then
+ AppendToStream(AChartStream, Format(
+ indent + '' + LE,
+ [ domainRangeX ]
+ ));
+
AppendToStream(AChartStream, Format(
indent + ' ' + LE,
[ count ]
@@ -7478,7 +7503,7 @@ begin
end;
Result := Format(
- indent + ' ' + LE +
+ indent + ' ', [ AStyleID ]) + LE +
indent + ' ' + LE +
- indent + ' ' + LE,
- [ AStyleID ]
- );
+ indent + ' ' + LE;
end;
{