lazarus/components/tachart/tatextelements.pas
2019-03-29 23:02:48 +00:00

939 lines
25 KiB
ObjectPascal

{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Authors: Alexander Klenin
}
unit TATextElements;
{$H+}
interface
uses
Classes, Graphics, Types,
TAChartUtils, TADrawUtils, TATypes,
// Workaround for issue #22850.
GraphMath, Math, SysUtils,
TACustomSource, TAGeometry;
const
DEF_LABEL_MARGIN_X = 4;
DEF_LABEL_MARGIN_Y = 2;
type
TChartMarksOverlapPolicy = (opIgnore, opHideNeighbour);
TChartLabelMargins = class(TChartMargins)
published
property Bottom default DEF_LABEL_MARGIN_Y;
property Left default DEF_LABEL_MARGIN_X;
property Right default DEF_LABEL_MARGIN_X;
property Top default DEF_LABEL_MARGIN_Y;
end;
TChartLabelShape = (
clsRectangle, clsEllipse, clsRoundRect, clsRoundSide, clsUserDefined);
TChartTextRotationCenter = (rcCenter, rcEdge, rcLeft, rcRight);
TChartTextElement = class;
TChartGetShapeEvent = procedure (
ASender: TChartTextElement; const ABoundingBox: TRect;
var APolygon: TPointArray) of object;
TChartTextElement = class(TChartElement)
strict private
FCalloutAngle: Cardinal;
FClipped: Boolean;
FMargins: TChartLabelMargins;
FOnGetShape: TChartGetShapeEvent;
FOverlapPolicy: TChartMarksOverlapPolicy;
FShape: TChartLabelShape;
FTextFormat: TChartTextFormat;
FTextRect: TRect;
procedure SetAlignment(AValue: TAlignment);
procedure SetCalloutAngle(AValue: Cardinal);
procedure SetClipped(AValue: Boolean);
procedure SetMargins(AValue: TChartLabelMargins);
procedure SetOnGetShape(AValue: TChartGetShapeEvent);
procedure SetOverlapPolicy(AValue: TChartMarksOverlapPolicy);
procedure SetRotationCenter(AValue: TChartTextRotationCenter);
procedure SetShape(AValue: TChartLabelShape);
procedure SetTextFormat(AValue: TChartTextFormat);
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;
function GetLabelAngle: Double; virtual;
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;
public
procedure Assign(ASource: TPersistent); override;
procedure DrawLabel(
ADrawer: IChartDrawer; const ADataPoint, ALabelCenter: TPoint;
const AText: String; var APrevLabelPoly: TPointArray);
function GetLabelPolygon(
ADrawer: IChartDrawer; ASize: TPoint): TPointArray;
function GetTextRect: TRect;
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;
// If false, labels may overlap axises and legend.
property Clipped: Boolean read FClipped write SetClipped default true;
property OverlapPolicy: TChartMarksOverlapPolicy
read FOverlapPolicy write SetOverlapPolicy default opIgnore;
property OnGetShape: TChartGetShapeEvent
read FOnGetShape write SetOnGetShape;
property Shape: TChartLabelShape
read FShape write SetShape default clsRectangle;
property TextFormat: TChartTextFormat
read FTextFormat write SetTextFormat default tfNormal;
published
property Alignment: TAlignment
read FAlignment write SetAlignment;
property Margins: TChartLabelMargins read FMargins write SetMargins;
end;
TChartTitleFramePen = class(TChartPen)
published
property Visible default false;
end;
{ TChartTitle }
TChartTitle = class(TChartTextElement)
strict private
FBrush: TBrush;
FCenter: TPoint;
FFont: TFont;
FFrame: TChartTitleFramePen;
FMargin: TChartDistance;
FText: TStrings;
procedure SetBrush(AValue: TBrush);
procedure SetFont(AValue: TFont);
procedure SetFrame(AValue: TChartTitleFramePen);
procedure SetMargin(AValue: TChartDistance);
procedure SetText(AValue: TStrings);
strict protected
function GetFrame: TChartPen; override;
function GetLabelBrush: TBrush; override;
function GetLabelFont: TFont; override;
public
constructor Create(AOwner: TCustomChart);
destructor Destroy; override;
public
procedure Assign(ASource: TPersistent); override;
procedure Draw(ADrawer: IChartDrawer);
procedure Measure(
ADrawer: IChartDrawer; ADir, ALeft, ARight: Integer; var AY: Integer);
procedure UpdateBidiMode;
published
property Alignment default taCenter;
property Brush: TBrush read FBrush write SetBrush;
property Font: TFont read FFont write SetFont;
property Frame: TChartTitleFramePen read FFrame write SetFrame;
property Margin: TChartDistance
read FMargin write SetMargin default DEF_MARGIN;
property OnGetShape;
property Shape;
property Text: TStrings read FText write SetText;
property TextFormat;
property Visible default false;
end;
TChartMarkAttachment = (maDefault, maEdge, maCenter);
{ TGenericChartMarks }
{$IFNDEF fpdoc} // Workaround for issue #18549.
generic TGenericChartMarks<_TLabelBrush, _TLinkPen, _TFramePen> =
class(TChartTextElement)
{$ELSE}
TGenericChartMarks = class(TChartTextElement)
{$ENDIF}
strict private
FAdditionalAngle: Double;
FArrow: TChartArrow;
FLinkDistance: Integer;
FAttachment: TChartMarkAttachment;
FAutoMargins: Boolean;
FFrame: _TFramePen;
FYIndex: Integer;
function GetDistanceToCenter: Boolean;
procedure SetArrow(AValue: TChartArrow);
procedure SetAttachment(AValue: TChartMarkAttachment);
procedure SetAutoMargins(AValue: Boolean);
procedure SetDistance(AValue: TChartDistance);
procedure SetDistanceToCenter(AValue: Boolean);
procedure SetFormat(AValue: String);
procedure SetFrame(AValue: _TFramePen);
procedure SetLabelBrush(AValue: _TLabelBrush);
procedure SetLabelFont(AValue: TFont);
procedure SetLinkDistance(AValue: Integer);
procedure SetLinkPen(AValue: _TLinkPen);
procedure SetStyle(AValue: TSeriesMarksStyle);
procedure SetYIndex(AValue: Integer);
strict protected
FDistance: TChartDistance;
FFormat: String;
FLabelBrush: _TLabelBrush;
FLabelFont: TFont;
FLinkPen: _TLinkPen;
FStyle: TSeriesMarksStyle;
strict protected
procedure ApplyLabelFont(ADrawer: IChartDrawer); override;
procedure DrawLink(
ADrawer: IChartDrawer; ADataPoint, ALabelCenter: TPoint); override;
function GetFrame: TChartPen; override;
function GetLabelAngle: Double; override;
function GetLabelBrush: TBrush; override;
function GetLabelFont: TFont; override;
function GetLinkPen: TChartPen; override;
public
constructor Create(AOwner: TCustomChart);
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);
public
property Arrow: TChartArrow read FArrow write SetArrow;
property AutoMargins: Boolean
read FAutoMargins write SetAutoMargins default true;
property DistanceToCenter: Boolean
read GetDistanceToCenter write SetDistanceToCenter
stored false default false;
property Format: String read FFormat write SetFormat;
property Frame: _TFramePen read FFrame write SetFrame;
property LabelBrush: _TLabelBrush read FLabelBrush write SetLabelBrush;
property LinkDistance: Integer read FLinkDistance write SetLinkDistance default 0;
property LinkPen: _TLinkPen read FLinkPen write SetLinkPen;
property Style: TSeriesMarksStyle read FStyle write SetStyle;
property YIndex: Integer read FYIndex write SetYIndex default 0;
published
property Alignment default taLeftJustify;
property Attachment: TChartMarkAttachment
read FAttachment write SetAttachment default maDefault;
// Distance between labelled object and label.
property Clipped;
property Distance: TChartDistance read FDistance write SetDistance;
property LabelFont: TFont read FLabelFont write SetLabelFont;
property OnGetShape;
property Shape;
property Visible default true;
end;
TChartLinkPen = class(TChartPen)
published
property Color default clWhite;
end;
TChartLabelBrush = class(TBrush)
published
property Color default clYellow;
end;
{$IFNDEF fpdoc} // Workaround for issue #18549.
TCustomChartMarks =
specialize TGenericChartMarks<TChartLabelBrush, TChartLinkPen, TChartPen>;
{$ENDIF}
{ TChartMarks }
TChartMarks = class(TCustomChartMarks)
public
procedure Assign(Source: TPersistent); override;
constructor Create(AOwner: TCustomChart);
published
property Arrow;
property AutoMargins;
property CalloutAngle;
property Distance default DEF_MARKS_DISTANCE;
property Format;
property Frame;
property LabelBrush;
property LinkDistance;
property LinkPen;
property OverlapPolicy;
property RotationCenter;
property Style default smsNone;
property TextFormat;
property YIndex;
end;
implementation
{ TChartTextElement }
procedure TChartTextElement.ApplyLabelFont(ADrawer: IChartDrawer);
begin
ADrawer.Font := GetLabelFont;
end;
procedure TChartTextElement.Assign(ASource: TPersistent);
begin
if ASource is TChartTextElement then
with TChartTextElement(ASource) do begin
Self.FAlignment := Alignment;
Self.FCalloutAngle := FCalloutAngle;
Self.FClipped := FClipped;
Self.FMargins.Assign(FMargins);
Self.FOverlapPolicy := FOverlapPolicy;
Self.FShape := FShape;
Self.FTextFormat := FTextFormat;
Self.FInsideDir := FInsideDir;
end;
inherited Assign(ASource);
end;
constructor TChartTextElement.Create(AOwner: TCustomChart);
begin
inherited Create(AOwner);
FClipped := true;
FMargins := TChartLabelMargins.Create(AOwner);
FOverlapPolicy := opIgnore;
end;
destructor TChartTextElement.Destroy;
begin
FreeAndNil(FMargins);
inherited;
end;
procedure TChartTextElement.DrawLabel(
ADrawer: IChartDrawer; const ADataPoint, ALabelCenter: TPoint;
const AText: String; var APrevLabelPoly: TPointArray);
var
labelPoly: TPointArray;
ptText, P: TPoint;
i, w: Integer;
begin
ApplyLabelFont(ADrawer);
ptText := ADrawer.TextExtent(AText, FTextFormat);
w := ptText.X;
labelPoly := GetLabelPolygon(ADrawer, ptText);
for i := 0 to High(labelPoly) do
labelPoly[i] += ALabelCenter;
if CalloutAngle > 0 then
labelPoly := MakeCallout(
labelPoly, ALabelCenter, ADataPoint, OrientToRad(CalloutAngle));
if (OverlapPolicy = opHideNeighbour) and
IsPolygonIntersectsPolygon(APrevLabelPoly, labelPoly)
then
exit;
APrevLabelPoly := labelPoly;
if not Clipped then
ADrawer.ClippingStop;
DrawLink(ADrawer, ADataPoint, ALabelCenter);
ADrawer.Brush := GetLabelBrush;
if IsMarginRequired then begin
if GetFrame.Visible then
ADrawer.Pen := GetFrame
else
ADrawer.SetPenParams(psClear, clTAColor);
ADrawer.Polygon(labelPoly, 0, Length(labelPoly));
end;
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.TextFormat(FTextFormat).Pos(ptText).Alignment(Alignment).Width(w).Text(AText).Done;
if not Clipped then
ADrawer.ClippingStart;
end;
procedure TChartTextElement.DrawLink(
ADrawer: IChartDrawer; ADataPoint, ALabelCenter: TPoint);
var
p: TChartPen;
begin
if ADataPoint = ALabelCenter then exit;
p := GetLinkPen;
if p.Visible then begin
ADrawer.Pen := p;
ADrawer.Line(ADataPoint, ALabelCenter);
end;
end;
function TChartTextElement.GetBoundingBox(
ADrawer: IChartDrawer; const ATextSize: TPoint): TRect;
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;
function TChartTextElement.GetLabelAngle: Double;
begin
// Negate to take into account top-down Y axis.
Result := -OrientToRad(GetLabelFont.Orientation);
end;
function TChartTextElement.GetLabelPolygon(
ADrawer: IChartDrawer; ASize: TPoint): TPointArray;
const
STEP = 3;
var
a: Double;
b: TRect;
i: Integer;
begin
b := GetBoundingBox(ADrawer, ASize);
case Shape of
clsRectangle:
Result := TesselateRect(b);
clsEllipse:
Result := TesselateEllipse(b, STEP);
clsRoundRect:
Result := TesselateRoundRect(
b, Min(b.Right - b.Left, b.Bottom - b.Top) div 3, STEP);
clsRoundSide:
Result := TesselateRoundRect(
b, Min(b.Right - b.Left, b.Bottom - b.Top) div 2, STEP);
clsUserDefined: ;
end;
if Assigned(OnGetShape) then
OnGetShape(Self, b, Result);
a := GetLabelAngle;
for i := 0 to High(Result) do
Result[i] := RotatePoint(Result[i], a);
end;
function TChartTextElement.GetLinkPen: TChartPen;
begin
Result := nil;
end;
function TChartTextElement.GetTextRect: TRect;
begin
Result := FTextRect;
end;
function TChartTextElement.GetTextShiftNeeded: Boolean;
var
textdir: TDoublePoint;
lSin, lCos: Math.float;
begin
SinCos(-GetLabelAngle, lSin, lCos);
textdir.y := lSin;
textdir.x := lCos;
Result := DotProduct(textdir, FInsideDir) > 0;
end;
function TChartTextElement.IsMarginRequired: Boolean;
begin
Result := (GetLabelBrush.Style <> bsClear) or GetFrame.EffVisible;
end;
function TChartTextElement.MeasureLabel(
ADrawer: IChartDrawer; const AText: String): TSize;
begin
ApplyLabelFont(ADrawer);
with GetBoundingBox(ADrawer, ADrawer.TextExtent(AText, FTextFormat)) do
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, FTextFormat).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;
FAlignment := AValue;
StyleChanged(Self);
end;
procedure TChartTextElement.SetCalloutAngle(AValue: Cardinal);
begin
if FCalloutAngle = AValue then exit;
FCalloutAngle := AValue;
StyleChanged(Self);
end;
procedure TChartTextElement.SetClipped(AValue: Boolean);
begin
if FClipped = AValue then exit;
FClipped := AValue;
StyleChanged(Self);
end;
procedure TChartTextElement.SetMargins(AValue: TChartLabelMargins);
begin
if FMargins = AValue then exit;
FMargins.Assign(AValue);
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;
FOnGetShape := AValue;
StyleChanged(Self);
end;
procedure TChartTextElement.SetOverlapPolicy(AValue: TChartMarksOverlapPolicy);
begin
if FOverlapPolicy = AValue then exit;
FOverlapPolicy := AValue;
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;
FShape := AValue;
StyleChanged(Self);
end;
procedure TChartTextElement.SetTextFormat(AValue: TChartTextFormat);
begin
if FTextFormat = AValue then exit;
FTextFormat := AValue;
StyleChanged(Self);
end;
{ TChartTitle }
procedure TChartTitle.Assign(ASource: TPersistent);
begin
if ASource is TChartTitle then
with TChartTitle(ASource) do begin
Self.FBrush.Assign(Brush);
Self.FFont.Assign(Font);
Self.FFrame.Assign(Frame);
Self.FText.Assign(Text);
end;
inherited Assign(ASource);
end;
constructor TChartTitle.Create(AOwner: TCustomChart);
begin
inherited Create(AOwner);
FAlignment := taCenter;
InitHelper(FBrush, TBrush);
FBrush.Color := FOwner.Color;
InitHelper(FFont, TFont);
FFont.Color := clBlue;
InitHelper(FFrame, TChartTitleFramePen);
FMargin := DEF_MARGIN;
FText := TStringList.Create;
TStringList(FText).OnChange := @StyleChanged;
end;
destructor TChartTitle.Destroy;
begin
FreeAndNil(FBrush);
FreeAndNil(FFont);
FreeAndNil(FFrame);
FreeAndNil(FText);
inherited;
end;
procedure TChartTitle.Draw(ADrawer: IChartDrawer);
var
dummy: TPointArray = nil;
begin
if not Visible or (Text.Count = 0) then exit;
DrawLabel(ADrawer, FCenter, FCenter, Text.Text, dummy);
end;
function TChartTitle.GetFrame: TChartPen;
begin
Result := Frame;
end;
function TChartTitle.GetLabelBrush: TBrush;
begin
Result := Brush;
end;
function TChartTitle.GetLabelFont: TFont;
begin
Result := Font;
end;
procedure TChartTitle.Measure(ADrawer: IChartDrawer;
ADir, ALeft, ARight: Integer; var AY: Integer);
var
ptSize: TPoint;
begin
if not Visible or (Text.Count = 0) then exit;
ptSize := MeasureLabel(ADrawer, Text.Text);
case Alignment of
taLeftJustify: FCenter.X := ALeft + ptSize.X div 2;
taRightJustify: FCenter.X := ARight - ptSize.X div 2;
taCenter: FCenter.X := (ALeft + ARight) div 2;
end;
FCenter.Y := AY + ADir * ptSize.Y div 2;
AY += ADir * (ptSize.Y + Margin);
end;
procedure TChartTitle.SetBrush(AValue: TBrush);
begin
FBrush.Assign(AValue);
StyleChanged(Self);
end;
procedure TChartTitle.SetFont(AValue: TFont);
begin
FFont.Assign(AValue);
StyleChanged(Self);
end;
procedure TChartTitle.SetFrame(AValue: TChartTitleFramePen);
begin
FFrame.Assign(AValue);
StyleChanged(Self);
end;
procedure TChartTitle.SetMargin(AValue: TChartDistance);
begin
if FMargin = AValue then exit;
FMargin := AValue;
StyleChanged(Self);
end;
procedure TChartTitle.SetText(AValue: TStrings);
begin
FText.Assign(AValue);
StyleChanged(Self);
end;
procedure TChartTitle.UpdateBidiMode;
begin
case Alignment of
taLeftJustify : Alignment := taRightJustify;
taRightJustify : Alignment := taLeftJustify;
end;
end;
{ TGenericChartMarks }
procedure TGenericChartMarks.ApplyLabelFont(ADrawer: IChartDrawer);
begin
inherited ApplyLabelFont(ADrawer);
if FAdditionalAngle <> 0 then
ADrawer.AddToFontOrientation(RadToOrient(FAdditionalAngle));
end;
procedure TGenericChartMarks.Assign(ASource: TPersistent);
begin
if ASource is Self.ClassType then
with TGenericChartMarks(ASource) do begin
Self.FArrow.Assign(FArrow);
Self.FAutoMargins := FAutoMargins;
Self.FAttachment := FAttachment;
Self.FDistance := FDistance;
Self.FLinkDistance := FLinkDistance;
Self.FFormat := FFormat;
Self.FFrame.Assign(FFrame);
// FPC miscompiles virtual calls to generic type arguments,
// so as a workaround these assignments are moved to the specializations.
// Self.FLabelBrush.Assign(FLabelBrush);
// Self.FLabelFont.Assign(FLabelFont);
// Self.FLinkPen.Assign(FLinkPen);
Self.FStyle := FStyle;
Self.FYIndex := FYIndex;
end;
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
d: Integer;
begin
d := ADrawer.Scale(Distance);
Result := Size(d, d);
if not DistanceToCenter then
Result += MeasureLabel(ADrawer, AText) div 2;
end;
constructor TGenericChartMarks.Create(AOwner: TCustomChart);
begin
inherited Create(AOwner);
FArrow := TChartArrow.Create(AOwner);
FAutoMargins := true;
InitHelper(FFrame, _TFramePen);
InitHelper(FLabelBrush, _TLabelBrush);
InitHelper(FLabelFont, TFont);
InitHelper(FLinkPen, _TLinkPen);
FStyle := smsNone;
FVisible := true;
end;
destructor TGenericChartMarks.Destroy;
begin
FreeAndNil(FArrow);
FreeAndNil(FFrame);
FreeAndNil(FLabelBrush);
FreeAndNil(FLabelFont);
FreeAndNil(FLinkPen);
inherited;
end;
procedure TGenericChartMarks.DrawLink(
ADrawer: IChartDrawer; ADataPoint, ALabelCenter: TPoint);
var
phi: Double;
begin
if ADataPoint = ALabelCenter then exit;
with (ADataPoint - ALabelCenter) do phi := ArcTan2(Y, X);
if (FLinkDistance <> 0) then
ADataPoint := ADataPoint + Point(round(FLinkDistance*cos(phi)), -round(FLinkDistance*sin(phi)));
inherited;
Arrow.Draw(ADrawer, ADataPoint, phi, GetLinkPen);
end;
function TGenericChartMarks.GetDistanceToCenter: Boolean;
begin
Result := Attachment = maCenter;
end;
function TGenericChartMarks.GetFrame: TChartPen;
begin
Result := Frame;
end;
function TGenericChartMarks.GetLabelAngle: Double;
begin
Result := inherited GetLabelAngle - FAdditionalAngle;
end;
function TGenericChartMarks.GetLabelBrush: TBrush;
begin
Result := LabelBrush;
end;
function TGenericChartMarks.GetLabelFont: TFont;
begin
Result := LabelFont;
end;
function TGenericChartMarks.GetLinkPen: TChartPen;
begin
Result := LinkPen;
end;
function TGenericChartMarks.IsMarkLabelsVisible: Boolean;
begin
Result := Visible and (Style <> smsNone) and (Format <> '');
end;
procedure TGenericChartMarks.SetAdditionalAngle(AAngle: Double);
begin
FAdditionalAngle := AAngle;
end;
procedure TGenericChartMarks.SetArrow(AValue: TChartArrow);
begin
if FArrow = AValue then exit;
FArrow.Assign(AValue);
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetAttachment(AValue: TChartMarkAttachment);
begin
if FAttachment = AValue then exit;
FAttachment := AValue;
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetAutoMargins(AValue: Boolean);
begin
if FAutoMargins = AValue then exit;
FAutoMargins := AValue;
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetDistance(AValue: TChartDistance);
begin
if FDistance = AValue then exit;
FDistance := AValue;
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetDistanceToCenter(AValue: Boolean);
begin
if AValue then
Attachment := maCenter
else
Attachment := maDefault;
end;
procedure TGenericChartMarks.SetFormat(AValue: String);
begin
if FFormat = AValue then exit;
TCustomChartSource.CheckFormat(AValue);
FFormat := AValue;
FStyle := High(FStyle);
while (FStyle > smsCustom) and (SERIES_MARK_FORMATS[FStyle] <> AValue) do
Dec(FStyle);
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetFrame(AValue: _TFramePen);
begin
if FFrame = AValue then exit;
FFrame.Assign(AValue);
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetLabelBrush(AValue: _TLabelBrush);
begin
if FLabelBrush = AValue then exit;
FLabelBrush.Assign(AValue);
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetLabelFont(AValue: TFont);
begin
if FLabelFont = AValue then exit;
FLabelFont.Assign(AValue);
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetLinkDistance(AValue: Integer);
begin
if FLinkDistance = AValue then exit;
FLinkDistance := AValue;
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetLinkPen(AValue: _TLinkPen);
begin
if FLinkPen = AValue then exit;
FLinkPen.Assign(AValue);
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetStyle(AValue: TSeriesMarksStyle);
begin
if FStyle = AValue then exit;
FStyle := AValue;
if FStyle <> smsCustom then
FFormat := SERIES_MARK_FORMATS[FStyle];
StyleChanged(Self);
end;
procedure TGenericChartMarks.SetYIndex(AValue: Integer);
begin
if FYIndex = AValue then exit;
FYIndex := AValue;
StyleChanged(Self);
end;
{ TChartMarks }
procedure TChartMarks.Assign(Source: TPersistent);
begin
if Source is TChartMarks then
with TChartMarks(Source) do begin
Self.FLabelBrush.Assign(FLabelBrush);
Self.FLabelFont.Assign(FLabelFont);
Self.FLinkPen.Assign(FLinkPen);
end;
inherited Assign(Source);
end;
constructor TChartMarks.Create(AOwner: TCustomChart);
begin
inherited Create(AOwner);
FDistance := DEF_MARKS_DISTANCE;
FLabelBrush.Color := clYellow;
end;
end.