diff --git a/components/fpspreadsheet/examples/visual/fpschart/fpschartlink/main.pas b/components/fpspreadsheet/examples/visual/fpschart/fpschartlink/main.pas index 929246e9a..45639d55c 100644 --- a/components/fpspreadsheet/examples/visual/fpschart/fpschartlink/main.pas +++ b/components/fpspreadsheet/examples/visual/fpschart/fpschartlink/main.pas @@ -37,7 +37,9 @@ implementation const // FILE_NAME = '../../../other/chart/bars.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 } diff --git a/components/fpspreadsheet/source/common/fpschart.pas b/components/fpspreadsheet/source/common/fpschart.pas index c418af65c..77cf4e749 100644 --- a/components/fpspreadsheet/source/common/fpschart.pas +++ b/components/fpspreadsheet/source/common/fpschart.pas @@ -421,7 +421,7 @@ type function GetChartType: TsChartType; override; end; - TsRingSeries = class(TsChartSeries) + TsRingSeries = class(TsPieSeries) private FInnerRadiusPercent: Integer; public diff --git a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas index 2f019e58d..1fe8bfa80 100644 --- a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas +++ b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas @@ -534,8 +534,10 @@ begin else exit; end; - // Set default + // Set defaults + Axis.MajorTicks := [catOutside]; grid.Style := clsSolid; + grid.Color := $c0c0c0; s := GetAttrValue(ANode, 'chart:style-name'); styleNode := FindStyleNode(AStyleNode, s); diff --git a/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas b/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas index 95a3f20b0..8bf2ce608 100644 --- a/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas +++ b/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas @@ -18,12 +18,13 @@ interface uses // RTL/FCL - Classes, SysUtils, + Classes, SysUtils, Types, // LCL LCLVersion, Forms, Controls, Graphics, Dialogs, // TAChart - TATypes, TATextElements, TAChartUtils, TACustomSource, TACustomSeries, - TASeries, TARadialSeries, TAChartAxisUtils, TAChartAxis, TALegend, TAGraph, + TATypes, TATextElements, TAChartUtils, TALegend, TACustomSource, + TACustomSeries, TASeries, TARadialSeries, TAFitUtils, TAFuncSeries, + TAChartAxisUtils, TAChartAxis, TAGraph, // FPSpreadsheet fpsTypes, fpSpreadsheet, fpsUtils, fpsChart, // FPSpreadsheet Visual @@ -105,6 +106,8 @@ type procedure SetWorkbookChartIndex(AValue: Integer); procedure SetWorkbookSource(AValue: TsWorkbookSource); + //procedure FitSeriesFitEquationText(ASeries: TFitSeries; AEquationText: IFitEquationText); + protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; @@ -120,10 +123,13 @@ type procedure UpdateChartBrush(AWorkbookFill: TsChartFill; ABrush: TBrush); procedure UpdateChartLegend(AWorkbookLegend: TsChartLegend; ALegend: TChartLegend); procedure UpdateChartPen(AWorkbookLine: TsChartLine; APen: TPen); - procedure UpdateChartPieSeries(AWorkbookSeries: TsChartSeries; APieSeries: TPieSeries); procedure UpdateChartSeriesMarks(AWorkbookSeries: TsChartSeries; AChartSeries: TChartSeries); 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 constructor Create(AOwner: TComponent); override; destructor Destroy; override; @@ -630,24 +636,9 @@ begin end; procedure TsWorkbookChartLink.AddSeries(ASeries: TsChartSeries); -const - POINTER_STYLES: array[TsChartSeriesSymbol] of TSeriesPointerstyle = ( - psRectangle, - psDiamond, - psTriangle, - psDownTriangle, - psLeftTriangle, - psRightTriangle, - psCircle, - psStar, - psDiagCross, - psCross, - psFullStar - ); var src: TsWorkbookChartSource; ser: TChartSeries; - ppi: Integer; begin src := TsWorkbookChartSource.Create(self); src.WorkbookSource := FWorkbookSource; @@ -656,41 +647,15 @@ begin if not ASeries.YRange.IsEmpty then src.SetYRange(ASeries.YRange); if not ASeries.FillColorRange.IsEmpty then src.SetColorRange(ASeries.FillColorRange); - ppi := GetParentForm(FChart).PixelsPerInch; case ASeries.ChartType of ctBar: - begin - ser := TBarSeries.Create(FChart); - UpdateChartBrush(ASeries.Fill, TBarSeries(ser).BarBrush); - UpdateChartPen(ASeries.Line, TBarSeries(ser).BarPen); - end; + ser := TBarSeries.Create(FChart); ctLine, ctScatter: - begin - 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; + ser := TLineSeries.Create(FChart); ctArea: - begin - ser := TAreaSeries.Create(FChart); - UpdateChartBrush(ASeries.Fill, TAreaSeries(ser).AreaBrush); - UpdateChartPen(ASeries.Line, TAreaSeries(ser).AreaContourPen); - TAreaSeries(ser).AreaLinesPen.Style := psClear; - end; + ser := TAreaSeries.Create(FChart); ctPie, ctRing: - begin - ser := TPieSeries.Create(FChart); - UpdateChartPieSeries(ASeries, TPieSeries(ser)); - end; + ser := TPieSeries.Create(FChart); end; src.SetTitleAddr(ASeries.TitleAddr); @@ -700,6 +665,26 @@ begin UpdateChartSeriesMarks(ASeries, 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; procedure TsWorkbookChartLink.ClearChart; @@ -777,6 +762,17 @@ begin AChartSeries.Marks.Alignment := taCenter; 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. procedure TsWorkbookChartLink.FixAreaSeries(AWorkbookChart: TsChart); var @@ -1075,6 +1071,7 @@ begin ALegend.UseSidebar := not AWorkbookLegend.CanOverlapPlotArea; ALegend.Visible := AWorkbookLegend.Visible; ALegend.Inverted := true; + ALegend.TextFormat := tfHTML; end; end; @@ -1109,21 +1106,6 @@ begin 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; AChartSeries: TChartSeries); begin @@ -1177,4 +1159,120 @@ begin 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.