TAChart: Use TAxisDrawHelper for axis measuring

git-svn-id: trunk@31712 -
This commit is contained in:
ask 2011-07-16 17:37:01 +00:00
parent b237589161
commit 4ed00f8de5
3 changed files with 107 additions and 102 deletions

View File

@ -97,6 +97,7 @@ type
strict private strict private
FAlignment: TChartAxisAlignment; FAlignment: TChartAxisAlignment;
FGroup: Integer; FGroup: Integer;
FHelper: TAxisDrawHelper;
FInverted: Boolean; FInverted: Boolean;
FMinors: TChartMinorAxisList; FMinors: TChartMinorAxisList;
FOnMarkToText: TChartAxisMarkToTextEvent; FOnMarkToText: TChartAxisMarkToTextEvent;
@ -124,16 +125,15 @@ type
destructor Destroy; override; destructor Destroy; override;
public public
procedure Assign(ASource: TPersistent); override; procedure Assign(ASource: TPersistent); override;
procedure Draw( procedure Draw;
ADrawer: IChartDrawer; const AClipRect: TRect; procedure DrawTitle(const ACenter: TPoint; ASize: Integer);
const ATransf: ICoordTransformer; const AZOffset: TPoint);
procedure DrawTitle(
ADrawer: IChartDrawer; const ACenter, AZOffset: TPoint; ASize: Integer);
function GetChart: TCustomChart; inline; function GetChart: TCustomChart; inline;
function IsVertical: Boolean; inline; function IsVertical: Boolean; inline;
procedure Measure( procedure Measure(
ADrawer: IChartDrawer; const AExtent: TDoubleRect; const AExtent: TDoubleRect; var AMeasureData: TChartAxisGroup);
var AMeasureData: TChartAxisGroup); procedure PrepareHelper(
ADrawer: IChartDrawer; const ATransf: ICoordTransformer;
AClipRect: PRect; AMaxZPosition: Integer);
published published
property Alignment default calLeft; property Alignment default calLeft;
property Group: Integer read FGroup write SetGroup default 0; property Group: Integer read FGroup write SetGroup default 0;
@ -183,14 +183,10 @@ type
destructor Destroy; override; destructor Destroy; override;
public public
function Add: TChartAxis; inline; function Add: TChartAxis; inline;
procedure Draw( procedure Draw(ACurrentZ: Integer; var AIndex: Integer);
ADrawer: IChartDrawer; const AClipRect: TRect;
const ATransf: ICoordTransformer; ACurrentZ, AMaxZ: Integer;
var AIndex: Integer);
function GetAxis(AIndex: Integer): TChartAxis; function GetAxis(AIndex: Integer): TChartAxis;
function GetEnumerator: TChartAxisEnumerator; function GetEnumerator: TChartAxisEnumerator;
function Measure( function Measure(const AExtent: TDoubleRect): TChartAxisMargins;
ADrawer: IChartDrawer; const AExtent: TDoubleRect): TChartAxisMargins;
procedure Prepare(ARect: TRect); procedure Prepare(ARect: TRect);
procedure PrepareGroups; procedure PrepareGroups;
procedure SetAxis(AIndex: Integer; AValue: TChartAxis); procedure SetAxis(AIndex: Integer; AValue: TChartAxis);
@ -375,72 +371,48 @@ begin
FreeAndNil(FTitle); FreeAndNil(FTitle);
FreeAndNil(FMinors); FreeAndNil(FMinors);
FreeAndNil(FListener); FreeAndNil(FListener);
FreeAndNil(FHelper);
inherited; inherited;
end; end;
procedure TChartAxis.Draw( procedure TChartAxis.Draw;
ADrawer: IChartDrawer; const AClipRect: TRect;
const ATransf: ICoordTransformer; const AZOffset: TPoint);
function MakeDrawHelper(AAxis: TChartBasicAxis): TAxisDrawHelper;
begin
if IsVertical then
Result := TAxisDrawHelperY.Create
else
Result := TAxisDrawHelperX.Create;
try
Result.FAxis := AAxis;
Result.FClipRect := AClipRect;
Result.FDrawer := ADrawer;
Result.FTransf := ATransf;
Result.FZOffset := AZOffset;
Result.BeginDrawing;
except
Result.Free;
raise;
end;
end;
var var
i, j, c, ic, fixedCoord: Integer; i, j, c, ic, fixedCoord: Integer;
axisTransf: TTransformFunc; axisTransf: TTransformFunc;
dh, dhMinor: TAxisDrawHelper; dhMinor: TAxisDrawHelper;
pv, v: Double; pv, v: Double;
begin begin
if not Visible then exit; if not Visible then exit;
if Marks.Visible then if Marks.Visible then
ADrawer.Font := Marks.LabelFont; FHelper.FDrawer.Font := Marks.LabelFont;
fixedCoord := TChartAxisMargins(FAxisRect)[Alignment]; fixedCoord := TChartAxisMargins(FAxisRect)[Alignment];
v := 0; v := 0;
dh := MakeDrawHelper(Self); FHelper.BeginDrawing;
try axisTransf := @GetTransform.AxisToGraph;
axisTransf := @GetTransform.AxisToGraph; for i := 0 to High(FMarkValues) do begin
for i := 0 to High(FMarkValues) do begin pv := v;
pv := v; v := axisTransf(FMarkValues[i]);
v := axisTransf(FMarkValues[i]); FHelper.DrawMark(fixedCoord, v, FMarkTexts[i]);
dh.DrawMark(fixedCoord, v, FMarkTexts[i]); if (i = 0) or (v = pv) then continue;
if (i = 0) or (v = pv) then continue; for j := 0 to Minors.Count - 1 do begin
for j := 0 to Minors.Count - 1 do begin ic := Minors[j].IntervalsCount;
ic := Minors[j].IntervalsCount; if not Minors[j].Visible or (ic < 2) then continue;
if not Minors[j].Visible or (ic < 2) then continue; dhMinor := FHelper.Clone;
dhMinor := MakeDrawHelper(Minors[j]); dhMinor.FAxis := Minors[j];
try try
for c := 1 to ic - 1 do dhMinor.BeginDrawing;
dhMinor.DrawMark(fixedCoord, WeightedAverage(pv, v, c / ic), ''); for c := 1 to ic - 1 do
dhMinor.EndDrawing; dhMinor.DrawMark(fixedCoord, WeightedAverage(pv, v, c / ic), '');
finally dhMinor.EndDrawing;
dhMinor.Free; finally
end; dhMinor.Free;
end; end;
end; end;
dh.EndDrawing;
finally
dh.Free;
end; end;
FHelper.EndDrawing;
end; end;
procedure TChartAxis.DrawTitle( procedure TChartAxis.DrawTitle(const ACenter: TPoint; ASize: Integer);
ADrawer: IChartDrawer; const ACenter, AZOffset: TPoint; ASize: Integer);
var var
p: TPoint; p: TPoint;
dummy: TPointArray = nil; dummy: TPointArray = nil;
@ -455,8 +427,8 @@ begin
calRight: p.X := FTitleRect.Right + d; calRight: p.X := FTitleRect.Right + d;
calBottom: p.Y := FTitleRect.Bottom + d; calBottom: p.Y := FTitleRect.Bottom + d;
end; end;
p += AZOffset; p += FHelper.FZOffset;
Title.DrawLabel(ADrawer, p, p, Title.Caption, dummy); Title.DrawLabel(FHelper.FDrawer, p, p, Title.Caption, dummy);
end; end;
function TChartAxis.GetAlignment: TChartAxisAlignment; function TChartAxis.GetAlignment: TChartAxisAlignment;
@ -527,8 +499,7 @@ begin
end; end;
procedure TChartAxis.Measure( procedure TChartAxis.Measure(
ADrawer: IChartDrawer; const AExtent: TDoubleRect; const AExtent: TDoubleRect; var AMeasureData: TChartAxisGroup);
var AMeasureData: TChartAxisGroup);
function MaxMarksSize(AMin, AMax: Double): TPoint; function MaxMarksSize(AMin, AMax: Double): TPoint;
var var
@ -539,7 +510,7 @@ procedure TChartAxis.Measure(
GetMarkValues(AMin, AMax); GetMarkValues(AMin, AMax);
if not Marks.Visible then exit; if not Marks.Visible then exit;
for t in FMarkTexts do for t in FMarkTexts do
Result := MaxPoint(Marks.MeasureLabel(ADrawer, t), Result); Result := MaxPoint(Marks.MeasureLabel(FHelper.FDrawer, t), Result);
end; end;
function TitleSize: Integer; function TitleSize: Integer;
@ -548,13 +519,13 @@ procedure TChartAxis.Measure(
begin begin
if not Title.Visible or (Title.Caption = '') then if not Title.Visible or (Title.Caption = '') then
exit(0); exit(0);
sz := Title.MeasureLabel(ADrawer, Title.Caption); sz := Title.MeasureLabel(FHelper.FDrawer, Title.Caption);
Result := IfThen(IsVertical, sz.cx, sz.cy) + Title.Distance; Result := IfThen(IsVertical, sz.cx, sz.cy) + Title.Distance;
end; end;
function FirstLastSize(AIndex: Integer): Integer; function FirstLastSize(AIndex: Integer): Integer;
begin begin
with Marks.MeasureLabel(ADrawer, FMarkTexts[AIndex]) do with Marks.MeasureLabel(FHelper.FDrawer, FMarkTexts[AIndex]) do
Result := IfThen(IsVertical, cy, cx) div 2; Result := IfThen(IsVertical, cy, cx) div 2;
end; end;
@ -569,7 +540,8 @@ begin
if Marks.DistanceToCenter then if Marks.DistanceToCenter then
sz := sz div 2; sz := sz div 2;
if sz > 0 then if sz > 0 then
sz += ADrawer.Scale(TickLength) + ADrawer.Scale(Marks.Distance); sz += FHelper.FDrawer.Scale(TickLength) +
FHelper.FDrawer.Scale(Marks.Distance);
with AMeasureData do begin with AMeasureData do begin
FSize := Max(sz, FSize); FSize := Max(sz, FSize);
FTitleSize := Max(TitleSize, FTitleSize); FTitleSize := Max(TitleSize, FTitleSize);
@ -580,6 +552,23 @@ begin
end; end;
end; end;
procedure TChartAxis.PrepareHelper(
ADrawer: IChartDrawer; const ATransf: ICoordTransformer;
AClipRect: PRect; AMaxZPosition: Integer);
begin
FreeAndNil(FHelper);
if IsVertical then
FHelper := TAxisDrawHelperY.Create
else
FHelper := TAxisDrawHelperX.Create;
FHelper.FAxis := Self;
FHelper.FClipRect := AClipRect;
FHelper.FDrawer := ADrawer;
FHelper.FTransf := ATransf;
FHelper.FZOffset.X := Min(ZPosition, AMaxZPosition);
FHelper.FZOffset.Y := -FHelper.FZOffset.X;
end;
procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment); procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment);
begin begin
if FAlignment = AValue then exit; if FAlignment = AValue then exit;
@ -695,20 +684,13 @@ begin
inherited Destroy; inherited Destroy;
end; end;
procedure TChartAxisList.Draw( procedure TChartAxisList.Draw(ACurrentZ: Integer; var AIndex: Integer);
ADrawer: IChartDrawer; const AClipRect: TRect;
const ATransf: ICoordTransformer; ACurrentZ, AMaxZ: Integer;
var AIndex: Integer);
var
zoffset: TPoint;
begin begin
while AIndex < FZOrder.Count do while AIndex < FZOrder.Count do
with TChartAxis(FZOrder[AIndex]) do begin with TChartAxis(FZOrder[AIndex]) do begin
if ACurrentZ < ZPosition then break; if ACurrentZ < ZPosition then break;
zoffset.Y := Min(ZPosition, AMaxZ); Draw;
zoffset.X := - zoffset.Y; DrawTitle(FCenterPoint, FGroups[FGroupIndex].FTitleSize);
Draw(ADrawer, AClipRect, ATransf, zoffset);
DrawTitle(ADrawer, FCenterPoint, zoffset, FGroups[FGroupIndex].FTitleSize);
AIndex += 1; AIndex += 1;
end; end;
end; end;
@ -746,8 +728,7 @@ begin
AList.Sort(ACompare); AList.Sort(ACompare);
end; end;
function TChartAxisList.Measure( function TChartAxisList.Measure(const AExtent: TDoubleRect): TChartAxisMargins;
ADrawer: IChartDrawer; const AExtent: TDoubleRect): TChartAxisMargins;
var var
g: ^TChartAxisGroup; g: ^TChartAxisGroup;
@ -771,7 +752,7 @@ begin
g^.FTitleSize := 0; g^.FTitleSize := 0;
for j := 0 to g^.FCount - 1 do begin for j := 0 to g^.FCount - 1 do begin
axis := TChartAxis(FGroupOrder[ai]); axis := TChartAxis(FGroupOrder[ai]);
axis.Measure(ADrawer, AExtent, g^); axis.Measure(AExtent, g^);
ai += 1; ai += 1;
end; end;
Result[axis.Alignment] += g^.FSize + g^.FTitleSize; Result[axis.Alignment] += g^.FSize + g^.FTitleSize;

