26
Classes, Graphics, SysUtils, Types,
27
TAChartUtils, TACustomSource, TATransformations, TATypes;
26
Classes, SysUtils, Types,
27
TAChartAxisUtils, TAChartUtils, TACustomSource, TADrawUtils,
28
TATransformations, TATypes;
30
31
DEF_TICK_LENGTH = 4;
31
DEF_TITLE_DISTANCE = 4;
35
TChartAxisBrush = class(TBrush)
37
property Style default bsClear;
40
TChartAxisFramePen = class(TChartPen)
42
property Style default psClear;
45
{$IFNDEF fpdoc} // Workaround for issue #18549.
46
TCustomChartAxisTitle =
47
specialize TGenericChartMarks<TChartAxisBrush, TChartPen, TChartAxisFramePen>;
52
TChartAxisTitle = class(TCustomChartAxisTitle)
56
function GetFont: TFont;
57
procedure SetCaption(AValue: String);
58
procedure SetFont(AValue: TFont);
60
constructor Create(AOwner: TCustomChart);
63
procedure Assign(Source: TPersistent); override;
65
property Caption: String read FCaption write SetCaption;
66
property Distance default DEF_TITLE_DISTANCE;
67
// Use LabelFont instead.
68
property Font: TFont read GetFont write SetFont stored false; deprecated;
71
property Visible default false;
74
ICoordTransformer = interface
75
['{6EDA0F9F-ED59-4CA6-BA68-E247EB88AE3D}']
76
function XGraphToImage(AX: Double): Integer;
77
function YGraphToImage(AY: Double): Integer;
80
TChartAxisAlignment = (calLeft, calTop, calRight, calBottom);
81
TChartAxisMargins = array [TChartAxisAlignment] of Integer;
82
TChartAxisMarkToTextEvent =
83
procedure (var AText: String; AMark: Double) of object;
85
TChartAxisPen = class(TChartPen)
87
property Style default psDot;
90
{$IFNDEF fpdoc} // Workaround for issue #18549.
91
TCustomChartAxisMarks =
92
specialize TGenericChartMarks<TChartAxisBrush, TChartPen, TChartAxisFramePen>;
97
TChartAxisMarks = class(TCustomChartAxisMarks)
100
FDefaultSource: TCustomChartSource;
101
FListener: TListener;
102
FSource: TCustomChartSource;
104
function IsFormatStored: Boolean;
105
procedure SetAtDataOnly(AValue: Boolean);
106
procedure SetSource(AValue: TCustomChartSource);
108
constructor Create(AOwner: TCustomChart);
109
destructor Destroy; override;
111
function SourceDef: TCustomChartSource;
113
property AtDataOnly: Boolean
114
read FAtDataOnly write SetAtDataOnly default false;
115
property Distance default 1;
116
property Format stored IsFormatStored;
119
property OverlapPolicy;
120
property Source: TCustomChartSource read FSource write SetSource;
121
property Style default smsValue;
37
TChartMinorAxis = class(TChartBasicAxis)
39
function GetMarks: TChartMinorAxisMarks; inline;
40
procedure SetMarks(AValue: TChartMinorAxisMarks);
42
function GetDisplayName: String; override;
44
function GetAlignment: TChartAxisAlignment; override;
45
procedure SetAlignment(AValue: TChartAxisAlignment); override;
46
procedure StyleChanged(ASender: TObject); override;
48
constructor Create(ACollection: TCollection); override;
49
function GetMarkValues(AMin, AMax: Double): TChartValueTextArray;
51
property Marks: TChartMinorAxisMarks read GetMarks write SetMarks;
52
property TickLength default DEF_TICK_LENGTH div 2;
57
{ TChartMinorAxisList }
59
TChartMinorAxisList = class(TCollection)
62
function GetAxes(AIndex: Integer): TChartMinorAxis;
64
function GetOwner: TPersistent; override;
65
procedure Update(AItem: TCollectionItem); override;
67
constructor Create(AOwner: TChartAxis);
69
function Add: TChartMinorAxis;
70
function GetChart: TCustomChart; inline;
71
property Axes[AIndex: Integer]: TChartMinorAxis read GetAxes; default;
72
property ParentAxis: TChartAxis read FAxis;
125
75
TChartAxisGroup = record
128
81
FTitleSize: Integer;
84
TChartAxisPen = class(TChartPen)
86
property Visible default false;
133
TChartAxis = class(TCollectionItem)
91
TChartAxis = class(TChartBasicAxis)
135
93
FListener: TListener;
136
FMarkTexts: TStringDynArray;
137
FMarkValues: TDoubleDynArray;
94
FMarkValues: TChartValueTextArray;
139
procedure GetMarkValues(AMin, AMax: Double);
97
procedure GetMarkValues;
140
98
procedure VisitSource(ASource: TCustomChartSource; var AData);
142
100
FAxisRect: TRect;
143
101
FGroupIndex: Integer;
144
102
FTitleRect: TRect;
103
function MakeValuesInRangeParams(AMin, AMax: Double): TValuesInRangeParams;
146
105
FAlignment: TChartAxisAlignment;
147
FGrid: TChartAxisPen;
106
FAxisPen: TChartAxisPen;
108
FHelper: TAxisDrawHelper;
149
109
FInverted: Boolean;
150
FMarks: TChartAxisMarks;
110
FMargin: TChartDistance;
111
FMarginsForMarks: Boolean;
112
FMinors: TChartMinorAxisList;
151
113
FOnMarkToText: TChartAxisMarkToTextEvent;
153
FTickLength: Integer;
154
115
FTitle: TChartAxisTitle;
155
116
FTransformations: TChartAxisTransformations;
157
117
FZPosition: TChartDistance;
159
function GetTransform: TChartAxisTransformations;
160
procedure SetAlignment(AValue: TChartAxisAlignment);
161
procedure SetGrid(AValue: TChartAxisPen);
119
function GetMarks: TChartAxisMarks; inline;
120
procedure SetAxisPen(AValue: TChartAxisPen);
162
121
procedure SetGroup(AValue: Integer);
163
122
procedure SetInverted(AValue: Boolean);
164
procedure SetMarks(const AValue: TChartAxisMarks);
165
procedure SetOnMarkToText(const AValue: TChartAxisMarkToTextEvent);
166
procedure SetTickColor(AValue: TColor);
167
procedure SetTickLength(AValue: Integer);
123
procedure SetMargin(AValue: TChartDistance);
124
procedure SetMarginsForMarks(AValue: Boolean);
125
procedure SetMarks(AValue: TChartAxisMarks);
126
procedure SetMinors(AValue: TChartMinorAxisList);
127
procedure SetOnMarkToText(AValue: TChartAxisMarkToTextEvent);
128
procedure SetRange(AValue: TChartRange);
168
129
procedure SetTitle(AValue: TChartAxisTitle);
169
130
procedure SetTransformations(AValue: TChartAxisTransformations);
170
procedure SetVisible(const AValue: Boolean);
171
procedure SetZPosition(const AValue: TChartDistance);
131
procedure SetZPosition(AValue: TChartDistance);
173
procedure StyleChanged(ASender: TObject);
175
function GetDisplayName: string; override;
134
function GetDisplayName: String; override;
135
procedure StyleChanged(ASender: TObject); override;
137
function GetAlignment: TChartAxisAlignment; override;
138
procedure SetAlignment(AValue: TChartAxisAlignment); override;
177
140
constructor Create(ACollection: TCollection); override;
178
141
destructor Destroy; override;
180
143
procedure Assign(ASource: TPersistent); override;
182
ACanvas: TCanvas; const AExtent: TDoubleRect;
183
const ATransf: ICoordTransformer; const AZOffset: TPoint);
185
ACanvas: TCanvas; const ACenter, AZOffset: TPoint; ASize: Integer);
145
procedure DrawTitle(ASize: Integer);
146
function GetChart: TCustomChart; inline;
147
function GetTransform: TChartAxisTransformations;
186
148
function IsVertical: Boolean; inline;
187
149
procedure Measure(
188
ACanvas: TCanvas; const AExtent: TDoubleRect; AFirstPass: Boolean;
189
var AMeasureData: TChartAxisGroup);
150
const AExtent: TDoubleRect; var AMeasureData: TChartAxisGroup);
151
procedure PrepareHelper(
152
ADrawer: IChartDrawer; const ATransf: ICoordTransformer;
153
AClipRect: PRect; AMaxZPosition: Integer);
154
procedure UpdateBounds(var AMin, AMax: Double);
191
property Alignment: TChartAxisAlignment
192
read FAlignment write SetAlignment default calLeft;
193
property Grid: TChartAxisPen read FGrid write SetGrid;
156
property Alignment default calLeft;
158
property AxisPen: TChartAxisPen read FAxisPen write SetAxisPen;
194
159
property Group: Integer read FGroup write SetGroup default 0;
195
160
// Inverts the axis scale from increasing to decreasing.
196
161
property Inverted: boolean read FInverted write SetInverted default false;
197
property Marks: TChartAxisMarks read FMarks write SetMarks;
198
property TickColor: TColor read FTickColor write SetTickColor default clBlack;
199
property TickLength: Integer
200
read FTickLength write SetTickLength default DEF_TICK_LENGTH;
162
property Margin: TChartDistance read FMargin write SetMargin default 0;
163
property MarginsForMarks: Boolean
164
read FMarginsForMarks write SetMarginsForMarks default true;
165
property Marks: TChartAxisMarks read GetMarks write SetMarks;
166
property Minors: TChartMinorAxisList read FMinors write SetMinors;
167
property Range: TChartRange read FRange write SetRange;
168
property TickLength default DEF_TICK_LENGTH;
201
169
property Title: TChartAxisTitle read FTitle write SetTitle;
202
170
property Transformations: TChartAxisTransformations
203
171
read FTransformations write SetTransformations;
204
property Visible: Boolean read FVisible write SetVisible default true;
205
172
property ZPosition: TChartDistance read FZPosition write SetZPosition default 0;
207
174
property OnMarkToText: TChartAxisMarkToTextEvent
327
293
Result := VIdentityTransform;
332
procedure TChartAxisTitle.Assign(Source: TPersistent);
334
if Source is TChartAxisTitle then
335
with TChartAxisTitle(Source) do
337
inherited Assign(Source);
340
constructor TChartAxisTitle.Create(AOwner: TCustomChart);
342
inherited Create(AOwner);
343
FDistance := DEF_TITLE_DISTANCE;
344
FFrame.Style := psClear;
345
FLabelBrush.Style := bsClear;
349
function TChartAxisTitle.GetFont: TFont;
354
procedure TChartAxisTitle.SetCaption(AValue: String);
356
if FCaption = AValue then exit;
361
procedure TChartAxisTitle.SetFont(AValue: TFont);
368
constructor TChartAxisMarks.Create(AOwner: TCustomChart);
370
inherited Create(AOwner);
371
FDefaultSource := TIntervalChartSource.Create(AOwner);
373
FFrame.Style := psClear;
374
FLabelBrush.Style := bsClear;
375
FListener := TListener.Create(@FSource, @StyleChanged);
377
FFormat := SERIES_MARK_FORMATS[FStyle];
380
destructor TChartAxisMarks.Destroy;
382
FreeAndNil(FListener);
383
FreeAndNil(FDefaultSource);
387
function TChartAxisMarks.IsFormatStored: Boolean;
389
Result := FStyle <> smsValue;
392
procedure TChartAxisMarks.SetAtDataOnly(AValue: Boolean);
394
if FAtDataOnly = AValue then exit;
395
FAtDataOnly := AValue;
399
procedure TChartAxisMarks.SetSource(AValue: TCustomChartSource);
401
if FSource = AValue then exit;
402
if FListener.IsListening then
403
FSource.Broadcaster.Unsubscribe(FListener);
405
if FSource <> nil then
406
FSource.Broadcaster.Subscribe(FListener);
410
function TChartAxisMarks.SourceDef: TCustomChartSource;
414
Result := FDefaultSource;
296
procedure UpdateBoundsByAxisRange(
297
AAxisList: TChartAxisList; AIndex: Integer; var AMin, AMax: Double);
299
if not InRange(AIndex, 0, AAxisList.Count - 1) then exit;
300
AAxisList[AIndex].UpdateBounds(AMin, AMax);
303
{ TChartAxisEnumerator }
305
function TChartAxisEnumerator.GetCurrent: TChartAxis;
307
Result := TChartAxis(inherited GetCurrent);
312
constructor TChartMinorAxis.Create(ACollection: TCollection);
314
inherited Create(ACollection, (ACollection as TChartMinorAxisList).GetChart);
315
FMarks := TChartMinorAxisMarks.Create(
316
(ACollection as TChartMinorAxisList).GetChart);
317
with Intervals do begin
318
Options := [aipUseCount, aipUseMinLength];
321
TickLength := DEF_TICK_LENGTH div 2;
324
function TChartMinorAxis.GetAlignment: TChartAxisAlignment;
326
Result := (Collection.Owner as TChartAxis).Alignment;
329
function TChartMinorAxis.GetDisplayName: String;
334
function TChartMinorAxis.GetMarks: TChartMinorAxisMarks;
336
Result := TChartMinorAxisMarks(inherited Marks);
339
function TChartMinorAxis.GetMarkValues(AMin, AMax: Double): TChartValueTextArray;
341
vp: TValuesInRangeParams;
343
if not Visible then exit(nil);
344
with Collection as TChartMinorAxisList do
345
vp := ParentAxis.MakeValuesInRangeParams(AMin, AMax);
346
vp.FFormat := Marks.Format;
347
Marks.DefaultSource.ValuesInRange(vp, Result);
350
procedure TChartMinorAxis.SetAlignment(AValue: TChartAxisAlignment);
353
raise EChartError.Create('TChartMinorAxis.SetAlignment');
356
procedure TChartMinorAxis.SetMarks(AValue: TChartMinorAxisMarks);
358
inherited Marks := AValue;
361
procedure TChartMinorAxis.StyleChanged(ASender: TObject);
363
(Collection.Owner as TChartAxis).StyleChanged(ASender);
366
{ TChartMinorAxisList }
368
function TChartMinorAxisList.Add: TChartMinorAxis;
370
Result := TChartMinorAxis(inherited Add);
373
constructor TChartMinorAxisList.Create(AOwner: TChartAxis);
375
inherited Create(TChartMinorAxis);
379
function TChartMinorAxisList.GetAxes(AIndex: Integer): TChartMinorAxis;
381
Result := TChartMinorAxis(Items[AIndex]);
384
function TChartMinorAxisList.GetChart: TCustomChart;
386
Result := FAxis.GetChart;
389
function TChartMinorAxisList.GetOwner: TPersistent;
394
procedure TChartMinorAxisList.Update(AItem: TCollectionItem);
396
FAxis.StyleChanged(AItem);
421
403
if ASource is TChartAxis then
422
404
with TChartAxis(ASource) do begin
423
Self.FGrid.Assign(Grid);
405
Self.FAxisPen.Assign(AxisPen);
424
406
Self.FGroup := Group;
425
407
Self.FInverted := Inverted;
426
Self.FMarks.Assign(Marks);
427
Self.FTickColor := TickColor;
428
Self.FTickLength := TickLength;
408
Self.FRange.Assign(Range);
429
409
Self.FTitle.Assign(Title);
430
410
Self.FTransformations := Transformations;
431
411
Self.FZPosition := ZPosition;
432
Self.FVisible := Visible;
412
Self.FMarginsForMarks := MarginsForMarks;
434
414
Self.FOnMarkToText := OnMarkToText;
437
inherited Assign(ASource);
416
inherited Assign(ASource);
440
419
constructor TChartAxis.Create(ACollection: TCollection);
442
inherited Create(ACollection);
421
inherited Create(ACollection, ACollection.Owner as TCustomChart);
422
FAxisPen := TChartAxisPen.Create;
423
FAxisPen.OnChange := @StyleChanged;
443
424
FListener := TListener.Create(@FTransformations, @StyleChanged);
444
FGrid := TChartAxisPen.Create;
445
FGrid.OnChange := @StyleChanged;
446
FGrid.Style := psDot;
447
425
FMarks := TChartAxisMarks.Create(ACollection.Owner as TCustomChart);
448
FTickColor := clBlack;
449
FTickLength := DEF_TICK_LENGTH;
426
FMinors := TChartMinorAxisList.Create(Self);
427
FRange := TChartRange.Create(ACollection.Owner as TCustomChart);
428
TickLength := DEF_TICK_LENGTH;
450
429
FTitle := TChartAxisTitle.Create(ACollection.Owner as TCustomChart);
430
FMarginsForMarks := true;
454
433
destructor TChartAxis.Destroy;
456
435
FreeAndNil(FTitle);
458
438
FreeAndNil(FListener);
440
FreeAndNil(FAxisPen);
463
procedure TChartAxis.Draw(
464
ACanvas: TCanvas; const AExtent: TDoubleRect;
465
const ATransf: ICoordTransformer; const AZOffset: TPoint);
468
prevLabelPoly: TPointArray = nil;
471
procedure LineZ(AP1, AP2: TPoint);
473
ACanvas.Line(AP1 + AZOffset, AP2 + AZOffset);
476
procedure DrawLabelAndTick(
477
ALabelCenter: TPoint; const ATickRect: TRect; const AText: String);
479
PrepareSimplePen(ACanvas, TickColor);
480
LineZ(ATickRect.TopLeft, ATickRect.BottomRight);
481
ALabelCenter += AZOffset;
482
Marks.DrawLabel(ACanvas, ALabelCenter, ALabelCenter, AText, prevLabelPoly);
485
procedure DrawXMark(AY: Integer; AMark: Double; const AText: String);
489
x := ATransf.XGraphToImage(AMark);
491
if Grid.Visible then begin
492
ACanvas.Pen.Assign(Grid);
493
ACanvas.Brush.Style := bsClear;
495
Point(x, ATransf.YGraphToImage(AExtent.a.Y)),
496
Point(x, ATransf.YGraphToImage(AExtent.b.Y)));
499
if Marks.Visible then begin
500
d := TickLength + Marks.CenterOffset(ACanvas, AText).cy;
501
if Alignment = calTop then
504
Point(x, AY + d), Rect(x, AY - TickLength, x, AY + TickLength), AText);
508
procedure DrawYMark(AX: Integer; AMark: Double; const AText: String);
512
y := ATransf.YGraphToImage(AMark);
514
if Grid.Visible then begin
515
ACanvas.Pen.Assign(Grid);
516
ACanvas.Brush.Style := bsClear;
518
Point(ATransf.XGraphToImage(AExtent.a.X), y),
519
Point(ATransf.XGraphToImage(AExtent.b.X), y));
522
if Marks.Visible then begin
523
d := TickLength + Marks.CenterOffset(ACanvas, AText).cx;
524
if Alignment = calLeft then
527
Point(AX + d, y), Rect(AX - TickLength, y, AX + TickLength, y), AText);
444
procedure TChartAxis.Draw;
446
procedure DrawMinors(AFixedCoord: Integer; AMin, AMax: Double);
451
minorMarks: TChartValueTextArray;
454
if IsNan(AMin) or (AMin = AMax) then exit;
455
for j := 0 to Minors.Count - 1 do begin
456
minorMarks := Minors[j].GetMarkValues(AMin, AMax);
457
if minorMarks = nil then continue;
458
with FHelper.Clone do begin
460
// Only draw minor marks strictly inside the major mark interval.
461
FValueMin := Max(FAxisTransf(AMin), FValueMin);
462
FValueMax := Min(FAxisTransf(AMax), FValueMax);
463
if FValueMax <= FValueMin then continue;
464
ExpandRange(FValueMin, FValueMax, -EPS);
465
FClipRangeDelta := 1;
468
for m in minorMarks do
469
DrawMark(AFixedCoord, FHelper.FAxisTransf(m.FValue), m.FText);
535
483
if not Visible then exit;
536
ACanvas.Font := Marks.LabelFont;
537
coord := TChartAxisMargins(FAxisRect)[Alignment];
538
for i := 0 to High(FMarkValues) do begin
539
v := GetTransform.AxisToGraph(FMarkValues[i]);
541
DrawYMark(coord, v, FMarkTexts[i])
543
DrawXMark(coord, v, FMarkTexts[i]);
484
if Marks.Visible then
485
FHelper.FDrawer.Font := Marks.LabelFont;
486
fixedCoord := TChartAxisMargins(FAxisRect)[Alignment];
488
FHelper.BeginDrawing;
489
FHelper.DrawAxisLine(AxisPen, fixedCoord);
490
for t in FMarkValues do begin
491
v := FHelper.FAxisTransf(t.FValue);
492
FHelper.DrawMark(fixedCoord, v, t.FText);
493
DrawMinors(fixedCoord, pv, t.FValue);
547
procedure TChartAxis.DrawTitle(
548
ACanvas: TCanvas; const ACenter, AZOffset: TPoint; ASize: Integer);
499
procedure TChartAxis.DrawTitle(ASize: Integer);
551
502
dummy: TPointArray = nil;
554
if not Visible or (ASize = 0) then exit;
556
d := (ASize + Title.Distance) div 2;
505
if not Visible or (ASize = 0) or (FTitlePos = MaxInt) then exit;
506
if Title.DistanceToCenter then
509
d := (ASize + Title.Distance) div 2;
557
510
case Alignment of
558
511
calLeft: p.X := FTitleRect.Left - d;
559
512
calTop: p.Y := FTitleRect.Top - d;
560
513
calRight: p.X := FTitleRect.Right + d;
561
514
calBottom: p.Y := FTitleRect.Bottom + d;
564
Title.DrawLabel(ACanvas, p, p, Title.Caption, dummy);
516
TPointBoolArr(p)[IsVertical] := FTitlePos;
517
p += FHelper.FZOffset;
518
Title.DrawLabel(FHelper.FDrawer, p, p, Title.Caption, dummy);
521
function TChartAxis.GetAlignment: TChartAxisAlignment;
523
Result := FAlignment;
526
function TChartAxis.GetChart: TCustomChart;
528
Result := Collection.Owner as TCustomChart;
567
531
function TChartAxis.GetDisplayName: string;
570
534
('Left', 'Top', 'Right', 'Bottom');
571
535
VISIBLE_NAME: array [Boolean] of String = (' Hidden', '');
572
536
INVERTED_NAME: array [Boolean] of String = ('', ' Inverted');
573
CAPTION_FMT = ' (%s)';
576
SIDE_NAME[Alignment] + VISIBLE_NAME[Visible] + INVERTED_NAME[Inverted];
577
if Title.Caption <> '' then
578
Result += Format(CAPTION_FMT, [Title.Caption]);
581
procedure TChartAxis.GetMarkValues(AMin, AMax: Double);
539
SIDE_NAME[Alignment] + VISIBLE_NAME[Visible] + INVERTED_NAME[Inverted] +
540
FormatIfNotEmpty(' (%s)', Title.Caption);
543
function TChartAxis.GetMarks: TChartAxisMarks;
545
Result := TChartAxisMarks(inherited Marks);
548
procedure TChartAxis.GetMarkValues;
551
d: TValuesInRangeParams;
585
552
vis: TChartOnVisitSources;
554
axisMin, axisMax: Double;
587
AMin := GetTransform.GraphToAxis(AMin);
588
AMax := GetTransform.GraphToAxis(AMax);
589
EnsureOrder(AMin, AMax);
556
with FHelper do begin
557
axisMin := GetTransform.GraphToAxis(FValueMin);
558
axisMax := GetTransform.GraphToAxis(FValueMax);
560
EnsureOrder(axisMin, axisMax);
561
Marks.Range.Intersect(axisMin, axisMax);
562
d := MakeValuesInRangeParams(axisMin, axisMax);
590
563
SetLength(FMarkValues, 0);
591
SetLength(FMarkTexts, 0);
592
564
vis := TChartAxisList(Collection).OnVisitSources;
593
565
if Marks.AtDataOnly and Assigned(vis) then begin
596
566
vis(@VisitSource, Self, d);
567
// FIXME: Intersect axisMin/Max with the union of series extents.
599
Marks.SourceDef.ValuesInRange(
600
AMin, AMax, Marks.Format, IsVertical, FMarkValues, FMarkTexts);
570
Marks.SourceDef.ValuesInRange(d, FMarkValues);
571
with FHelper do begin
572
FValueMin := GetTransform.AxisToGraph(axisMin);
573
FValueMax := GetTransform.AxisToGraph(axisMax);
602
576
for i := 0 to High(FMarkValues) div 2 do begin
603
Exchange(FMarkValues[i], FMarkValues[High(FMarkValues) - i]);
604
Exchange(FMarkTexts[i], FMarkTexts[High(FMarkValues) - i]);
578
FMarkValues[i] := FMarkValues[High(FMarkValues) - i];
579
FMarkValues[High(FMarkValues) - i] := t;
607
582
if Assigned(FOnMarkToText) then
608
for i := 0 to High(FMarkTexts) do
609
FOnMarkToText(FMarkTexts[i], FMarkValues[i]);
583
for i := 0 to High(FMarkValues) do
584
FOnMarkToText(FMarkValues[i].FText, FMarkValues[i].FValue);
612
587
function TChartAxis.GetTransform: TChartAxisTransformations;
621
596
Result := Alignment in [calLeft, calRight];
599
function TChartAxis.MakeValuesInRangeParams(
600
AMin, AMax: Double): TValuesInRangeParams;
604
Result.FFormat := Marks.Format;
605
Result.FUseY := IsVertical;
606
Result.FAxisToGraph := @GetTransform.AxisToGraph;
607
Result.FGraphToAxis := @GetTransform.GraphToAxis;
608
Result.FGraphToImage := @FHelper.GraphToImage;
609
Result.FScale := @FHelper.FDrawer.Scale;
610
Result.FIntervals := Intervals;
611
Result.FMinStep := 0;
624
614
procedure TChartAxis.Measure(
625
ACanvas: TCanvas; const AExtent: TDoubleRect;
626
AFirstPass: Boolean; var AMeasureData: TChartAxisGroup);
628
function CalcMarksSize(AMin, AMax: Double): TSize;
615
const AExtent: TDoubleRect; var AMeasureData: TChartAxisGroup);
620
function TitleSize: Integer;
622
if not Title.Visible or (Title.Caption = '') then exit(0);
623
// Workaround for issue #19780, fix after upgrade to FPC 2.6.
624
with Title.MeasureLabel(d, Title.Caption) do
625
Result := IfThen(v, cx, cy);
626
if Title.DistanceToCenter then
627
Result := Result div 2;
628
Result += d.Scale(Title.Distance);
631
procedure UpdateFirstLast(ACoord, AIndex, ARMin, ARMax: Integer);
635
Result := Size(0, 0);
636
if AMin = AMax then exit;
637
GetMarkValues(AMin, AMax);
638
if not Marks.Visible then exit;
639
for i := 0 to High(FMarkTexts) do begin
640
// CalculateTransformationCoeffs changes axis interval, so it is possibile
641
// that a new mark longer then existing ones is introduced.
642
// That will change marks width and reduce view area,
643
// requiring another call to CalculateTransformationCoeffs...
644
// So punt for now and just reserve space for extra digit unconditionally.
648
d := IfThen(Marks.DistanceToCenter, 2, 1);
649
with Marks.MeasureLabel(ACanvas, t) do begin
650
Result.cx := Max(cx div d, Result.cx);
651
Result.cy := Max(cy div d, Result.cy);
635
if not MarginsForMarks or not Marks.Visible then exit;
636
// Workaround for issue #19780, fix after upgrade to FPC 2.6.
637
with Marks.MeasureLabel(d, FMarkValues[AIndex].FText) do
638
sz := IfThen(v, cy, cx) div 2;
639
fm := sz - ACoord + ARMin;
640
lm := sz - ARMax + ACoord;
643
with AMeasureData do begin
644
FFirstMark := Max(fm, FFirstMark);
645
FLastMark := Max(lm, FLastMark);
656
function CalcTitleSize: Integer;
660
if not Title.Visible or (Title.Caption = '') then
662
sz := Title.MeasureLabel(ACanvas, Title.Caption);
664
Result := IfThen(IsVertical, sz.cx, sz.cy) + Title.Distance;
650
sz, rmin, rmax, c, i, j, minc, maxc, mini, maxi: Integer;
651
minorValues: TChartValueTextArray;
670
655
if not Visible then exit;
672
sz := CalcMarksSize(AExtent.a.Y, AExtent.b.Y).cx
674
sz := CalcMarksSize(AExtent.a.X, AExtent.b.X).cy;
676
sz += TickLength + Marks.Distance;
657
d := FHelper.FDrawer;
658
FHelper.FValueMin := TDoublePointBoolArr(AExtent.a)[v];
659
FHelper.FValueMax := TDoublePointBoolArr(AExtent.b)[v];
661
sz := Marks.Measure(d, not v, TickLength, FMarkValues);
662
FHelper.GetClipRange(rmin, rmax);
665
for i := 0 to High(FMarkValues) do begin
666
cv := FMarkValues[i].FValue;
667
if not IsNan(pv) then begin
668
for j := 0 to Minors.Count - 1 do
669
with Minors[j] do begin
670
minorValues := GetMarkValues(pv, cv);
671
sz := Max(Marks.Measure(d, not v, TickLength, minorValues), sz);
675
// Optimization: only measure edge labels to calculate longitudinal margins.
676
c := FHelper.GraphToImage(FHelper.FAxisTransf(cv));
677
if not InRange(c, rmin, rmax) then continue;
678
if c < minc then begin
682
if c > maxc then begin
677
687
with AMeasureData do begin
678
688
FSize := Max(sz, FSize);
679
FTitleSize := Max(CalcTitleSize, FTitleSize);
689
FTitleSize := Max(TitleSize, FTitleSize);
690
FMargin := Max(Margin, FMargin);
692
if minc < MaxInt then begin
693
UpdateFirstLast(minc, mini, rmin, rmax);
694
UpdateFirstLast(maxc, maxi, rmin, rmax);
696
if not Title.PositionOnMarks then
697
FTitlePos := (rmin + rmax) div 2
698
else if minc < MaxInt then
699
FTitlePos := (maxc + minc) div 2
703
if Arrow.Visible then
704
with AMeasureData do begin
705
FSize := Max(d.Scale(Arrow.Width), FSize);
706
FLastMark := Max(d.Scale(Arrow.Length), FLastMark);
710
procedure TChartAxis.PrepareHelper(
711
ADrawer: IChartDrawer; const ATransf: ICoordTransformer;
712
AClipRect: PRect; AMaxZPosition: Integer);
716
FHelper := TAxisDrawHelperY.Create
718
FHelper := TAxisDrawHelperX.Create;
719
FHelper.FAxis := Self;
720
FHelper.FAxisTransf := @GetTransform.AxisToGraph;
721
FHelper.FClipRect := AClipRect;
722
FHelper.FDrawer := ADrawer;
723
FHelper.FTransf := ATransf;
724
FHelper.FZOffset.Y := Min(ZPosition, AMaxZPosition);
725
FHelper.FZOffset.X := -FHelper.FZOffset.Y;
683
728
procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment);
863
914
procedure TChartAxisList.InitAndSort(
864
915
AList: TFPList; ACompare: TListSortCompare);
869
for i := 0 to Count - 1 do
870
AList.Add(Pointer(Axes[i]));
921
AList.Add(Pointer(a));
871
922
AList.Sort(ACompare);
874
procedure TChartAxisList.Measure(
875
ACanvas: TCanvas; const AExtent: TDoubleRect;
876
AFirstPass: Boolean; var AMargins: TChartAxisMargins);
925
function TChartAxisList.Measure(
926
const AExtent: TDoubleRect; ADepth: Integer): TChartAxisMargins;
930
procedure UpdateMarginsForMarks(AFirst, ALast: TChartAxisAlignment);
932
Result[AFirst] := Max(Result[AFirst], g^.FFirstMark);
933
Result[ALast] := Max(Result[ALast], g^.FLastMark);
937
ALIGN_TO_ZDIR: array [TChartAxisAlignment] of Integer = (1, -1, -1, 1);
878
939
i, j, ai: Integer;
879
940
axis: TChartAxis;
942
FillChar(Result, SizeOf(Result), 0);
883
944
for i := 0 to High(FGroups) do begin
884
945
g := @FGroups[i];
886
950
g^.FTitleSize := 0;
887
951
for j := 0 to g^.FCount - 1 do begin
888
952
axis := TChartAxis(FGroupOrder[ai]);
889
axis.Measure(ACanvas, AExtent, AFirstPass, g^);
954
axis.Measure(AExtent, g^);
956
axis.Visible := false;
893
AMargins[axis.Alignment] += g^.FSize + g^.FTitleSize;
961
// Axises of the same group should have the same Alignment and ZPosition.
962
Result[axis.Alignment] += Max(0,
963
g^.FSize + g^.FTitleSize + g^.FMargin +
964
ALIGN_TO_ZDIR[axis.Alignment] * Min(axis.ZPosition, ADepth));
967
for i := 0 to High(FGroups) do begin
969
if TChartAxis(FGroupOrder[ai]).IsVertical then
970
UpdateMarginsForMarks(calBottom, calTop)
972
UpdateMarginsForMarks(calLeft, calRight);
897
977
procedure TChartAxisList.Prepare(ARect: TRect);
900
980
axis: TChartAxis;
903
FCenterPoint := CenterPoint(ARect);
905
for i := 0 to High(FGroups) do begin
907
for j := 0 to g^.FCount - 1 do begin
908
axis := TChartAxis(FGroupOrder[ai + j]);
909
axis.FAxisRect := ARect;
911
SideByAlignment(ARect, axis.Alignment, g^.FSize);
912
for j := 0 to g^.FCount - 1 do begin
985
for g in FGroups do begin
987
SideByAlignment(ARect, TChartAxis(FGroupOrder[ai]).Alignment, g.FSize);
988
for i := 0 to g.FCount - 1 do begin
913
989
axis := TChartAxis(FGroupOrder[ai]);
990
axis.FAxisRect := axisRect;
914
991
axis.FTitleRect := ARect;
917
SideByAlignment(ARect, axis.Alignment, g^.FTitleSize);
994
SideByAlignment(ARect, axis.Alignment, g.FTitleSize + g.FMargin);
919
996
InitAndSort(FZOrder, @AxisZCompare);