diff --git a/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr b/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr
index 482e8e812..4b2ccc7e7 100644
--- a/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr
+++ b/components/fpspreadsheet/examples/other/chart/write_chart_demo.lpr
@@ -9,9 +9,9 @@ const
// SERIES_CLASS: TsChartSeriesClass = TsBarSeries;
// SERIES_CLASS: TsChartSeriesClass = TsBubbleSeries;
// SERIES_CLASS: TsChartSeriesClass = TsLineSeries;
-// SERIES_CLASS: TsChartSeriesClass = TsScatterSeries;
+ SERIES_CLASS: TsChartSeriesClass = TsScatterSeries;
// SERIES_CLASS: TsChartSeriesClass = TsRadarSeries;
- SERIES_CLASS: TsChartSeriesClass = TsPieSeries;
+// SERIES_CLASS: TsChartSeriesClass = TsPieSeries;
r1 = 1;
r2 = 8;
FILL_COLORS: array[0..r2-r1] of TsColor = (scRed, scGreen, scBlue, scYellow, scMagenta, scSilver, scBlack, scOlive);
diff --git a/components/fpspreadsheet/source/common/fpschart.pas b/components/fpspreadsheet/source/common/fpschart.pas
index 40ae892f1..0d7c5d685 100644
--- a/components/fpspreadsheet/source/common/fpschart.pas
+++ b/components/fpspreadsheet/source/common/fpschart.pas
@@ -311,9 +311,50 @@ type
constructor Create(AChart: TsChart); override;
end;
+ TsRegressionType = (rtNone, rtLinear, rtLogarithmic, rtExponential, rtPower, rtPolynomial);
+
+ TsRegressionEquation = class
+ Fill: TsChartFill;
+ Font: TsFont;
+ Border: TsChartLine;
+ NumberFormat: String;
+ Left, Top: Double; // mm, relative to outer chart boundaries!
+ XName: String;
+ YName: String;
+ constructor Create;
+ destructor Destroy; override;
+ function DefaultBorder: Boolean;
+ function DefaultFill: Boolean;
+ function DefaultFont: Boolean;
+ function DefaultNumberFormat: Boolean;
+ function DefaultPosition: Boolean;
+ function DefaultXName: Boolean;
+ function DefaultYName: Boolean;
+ end;
+
+ TsChartRegression = class
+ Title: String;
+ RegressionType: TsRegressionType;
+ ExtrapolateForwardBy: Double;
+ ExtrapolateBackwardBy: Double;
+ ForceYIntercept: Boolean;
+ YInterceptValue: Double;
+ PolynomialDegree: Integer;
+ DisplayEquation: Boolean;
+ DisplayRSquare: Boolean;
+ Equation: TsRegressionEquation;
+ Line: TsChartLine;
+ constructor Create;
+ destructor Destroy; override;
+ end;
+
TsScatterSeries = class(TsLineSeries)
+ private
+ FRegression: TsChartRegression;
public
constructor Create(AChart: TsChart); override;
+ destructor Destroy; override;
+ property Regression: TsChartRegression read FRegression write FRegression;
end;
TsChartSeriesList = class(TFPList)
@@ -846,6 +887,7 @@ begin
Result := ctRadar;
end;
+
{ TsRingSeries }
constructor TsRingSeries.Create(AChart: TsChart);
begin
@@ -855,12 +897,99 @@ begin
end;
+{ TsRegressionEquation }
+constructor TsRegressionEquation.Create;
+begin
+ inherited Create;
+ Font := TsFont.Create;
+ Font.Size := 9;
+ Border := TsChartLine.Create;
+ Border.Style := clsNoLine;
+ Border.Width := PtsToMM(DEFAULT_CHART_LINEWIDTH);
+ Border.Color := scBlack;
+ Fill := TsChartFill.Create;
+ Fill.FgColor := scWhite;
+ XName := 'x';
+ YName := 'f(x)';
+end;
+
+destructor TsRegressionEquation.Destroy;
+begin
+ Fill.Free;
+ Border.Free;
+ Font.Free;
+ inherited;
+end;
+
+function TsRegressionEquation.DefaultBorder: Boolean;
+begin
+ Result := Border.Style = clsNoLine;
+end;
+
+function TsRegressionEquation.DefaultFill: Boolean;
+begin
+ Result := Fill.Style = fsNoFill;
+end;
+
+function TsRegressionEquation.DefaultFont: Boolean;
+begin
+ Result := (Font.FontName = '') and (Font.Size = 9) and (Font.Style = []) and
+ (Font.Color = scBlack);
+end;
+
+function TsRegressionEquation.DefaultNumberFormat: Boolean;
+begin
+ Result := NumberFormat = '';
+end;
+
+function TsRegressionEquation.DefaultPosition: Boolean;
+begin
+ Result := (Left = 0) and (Top = 0);
+end;
+
+function TsRegressionEquation.DefaultXName: Boolean;
+begin
+ Result := XName = 'x';
+end;
+
+function TsRegressionEquation.DefaultYName: Boolean;
+begin
+ Result := YName = 'f(x)';
+end;
+
+
+{ TsChartRegression }
+constructor TsChartRegression.Create;
+begin
+ inherited Create;
+ Line := TsChartLine.Create;
+ Line.Style := clsSolid;
+ Line.Width := PtsToMM(DEFAULT_CHART_LINEWIDTH);
+ Line.Color := scBlack;
+
+ Equation := TsRegressionEquation.Create;
+end;
+
+destructor TsChartRegression.Destroy;
+begin
+ Line.Free;
+ inherited;
+end;
+
+
{ TsScatterSeries }
constructor TsScatterSeries.Create(AChart: TsChart);
begin
inherited Create(AChart);
FChartType := ctScatter;
+ FRegression := TsChartRegression.Create;
+end;
+
+destructor TsScatterSeries.Destroy;
+begin
+ FRegression.Free;
+ inherited;
end;
diff --git a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
index a20893843..8b3e94a71 100644
--- a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
+++ b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
@@ -23,11 +23,19 @@ type
function GetChartBackgroundStyleAsXML(AChart: TsChart; AFill: TsChartFill;
ABorder: TsChartLine; AIndent: Integer; AStyleID: Integer): String;
function GetChartCaptionStyleAsXML(AChart: TsChart; ACaptionKind, AIndent, AStyleID: Integer): String;
- function GetChartFillStyleGraphicPropsAsXML(AChart: TsChart; AFill: TsChartFill): String;
- function GetChartLegendStyleAsXML(AChart: TsChart; AIndent, AStyleID: Integer): String;
- function GetChartLineStyleAsXML(AChart: TsChart; ALine: TsChartLine; AIndent, AStyleID: Integer): String;
- function GetChartLineStyleGraphicPropsAsXML(AChart: TsChart; ALine: TsChartLine): String;
- function GetChartPlotAreaStyleAsXML(AChart: TsChart; AIndent, AStyleID: Integer): String;
+ function GetChartFillStyleGraphicPropsAsXML(AChart: TsChart;
+ AFill: TsChartFill): String;
+ function GetChartLegendStyleAsXML(AChart: TsChart;
+ AIndent, AStyleID: Integer): String;
+ function GetChartLineStyleAsXML(AChart: TsChart;
+ ALine: TsChartLine; AIndent, AStyleID: Integer): String;
+ function GetChartLineStyleGraphicPropsAsXML(AChart: TsChart;
+ ALine: TsChartLine): String;
+ function GetChartPlotAreaStyleAsXML(AChart: TsChart;
+ AIndent, AStyleID: Integer): String;
+ function GetChartRegressionEquationStyleAsXML(AChart: TsChart;
+ AEquation: TsRegressionEquation; AIndent, AStyleID: Integer): String;
+ function GetChartRegressionStyleAsXML(AChart: TsChart; ASeriesIndex, AIndent, AStyleID: Integer): String;
function GetChartSeriesStyleAsXML(AChart: TsChart; ASeriesIndex, AIndent, AStyleID: integer): String;
// function GetChartTitleStyleAsXML(AChart: TsChart; AStyleIndex, AIndent: Integer): String;
procedure PrepareChartTable(AChart: TsChart; AWorksheet: TsBasicWorksheet);
@@ -525,6 +533,100 @@ begin
indent + ' ' + LE;
end;
+function TsSpreadOpenDocChartWriter.GetChartRegressionEquationStyleAsXML(
+ AChart: TsChart; AEquation: TsRegressionEquation; AIndent, AStyleID: Integer): String;
+var
+ indent: String;
+ numStyle: String = 'N0';
+ chartprops: String = '';
+ lineprops: String = '';
+ fillprops: String = '';
+ textprops: String = '';
+begin
+ Result := '';
+
+ indent := DupeString(' ', AIndent);
+
+ // TO DO: Create chart number style list and find the current style there!
+ if not AEquation.DefaultNumberFormat then
+ numStyle := 'N0';
+
+ if not AEquation.DefaultXName then
+ chartprops := chartprops + Format('loext:regression-x-name="%s" ', [AEquation.XName]);
+ if not AEquation.DefaultYName then
+ chartprops := chartprops + Format('loext:regression-y-name="%s" ', [AEquation.YName]) ;
+
+ if not AEquation.DefaultBorder then
+ lineProps := GetChartLineStyleGraphicPropsAsXML(AChart, AEquation.Border);
+
+ if not AEquation.DefaultFill then
+ fillProps := GetChartFillStyleGraphicPropsAsXML(AChart, AEquation.Fill);
+
+ if not AEquation.DefaultFont then
+ textprops := TsSpreadOpenDocWriter(Writer).WriteFontStyleXMLAsString(AEquation.Font);
+
+ Result := Format(
+ indent + '' + LE +
+ indent + ' ' + LE +
+ indent + ' ' + LE +
+ indent + ' ' + LE +
+ indent + '' + LE,
+ [ AStyleID, numStyle, chartprops, fillprops + lineprops, textprops ]
+ );
+end;
+
+function TsSpreadOpenDocChartWriter.GetChartRegressionStyleAsXML(AChart: TsChart;
+ ASeriesIndex, AIndent, AStyleID: Integer): String;
+const
+ REGRESSION_TYPE: array [TsRegressionType] of string = (
+ '', 'linear', 'logarithmic', 'exponential', 'power', 'polynomial');
+var
+ series: TsScatterSeries;
+ indent: String;
+ chartProps: String = '';
+ graphProps: String = '';
+ textProps: String = '';
+ lineProps: String = '';
+ fillProps: String = '';
+ labelSeparator: String = '';
+begin
+ Result := '';
+ series := AChart.Series[ASeriesIndex] as TsScatterSeries;
+ if series.Regression.RegressionType = rtNone then
+ exit;
+
+ indent := DupeString(' ', AIndent);
+
+ chartprops := Format(
+ 'chart:regression-name="%s" ' +
+ 'chart:regression-type="%s" ' +
+ 'chart:regression-extrapolate-forward="%g" ' +
+ 'chart:regression-extrapolate-backward="%g" ' +
+ 'chart:regression-force-intercept="%s" ' +
+ 'chart:regression-intercept-value="%g" ' +
+ 'chart:regression-max-degree="%d" ',
+ [ series.Regression.Title,
+ REGRESSION_TYPE[series.Regression.RegressionType] ,
+ series.Regression.ExtrapolateForwardBy,
+ series.Regression.ExtrapolateBackwardBy,
+ FALSE_TRUE[series.Regression.ForceYIntercept],
+ series.Regression.YInterceptValue,
+ series.Regression.PolynomialDegree
+ ], FPointSeparatorSettings
+ );
+
+ graphprops := GetChartLineStyleGraphicPropsAsXML(AChart, series.Regression.Line);
+
+ Result := Format(
+ indent + ' ' + LE +
+ indent + ' ' + LE +
+ indent + ' ' + LE +
+ indent + '' + LE,
+ [ AStyleID, chartprops, graphprops ]
+ );
+end;
+
+
{
clsNoLine) then
- graphProps := graphProps + lineProps;
+ graphProps := graphProps + lineProps
+ else
+ graphProps := graphProps + 'draw:stroke="none" ';
end else
graphProps := fillProps + lineProps;
@@ -1294,6 +1398,10 @@ var
domainRangeY: String = '';
fillColorRange: String = '';
chartClass: String = '';
+ regressionEquation: String = '';
+ needRegressionStyle: Boolean = false;
+ needRegressionEquationStyle: Boolean = false;
+ regression: TsChartRegression = nil;
titleAddr: String;
count: Integer;
begin
@@ -1366,10 +1474,10 @@ begin
// Store the series properties
AppendToStream(AChartStream, Format(
indent + '' + LE,
- [ AStyleID, valuesRange, titleAddr, chartClass ]
+ 'chart:label-cell-address="%s">' + LE, // series title
+ [ AStyleID, chartClass, valuesRange, titleAddr, chartClass ]
));
if domainRangeY <> '' then
AppendToStream(AChartStream, Format(
@@ -1390,11 +1498,57 @@ begin
{ --- not working...
if borderColorRange <> '' then
AppendToStream(AChartStream, Format(
- indent + '' + LE,
+ indent + '' + LE,
[ borderColorRange ]
));
}
+ // Regression
+ if (series is TsScatterSeries) then
+ begin
+ regression := TsScatterSeries(series).Regression;
+ if regression.RegressionType <> rtNone then
+ begin
+ if regression.DisplayEquation or regression.DisplayRSquare then
+ begin
+ if (not regression.Equation.DefaultXName) or (not regression.Equation.DefaultYName) or
+ (not regression.Equation.DefaultBorder) or (not regression.Equation.DefaultFill) or
+ (not regression.Equation.DefaultFont) or (not regression.Equation.DefaultNumberFormat) or
+ (not regression.Equation.DefaultPosition) then
+ begin
+ regressionEquation := regressionEquation + Format('chart:style-name="ch%d" ', [AStyleID + 2]);
+ needRegressionEquationStyle := true;
+ end;
+ end;
+ if regression.DisplayEquation then
+ regressionEquation := regressionEquation + 'chart:display-equation="true" ';
+ if regression.DisplayRSquare then
+ regressionEquation := regressionEquation + 'chart:display-r-square="true" ';
+
+ if regressionEquation <> '' then
+ begin
+ if not regression.Equation.DefaultPosition then
+ regressionEquation := regressionEquation + Format(
+ 'svg:x="%.2fmm" svg:y="%.2fmm" ',
+ [ regression.Equation.Left, regression.Equation.Top ],
+ FPointSeparatorSettings
+ );
+
+ AppendToStream(AChartStream, Format(
+ indent + ' ' + LE +
+ indent + ' ' + LE +
+ indent + ' ' + LE,
+ [ AStyleID + 1, regressionEquation ]
+ ))
+ end else
+ AppendToStream(AChartStream, Format(
+ indent + ' ',
+ [ AStyleID + 1 ]
+ ));
+ needRegressionStyle := true;
+ end;
+ end;
+
AppendToStream(AChartStream, Format(
indent + ' ' + LE,
[ count ]
@@ -1408,6 +1562,24 @@ begin
GetChartSeriesStyleAsXML(AChart, ASeriesIndex, AStyleIndent, AStyleID)
);
+ // Regression style
+ if needRegressionStyle then
+ begin
+ inc(AStyleID);
+ AppendToStream(AStyleStream,
+ GetChartRegressionStyleAsXML(AChart, ASeriesIndex, AStyleIndent, AStyleID)
+ );
+
+ // Style of regression equation
+ if needRegressionEquationStyle then
+ begin
+ inc(AStyleID);
+ AppendToStream(AStyleStream,
+ GetChartRegressionEquationStyleAsXML(AChart, regression.Equation, AStyleIndent, AStyleID)
+ );
+ end;
+ end;
+
// Next style
inc(AStyleID);
end;