View File

@ -170,7 +170,7 @@ type
function TryApplyStripes: Boolean; inline; function TryApplyStripes: Boolean; inline;
public public
FAxis: TChartBasicAxis; FAxis: TChartBasicAxis;
FClipRect: TRect; FClipRect: ^TRect;
FDrawer: IChartDrawer; FDrawer: IChartDrawer;
FPrevCoord: Integer; FPrevCoord: Integer;
FPrevLabelPoly: TPointArray; FPrevLabelPoly: TPointArray;
@ -180,11 +180,15 @@ type
FZOffset: TPoint; FZOffset: TPoint;
procedure BeginDrawing; virtual; procedure BeginDrawing; virtual;
constructor Create; virtual;
function Clone: TAxisDrawHelper;
procedure DrawMark( procedure DrawMark(
AFixedCoord: Integer; AMark: Double; const AText: String); AFixedCoord: Integer; AMark: Double; const AText: String);
procedure EndDrawing; virtual; abstract; procedure EndDrawing; virtual; abstract;
end; end;
TAxisDrawHelperClass = class of TAxisDrawHelper;
{ TAxisDrawHelperX } { TAxisDrawHelperX }
TAxisDrawHelperX = class(TAxisDrawHelper) TAxisDrawHelperX = class(TAxisDrawHelper)
@ -232,6 +236,21 @@ begin
FScaledTickLength := FDrawer.Scale(FAxis.TickLength); FScaledTickLength := FDrawer.Scale(FAxis.TickLength);
end; end;
function TAxisDrawHelper.Clone: TAxisDrawHelper;
begin
Result := TAxisDrawHelperClass(ClassType).Create;
Result.FAxis := FAxis;
Result.FClipRect := FClipRect;
Result.FDrawer := FDrawer;
Result.FTransf := FTransf;
Result.FZOffset := FZOffset;
end;
constructor TAxisDrawHelper.Create;
begin
inherited;
end;
procedure TAxisDrawHelper.DrawLabel(ALabelCenter: TPoint; const AText: String); procedure TAxisDrawHelper.DrawLabel(ALabelCenter: TPoint; const AText: String);
begin begin
ALabelCenter += FZOffset; ALabelCenter += FZOffset;
@ -275,7 +294,7 @@ end;
procedure TAxisDrawHelperX.BeginDrawing; procedure TAxisDrawHelperX.BeginDrawing;
begin begin
inherited; inherited;
FPrevCoord := FClipRect.Left; FPrevCoord := FClipRect^.Left;
end; end;
procedure TAxisDrawHelperX.DrawLabelAndTick( procedure TAxisDrawHelperX.DrawLabelAndTick(
@ -295,7 +314,7 @@ end;
procedure TAxisDrawHelperX.EndDrawing; procedure TAxisDrawHelperX.EndDrawing;
begin begin
if FAxis.Grid.Visible and TryApplyStripes then if FAxis.Grid.Visible and TryApplyStripes then
BarZ(FPrevCoord + 1, FClipRect.Top + 1, FClipRect.Right, FClipRect.Bottom); BarZ(FPrevCoord + 1, FClipRect^.Top + 1, FClipRect^.Right, FClipRect^.Bottom);
end; end;
function TAxisDrawHelperX.GraphToImage(AGraph: Double): Integer; function TAxisDrawHelperX.GraphToImage(AGraph: Double): Integer;
@ -306,13 +325,13 @@ end;
procedure TAxisDrawHelperX.GridLine(ACoord: Integer); procedure TAxisDrawHelperX.GridLine(ACoord: Integer);
begin begin
if TryApplyStripes then if TryApplyStripes then
BarZ(FPrevCoord + 1, FClipRect.Top + 1, ACoord, FClipRect.Bottom); BarZ(FPrevCoord + 1, FClipRect^.Top + 1, ACoord, FClipRect^.Bottom);
LineZ(Point(ACoord, FClipRect.Top), Point(ACoord, FClipRect.Bottom)); LineZ(Point(ACoord, FClipRect^.Top), Point(ACoord, FClipRect^.Bottom));
end; end;
function TAxisDrawHelperX.IsInClipRange(ACoord: Integer): Boolean; function TAxisDrawHelperX.IsInClipRange(ACoord: Integer): Boolean;
begin begin
Result := InRange(ACoord, FClipRect.Left, FClipRect.Right); Result := InRange(ACoord, FClipRect^.Left, FClipRect^.Right);
end; end;
{ TAxisDrawHelperY } { TAxisDrawHelperY }
@ -320,7 +339,7 @@ end;
procedure TAxisDrawHelperY.BeginDrawing; procedure TAxisDrawHelperY.BeginDrawing;
begin begin
inherited; inherited;
FPrevCoord := FClipRect.Bottom; FPrevCoord := FClipRect^.Bottom;
end; end;
procedure TAxisDrawHelperY.DrawLabelAndTick( procedure TAxisDrawHelperY.DrawLabelAndTick(
@ -340,7 +359,7 @@ end;
procedure TAxisDrawHelperY.EndDrawing; procedure TAxisDrawHelperY.EndDrawing;
begin begin
if FAxis.Grid.Visible and TryApplyStripes then if FAxis.Grid.Visible and TryApplyStripes then
BarZ(FClipRect.Left + 1, FClipRect.Top + 1, FClipRect.Right, FPrevCoord); BarZ(FClipRect^.Left + 1, FClipRect^.Top + 1, FClipRect^.Right, FPrevCoord);
end; end;
function TAxisDrawHelperY.GraphToImage(AGraph: Double): Integer; function TAxisDrawHelperY.GraphToImage(AGraph: Double): Integer;
@ -351,13 +370,13 @@ end;
procedure TAxisDrawHelperY.GridLine(ACoord: Integer); procedure TAxisDrawHelperY.GridLine(ACoord: Integer);
begin begin
if TryApplyStripes then if TryApplyStripes then
BarZ(FClipRect.Left + 1, FPrevCoord, FClipRect.Right, ACoord); BarZ(FClipRect^.Left + 1, FPrevCoord, FClipRect^.Right, ACoord);
LineZ(Point(FClipRect.Left, ACoord), Point(FClipRect.Right, ACoord)); LineZ(Point(FClipRect^.Left, ACoord), Point(FClipRect^.Right, ACoord));
end; end;
function TAxisDrawHelperY.IsInClipRange(ACoord: Integer): Boolean; function TAxisDrawHelperY.IsInClipRange(ACoord: Integer): Boolean;
begin begin
Result := InRange(ACoord, FClipRect.Top, FClipRect.Bottom); Result := InRange(ACoord, FClipRect^.Top, FClipRect^.Bottom);
end; end;
{ TChartAxisTitle } { TChartAxisTitle }

View File

@ -668,7 +668,7 @@ begin
if not Active then continue; if not Active then continue;
// Interleave axises with series according to ZPosition. // Interleave axises with series according to ZPosition.
if AxisVisible then if AxisVisible then
AxisList.Draw(ADrawer, FClipRect, Self, ZPosition, d, axisIndex); AxisList.Draw(ZPosition, axisIndex);
OffsetDrawArea(Min(ZPosition, d), Min(Depth, d)); OffsetDrawArea(Min(ZPosition, d), Min(Depth, d));
ADrawer.ClippingStart(FClipRect); ADrawer.ClippingStart(FClipRect);
try try
@ -688,7 +688,7 @@ begin
end; end;
end; end;
if AxisVisible then if AxisVisible then
AxisList.Draw(ADrawer, FClipRect, Self, MaxInt, d, axisIndex); AxisList.Draw(MaxInt, axisIndex);
end; end;
{$IFDEF LCLGtk2} {$IFDEF LCLGtk2}
@ -1105,10 +1105,11 @@ end;
procedure TChart.PrepareAxis(ADrawer: IChartDrawer); procedure TChart.PrepareAxis(ADrawer: IChartDrawer);
var var
axisMargin: TChartAxisMargins; axisMargin: TChartAxisMargins;
a: TChartAxisAlignment; aa: TChartAxisAlignment;
cr: TRect; cr: TRect;
tries: Integer; tries: Integer;
prevExt: TDoubleRect; prevExt: TDoubleRect;
axis: TChartAxis;
begin begin
if not AxisVisible then begin if not AxisVisible then begin
FClipRect.Left += Depth; FClipRect.Left += Depth;
@ -1117,24 +1118,28 @@ begin
exit; exit;
end; end;
AxisList.PrepareGroups;
for axis in AxisList do
axis.PrepareHelper(ADrawer, Self, @FClipRect, Depth);
// There is a cyclic dependency: extent -> visible marks -> margins. // There is a cyclic dependency: extent -> visible marks -> margins.
// We recalculate them iteratively hoping that the process converges. // We recalculate them iteratively hoping that the process converges.
AxisList.PrepareGroups;
CalculateTransformationCoeffs(Rect(0, 0, 0, 0)); CalculateTransformationCoeffs(Rect(0, 0, 0, 0));
cr := FClipRect; cr := FClipRect;
for tries := 1 to 10 do begin for tries := 1 to 10 do begin
axisMargin := AxisList.Measure(ADrawer, CurrentExtent); FClipRect := cr;
axisMargin := AxisList.Measure(CurrentExtent);
axisMargin[calLeft] := Max(axisMargin[calLeft], Depth); axisMargin[calLeft] := Max(axisMargin[calLeft], Depth);
axisMargin[calBottom] := Max(axisMargin[calBottom], Depth); axisMargin[calBottom] := Max(axisMargin[calBottom], Depth);
FClipRect := cr; for aa := Low(aa) to High(aa) do
for a := Low(a) to High(a) do SideByAlignment(FClipRect, aa, -axisMargin[aa]);
SideByAlignment(FClipRect, a, -axisMargin[a]);
prevExt := FCurrentExtent; prevExt := FCurrentExtent;
FCurrentExtent := FLogicalExtent; FCurrentExtent := FLogicalExtent;
CalculateTransformationCoeffs(GetMargins(ADrawer)); CalculateTransformationCoeffs(GetMargins(ADrawer));
if prevExt = FCurrentExtent then break; if prevExt = FCurrentExtent then break;
prevExt := FCurrentExtent; prevExt := FCurrentExtent;
end; end;
AxisList.Prepare(FClipRect); AxisList.Prepare(FClipRect);
end; end;