diff --git a/components/tachart/demo/html/main.lfm b/components/tachart/demo/html/main.lfm index 11b7fc30a5..b8c09a44bb 100644 --- a/components/tachart/demo/html/main.lfm +++ b/components/tachart/demo/html/main.lfm @@ -1,17 +1,17 @@ object MainForm: TMainForm Left = 280 - Height = 432 + Height = 452 Top = 130 Width = 655 Caption = 'HTML demo' - ClientHeight = 432 + ClientHeight = 452 ClientWidth = 655 OnCreate = FormCreate LCLVersion = '1.9.0.0' object BottomPanel: TPanel Left = 8 Height = 91 - Top = 333 + Top = 353 Width = 647 Align = alBottom AutoSize = True @@ -26,11 +26,11 @@ object MainForm: TMainForm Left = 0 Height = 78 Top = 8 - Width = 221 + Width = 327 AutoFill = True AutoSize = True BorderSpacing.InnerBorder = 4 - Caption = 'HTML' + Caption = 'Show HTML codes' ChildSizing.LeftRightSpacing = 6 ChildSizing.TopBottomSpacing = 6 ChildSizing.EnlargeHorizontal = crsHomogenousChildResize @@ -38,21 +38,23 @@ object MainForm: TMainForm ChildSizing.ShrinkHorizontal = crsScaleChilds ChildSizing.ShrinkVertical = crsScaleChilds ChildSizing.Layout = cclLeftToRightThenTopToBottom - ChildSizing.ControlsPerLine = 3 + ChildSizing.ControlsPerLine = 4 ClientHeight = 58 - ClientWidth = 217 - Columns = 3 + ClientWidth = 323 + Columns = 4 Items.Strings = ( 'title' 'footer' 'legend' + 'series labels' + 'x axis labels' 'x axis title' 'y axis title' ) OnItemClick = CgHTMLItemClick TabOrder = 0 Data = { - 050000000202020202 + 0700000002020202020202 } end object BtnCopyToClipboard: TButton @@ -108,7 +110,7 @@ object MainForm: TMainForm end object Chart: TChart Left = 8 - Height = 317 + Height = 337 Top = 8 Width = 639 AxisList = < @@ -119,7 +121,7 @@ object MainForm: TMainForm Title.LabelFont.Height = -16 Title.LabelFont.Orientation = 900 Title.Visible = True - Title.Caption = 'Area, cm2' + Title.Caption = 'Area A, cm2' Title.TextFormat = tfHTML end item @@ -127,20 +129,22 @@ object MainForm: TMainForm Grid.Style = psSolid Grid.Visible = False Alignment = calBottom + Marks.TextFormat = tfHTML Minors = <> Title.LabelFont.Height = -16 Title.Visible = True - Title.Caption = 'Angle α' + Title.Caption = 'Angle α, °' Title.TextFormat = tfHTML + OnMarkToText = ChartAxisList1MarkToText end> BackColor = clWhite Foot.Alignment = taLeftJustify Foot.Brush.Color = clBtnFace Foot.Brush.Style = bsClear - Foot.Font.Color = clBlue + Foot.Font.Color = clGray Foot.Text.Strings = ( 'Reference:' - 'W. Shakespeare, "J. Irreproducible Results", 38, 512 (2017)' + 'W. Shakespeare, "J. Irreprod. Res.", 38, 512 (2017)' ) Foot.TextFormat = tfHTML Foot.Visible = True @@ -148,7 +152,7 @@ object MainForm: TMainForm Legend.Visible = True Title.Brush.Color = clBtnFace Title.Brush.Style = bsClear - Title.Font.Color = clBlue + Title.Font.Color = clNavy Title.Font.Height = -19 Title.Font.Style = [fsBold] Title.Text.Strings = ( @@ -159,35 +163,35 @@ object MainForm: TMainForm Align = alClient BorderSpacing.Around = 8 Color = clWhite - object RedSeries: TLineSeries - Title = 'Red series' + object DataSeries: TLineSeries + Marks.Format = '%2:s' + Marks.Frame.Color = clGray + Marks.LinkPen.Color = clGray + Marks.Style = smsLabel + Marks.TextFormat = tfHTML + Title = 'Measured' LinePen.Color = clRed - Source = RedSource + LineType = ltNone + Pointer.Brush.Color = clRed + Pointer.Style = psCircle + ShowPoints = True end - object BlueSeries: TLineSeries - Title = 'Blue series' - LinePen.Color = clBlue - Source = BlueSource + object FitSeries: TFitSeries + Marks.Alignment = taCenter + Marks.TextFormat = tfHTML + Title = 'Fit curve (blue)' + Pen.Color = clBlue + Pointer.Visible = False + Source = ListChartSource + OnFitComplete = FitSeriesFitComplete end end - object RedSource: TRandomChartSource - PointsNumber = 20 - RandSeed = 1926906778 - XMax = 90 - XMin = 0 - YMax = 10 - YMin = 4 - left = 121 - top = 52 + object ListChartSource: TListChartSource + left = 224 + top = 88 end - object BlueSource: TRandomChartSource - PointsNumber = 30 - RandSeed = 1926958054 - XMax = 90 - XMin = 0 - YMax = 8 - YMin = 0 - left = 120 - top = 111 + object ListChartSource_Fit: TListChartSource + left = 224 + top = 153 end end diff --git a/components/tachart/demo/html/main.pas b/components/tachart/demo/html/main.pas index 956321a2f2..bd6ae8a54a 100644 --- a/components/tachart/demo/html/main.pas +++ b/components/tachart/demo/html/main.pas @@ -6,7 +6,7 @@ interface uses Classes, SysUtils, FileUtil, TAGraph, TASeries, TASources, Forms, Controls, - Graphics, Dialogs, ExtCtrls, StdCtrls; + Graphics, Dialogs, ExtCtrls, StdCtrls, TAChartAxisUtils, TAFuncSeries; type @@ -18,17 +18,21 @@ type BtnSaveSVG: TButton; Chart: TChart; CgHTML: TCheckGroup; - RedSource: TRandomChartSource; - BlueSource: TRandomChartSource; - RedSeries: TLineSeries; - BlueSeries: TLineSeries; + FitSeries: TFitSeries; + ListChartSource: TListChartSource; + DataSeries: TLineSeries; BottomPanel: TPanel; + ListChartSource_Fit: TListChartSource; procedure BtnCopyToClipboardClick(Sender: TObject); procedure BtnSaveWMFClick(Sender: TObject); procedure BtnSaveSVGClick(Sender: TObject); procedure CgHTMLItemClick(Sender: TObject; Index: integer); + procedure ChartAxisList1MarkToText(var AText: String; AMark: Double); + procedure FitSeriesFitComplete(Sender: TObject); procedure FormCreate(Sender: TObject); + private + procedure CreateData; public @@ -42,7 +46,7 @@ implementation {$R *.lfm} uses - TAChartUtils, {$IFDEF WINDOWS}TADrawerWMF,{$ENDIF} TADrawerSVG; + Math, TAChartUtils, {$IFDEF WINDOWS}TADrawerWMF,{$ENDIF} TADrawerSVG; { TMainForm } @@ -70,26 +74,86 @@ procedure TMainForm.CgHTMLItemClick(Sender: TObject; Index: integer); var tf: TChartTextFormat; begin - if CgHTML.Checked[Index] then tf := tfHTML else tf := tfNormal; + if CgHTML.Checked[Index] then tf := tfNormal else tf := tfHTML; case Index of 0: Chart.Title.TextFormat := tf; 1: Chart.Foot.TextFormat := tf; 2: Chart.Legend.TextFormat := tf; - 3: Chart.BottomAxis.Title.TextFormat := tf; - 4: Chart.LeftAxis.Title.TextFormat := tf; + 3: DataSeries.Marks.TextFormat := tf; + 4: Chart.BottomAxis.Marks.TextFormat := tf; + 5: Chart.BottomAxis.Title.TextFormat := tf; + 6: Chart.LeftAxis.Title.TextFormat := tf; end; end; +procedure TMainForm.ChartAxisList1MarkToText(var AText: String; AMark: Double); +begin + AText := AText + '°'; +end; + +procedure TMainForm.CreateData; +const + N = 20; + MIN = 0; + MAX = 90; + OUTLIER_INDEX = 12; +var + i: Integer; + x, y: Double; + s: String; +begin + for i:=0 to N-1 do begin + x := MIN + (MAX - MIN) * i / (N-1) + 5*(random - 0.5); + if i = OUTLIER_INDEX then begin + y := 631; + s := 'Defective device!' + LineEnding + '(α = ' + FormatFloat('0.00', x) + '°)'; + end else + begin + y := x*x / 10 + (random - 0.5) * 100; + s := ''; + end; + ListChartSource.Add(x, y, s); + if i <> OUTLIER_INDEX then + ListChartSource_Fit.Add(x, y); + end; + DataSeries.Source := ListChartSource; + FitSeries.Source := ListChartSource_Fit; +end; + +procedure TMainForm.FitSeriesFitComplete(Sender: TObject); +var + p: Array of Double; + i: Integer; + s: String; +begin + SetLength(p, FitSeries.ParamCount); + for i:=0 to FitSeries.ParamCount-1 do + p[i] := FitSeries.Param[i]; + + s := FitSeries.EquationText. + x('α'). + y('A'). + NumFormat('%.2f'). + DecimalSeparator('.'). + Params(p). + Get; + FitSeries.Title := 'Fitted: ' + s; +end; + procedure TMainForm.FormCreate(Sender: TObject); begin - CgHTML.Checked[0] := Chart.Title.TextFormat = tfHTML; - CgHTML.Checked[1] := Chart.Foot.TextFormat = tfHTML; - CgHTML.Checked[2] := Chart.Legend.TextFormat = tfHTML; - CgHTML.Checked[3] := Chart.BottomAxis.Title.TextFormat = tfHTML; - CgHTML.Checked[4] := Chart.LeftAxis.Title.TextFormat = tfHTML; + CreateData; + + CgHTML.Checked[0] := Chart.Title.TextFormat = tfNormal; + CgHTML.Checked[1] := Chart.Foot.TextFormat = tfNormal; + CgHTML.Checked[2] := Chart.Legend.TextFormat = tfNormal; + CgHTML.Checked[3] := DataSeries.Marks.TextFormat = tfNormal; + CgHTML.Checked[4] := Chart.BottomAxis.Marks.TextFormat = tfNormal; + CgHTML.Checked[5] := Chart.BottomAxis.Title.TextFormat = tfNormal; + CgHTML.Checked[6] := Chart.LeftAxis.Title.TextFormat = tfNormal; {$IFDEF WINDOWS} - Chart.Foot.Text[1] := '' + Chart.Foot.Text[1] + ''; + Chart.Foot.Text[1] := '' + Chart.Foot.Text[1] + ''; {$ELSE} BtnSaveWMF.Hide; {$ENDIF} diff --git a/components/tachart/tafitutils.pas b/components/tachart/tafitutils.pas index 12cb029733..b5c320b14e 100644 --- a/components/tachart/tafitutils.pas +++ b/components/tachart/tafitutils.pas @@ -14,6 +14,9 @@ unit TAFitUtils; interface +uses + TAChartUtils; + type TFitEquation = ( fePolynomial, // y = b0 + b1*x + b2*x^2 + ... bn*x^n @@ -23,43 +26,51 @@ type ); IFitEquationText = interface + function DecimalSeparator(AValue: Char): IFitEquationText; function Equation(AEquation: TFitEquation): IFitEquationText; function X(AText: String): IFitEquationText; function Y(AText: String): IFitEquationText; function NumFormat(AFormat: String): IFitEquationText; function NumFormats(const AFormats: array of String): IFitEquationText; function Params(const AParams: array of Double): IFitEquationText; + function TextFormat(AFormat: TChartTextFormat): IFitEquationText; function Get: String; end; TFitEmptyEquationText = class(TInterfacedObject, IFitEquationText) public + function DecimalSeparator(AValue: Char): IFitEquationText; function Equation(AEquation: TFitEquation): IFitEquationText; function X(AText: String): IFitEquationText; function Y(AText: String): IFitEquationText; function NumFormat(AFormat: String): IFitEquationText; function NumFormats(const AFormats: array of String): IFitEquationText; function Params(const AParams: array of Double): IFitEquationText; + function TextFormat(AFormat: TChartTextFormat): IFitEquationText; function Get: String; end; TFitEquationText = class(TInterfacedObject, IFitEquationText) strict private + FDecSep: Char; FEquation: TFitEquation; FX: String; FY: String; FNumFormat: String; FNumFormats: array of String; FParams: array of Double; + FTextFormat: TChartTextFormat; function GetNumFormat(AIndex: Integer): String; public constructor Create; + function DecimalSeparator(AValue: Char): IFitEquationText; function Equation(AEquation: TFitEquation): IFitEquationText; function X(AText: String): IFitEquationText; function Y(AText: String): IFitEquationText; function NumFormat(AFormat: String): IFitEquationText; function NumFormats(const AFormats: array of String): IFitEquationText; function Params(const AParams: array of Double): IFitEquationText; + function TextFormat(AFormat: TChartTextFormat): IFitEquationText; function Get: String; end; @@ -68,15 +79,22 @@ type implementation uses - TAChartUtils, StrUtils, SysUtils; + StrUtils, SysUtils; operator := (AEq: IFitEquationText): String; begin Result := AEq.Get; end; + { TFitEmptyEquationText } +function TFitEmptyEquationText.DecimalSeparator(AValue: Char): IFitEquationText; +begin + Unused(AValue); + Result := Self; +end; + function TFitEmptyEquationText.Equation( AEquation: TFitEquation): IFitEquationText; begin @@ -109,6 +127,12 @@ begin Result := Self; end; +function TFitEmptyEquationText.TextFormat(AFormat: TChartTextFormat): IFitEquationText; +begin + Unused(AFormat); + Result := Self; +end; + function TFitEmptyEquationText.X(AText: String): IFitEquationText; begin Unused(AText); @@ -128,6 +152,13 @@ begin FX := 'x'; FY := 'y'; FNumFormat := '%.9g'; + FDecSep := DefaultFormatSettings.DecimalSeparator; +end; + +function TFitEquationText.DecimalSeparator(AValue: Char): IFitEquationText; +begin + FDecSep := AValue; + Result := self; end; function TFitEquationText.Equation(AEquation: TFitEquation): IFitEquationText; @@ -139,24 +170,54 @@ end; function TFitEquationText.Get: String; var ps: String = ''; + s: String; i: Integer; + fs: TFormatSettings; begin - if Length(FParams) = 0 then exit(''); - Result := Format('%s = ' + GetNumFormat(0), [FY, FParams[0]]); + if Length(FParams) = 0 then + exit(''); + + fs := DefaultFormatSettings; + fs.DecimalSeparator := FDecSep; + + Result := Format('%s = ' + GetNumFormat(0), [FY, FParams[0]], fs); if FEquation in [fePolynomial, feLinear] then for i := 1 to High(FParams) do begin - if FParams[i] = 0 then continue; - if i > 1 then ps := Format('^%d', [i]); - Result += Format( - ' %s ' + GetNumFormat(i) + '*%s%s', - [IfThen(FParams[i] > 0, '+', '-'), Abs(FParams[i]), FX, ps]); + if FParams[i] = 0 then + continue; + if FTextFormat = tfNormal then + begin + if i > 1 then ps := Format('^%d', [i]); + s := '*%s%s'; + end else + begin + if i > 1 then ps := Format('%d', [i]); + s := '·%s%s'; + end; + Result += Format(' %s ' + GetNumFormat(i) + s, + [IfThen(FParams[i] > 0, '+', '-'), Abs(FParams[i]), FX, ps], fs + ); end else if (Length(FParams) >= 2) and (FParams[0] <> 0) and (FParams[1] <> 0) then case FEquation of feExp: - Result += Format(' * exp(' + GetNumFormat(1) +' * %s)', [FParams[1], FX]); + if FTextFormat = tfNormal then + Result += Format(' * exp(' + GetNumFormat(1) +' * %s)', + [FParams[1], FX], fs + ) + else + Result += Format(' · e' + GetNumFormat(1) + '· %s', + [FParams[1], FX], fs + ); fePower: - Result += Format(' * %s^' + GetNumFormat(1), [FX, FParams[1]]); + if FTextFormat = tfNormal then + Result += Format(' * %s^' + GetNumFormat(1), + [FX, FParams[1]], fs + ) + else + Result += Format(' · %s' + GetNumFormat(1) + '', + [FX, FParams[1]], fs + ); end; end; @@ -196,6 +257,12 @@ begin Result := Self; end; +function TFitEquationText.TextFormat(AFormat: TChartTextFormat): IFitEquationText; +begin + FTextFormat := AFormat; + Result := Self; +end; + function TFitEquationText.X(AText: String): IFitEquationText; begin FX := AText; diff --git a/components/tachart/tafuncseries.pas b/components/tachart/tafuncseries.pas index 6b2a510261..079e14e65f 100644 --- a/components/tachart/tafuncseries.pas +++ b/components/tachart/tafuncseries.pas @@ -1489,7 +1489,7 @@ begin Result := TFitEquationText.Create else Result := TFitEmptyEquationText.Create; - Result.Equation(FitEquation).Params(FFitParams); + Result.TextFormat(Marks.TextFormat).Equation(FitEquation).Params(FFitParams); end; procedure TFitSeries.ExecFit;