TAChart: Refactor axis marks generation

git-svn-id: trunk@31764 -
This commit is contained in:
ask 2011-07-20 11:40:55 +00:00
parent 82d446ecb3
commit de32d99c42
3 changed files with 80 additions and 72 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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.