TAChart: Add new property RotationCenter to TChartTextElement and publish it for axis and series marks.

git-svn-id: trunk@54258 -
This commit is contained in:
wp 2017-02-23 22:03:25 +00:00
parent f6600384c3
commit 3456985cf7
4 changed files with 109 additions and 10 deletions

View File

@ -427,7 +427,6 @@ begin
Self.FTransformations := Transformations;
Self.FZPosition := ZPosition;
Self.FMarginsForMarks := MarginsForMarks;
Self.FOnMarkToText := OnMarkToText;
end;
inherited Assign(ASource);
@ -446,6 +445,7 @@ begin
TickLength := DEF_TICK_LENGTH;
FTitle := TChartAxisTitle.Create(ACollection.Owner as TCustomChart);
FMarginsForMarks := true;
FMarks.SetInsideDir(1, 0);
end;
destructor TChartAxis.Destroy;
@ -604,6 +604,7 @@ begin
FMaxForMarks := Max(FMaxForMarks, GetTransform.AxisToGraph(d.FMax));
EnsureOrder(FValueMin, FValueMax);
EnsureOrder(FMinForMarks, FMaxForMarks);
FRotationCenter := Marks.RotationCenter;
end;
if Assigned(FOnMarkToText) then
@ -826,12 +827,21 @@ begin
FHelper.FAtDataOnly := AtDataOnly;
FHelper.FMaxForMarks := NegInfinity;
FHelper.FMinForMarks := SafeInfinity;
FHelper.FRotationCenter := Marks.RotationCenter;
end;
procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment);
begin
if FAlignment = AValue then exit;
FAlignment := AValue;
// Define the "inside" direction of an axis such that rotated labels with
// rotation center at the text start or end never reach into the chart.
case FAlignment of
calBottom: FMarks.SetInsideDir(0, +1);
calTop : FMarks.SetInsideDir(0, -1);
calLeft : FMarks.SetInsideDir(+1, 0);
calRight : FMarks.SetInsideDir(-1, 0);
end;
StyleChanged(Self);
end;

View File

