fpspreadsheet: Chart link supports regression curve in scatter plot.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9039 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2023-11-23 00:08:33 +00:00
parent 6946f27d34
commit 49b6486be7
4 changed files with 169 additions and 67 deletions

View File

@ -37,7 +37,9 @@ implementation
const const
// FILE_NAME = '../../../other/chart/bars.ods'; // FILE_NAME = '../../../other/chart/bars.ods';
// FILE_NAME = '../../../other/chart/area.ods'; // FILE_NAME = '../../../other/chart/area.ods';
FILE_NAME = '../../../other/chart/pie.ods'; // FILE_NAME = '../../../other/chart/pie.ods';
// FILE_NAME = '../../../other/chart/scatter.ods';
FILE_NAME = '../../../other/chart/regression.ods';
{ TForm1 } { TForm1 }

View File

@ -421,7 +421,7 @@ type
function GetChartType: TsChartType; override; function GetChartType: TsChartType; override;
end; end;
TsRingSeries = class(TsChartSeries) TsRingSeries = class(TsPieSeries)
private private
FInnerRadiusPercent: Integer; FInnerRadiusPercent: Integer;
public public

View File

@ -534,8 +534,10 @@ begin
else exit; else exit;
end; end;
// Set default // Set defaults
Axis.MajorTicks := [catOutside];
grid.Style := clsSolid; grid.Style := clsSolid;
grid.Color := $c0c0c0;
s := GetAttrValue(ANode, 'chart:style-name'); s := GetAttrValue(ANode, 'chart:style-name');
styleNode := FindStyleNode(AStyleNode, s); styleNode := FindStyleNode(AStyleNode, s);

View File

