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.FTransformations := Transformations;
Self.FZPosition := ZPosition; Self.FZPosition := ZPosition;
Self.FMarginsForMarks := MarginsForMarks; Self.FMarginsForMarks := MarginsForMarks;
Self.FOnMarkToText := OnMarkToText; Self.FOnMarkToText := OnMarkToText;
end; end;
inherited Assign(ASource); inherited Assign(ASource);
@ -446,6 +445,7 @@ begin
TickLength := DEF_TICK_LENGTH; TickLength := DEF_TICK_LENGTH;
FTitle := TChartAxisTitle.Create(ACollection.Owner as TCustomChart); FTitle := TChartAxisTitle.Create(ACollection.Owner as TCustomChart);
FMarginsForMarks := true; FMarginsForMarks := true;
FMarks.SetInsideDir(1, 0);
end; end;
destructor TChartAxis.Destroy; destructor TChartAxis.Destroy;
@ -604,6 +604,7 @@ begin
FMaxForMarks := Max(FMaxForMarks, GetTransform.AxisToGraph(d.FMax)); FMaxForMarks := Max(FMaxForMarks, GetTransform.AxisToGraph(d.FMax));
EnsureOrder(FValueMin, FValueMax); EnsureOrder(FValueMin, FValueMax);
EnsureOrder(FMinForMarks, FMaxForMarks); EnsureOrder(FMinForMarks, FMaxForMarks);
FRotationCenter := Marks.RotationCenter;
end; end;
if Assigned(FOnMarkToText) then if Assigned(FOnMarkToText) then
@ -826,12 +827,21 @@ begin
FHelper.FAtDataOnly := AtDataOnly; FHelper.FAtDataOnly := AtDataOnly;
FHelper.FMaxForMarks := NegInfinity; FHelper.FMaxForMarks := NegInfinity;
FHelper.FMinForMarks := SafeInfinity; FHelper.FMinForMarks := SafeInfinity;
FHelper.FRotationCenter := Marks.RotationCenter;
end; end;
procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment); procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment);
begin begin
if FAlignment = AValue then exit; if FAlignment = AValue then exit;
FAlignment := AValue; 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); StyleChanged(Self);
end; end;

View File

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

View File

@ -1120,7 +1120,11 @@ var
center: TPoint; center: TPoint;
begin begin
if AText = '' then exit; 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); Marks.DrawLabel(ADrawer, ADataPoint, center, AText, prevLabelPoly);
end; end;

View File

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