diff --git a/components/tachart/tachartaxis.pas b/components/tachart/tachartaxis.pas index 168e3d7494..25214f2e59 100644 --- a/components/tachart/tachartaxis.pas +++ b/components/tachart/tachartaxis.pas @@ -33,13 +33,6 @@ const type - TChartValueText = record - FValue: Double; - FText: String; - end; - - TChartValueTextArray = array of TChartValueText; - { TChartMinorAxis } TChartMinorAxis = class(TChartBasicAxis) @@ -99,8 +92,7 @@ type TChartAxis = class(TChartBasicAxis) strict private FListener: TListener; - FMarkTexts: TStringDynArray; - FMarkValues: TDoubleDynArray; + FMarkValues: TChartValueTextArray; procedure GetMarkValues(AMin, AMax: Double); procedure VisitSource(ASource: TCustomChartSource; var AData); @@ -413,35 +405,23 @@ begin end; procedure TChartAxis.Draw; -var - i, j, fixedCoord: Integer; - axisTransf: TTransformFunc; - pv, v: Double; - minorMarks: TChartValueTextArray; - m: TChartValueText; -begin - if not Visible then exit; - if Marks.Visible then - FHelper.FDrawer.Font := Marks.LabelFont; - fixedCoord := TChartAxisMargins(FAxisRect)[Alignment]; - v := 0; - FHelper.BeginDrawing; - FHelper.DrawAxisLine(AxisPen, fixedCoord); - axisTransf := @GetTransform.AxisToGraph; - for i := 0 to High(FMarkValues) do begin - pv := v; - v := axisTransf(FMarkValues[i]); - FHelper.DrawMark(fixedCoord, v, FMarkTexts[i]); - if (i = 0) or (v = pv) then continue; + + procedure DrawMinors(AFixedCoord: Integer; AMin, AMax: Double); + var + j: Integer; + minorMarks: TChartValueTextArray; + m: TChartValueText; + begin + if IsNan(AMin) or (AMin = AMax) then exit; for j := 0 to Minors.Count - 1 do begin - minorMarks := Minors[j].GetMarkValues(pv, v); + minorMarks := Minors[j].GetMarkValues(AMin, AMax); if minorMarks = nil then continue; with FHelper.Clone do begin FAxis := Minors[j]; try BeginDrawing; for m in minorMarks do - DrawMark(fixedCoord, m.FValue, m.FText); + DrawMark(AFixedCoord, m.FValue, m.FText); EndDrawing; finally Free; @@ -449,6 +429,27 @@ begin end; end; end; + +var + fixedCoord: Integer; + pv, v: Double; + axisTransf: TTransformFunc; + t: TChartValueText; +begin + if not Visible then exit; + if Marks.Visible then + FHelper.FDrawer.Font := Marks.LabelFont; + fixedCoord := TChartAxisMargins(FAxisRect)[Alignment]; + pv := NaN; + FHelper.BeginDrawing; + FHelper.DrawAxisLine(AxisPen, fixedCoord); + axisTransf := @GetTransform.AxisToGraph; + for t in FMarkValues do begin + v := axisTransf(t.FValue); + FHelper.DrawMark(fixedCoord, v, t.FText); + DrawMinors(fixedCoord, pv, v); + pv := v; + end; FHelper.EndDrawing; end; @@ -500,12 +501,12 @@ var i: Integer; d: TAxisDataExtent; vis: TChartOnVisitSources; + t: TChartValueText; begin AMin := GetTransform.GraphToAxis(AMin); AMax := GetTransform.GraphToAxis(AMax); EnsureOrder(AMin, AMax); SetLength(FMarkValues, 0); - SetLength(FMarkTexts, 0); vis := TChartAxisList(Collection).OnVisitSources; if Marks.AtDataOnly and Assigned(vis) then begin d.FMin := AMin; @@ -514,16 +515,17 @@ begin end else Marks.SourceDef.ValuesInRange( - AMin, AMax, Marks.Format, IsVertical, FMarkValues, FMarkTexts); + AMin, AMax, Marks.Format, IsVertical, FMarkValues); if Inverted then for i := 0 to High(FMarkValues) div 2 do begin - Exchange(FMarkValues[i], FMarkValues[High(FMarkValues) - i]); - Exchange(FMarkTexts[i], FMarkTexts[High(FMarkValues) - i]); + t := FMarkValues[i]; + FMarkValues[i] := FMarkValues[High(FMarkValues) - i]; + FMarkValues[High(FMarkValues) - i] := t; end; if Assigned(FOnMarkToText) then - for i := 0 to High(FMarkTexts) do - FOnMarkToText(FMarkTexts[i], FMarkValues[i]); + for i := 0 to High(FMarkValues) do + FOnMarkToText(FMarkValues[i].FText, FMarkValues[i].FValue); end; function TChartAxis.GetTransform: TChartAxisTransformations; @@ -543,14 +545,14 @@ procedure TChartAxis.Measure( function MaxMarksSize(AMin, AMax: Double): TPoint; var - t: String; + t: TChartValueText; begin Result := Point(0, 0); if AMin = AMax then exit; GetMarkValues(AMin, AMax); if not Marks.Visible then exit; - for t in FMarkTexts do - Result := MaxPoint(Marks.MeasureLabel(FHelper.FDrawer, t), Result); + for t in FMarkValues do + Result := MaxPoint(Marks.MeasureLabel(FHelper.FDrawer, t.FText), Result); end; function TitleSize: Integer; @@ -564,9 +566,9 @@ procedure TChartAxis.Measure( Result += FHelper.FDrawer.Scale(Title.Distance); end; - function FirstLastSize(AIndex: Integer): Integer; + function FirstLastSize(AText: String): Integer; begin - with Marks.MeasureLabel(FHelper.FDrawer, FMarkTexts[AIndex]) do + with Marks.MeasureLabel(FHelper.FDrawer, AText) do Result := IfThen(IsVertical, cy, cx) div 2; end; @@ -581,6 +583,7 @@ procedure TChartAxis.Measure( var sz, rmin, rmax, c, i: Integer; + t: TChartValueText; begin if not Visible then exit; if IsVertical then @@ -597,16 +600,17 @@ begin with AMeasureData do begin FSize := Max(sz, FSize); FTitleSize := Max(TitleSize, FTitleSize); - for i := 0 to High(FMarkTexts) do begin - c := FHelper.GraphToImage(FMarkValues[i]); + for t in FMarkValues do begin + c := FHelper.GraphToImage(t.FValue); if not InRange(c, rmin, rmax) then continue; - FFirstMark := Max(FirstLastSize(i) - c + rmin, FFirstMark); + FFirstMark := Max(FirstLastSize(t.FText) - c + rmin, FFirstMark); break; end; - for i := High(FMarkTexts) downto 0 do begin - c := FHelper.GraphToImage(FMarkValues[i]); + for i := High(FMarkValues) downto 0 do begin + t := FMarkValues[i]; + c := FHelper.GraphToImage(t.FValue); if not InRange(c, rmin, rmax) then continue; - FLastMark := Max(FirstLastSize(i) - rmax + c, FLastMark); + FLastMark := Max(FirstLastSize(t.FText) - rmax + c, FLastMark); break; end; if Arrow.Visible then begin @@ -724,7 +728,7 @@ begin lmax := Min(ext.b.X, FMax); end; Marks.SourceDef.ValuesInRange( - lmin, lmax, Marks.Format, IsVertical, FMarkValues, FMarkTexts); + lmin, lmax, Marks.Format, IsVertical, FMarkValues); end; end; diff --git a/components/tachart/tacustomsource.pas b/components/tachart/tacustomsource.pas index 1eca748fdd..cc0dd6f8fb 100644 --- a/components/tachart/tacustomsource.pas +++ b/components/tachart/tacustomsource.pas @@ -29,6 +29,13 @@ type EEditableSourceRequired = class(EChartError); EYCountError = class(EChartError); + TChartValueText = record + FValue: Double; + FText: String; + end; + + TChartValueTextArray = array of TChartValueText; + { TChartDataItem } TChartDataItem = object @@ -79,7 +86,7 @@ type function IsSorted: Boolean; virtual; procedure ValuesInRange( AMin, AMax: Double; const AFormat: String; AUseY: Boolean; - var AValues: TDoubleDynArray; var ATexts: TStringDynArray); virtual; + var AValues: TChartValueTextArray); virtual; function ValuesTotal: Double; virtual; function XOfMax: Double; function XOfMin: Double; @@ -411,15 +418,15 @@ end; procedure TCustomChartSource.ValuesInRange( AMin, AMax: Double; const AFormat: String; AUseY: Boolean; - var AValues: TDoubleDynArray; var ATexts: TStringDynArray); + var AValues: TChartValueTextArray); var cnt: Integer; procedure Push(AValue: Double; AIndex: Integer); begin - AValues[cnt] := AValue; - ATexts[cnt] := FormatItem(AFormat, AIndex, 0); + AValues[cnt].FValue := AValue; + AValues[cnt].FText := FormatItem(AFormat, AIndex, 0); cnt += 1; end; @@ -429,7 +436,6 @@ var begin cnt := Length(AValues); SetLength(AValues, cnt + Count + 2); - SetLength(ATexts, cnt + Count + 2); v := 0; li := 0; for i := 0 to Count - 1 do begin @@ -444,7 +450,6 @@ begin if not InRange(v, AMin, AMax) then Push(v, li); SetLength(AValues, cnt); - SetLength(ATexts, cnt); end; function TCustomChartSource.ValuesTotal: Double; diff --git a/components/tachart/taintervalsources.pas b/components/tachart/taintervalsources.pas index 9a2452e6c8..ba9a3016f7 100644 --- a/components/tachart/taintervalsources.pas +++ b/components/tachart/taintervalsources.pas @@ -35,7 +35,7 @@ type public procedure ValuesInRange( AMin, AMax: Double; const AFormat: String; AUseY: Boolean; - var AValues: TDoubleDynArray; var ATexts: TStringDynArray); override; + var AValues: TChartValueTextArray); override; end; TDateTimeStep = ( @@ -59,7 +59,7 @@ type constructor Create(AOwner: TComponent); override; procedure ValuesInRange( AMin, AMax: Double; const AFormat: String; AUseY: Boolean; - var AValues: TDoubleDynArray; var ATexts: TStringDynArray); override; + var AValues: TChartValueTextArray); override; published property DateTimeFormat: String read FDateTimeFormat write FDateTimeFormat; property Steps: TDateTimeSteps @@ -144,7 +144,8 @@ begin end; {case AxisScale} end; -function GetIntervals(AMin, AMax: Double; AInverted: Boolean): TDoubleDynArray; +function GetIntervals( + AMin, AMax: Double; AInverted: Boolean): TChartValueTextArray; const INV_TO_SCALE: array [Boolean] of TAxisScale = (asIncreasing, asDecreasing); var @@ -169,12 +170,12 @@ begin repeat if IsZero(m) then m := 0; - Result[markCount] := m; + Result[markCount].FValue := m; markCount += 1; crossCount += Ord(InRange(m, AMin, AMax) <> InRange(m + step, AMin, AMax)); m += step; until (crossCount = 2) or (m + step = m); - Result[markCount] := m; + Result[markCount].FValue := m; end; procedure Register; @@ -206,17 +207,16 @@ end; procedure TIntervalChartSource.ValuesInRange( AMin, AMax: Double; const AFormat: String; AUseY: Boolean; - var AValues: TDoubleDynArray; var ATexts: TStringDynArray); + var AValues: TChartValueTextArray); var i: Integer; begin Unused(AUseY); if AMin > AMax then exit; AValues := GetIntervals(AMin, AMax, false); - SetLength(ATexts, Length(AValues)); for i := 0 to High(AValues) do // Extra format arguments for compatibility with FormatItem. - ATexts[i] := Format(AFormat, [AValues[i], 0.0, '', 0.0, 0.0]); + AValues[i].FText := Format(AFormat, [AValues[i].FValue, 0.0, '', 0.0, 0.0]); end; { TDateTimeIntervalChartSource } @@ -227,9 +227,9 @@ begin FSteps := DATE_TIME_STEPS_ALL; end; -procedure TDateTimeIntervalChartSource.ValuesInRange(AMin, AMax: Double; - const AFormat: String; AUseY: Boolean; var AValues: TDoubleDynArray; - var ATexts: TStringDynArray); +procedure TDateTimeIntervalChartSource.ValuesInRange( + AMin, AMax: Double; const AFormat: String; AUseY: Boolean; + var AValues: TChartValueTextArray); const YEAR = 365.25; STEP_INTERVALS: array [TDateTimeStep] of Double = ( @@ -284,7 +284,7 @@ begin if (AMax - AMin) / STEP_INTERVALS[dtsCentury] > MAX_STEPS then begin inherited ValuesInRange( AMin / STEP_INTERVALS[dtsYear], AMax / STEP_INTERVALS[dtsYear], - AFormat, AUseY, AValues, ATexts); + AFormat, AUseY, AValues); exit; end; s := Low(s); @@ -303,13 +303,12 @@ begin end; i := Length(AValues); SetLength(AValues, i + cnt); - SetLength(ATexts, i + cnt); FillChar(prevSt, SizeOf(prevSt), $FF); x := start; while x <= AMax do begin - AValues[i] := x; - ATexts[i] := Format(AFormat, [x, 0.0, FormatLabel, 0.0, 0.0]); + AValues[i].FValue := x; + AValues[i].FText := Format(AFormat, [x, 0.0, FormatLabel, 0.0, 0.0]); i += 1; case s of dtsCentury: x := IncYear(x, 100); @@ -319,8 +318,8 @@ begin otherwise x += si; end; end; - AValues[i] := x; - ATexts[i] := Format(AFormat, [x, 0.0, FormatLabel, 0.0, 0.0]); + AValues[i].FValue := x; + AValues[i].FText := Format(AFormat, [x, 0.0, FormatLabel, 0.0, 0.0]); end; end.