@ -18,12 +18,13 @@ interface
uses uses
// RTL/FCL // RTL/FCL
Classes, SysUtils, Classes, SysUtils, Types,
// LCL // LCL
LCLVersion, Forms, Controls, Graphics, Dialogs, LCLVersion, Forms, Controls, Graphics, Dialogs,
// TAChart // TAChart
TATypes, TATextElements, TAChartUtils, TACustomSource, TACustomSeries, TATypes, TATextElements, TAChartUtils, TALegend, TACustomSource,
TASeries, TARadialSeries, TAChartAxisUtils, TAChartAxis, TALegend, TAGraph, TACustomSeries, TASeries, TARadialSeries, TAFitUtils, TAFuncSeries,
TAChartAxisUtils, TAChartAxis, TAGraph,
// FPSpreadsheet // FPSpreadsheet
fpsTypes, fpSpreadsheet, fpsUtils, fpsChart, fpsTypes, fpSpreadsheet, fpsUtils, fpsChart,
// FPSpreadsheet Visual // FPSpreadsheet Visual
@ -105,6 +106,8 @@ type
procedure SetWorkbookChartIndex(AValue: Integer); procedure SetWorkbookChartIndex(AValue: Integer);
procedure SetWorkbookSource(AValue: TsWorkbookSource); procedure SetWorkbookSource(AValue: TsWorkbookSource);
//procedure FitSeriesFitEquationText(ASeries: TFitSeries; AEquationText: IFitEquationText);
protected protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure Notification(AComponent: TComponent; Operation: TOperation); override;
@ -120,10 +123,13 @@ type
procedure UpdateChartBrush(AWorkbookFill: TsChartFill; ABrush: TBrush); procedure UpdateChartBrush(AWorkbookFill: TsChartFill; ABrush: TBrush);
procedure UpdateChartLegend(AWorkbookLegend: TsChartLegend; ALegend: TChartLegend); procedure UpdateChartLegend(AWorkbookLegend: TsChartLegend; ALegend: TChartLegend);
procedure UpdateChartPen(AWorkbookLine: TsChartLine; APen: TPen); procedure UpdateChartPen(AWorkbookLine: TsChartLine; APen: TPen);
procedure UpdateChartPieSeries(AWorkbookSeries: TsChartSeries; APieSeries: TPieSeries);
procedure UpdateChartSeriesMarks(AWorkbookSeries: TsChartSeries; AChartSeries: TChartSeries); procedure UpdateChartSeriesMarks(AWorkbookSeries: TsChartSeries; AChartSeries: TChartSeries);
procedure UpdateChartTitle(AWorkbookTitle: TsChartText; AChartTitle: TChartTitle); procedure UpdateChartTitle(AWorkbookTitle: TsChartText; AChartTitle: TChartTitle);
procedure UpdateLineSeries(AWorkbookSeries: TsLineSeries; AChartSeries: TLineSeries);
procedure UpdatePieSeries(AWorkbookSeries: TsPieSeries; AChartSeries: TPieSeries);
procedure UpdateScatterSeries(AWorkbookSeries: TsScatterSeries; AChartSeries: TLineSeries);
public public
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
destructor Destroy; override; destructor Destroy; override;
@ -630,24 +636,9 @@ begin
end; end;
procedure TsWorkbookChartLink.AddSeries(ASeries: TsChartSeries); procedure TsWorkbookChartLink.AddSeries(ASeries: TsChartSeries);
const
POINTER_STYLES: array[TsChartSeriesSymbol] of TSeriesPointerstyle = (
psRectangle,
psDiamond,
psTriangle,
psDownTriangle,
psLeftTriangle,
psRightTriangle,
psCircle,
psStar,
psDiagCross,
psCross,
psFullStar
);
var var
src: TsWorkbookChartSource; src: TsWorkbookChartSource;
ser: TChartSeries; ser: TChartSeries;
ppi: Integer;
begin begin
src := TsWorkbookChartSource.Create(self); src := TsWorkbookChartSource.Create(self);
src.WorkbookSource := FWorkbookSource; src.WorkbookSource := FWorkbookSource;
@ -656,41 +647,15 @@ begin
if not ASeries.YRange.IsEmpty then src.SetYRange(ASeries.YRange); if not ASeries.YRange.IsEmpty then src.SetYRange(ASeries.YRange);
if not ASeries.FillColorRange.IsEmpty then src.SetColorRange(ASeries.FillColorRange); if not ASeries.FillColorRange.IsEmpty then src.SetColorRange(ASeries.FillColorRange);
ppi := GetParentForm(FChart).PixelsPerInch;
case ASeries.ChartType of case ASeries.ChartType of
ctBar: ctBar:
begin ser := TBarSeries.Create(FChart);
ser := TBarSeries.Create(FChart);
UpdateChartBrush(ASeries.Fill, TBarSeries(ser).BarBrush);
UpdateChartPen(ASeries.Line, TBarSeries(ser).BarPen);
end;
ctLine, ctScatter: ctLine, ctScatter:
begin ser := TLineSeries.Create(FChart);
ser := TLineSeries.Create(FChart);
UpdateChartPen(ASeries.Line, TLineSeries(ser).LinePen);
TLineSeries(ser).ShowLines := ASeries.Line.Style <> clsNoLine;
TLineSeries(ser).ShowPoints := TsLineSeries(ASeries).ShowSymbols;
if TLineSeries(ser).ShowPoints then
begin
UpdateChartBrush(ASeries.Fill, TLineSeries(ser).Pointer.Brush);
TLineSeries(ser).Pointer.Pen.Color := TLineSeries(ser).LinePen.Color;
TLineSeries(ser).Pointer.Style := POINTER_STYLES[TsLineSeries(ASeries).Symbol];
TlineSeries(ser).Pointer.HorizSize := mmToPx(TsLineSeries(ASeries).SymbolWidth, ppi);
TlineSeries(ser).Pointer.VertSize := mmToPx(TsLineSeries(ASeries).SymbolHeight, ppi);
end;
end;
ctArea: ctArea:
begin ser := TAreaSeries.Create(FChart);
ser := TAreaSeries.Create(FChart);
UpdateChartBrush(ASeries.Fill, TAreaSeries(ser).AreaBrush);
UpdateChartPen(ASeries.Line, TAreaSeries(ser).AreaContourPen);
TAreaSeries(ser).AreaLinesPen.Style := psClear;
end;
ctPie, ctRing: ctPie, ctRing:
begin ser := TPieSeries.Create(FChart);
ser := TPieSeries.Create(FChart);
UpdateChartPieSeries(ASeries, TPieSeries(ser));
end;
end; end;
src.SetTitleAddr(ASeries.TitleAddr); src.SetTitleAddr(ASeries.TitleAddr);
@ -700,6 +665,26 @@ begin
UpdateChartSeriesMarks(ASeries, ser); UpdateChartSeriesMarks(ASeries, ser);
FChart.AddSeries(ser); FChart.AddSeries(ser);
case ASeries.ChartType of
ctArea:
begin
UpdateChartBrush(ASeries.Fill, TAreaSeries(ser).AreaBrush);
UpdateChartPen(ASeries.Line, TAreaSeries(ser).AreaContourPen);
TAreaSeries(ser).AreaLinesPen.Style := psClear;
end;
ctBar:
begin
UpdateChartBrush(ASeries.Fill, TBarSeries(ser).BarBrush);
UpdateChartPen(ASeries.Line, TBarSeries(ser).BarPen);
end;
ctLine:
UpdateLineSeries(TsLineSeries(ASeries), TLineSeries(ser));
ctScatter:
UpdateScatterSeries(TsScatterSeries(ASeries), TLineSeries(ser));
ctPie, ctRing:
UpdatePieSeries(TsPieSeries(ASeries), TPieSeries(ser));
end;
end; end;
procedure TsWorkbookChartLink.ClearChart; procedure TsWorkbookChartLink.ClearChart;
@ -777,6 +762,17 @@ begin
AChartSeries.Marks.Alignment := taCenter; AChartSeries.Marks.Alignment := taCenter;
end; end;
{
procedure TsWorkbookChartLink.FitSeriesFitEquationText(ASeries: TFitSeries;
AEquationText: IFitEquationText);
begin
if ASeries.ErrCode = fitOK then
begin
AEquationText.NumFormat('%.5f');
AEquationText.TextFormat(tfHtml)
end;
end;
}
// Fix area series zero level not being clipped at chart's plotrect. // Fix area series zero level not being clipped at chart's plotrect.
procedure TsWorkbookChartLink.FixAreaSeries(AWorkbookChart: TsChart); procedure TsWorkbookChartLink.FixAreaSeries(AWorkbookChart: TsChart);
var var
@ -1075,6 +1071,7 @@ begin
ALegend.UseSidebar := not AWorkbookLegend.CanOverlapPlotArea; ALegend.UseSidebar := not AWorkbookLegend.CanOverlapPlotArea;
ALegend.Visible := AWorkbookLegend.Visible; ALegend.Visible := AWorkbookLegend.Visible;
ALegend.Inverted := true; ALegend.Inverted := true;
ALegend.TextFormat := tfHTML;
end; end;
end; end;
@ -1109,21 +1106,6 @@ begin
end; end;
end; end;
procedure TsWorkbookChartLink.UpdateChartPieSeries(AWorkbookSeries: TsChartSeries;
APieSeries: TPieSeries);
begin
APieSeries.StartAngle := TsPieSeries(AWorkbookSeries).StartAngle;
APieSeries.Legend.Multiplicity := lmPoint;
APieSeries.Legend.Format := '%2:s';
if AWorkbookSeries is TsRingSeries then
APieSeries.InnerRadiusPercent := TsRingSeries(AWorkbookSeries).InnerRadiusPercent;
FChart.BottomAxis.Visible := false;
FChart.LeftAxis.Visible := false;
FChart.Legend.Inverted := false;
FChart.Frame.Visible := false;
end;
procedure TsWorkbookChartLink.UpdateChartSeriesMarks(AWorkbookSeries: TsChartSeries; procedure TsWorkbookChartLink.UpdateChartSeriesMarks(AWorkbookSeries: TsChartSeries;
AChartSeries: TChartSeries); AChartSeries: TChartSeries);
begin begin
@ -1177,4 +1159,120 @@ begin
end; end;
end; end;
procedure TsWorkbookChartLink.UpdateLineSeries(AWorkbookSeries: TsLineSeries;
AChartSeries: TLineSeries);
const
POINTER_STYLES: array[TsChartSeriesSymbol] of TSeriesPointerstyle = (
psRectangle,
psDiamond,
psTriangle,
psDownTriangle,
psLeftTriangle,
psRightTriangle,
psCircle,
psStar,
psDiagCross,
psCross,
psFullStar
);
var
ppi: Integer;
begin
ppi := GetParentForm(FChart).PixelsPerInch;
UpdateChartPen(AWorkbookSeries.Line, AChartSeries.LinePen);
AChartSeries.ShowLines := AWorkbookSeries.Line.Style <> clsNoLine;
AChartSeries.ShowPoints := AWorkbookSeries.ShowSymbols;
if AChartSeries.ShowPoints then
begin
UpdateChartBrush(AWorkbookSeries.Fill, AChartSeries.Pointer.Brush);
AChartSeries.Pointer.Pen.Color := AChartSeries.LinePen.Color;
AChartSeries.Pointer.Style := POINTER_STYLES[AWorkbookSeries.Symbol];
AChartSeries.Pointer.HorizSize := mmToPx(AWorkbookSeries.SymbolWidth, ppi);
AChartSeries.Pointer.VertSize := mmToPx(AWorkbookSeries.SymbolHeight, ppi);
end;
end;
procedure TsWorkbookChartLink.UpdatePieSeries(AWorkbookSeries: TsPieSeries;
AChartSeries: TPieSeries);
begin
AChartSeries.StartAngle := AWorkbookSeries.StartAngle;
AChartSeries.Legend.Multiplicity := lmPoint;
AChartSeries.Legend.Format := '%2:s';
if AWorkbookSeries is TsRingSeries then
AChartSeries.InnerRadiusPercent := TsRingSeries(AWorkbookSeries).InnerRadiusPercent;
FChart.BottomAxis.Visible := false;
FChart.LeftAxis.Visible := false;
FChart.Legend.Inverted := false;
FChart.Frame.Visible := false;
end;
procedure TsWorkbookChartLink.UpdateScatterSeries(AWorkbookSeries: TsScatterSeries;
AChartSeries: TLineSeries);
var
ser: TFitSeries;
s: String;
begin
UpdateLineSeries(AWorkbookSeries, AChartSeries);
if AWorkbookSeries.Regression.RegressionType = rtNone then
exit;
// Create series and assign chartsource
ser := TFitSeries.Create(FChart);
ser.Source := AChartSeries.Source;
// Fit equation
case AWorkbookSeries.Regression.RegressionType of
rtLinear: ser.FitEquation := feLinear;
// rtLogarithmic: ser.FitEquation := feLogarithmic; // to do: implement this!
rtExponential: ser.FitEquation := feExp;
rtPower: ser.FitEquation := fePower;
rtPolynomial:
begin
ser.FitEquation := fePolynomial;
ser.ParamCount := AWorkbookSeries.Regression.PolynomialDegree + 1;
end;
end;
// Take care of y intercept
if AWorkbookSeries.Regression.ForceYIntercept then
begin
str(AWorkbookSeries.Regression.YInterceptValue, s);
ser.FixedParams := s;
end;
// style of regression line
UpdateChartPen(AWorkbookSeries.Regression.Line, ser.Pen);
FChart.AddSeries(ser);
// Legend text
ser.Title := AWorkbookSeries.Regression.Title;
// Show fit curve in legend after series.
ser.Legend.Order := AChartseries.Legend.Order + 1;
// Regression equation
if AWorkbookSeries.Regression.DisplayEquation or AWorkbookSeries.Regression.DisplayRSquare then
begin
ser.ExecFit;
s := '';
if AWorkbookSeries.Regression.DisplayEquation then
s := s + ser.EquationText.
X(AWorkbookSeries.Regression.Equation.XName).
Y(AWorkbookSeries.Regression.Equation.YName).
NumFormat('%.3f'). // to do: convert from AWorkbookSeries.Regression.Equation.NumberFormat
DecimalSeparator('.').
TextFormat(tfHtml).
Get;
if AWorkbookSeries.Regression.DisplayRSquare then
s := s + LineEnding + 'R² = ' + FormatFloat('0.00', ser.FitStatistics.R2);
if s <> '' then
ser.Title := ser.Title + LineEnding + s;
// ser.Legend.Format := '%0:s' + LineEnding + '%2:s';
end;
end;
end. end.