diff --git a/components/tachart/languages/tachartstrconsts.de.po b/components/tachart/languages/tachartstrconsts.de.po index ce488cc4ab..21c2d7fb97 100644 --- a/components/tachart/languages/tachartstrconsts.de.po +++ b/components/tachart/languages/tachartstrconsts.de.po @@ -471,6 +471,10 @@ msgstr "Diese %0:s-Instanz muss mindestens %1:d %2:s-Wert(e) pro Datenpunkt zur msgid "Editable chart source required" msgstr "Editierbare Chart-Datenquelle erforderlich" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "Stern (Linien)" diff --git a/components/tachart/languages/tachartstrconsts.fi.po b/components/tachart/languages/tachartstrconsts.fi.po index c479cf6c8d..63cb4ac1d1 100644 --- a/components/tachart/languages/tachartstrconsts.fi.po +++ b/components/tachart/languages/tachartstrconsts.fi.po @@ -459,6 +459,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "" diff --git a/components/tachart/languages/tachartstrconsts.fr.po b/components/tachart/languages/tachartstrconsts.fr.po index 959c5eb7b2..cfb92b175e 100644 --- a/components/tachart/languages/tachartstrconsts.fr.po +++ b/components/tachart/languages/tachartstrconsts.fr.po @@ -469,6 +469,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "Étoile (lignes)" diff --git a/components/tachart/languages/tachartstrconsts.hu.po b/components/tachart/languages/tachartstrconsts.hu.po index a19032b1e5..14dcf105c3 100644 --- a/components/tachart/languages/tachartstrconsts.hu.po +++ b/components/tachart/languages/tachartstrconsts.hu.po @@ -469,6 +469,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "Csillag (vonalak)" diff --git a/components/tachart/languages/tachartstrconsts.lt.po b/components/tachart/languages/tachartstrconsts.lt.po index e2ecdd01bd..60ad2eebd3 100644 --- a/components/tachart/languages/tachartstrconsts.lt.po +++ b/components/tachart/languages/tachartstrconsts.lt.po @@ -470,6 +470,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "Žvaigždės (linijos)" diff --git a/components/tachart/languages/tachartstrconsts.pl.po b/components/tachart/languages/tachartstrconsts.pl.po index a99924c84c..4a9b7f8352 100644 --- a/components/tachart/languages/tachartstrconsts.pl.po +++ b/components/tachart/languages/tachartstrconsts.pl.po @@ -468,6 +468,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "" diff --git a/components/tachart/languages/tachartstrconsts.pot b/components/tachart/languages/tachartstrconsts.pot index b1ca782c26..00bb64345f 100644 --- a/components/tachart/languages/tachartstrconsts.pot +++ b/components/tachart/languages/tachartstrconsts.pot @@ -459,6 +459,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "" diff --git a/components/tachart/languages/tachartstrconsts.pt_BR.po b/components/tachart/languages/tachartstrconsts.pt_BR.po index 75fb0b08b2..7f914a8977 100644 --- a/components/tachart/languages/tachartstrconsts.pt_BR.po +++ b/components/tachart/languages/tachartstrconsts.pt_BR.po @@ -469,6 +469,10 @@ msgstr "Esta instância %0:s deve ter ao menos %1:d %2:s valor(es) por ponto de msgid "Editable chart source required" msgstr "Fonte editável de gráfico requerida" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "Estrela (linhas)" diff --git a/components/tachart/languages/tachartstrconsts.ru.po b/components/tachart/languages/tachartstrconsts.ru.po index c3004f6300..a6a602c63f 100644 --- a/components/tachart/languages/tachartstrconsts.ru.po +++ b/components/tachart/languages/tachartstrconsts.ru.po @@ -469,6 +469,10 @@ msgstr "Этот экземпляр %0:s должен иметь не менее msgid "Editable chart source required" msgstr "Требуется доступный для редактирования источник данных для диаграммы" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "Звезда (из линий)" diff --git a/components/tachart/languages/tachartstrconsts.se.po b/components/tachart/languages/tachartstrconsts.se.po index 2192b0857d..8bda8d765f 100644 --- a/components/tachart/languages/tachartstrconsts.se.po +++ b/components/tachart/languages/tachartstrconsts.se.po @@ -472,6 +472,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "" diff --git a/components/tachart/languages/tachartstrconsts.uk.po b/components/tachart/languages/tachartstrconsts.uk.po index 58fe8f5780..22eab8a3d6 100644 --- a/components/tachart/languages/tachartstrconsts.uk.po +++ b/components/tachart/languages/tachartstrconsts.uk.po @@ -472,6 +472,10 @@ msgstr "Цей примірник %0:s повинен мати принаймн msgid "Editable chart source required" msgstr "Потрібне джерело діаграми, доступне для редагування" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "Зірка (з ліній)" diff --git a/components/tachart/languages/tachartstrconsts.zh_CN.po b/components/tachart/languages/tachartstrconsts.zh_CN.po index 510d417ed2..1e826e2fca 100644 --- a/components/tachart/languages/tachartstrconsts.zh_CN.po +++ b/components/tachart/languages/tachartstrconsts.zh_CN.po @@ -470,6 +470,10 @@ msgstr "" msgid "Editable chart source required" msgstr "" +#: tachartstrconsts.rssourcesorterror +msgid "Selected sorting parameters are not supported by %s." +msgstr "" + #: tachartstrconsts.rsstarsymbol msgid "Star (lines)" msgstr "星(线)" diff --git a/components/tachart/tachartstrconsts.pas b/components/tachart/tachartstrconsts.pas index 3830aa8192..b7f00a14a1 100644 --- a/components/tachart/tachartstrconsts.pas +++ b/components/tachart/tachartstrconsts.pas @@ -78,12 +78,12 @@ resourcestring rsSourceNotEditable = 'Editable chart source required'; rsSourceCountError = '%0:s requires a chart source with at least %1:d %2:s value(s) per data point.'; rsSourceCountError2 = 'This %0:s instance must have at least %1:d %2:s value(s) per data point.'; + rsSourceSortError = 'Selected sorting parameters are not supported by %s.'; rsListSourceStringFormatError = 'The data value count in the %0:s.DataPoints '+ 'string "%1:s" differs from what is expected from XCount and YCount.'; rsListSourceNumericError = 'The %0:s.DataPoints string "%1:s" is not a valid number.'; rsListSourceColorError = 'The %0:s.DataPoints string "%1:s" is not an integer.'; - // Transformations tasAxisTransformsEditorTitle = 'Edit axis transformations'; rsAutoScale = 'Auto scale'; diff --git a/components/tachart/tacustomseries.pas b/components/tachart/tacustomseries.pas index 5fee42055e..62ec0db62c 100644 --- a/components/tachart/tacustomseries.pas +++ b/components/tachart/tacustomseries.pas @@ -1940,15 +1940,15 @@ begin {FLoBound and FUpBound fields may be outdated here (if axis' range has been changed after the last series' painting). FLoBound and FUpBound will be fully updated later, in a PrepareGraphPoints() call. But we need them now. If data - source is sorted, obtaining FLoBound and FUpBound is very fast (binary search) - - so we call FindExtentInterval() with True as the second parameter. If data - source is not sorted, obtaining FLoBound and FUpBound requires enumerating all - the data points to see, if they are in the current chart's viewport. But this - is exactly what we are going to do in the loop below, so obtaining true FLoBound - and FUpBound values makes no sense in this case - so we call FindExtentInterval() - with False as the second parameter, thus setting FLoBound to 0 and FUpBound to - Count-1} - FindExtentInterval(ParentChart.CurrentExtent, Source.IsSorted); + source is sorted by X in the ascending order, obtaining FLoBound and FUpBound + is very fast (binary search) - so we call FindExtentInterval() with True as + the second parameter. Otherwise, obtaining FLoBound and FUpBound requires + enumerating all the data points to see, if they are in the current chart's + viewport. But this is exactly what we are going to do in the loop below, so + obtaining true FLoBound and FUpBound values makes no sense in this case - so + we call FindExtentInterval() with False as the second parameter, thus setting + FLoBound to 0 and FUpBound to Count-1} + FindExtentInterval(ParentChart.CurrentExtent, Source.IsSortedByXAsc); with Extent do center := AxisToGraphY((a.y + b.y) * 0.5); diff --git a/components/tachart/tacustomsource.pas b/components/tachart/tacustomsource.pas index 210e80e2b7..a5b2bb90fc 100644 --- a/components/tachart/tacustomsource.pas +++ b/components/tachart/tacustomsource.pas @@ -71,9 +71,10 @@ type type EBufferError = class(EChartError); EEditableSourceRequired = class(EChartError); + EListSourceStringError = class(EChartError); + ESortError = class(EChartError); EXCountError = class(EChartError); EYCountError = class(EChartError); - EListSourceStringError = class(EChartError); TChartValueText = record FText: String; @@ -175,9 +176,11 @@ type property IndexPlus: Integer index 0 read GetIndex write SetIndex default -1; property ValueMinus: Double index 1 read GetValue write SetValue stored IsErrorBarValueStored; property ValuePlus: Double index 0 read GetValue write SetValue stored IsErrorBarValueStored; - end; + TChartSortBy = (sbX, sbY, sbColor, sbText, sbCustom); + TChartSortDir = (sdAscending, sdDescending); + TCustomChartSource = class(TBasicChartSource) strict private FErrorBarData: array[0..1] of TChartErrorBarData; @@ -197,6 +200,9 @@ type FYListExtentIsValid: Boolean; FValuesTotal: Double; FValuesTotalIsValid: Boolean; + FSortBy: TChartSortBy; + FSortDir: TChartSortDir; + FSortIndex: Cardinal; FXCount: Cardinal; FYCount: Cardinal; function CalcExtentXYList(UseXList: Boolean): TDoubleRect; @@ -207,12 +213,19 @@ type function GetHasErrorBars(Which: Integer): Boolean; function GetItem(AIndex: Integer): PChartDataItem; virtual; abstract; procedure InvalidateCaches; + procedure SetSortBy(AValue: TChartSortBy); virtual; + procedure SetSortDir(AValue: TChartSortDir); virtual; + procedure SetSortIndex(AValue: Cardinal); virtual; procedure SetXCount(AValue: Cardinal); virtual; abstract; procedure SetYCount(AValue: Cardinal); virtual; abstract; property XErrorBarData: TChartErrorBarData index 0 read GetErrorBarData write SetErrorBarData stored IsErrorBarDataStored; property YErrorBarData: TChartErrorBarData index 1 read GetErrorBarData write SetErrorBarData stored IsErrorBarDataStored; + protected + property SortBy: TChartSortBy read FSortBy write SetSortBy default sbX; + property SortDir: TChartSortDir read FSortDir write SetSortDir default sdAscending; + property SortIndex: Cardinal read FSortIndex write SetSortIndex default 0; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; @@ -247,6 +260,7 @@ type function IsXErrorIndex(AXIndex: Integer): Boolean; function IsYErrorIndex(AYIndex: Integer): Boolean; function IsSorted: Boolean; virtual; + function IsSortedByXAsc: Boolean; procedure ValuesInRange( AParams: TValuesInRangeParams; var AValues: TChartValueTextArray); virtual; function ValuesTotal: Double; virtual; @@ -288,7 +302,7 @@ procedure SetDataItemDefaults(var AItem: TChartDataItem); implementation uses - Math, StrUtils, SysUtils, TAMath; + Math, StrUtils, SysUtils, TAMath, TAChartStrConsts; function CompareChartValueTextPtr(AItem1, AItem2: Pointer): Integer; begin @@ -895,8 +909,6 @@ begin end; end; - - class procedure TCustomChartSource.CheckFormat(const AFormat: String); begin Format(AFormat, [0.0, 0.0, '', 0.0, 0.0]); @@ -907,6 +919,9 @@ var i: Integer; begin inherited Create(AOwner); + FSortBy := sbX; + FSortDir := sdAscending; + FSortIndex := 0; FXCount := 1; FYCount := 1; for i:=Low(FErrorBarData) to High(FErrorBarData) do begin @@ -1011,7 +1026,8 @@ end; // ALB -> leftmost item where X >= AXMin, or Count if no such item // ALB -> rightmost item where X <= AXMax, or -1 if no such item -// If the source is sorted, performs binary search. Otherwise, skips NaNs. +// If the source is sorted by X in the ascending order, performs +// binary search. Otherwise, skips NaNs. procedure TCustomChartSource.FindBounds( AXMin, AXMax: Double; out ALB, AUB: Integer); @@ -1041,7 +1057,7 @@ procedure TCustomChartSource.FindBounds( begin EnsureOrder(AXMin, AXMax); - if IsSorted then begin + if IsSortedByXAsc then begin ALB := FindLB(AXMin, 0, Count - 1); AUB := FindUB(AXMax, 0, Count - 1); end @@ -1250,6 +1266,16 @@ begin Result := (FIndex[AIndex] <> -1) or (FValue[AIndex] <> -1) or (FKind <> ebkNone); end; +function TCustomChartSource.IsSorted: Boolean; inline; +begin + Result := false; +end; + +function TCustomChartSource.IsSortedByXAsc: Boolean; +begin + Result := IsSorted and (FSortBy = sbX) and (FSortDir = sdAscending) and (FSortIndex = 0); +end; + function TCustomChartSource.IsXErrorIndex(AXIndex: Integer): Boolean; begin Result := @@ -1267,11 +1293,6 @@ begin (AYIndex > -1); end; -function TCustomChartSource.IsSorted: Boolean; -begin - Result := false; -end; - procedure TCustomChartSource.SetErrorBarData(AIndex: Integer; AValue: TChartErrorBarData); begin @@ -1279,6 +1300,24 @@ begin Notify; end; +procedure TCustomChartSource.SetSortBy(AValue: TChartSortBy); +begin + if FSortBy <> AValue then + raise ESortError.CreateFmt(rsSourceSortError, [ClassName]); +end; + +procedure TCustomChartSource.SetSortDir(AValue: TChartSortDir); +begin + if FSortDir <> AValue then + raise ESortError.CreateFmt(rsSourceSortError, [ClassName]); +end; + +procedure TCustomChartSource.SetSortIndex(AValue: Cardinal); +begin + if FSortIndex <> AValue then + raise ESortError.CreateFmt(rsSourceSortError, [ClassName]); +end; + procedure TCustomChartSource.SortValuesInRange( var AValues: TChartValueTextArray; AStart, AEnd: Integer); var @@ -1421,7 +1460,7 @@ begin cnt += 1; end; - if not IsSorted and not IsValueTextsSorted(AValues, start, cnt - 1) then begin + if not IsSortedByXAsc and not IsValueTextsSorted(AValues, start, cnt - 1) then begin SortValuesInRange(AValues, start, cnt - 1); if aipUseMinLength in AParams.FIntervals.Options then cnt := EnsureMinLength(start, cnt - 1); diff --git a/components/tachart/tamultiseries.pas b/components/tachart/tamultiseries.pas index eb32a9287f..b74b9ebcfb 100644 --- a/components/tachart/tamultiseries.pas +++ b/components/tachart/tamultiseries.pas @@ -879,7 +879,7 @@ begin if Count = 0 then exit; if not RequestValidChartScaling then exit; - FindExtentInterval(ParentChart.CurrentExtent, Source.IsSorted); + FindExtentInterval(ParentChart.CurrentExtent, Source.IsSortedByXAsc); with Extent do center := AxisToGraphY((a.y + b.y) * 0.5); UpdateLabelDirectionReferenceLevel(0, 0, center); diff --git a/components/tachart/tasources.pas b/components/tachart/tasources.pas index 32a6200c5e..dcd696c502 100644 --- a/components/tachart/tasources.pas +++ b/components/tachart/tasources.pas @@ -19,13 +19,33 @@ uses Classes, Types, TAChartUtils, TACustomSource; type - { TListChartSource } + TChartSortCompare = function(AItem1, AItem2: Pointer): Integer of object; - TListChartSource = class(TCustomChartSource) + { TCustomSortedChartSource } + + TCustomSortedChartSource = class(TCustomChartSource) private + FOnCompare: TChartSortCompare; + procedure SetSorted(AValue: Boolean); + protected FData: TFPList; - FDataPoints: TStrings; FSorted: Boolean; + function DefaultCompare(AItem1, AItem2: Pointer): Integer; virtual; abstract; + procedure ExecSort(ACompare: TChartSortCompare); virtual; + procedure SetSortBy(AValue: TChartSortBy); override; + procedure SetSortDir(AValue: TChartSortDir); override; + procedure SetSortIndex(AValue: Cardinal); override; + property Sorted: Boolean read FSorted write SetSorted default false; + property OnCompare: TChartSortCompare read FOnCompare write FOnCompare; + public + function IsSorted: Boolean; override; + procedure Sort; + end; + + { TListChartSource } + TListChartSource = class(TCustomSortedChartSource) + private + FDataPoints: TStrings; FXCountMin: Cardinal; FYCountMin: Cardinal; procedure AddAt( @@ -33,9 +53,9 @@ type procedure ClearCaches; function NewItem: PChartDataItem; procedure SetDataPoints(AValue: TStrings); - procedure SetSorted(AValue: Boolean); procedure UpdateCachesAfterAdd(AX, AY: Double); protected + function DefaultCompare(AItem1, AItem2: Pointer): Integer; override; function GetCount: Integer; override; function GetItem(AIndex: Integer): PChartDataItem; override; procedure Loaded; override; @@ -61,23 +81,24 @@ type procedure Clear; procedure CopyFrom(ASource: TCustomChartSource); procedure Delete(AIndex: Integer); - function IsSorted: Boolean; override; - procedure SetColor(AIndex: Integer; AColor: TChartColor); procedure SetText(AIndex: Integer; AValue: String); function SetXValue(AIndex: Integer; AValue: Double): Integer; procedure SetXList(AIndex: Integer; const AXList: array of Double); procedure SetYList(AIndex: Integer; const AYList: array of Double); procedure SetYValue(AIndex: Integer; AValue: Double); - - procedure Sort; published property DataPoints: TStrings read FDataPoints write SetDataPoints; - property Sorted: Boolean read FSorted write SetSorted default false; property XCount; property XErrorBarData; property YCount; property YErrorBarData; + // Sorting + property SortBy; + property SortDir; + property Sorted; + property SortIndex; + property OnCompare; end; { TMWCRandomGenerator } @@ -203,6 +224,7 @@ type FOriginYCount: Cardinal; FPercentage: Boolean; FReorderYList: String; + FSorted: Boolean; FYOrder: array of Integer; procedure CalcAccumulation(AIndex: Integer); @@ -228,7 +250,6 @@ type public constructor Create(AOwner: TComponent); override; destructor Destroy; override; - function IsSorted: Boolean; override; published property AccumulationDirection: TChartAccumulationDirection @@ -253,6 +274,7 @@ uses Math, StrUtils, SysUtils, TAMath, TAChartStrConsts; type + TCustomChartSourceAccess = class(TCustomChartSource); { TListChartSourceStrings } @@ -276,6 +298,18 @@ type procedure Insert(Index: Integer; const S: String); override; end; +function CompareFloat(x1, x2: Double): Integer; +begin + if IsNaN(x1) and IsNaN(x2) then + Result := 0 + else if IsNaN(x1) then + Result := +1 + else if IsNaN(x2) then + Result := -1 + else + Result := CompareValue(x1, x2); +end; + procedure Register; begin RegisterComponents( @@ -285,6 +319,7 @@ begin ]); end; + { TListChartSourceStrings } procedure TListChartSourceStrings.Clear; @@ -484,13 +519,106 @@ begin end; end; + +{ TCustomSortedChartSource } + +{ Built-in sorting algorithm of the ChartSource, a standard QuickSort. + Copied from the classes unit because the compare function must be a method. } +procedure TCustomSortedChartSource.ExecSort(ACompare: TChartSortCompare); + + procedure QuickSort(L, R: Longint); + var + I, J : Longint; + P, Q : Pointer; + begin + repeat + I := L; + J := R; + P := FData[(L + R) div 2]; + repeat + while ACompare(P, FData[i]) > 0 do + I := I + 1; + while ACompare(P, FData[J]) < 0 do + J := J - 1; + If I <= J then + begin + Q := FData[I]; + FData[I] := FData[J]; + FData[J] := Q; + I := I + 1; + J := J - 1; + end; + until I > J; + if J - L < R - I then + begin + if L < J then + QuickSort(L, J); + L := I; + end + else + begin + if I < R then + QuickSort(I, R); + R := J; + end; + until L >= R; + end; + +begin + if FData.Count < 2 then exit; + QuickSort(0, FData.Count-1); +end; + +function TCustomSortedChartSource.IsSorted: Boolean; inline; +begin + Result := FSorted; +end; + +procedure TCustomSortedChartSource.SetSortBy(AValue: TChartSortBy); +begin + if FSortBy = AValue then exit; + FSortBy := AValue; + Sort; +end; + +procedure TCustomSortedChartSource.SetSorted(AValue: Boolean); +begin + if FSorted = AValue then exit; + FSorted := AValue; + Sort; +end; + +procedure TCustomSortedChartSource.SetSortDir(AValue: TChartSortDir); +begin + if FSortDir = AValue then exit; + FSortDir := AValue; + Sort; +end; + +procedure TCustomSortedChartSource.SetSortIndex(AValue: Cardinal); +begin + if FSortIndex = AValue then exit; + FSortIndex := AValue; + Sort; +end; + +procedure TCustomSortedChartSource.Sort; +begin + if (FSortBy = sbCustom) then begin + if Assigned(FOnCompare) then ExecSort(FOnCompare); + end else + ExecSort(@DefaultCompare); + Notify; +end; + + { TListChartSource } function TListChartSource.Add( AX, AY: Double; const ALabel: String; AColor: TChartColor): Integer; begin Result := FData.Count; - if Sorted then + if IsSortedByXAsc then // Keep data points ordered by X coordinate. // Note that this leads to O(N^2) time except // for the case of adding already ordered points. @@ -603,7 +731,17 @@ begin SetXList(FData.Count - 1, XList); SetYList(FData.Count - 1, YList); end; - if Sorted and not ASource.IsSorted then Sort; + + if IsSorted then begin + if ASource.IsSorted and + (SortBy = TCustomChartSourceAccess(ASource).SortBy) and + (SortDir = TCustomChartSourceAccess(ASource).SortDir) and + (SortIndex = TCustomChartSourceAccess(ASource).SortIndex) and + (SortBy <> sbCustom) + then + exit; + Sort; + end; finally EndUpdate; end; @@ -628,6 +766,21 @@ begin FYCount := FYCountMin; end; +function TListChartSource.DefaultCompare(AItem1, AItem2: Pointer): Integer; +var + item1: PChartDataItem absolute AItem1; + item2: PChartDataItem absolute AItem2; +begin + case FSortBy of + sbX: Result := CompareFloat(item1^.GetX(FSortIndex), item2^.GetX(FSortIndex)); + sbY: Result := CompareFloat(item1^.GetY(FSortIndex), item2^.GetY(FSortIndex)); + sbColor: Result := CompareValue(item1^.Color, item2^.Color); + sbText: Result := CompareText(item1^.Text, item2^.Text); + sbCustom: Result := FOnCompare(AItem1, AItem2); + end; + if FSortDir = sdDescending then Result := -Result; +end; + procedure TListChartSource.Delete(AIndex: Integer); begin // Optimization @@ -670,11 +823,6 @@ begin Result := PChartDataItem(FData.Items[AIndex]); end; -function TListChartSource.IsSorted: Boolean; -begin - Result := Sorted; -end; - function TListChartSource.NewItem: PChartDataItem; begin New(Result); @@ -697,19 +845,12 @@ begin BeginUpdate; try FDataPoints.Assign(AValue); - if Sorted then Sort; + if IsSorted then Sort; finally EndUpdate; end; end; -procedure TListChartSource.SetSorted(AValue: Boolean); -begin - if FSorted = AValue then exit; - FSorted := AValue; - if Sorted then Sort; -end; - procedure TListChartSource.SetText(AIndex: Integer; AValue: String); begin with Item[AIndex]^ do begin @@ -766,7 +907,7 @@ var end; begin - if Sorted then + if IsSortedByXAsc then if IsNan(AValue) then raise EChartError.CreateFmt('X = NaN in sorted source %s', [NameOrClassName(Self)]); oldX := Item[AIndex]^.X; @@ -774,7 +915,7 @@ begin if IsEquivalent(oldX, AValue) then exit; Item[AIndex]^.X := AValue; UpdateExtent; - if Sorted then begin + if IsSortedByXAsc then begin if AValue > oldX then while (Result < Count - 1) and (Item[Result + 1]^.X < AValue) do Inc(Result) @@ -844,32 +985,6 @@ begin Notify; end; -function CompareFloat(x1, x2: Double): Integer; -begin - if IsNaN(x1) and IsNaN(x2) then - Result := 0 - else if IsNaN(x1) then - Result := +1 - else if IsNaN(x2) then - Result := -1 - else - Result := CompareValue(x1, x2); -end; - -function CompareDataItemX(AItem1, AItem2: Pointer): Integer; -var - item1: PChartDataItem absolute AItem1; - item2: PChartDataItem absolute AItem2; -begin - Result := CompareFloat(item1^.X, item2^.X); -end; - -procedure TListChartSource.Sort; -begin - FData.Sort(@CompareDataItemX); - Notify; -end; - procedure TListChartSource.UpdateCachesAfterAdd(AX, AY: Double); begin if IsUpdating then exit; // Optimization @@ -1035,7 +1150,7 @@ begin Result := @FCurItem; end; -function TRandomChartSource.IsSorted: Boolean; +function TRandomChartSource.IsSorted: Boolean; inline; begin Result := not RandomX; end; @@ -1142,9 +1257,9 @@ begin Result := @FItem; end; -function TUserDefinedChartSource.IsSorted: Boolean; +function TUserDefinedChartSource.IsSorted: Boolean; inline; begin - Result := Sorted; + Result := FSorted; end; procedure TUserDefinedChartSource.Reset; @@ -1350,6 +1465,22 @@ end; procedure TCalculatedChartSource.Changed(ASender: TObject); begin + if FOrigin <> nil then begin + FSortBy := TCustomChartSourceAccess(Origin).SortBy; + FSortDir := TCustomChartSourceAccess(Origin).SortDir; + FSortIndex := TCustomChartSourceAccess(Origin).SortIndex; + // We recalculate Y values, so we can't guarantee, that transformed + // data is still sorted by Y or by Origin's custom algorithm + FSorted := (FSortBy in [sbX, sbColor, sbText]) and Origin.IsSorted; + FXCount := Origin.XCount; + end else begin + FSortBy := sbX; + FSortDir := sdAscending; + FSortIndex := 0; + FSorted := false; + FXCount := 0; + end; + if (FOrigin <> nil) and (ASender = FOrigin) and (FOrigin.YCount <> FOriginYCount) @@ -1437,10 +1568,7 @@ end; function TCalculatedChartSource.IsSorted: Boolean; begin - if Origin <> nil then - Result := Origin.IsSorted - else - Result := false; + Result := FSorted; end; procedure TCalculatedChartSource.RangeAround(