@ -134,6 +134,7 @@ type
property LabelBrush;
property OverlapPolicy;
property Range: TChartRange read FRange write SetRange;
property RotationCenter;
property Source: TCustomChartSource read FSource write SetSource;
property Stripes;
property Style default smsValue;
@ -223,6 +224,7 @@ type
FValueMin: Double;
FMaxForMarks: Double;
FMinForMarks: Double;
FRotationCenter: TChartTextRotationCenter;
FZOffset: TPoint;
procedure BeginDrawing; virtual;
@ -310,6 +312,7 @@ begin
Result.FValueMin := FValueMin;
Result.FMinForMarks := FMinForMarks;
Result.FMaxForMarks := FMaxForMarks;
Result.FRotationCenter := FRotationCenter;
Result.FZOffset := FZOffset;
end;
@ -321,8 +324,7 @@ end;
procedure TAxisDrawHelper.DrawLabel(ALabelCenter: TPoint; const AText: String);
begin
ALabelCenter += FZOffset;
FAxis.Marks.DrawLabel(
FDrawer, ALabelCenter, ALabelCenter, AText, FPrevLabelPoly);
FAxis.Marks.DrawLabel(FDrawer, ALabelCenter, ALabelCenter, AText, FPrevLabelPoly);
end;
procedure TAxisDrawHelper.DrawMark(
@ -426,7 +428,10 @@ procedure TAxisDrawHelperX.DrawLabelAndTick(
var
d, up, down: Integer;
begin
d := FScaledTickLength + FAxis.Marks.CenterOffset(FDrawer, AText).cy;
if FRotationCenter = rcCenter then
d := FScaledTickLength + FAxis.Marks.CenterOffset(FDrawer, AText).cy
else
d := FScaledTickLength + FAxis.Marks.CenterHeightOffset(FDrawer, AText).cy;
up := FScaledTickInnerLength;
down := FScaledTickLength;
if FAxis.Alignment = calTop then begin
@ -492,7 +497,10 @@ procedure TAxisDrawHelperY.DrawLabelAndTick(
var
d, left, right: Integer;
begin
d := FScaledTickLength + FAxis.Marks.CenterOffset(FDrawer, AText).cx;
if FRotationCenter = rcCenter then
d := FScaledTickLength + FAxis.Marks.CenterOffset(FDrawer, AText).cx
else
d := FScaledTickLength + FAxis.Marks.CenterHeightOffset(FDrawer, AText).cx;
left := FScaledTickInnerLength;
right := FScaledTickLength;
if FAxis.Alignment = calLeft then begin

View File

@ -1120,7 +1120,11 @@ var
center: TPoint;
begin
if AText = '' then exit;
center := ADataPoint + OFFSETS[ADir] * Marks.CenterOffset(ADrawer, AText);
if Marks.RotationCenter = rcCenter then
center := ADataPoint + OFFSETS[ADir] * Marks.CenterOffset(ADrawer, AText)
else
center := ADataPoint + OFFSETS[ADir] * Marks.CenterHeightOffset(ADrawer, AText);
Marks.DrawLabel(ADrawer, ADataPoint, center, AText, prevLabelPoly);
end;

View File

@ -40,6 +40,8 @@ type
TChartLabelShape = (
clsRectangle, clsEllipse, clsRoundRect, clsRoundSide, clsUserDefined);
TChartTextRotationCenter = (rcCenter, rcEdge, rcLeft, rcRight);
TChartTextElement = class;
TChartGetShapeEvent = procedure (
@ -60,14 +62,18 @@ type
procedure SetMargins(AValue: TChartLabelMargins);
procedure SetOnGetShape(AValue: TChartGetShapeEvent);
procedure SetOverlapPolicy(AValue: TChartMarksOverlapPolicy);
procedure SetRotationCenter(AValue: TChartTextRotationCenter);
procedure SetShape(AValue: TChartLabelShape);
strict protected
FAlignment: TAlignment;
FInsideDir: TDoublePoint;
FRotationCenter: TChartTextRotationCenter;
procedure ApplyLabelFont(ADrawer: IChartDrawer); virtual;
procedure DrawLink(
ADrawer: IChartDrawer; ADataPoint, ALabelCenter: TPoint); virtual;
function GetBoundingBox(
ADrawer: IChartDrawer; const ATextSize: TPoint): TRect;
function GetTextShiftNeeded: Boolean;
function IsMarginRequired: Boolean;
strict protected
function GetFrame: TChartPen; virtual; abstract;
@ -75,6 +81,8 @@ type
function GetLabelBrush: TBrush; virtual; abstract;
function GetLabelFont: TFont; virtual; abstract;
function GetLinkPen: TChartPen; virtual;
property RotationCenter: TChartTextRotationCenter
read FRotationCenter write SetRotationCenter default rcCenter;
public
constructor Create(AOwner: TCustomChart);
destructor Destroy; override;
@ -86,6 +94,8 @@ type
function GetLabelPolygon(
ADrawer: IChartDrawer; ASize: TPoint): TPointArray;
function MeasureLabel(ADrawer: IChartDrawer; const AText: String): TSize;
function MeasureLabelHeight(ADrawer: IChartDrawer; const AText: String): TSize;
procedure SetInsideDir(dx, dy: Double);
public
property CalloutAngle: Cardinal
read FCalloutAngle write SetCalloutAngle default 0;
@ -203,6 +213,7 @@ type
destructor Destroy; override;
public
procedure Assign(ASource: TPersistent); override;
function CenterHeightOffset(ADrawer: IChartDrawer; const AText: String): TSize;
function CenterOffset(ADrawer: IChartDrawer; const AText: String): TSize;
function IsMarkLabelsVisible: Boolean;
procedure SetAdditionalAngle(AAngle: Double);
@ -265,6 +276,7 @@ type
property LinkDistance;
property LinkPen;
property OverlapPolicy;
property RotationCenter;
property Style default smsNone;
property YIndex;
end;
@ -288,6 +300,7 @@ begin
Self.FMargins.Assign(FMargins);
Self.FOverlapPolicy := FOverlapPolicy;
Self.FShape := FShape;
Self.FInsideDir := FInsideDir;
end;
inherited Assign(ASource);
end;
@ -311,7 +324,7 @@ procedure TChartTextElement.DrawLabel(
const AText: String; var APrevLabelPoly: TPointArray);
var
labelPoly: TPointArray;
ptText: TPoint;
ptText, P: TPoint;
i, w: Integer;
begin
ApplyLabelFont(ADrawer);
@ -324,8 +337,7 @@ begin
labelPoly := MakeCallout(
labelPoly, ALabelCenter, ADataPoint, OrientToRad(CalloutAngle));
if
(OverlapPolicy = opHideNeighbour) and
if (OverlapPolicy = opHideNeighbour) and
IsPolygonIntersectsPolygon(APrevLabelPoly, labelPoly)
then
exit;
@ -344,7 +356,18 @@ begin
ADrawer.Polygon(labelPoly, 0, Length(labelPoly));
end;
ptText := RotatePoint(-ptText div 2, GetLabelAngle) + ALabelCenter;
case FRotationCenter of
rcCenter: P := -ptText div 2;
rcEdge,
rcLeft : begin
P := Point(0, -ptText.y div 2);
if (FRotationCenter = rcEdge) and GetTextShiftNeeded then
P.x := -ptText.x;
end;
rcRight : P := Point(-ptText.x, -ptText.y div 2);
end;
ptText := RotatePoint(P, GetLabelAngle) + ALabelCenter;
ADrawer.TextOut.Pos(ptText).Alignment(Alignment).Width(w).Text(AText).Done;
if not Clipped then
ADrawer.ClippingStart;
@ -368,6 +391,18 @@ function TChartTextElement.GetBoundingBox(
begin
Result := ZeroRect;
InflateRect(Result, ATextSize.X div 2, ATextSize.Y div 2);
case FRotationCenter of
rcCenter : ;
rcLeft,
rcEdge : begin
OffsetRect(Result, ATextSize.x div 2, 0);
if (FRotationCenter = rcEdge) and GetTextShiftNeeded then
OffsetRect(Result, -ATextSize.x, 0);
end;
rcRight : OffsetRect(Result, -ATextSize.x div 2, 0);
end;
if IsMarginRequired then
Margins.ExpandRectScaled(ADrawer, Result);
end;
@ -413,6 +448,14 @@ begin
Result := nil;
end;
function TChartTextElement.GetTextShiftNeeded: Boolean;
var
textdir: TDoublePoint;
begin
SinCos(-GetLabelAngle, textdir.y, textdir.x);
Result := DotProduct(textdir, FInsideDir) > 0;
end;
function TChartTextElement.IsMarginRequired: Boolean;
begin
Result := (GetLabelBrush.Style <> bsClear) or GetFrame.EffVisible;
@ -426,6 +469,19 @@ begin
Result := MeasureRotatedRect(Point(Right - Left, Bottom - Top), GetLabelAngle);
end;
function TChartTextElement.MeasureLabelHeight(
ADrawer: IChartDrawer; const AText: String): TSize;
var
R: TRect;
begin
ApplyLabelFont(ADrawer);
R := Rect(0, 0, 0, ADrawer.TextExtent(AText).y);
OffsetRect(R, 0, -(R.Bottom - R.Top) div 2);
if IsMarginRequired then
Margins.ExpandRectScaled(ADrawer, R);
Result := MeasureRotatedRect(Point(R.Right - R.Left, R.Bottom - R.Top), GetLabelAngle);
end;
procedure TChartTextElement.SetAlignment(AValue: TAlignment);
begin
if FAlignment = AValue then exit;
@ -454,6 +510,11 @@ begin
StyleChanged(Self);
end;
procedure TChartTextElement.SetInsideDir(dx, dy: Double);
begin
FInsideDir := DoublePoint(dx, dy);
end;
procedure TChartTextElement.SetOnGetShape(AValue: TChartGetShapeEvent);
begin
if TMethod(FOnGetShape) = TMethod(AValue) then exit;
@ -468,6 +529,13 @@ begin
StyleChanged(Self);
end;
procedure TChartTextElement.SetRotationCenter(AValue: TChartTextRotationCenter);
begin
if FRotationCenter = AValue then exit;
FRotationCenter := AValue;
StyleChanged(Self);
end;
procedure TChartTextElement.SetShape(AValue: TChartLabelShape);
begin
if FShape = AValue then exit;
@ -624,6 +692,15 @@ begin
inherited Assign(ASource);
end;
function TGenericChartMarks.CenterHeightOffset(
ADrawer: IChartDrawer; const AText: String): TSize;
var
d: Integer;
begin
d := ADrawer.Scale(Distance);
Result := Size(d, d) + MeasureLabelHeight(ADrawer, AText) div 2;
end;
function TGenericChartMarks.CenterOffset(
ADrawer: IChartDrawer; const AText: String): TSize;
var