mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-10 14:36:09 +02:00
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:
parent
f6600384c3
commit
3456985cf7
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user