~ubuntu-branches/ubuntu/saucy/lazarus/saucy

« back to all changes in this revision

Viewing changes to components/tachart/tachartaxis.pas

  • Committer: Package Import Robot
  • Author(s): Paul Gevers, Abou Al Montacir, Bart Martens, Paul Gevers
  • Date: 2013-06-08 14:12:17 UTC
  • mfrom: (1.1.9)
  • Revision ID: package-import@ubuntu.com-20130608141217-7k0cy9id8ifcnutc
Tags: 1.0.8+dfsg-1
[ Abou Al Montacir ]
* New upstream major release and multiple maintenace release offering many
  fixes and new features marking a new milestone for the Lazarus development
  and its stability level.
  - The detailed list of changes can be found here:
    http://wiki.lazarus.freepascal.org/Lazarus_1.0_release_notes
    http://wiki.lazarus.freepascal.org/Lazarus_1.0_fixes_branch
* LCL changes:
  - LCL is now a normal package.
      + Platform independent parts of the LCL are now in the package LCLBase
      + LCL is automatically recompiled when switching the target platform,
        unless pre-compiled binaries for this target are already installed.
      + No impact on existing projects.
      + Linker options needed by LCL are no more added to projects that do
        not use the LCL package.
  - Minor changes in LCL basic classes behaviour
      + TCustomForm.Create raises an exception if a form resource is not
        found.
      + TNotebook and TPage: a new implementation of these classes was added.
      + TDBNavigator: It is now possible to have focusable buttons by setting
        Options = [navFocusableButtons] and TabStop = True, useful for
        accessibility and for devices with neither mouse nor touch screen.
      + Names of TControlBorderSpacing.GetSideSpace and GetSpace were swapped
        and are now consistent. GetSideSpace = Around + GetSpace.
      + TForm.WindowState=wsFullscreen was added
      + TCanvas.TextFitInfo was added to calculate how many characters will
        fit into a specified Width. Useful for word-wrapping calculations.
      + TControl.GetColorResolvingParent and
        TControl.GetRGBColorResolvingParent were added, simplifying the work
        to obtain the final color of the control while resolving clDefault
        and the ParentColor.
      + LCLIntf.GetTextExtentExPoint now has a good default implementation
        which works in any platform not providing a specific implementation.
        However, Widgetset specific implementation is better, when available.
      + TTabControl was reorganized. Now it has the correct class hierarchy
        and inherits from TCustomTabControl as it should.
  - New unit in the LCL:
      + lazdialogs.pas: adds non-native versions of various native dialogs,
        for example TLazOpenDialog, TLazSaveDialog, TLazSelectDirectoryDialog.
        It is used by widgetsets which either do not have a native dialog, or
        do not wish to use it because it is limited. These dialogs can also be
        used by user applications directly.
      + lazdeviceapis.pas: offers an interface to more hardware devices such
        as the accelerometer, GPS, etc. See LazDeviceAPIs
      + lazcanvas.pas: provides a TFPImageCanvas descendent implementing
        drawing in a LCL-compatible way, but 100% in Pascal.
      + lazregions.pas. LazRegions is a wholly Pascal implementation of
        regions for canvas clipping, event clipping, finding in which control
        of a region tree one an event should reach, for drawing polygons, etc.
      + customdrawncontrols.pas, customdrawndrawers.pas,
        customdrawn_common.pas, customdrawn_android.pas and
        customdrawn_winxp.pas: are the Lazarus Custom Drawn Controls -controls
        which imitate the standard LCL ones, but with the difference that they
        are non-native and support skinning.
  - New APIs added to the LCL to improve support of accessibility software
    such as screen readers.
* IDE changes:
  - Many improvments.
  - The detailed list of changes can be found here:
    http://wiki.lazarus.freepascal.org/New_IDE_features_since#v1.0_.282012-08-29.29
    http://wiki.lazarus.freepascal.org/Lazarus_1.0_release_notes#IDE_Changes
* Debugger / Editor changes:
  - Added pascal sources and breakpoints to the disassembler
  - Added threads dialog.
* Components changes:
  - TAChart: many fixes and new features
  - CodeTool: support Delphi style generics and new syntax extensions.
  - AggPas: removed to honor free licencing. (Closes: Bug#708695)
[Bart Martens]
* New debian/watch file fixing issues with upstream RC release.
[Abou Al Montacir]
* Avoid changing files in .pc hidden directory, these are used by quilt for
  internal purpose and could lead to surprises during build.
[Paul Gevers]
* Updated get-orig-source target and it compinion script orig-tar.sh so that they
  repack the source file, allowing bug 708695 to be fixed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
interface
24
24
 
25
25
uses
26
 
  Classes, Graphics, SysUtils, Types,
27
 
  TAChartUtils, TACustomSource, TATransformations, TATypes;
 
26
  Classes, SysUtils, Types,
 
27
  TAChartAxisUtils, TAChartUtils, TACustomSource, TADrawUtils,
 
28
  TATransformations, TATypes;
28
29
 
29
30
const
30
31
  DEF_TICK_LENGTH = 4;
31
 
  DEF_TITLE_DISTANCE = 4;
32
32
 
33
33
type
34
34
 
35
 
  TChartAxisBrush = class(TBrush)
36
 
  published
37
 
    property Style default bsClear;
38
 
  end;
39
 
 
40
 
  TChartAxisFramePen = class(TChartPen)
41
 
  published
42
 
    property Style default psClear;
43
 
  end;
44
 
 
45
 
  {$IFNDEF fpdoc}  // Workaround for issue #18549.
46
 
  TCustomChartAxisTitle =
47
 
    specialize TGenericChartMarks<TChartAxisBrush, TChartPen, TChartAxisFramePen>;
48
 
  {$ENDIF}
49
 
 
50
 
  { TChartAxisTitle }
51
 
 
52
 
  TChartAxisTitle = class(TCustomChartAxisTitle)
53
 
  private
54
 
    FCaption: String;
55
 
 
56
 
    function GetFont: TFont;
57
 
    procedure SetCaption(AValue: String);
58
 
    procedure SetFont(AValue: TFont);
59
 
  public
60
 
    constructor Create(AOwner: TCustomChart);
61
 
 
62
 
  public
63
 
    procedure Assign(Source: TPersistent); override;
64
 
  published
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;
69
 
    property Frame;
70
 
    property LabelBrush;
71
 
    property Visible default false;
72
 
  end;
73
 
 
74
 
  ICoordTransformer = interface
75
 
  ['{6EDA0F9F-ED59-4CA6-BA68-E247EB88AE3D}']
76
 
    function XGraphToImage(AX: Double): Integer;
77
 
    function YGraphToImage(AY: Double): Integer;
78
 
  end;
79
 
 
80
 
  TChartAxisAlignment = (calLeft, calTop, calRight, calBottom);
81
 
  TChartAxisMargins = array [TChartAxisAlignment] of Integer;
82
 
  TChartAxisMarkToTextEvent =
83
 
    procedure (var AText: String; AMark: Double) of object;
84
 
 
85
 
  TChartAxisPen = class(TChartPen)
86
 
  published
87
 
    property Style default psDot;
88
 
  end;
89
 
 
90
 
  {$IFNDEF fpdoc} // Workaround for issue #18549.
91
 
  TCustomChartAxisMarks =
92
 
    specialize TGenericChartMarks<TChartAxisBrush, TChartPen, TChartAxisFramePen>;
93
 
  {$ENDIF}
94
 
 
95
 
  { TChartAxisMarks }
96
 
 
97
 
  TChartAxisMarks = class(TCustomChartAxisMarks)
98
 
  private
99
 
    FAtDataOnly: Boolean;
100
 
    FDefaultSource: TCustomChartSource;
101
 
    FListener: TListener;
102
 
    FSource: TCustomChartSource;
103
 
 
104
 
    function IsFormatStored: Boolean;
105
 
    procedure SetAtDataOnly(AValue: Boolean);
106
 
    procedure SetSource(AValue: TCustomChartSource);
107
 
  public
108
 
    constructor Create(AOwner: TCustomChart);
109
 
    destructor Destroy; override;
110
 
 
111
 
    function SourceDef: TCustomChartSource;
112
 
  published
113
 
    property AtDataOnly: Boolean
114
 
      read FAtDataOnly write SetAtDataOnly default false;
115
 
    property Distance default 1;
116
 
    property Format stored IsFormatStored;
117
 
    property Frame;
118
 
    property LabelBrush;
119
 
    property OverlapPolicy;
120
 
    property Source: TCustomChartSource read FSource write SetSource;
121
 
    property Style default smsValue;
122
 
  end;
123
 
 
 
35
  { TChartMinorAxis }
 
36
 
 
37
  TChartMinorAxis = class(TChartBasicAxis)
 
38
  strict private
 
39
    function GetMarks: TChartMinorAxisMarks; inline;
 
40
    procedure SetMarks(AValue: TChartMinorAxisMarks);
 
41
  protected
 
42
    function GetDisplayName: String; override;
 
43
  strict protected
 
44
    function GetAlignment: TChartAxisAlignment; override;
 
45
    procedure SetAlignment(AValue: TChartAxisAlignment); override;
 
46
    procedure StyleChanged(ASender: TObject); override;
 
47
  public
 
48
    constructor Create(ACollection: TCollection); override;
 
49
    function GetMarkValues(AMin, AMax: Double): TChartValueTextArray;
 
50
  published
 
51
    property Marks: TChartMinorAxisMarks read GetMarks write SetMarks;
 
52
    property TickLength default DEF_TICK_LENGTH div 2;
 
53
  end;
 
54
 
 
55
  TChartAxis = class;
 
56
 
 
57
  { TChartMinorAxisList }
 
58
 
 
59
  TChartMinorAxisList = class(TCollection)
 
60
  strict private
 
61
    FAxis: TChartAxis;
 
62
    function GetAxes(AIndex: Integer): TChartMinorAxis;
 
63
  protected
 
64
    function GetOwner: TPersistent; override;
 
65
    procedure Update(AItem: TCollectionItem); override;
 
66
  public
 
67
    constructor Create(AOwner: TChartAxis);
 
68
  public
 
69
    function Add: TChartMinorAxis;
 
70
    function GetChart: TCustomChart; inline;
 
71
    property Axes[AIndex: Integer]: TChartMinorAxis read GetAxes; default;
 
72
    property ParentAxis: TChartAxis read FAxis;
 
73
  end;
124
74
 
125
75
  TChartAxisGroup = record
126
76
    FCount: Integer;
 
77
    FFirstMark: Integer;
 
78
    FLastMark: Integer;
 
79
    FMargin: Integer;
127
80
    FSize: Integer;
128
81
    FTitleSize: Integer;
129
82
  end;
130
83
 
 
84
  TChartAxisPen = class(TChartPen)
 
85
  published
 
86
    property Visible default false;
 
87
  end;
 
88
 
131
89
  { TChartAxis }
132
90
 
133
 
  TChartAxis = class(TCollectionItem)
134
 
  private
 
91
  TChartAxis = class(TChartBasicAxis)
 
92
  strict private
135
93
    FListener: TListener;
136
 
    FMarkTexts: TStringDynArray;
137
 
    FMarkValues: TDoubleDynArray;
 
94
    FMarkValues: TChartValueTextArray;
 
95
    FTitlePos: Integer;
138
96
 
139
 
    procedure GetMarkValues(AMin, AMax: Double);
 
97
    procedure GetMarkValues;
140
98
    procedure VisitSource(ASource: TCustomChartSource; var AData);
141
99
  private
142
100
    FAxisRect: TRect;
143
101
    FGroupIndex: Integer;
144
102
    FTitleRect: TRect;
145
 
  private
 
103
    function MakeValuesInRangeParams(AMin, AMax: Double): TValuesInRangeParams;
 
104
  strict private
146
105
    FAlignment: TChartAxisAlignment;
147
 
    FGrid: TChartAxisPen;
 
106
    FAxisPen: TChartAxisPen;
148
107
    FGroup: Integer;
 
108
    FHelper: TAxisDrawHelper;
149
109
    FInverted: Boolean;
150
 
    FMarks: TChartAxisMarks;
 
110
    FMargin: TChartDistance;
 
111
    FMarginsForMarks: Boolean;
 
112
    FMinors: TChartMinorAxisList;
151
113
    FOnMarkToText: TChartAxisMarkToTextEvent;
152
 
    FTickColor: TColor;
153
 
    FTickLength: Integer;
 
114
    FRange: TChartRange;
154
115
    FTitle: TChartAxisTitle;
155
116
    FTransformations: TChartAxisTransformations;
156
 
    FVisible: Boolean;
157
117
    FZPosition: TChartDistance;
158
118
 
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);
172
132
 
173
 
    procedure StyleChanged(ASender: TObject);
174
133
  protected
175
 
    function GetDisplayName: string; override;
 
134
    function GetDisplayName: String; override;
 
135
    procedure StyleChanged(ASender: TObject); override;
 
136
  strict protected
 
137
    function GetAlignment: TChartAxisAlignment; override;
 
138
    procedure SetAlignment(AValue: TChartAxisAlignment); override;
176
139
  public
177
140
    constructor Create(ACollection: TCollection); override;
178
141
    destructor Destroy; override;
179
142
  public
180
143
    procedure Assign(ASource: TPersistent); override;
181
 
    procedure Draw(
182
 
      ACanvas: TCanvas; const AExtent: TDoubleRect;
183
 
      const ATransf: ICoordTransformer; const AZOffset: TPoint);
184
 
    procedure DrawTitle(
185
 
      ACanvas: TCanvas; const ACenter, AZOffset: TPoint; ASize: Integer);
 
144
    procedure Draw;
 
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);
190
155
  published
191
 
    property Alignment: TChartAxisAlignment
192
 
      read FAlignment write SetAlignment default calLeft;
193
 
    property Grid: TChartAxisPen read FGrid write SetGrid;
 
156
    property Alignment default calLeft;
 
157
    property Arrow;
 
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;
206
173
  published
207
174
    property OnMarkToText: TChartAxisMarkToTextEvent
213
180
  TChartOnVisitSources = procedure (
214
181
    AVisitor: TChartOnSourceVisitor; AAxis: TChartAxis; var AData) of object;
215
182
 
 
183
  TChartAxisEnumerator = class(TCollectionEnumerator)
 
184
  public
 
185
    function GetCurrent: TChartAxis;
 
186
    property Current: TChartAxis read GetCurrent;
 
187
  end;
 
188
 
216
189
  { TChartAxisList }
217
190
 
218
191
  TChartAxisList = class(TCollection)
219
 
  private
 
192
  strict private
220
193
    FChart: TCustomChart;
221
194
    FOnVisitSources: TChartOnVisitSources;
222
195
    function GetAxes(AIndex: Integer): TChartAxis;
223
 
  private
224
 
    FCenterPoint: TPoint;
 
196
  strict private
225
197
    FGroupOrder: TFPList;
226
198
    FGroups: array of TChartAxisGroup;
227
199
    FZOrder: TFPList;
234
206
    destructor Destroy; override;
235
207
  public
236
208
    function Add: TChartAxis; inline;
237
 
    procedure Draw(
238
 
      ACanvas: TCanvas; const AExtent: TDoubleRect;
239
 
      const ATransf: ICoordTransformer; ACurrentZ, AMaxZ: Integer;
240
 
      var AIndex: Integer);
 
209
    procedure Draw(ACurrentZ: Integer; var AIndex: Integer);
241
210
    function GetAxis(AIndex: Integer): TChartAxis;
242
 
    procedure Measure(
243
 
      ACanvas: TCanvas; const AExtent: TDoubleRect;
244
 
      AFirstPass: Boolean; var AMargins: TChartAxisMargins);
 
211
    function GetEnumerator: TChartAxisEnumerator;
 
212
    function Measure(
 
213
      const AExtent: TDoubleRect; ADepth: Integer): TChartAxisMargins;
245
214
    procedure Prepare(ARect: TRect);
246
215
    procedure PrepareGroups;
247
216
    procedure SetAxis(AIndex: Integer; AValue: TChartAxis);
259
228
 
260
229
  TAxisCoeffHelper = object
261
230
    FAxis: TChartAxis;
262
 
    FImageLo, FImageHi, FMarginLo, FMarginHi: Integer;
 
231
    FImageLo, FImageHi: Integer;
263
232
    FLo, FHi: Integer;
264
233
    FMin, FMax: PDouble;
265
234
    function CalcOffset(AScale: Double): Double;
274
243
    var ARect: TRect; AAlignment: TChartAxisAlignment; ADelta: Integer);
275
244
  function TransformByAxis(
276
245
    AAxisList: TChartAxisList; AIndex: Integer): TChartAxisTransformations;
 
246
  procedure UpdateBoundsByAxisRange(
 
247
    AAxisList: TChartAxisList; AIndex: Integer; var AMin, AMax: Double);
277
248
 
278
249
implementation
279
250
 
280
251
uses
281
 
  LResources, Math, PropEdits, TADrawUtils, TASources;
282
 
 
283
 
type
284
 
  TAxisDataExtent = record
285
 
    FMin, FMax: Double;
286
 
  end;
 
252
  LResources, Math, PropEdits, TAGeometry, TAMath;
287
253
 
288
254
var
289
255
  VIdentityTransform: TChartAxisTransformations;
327
293
    Result := VIdentityTransform;
328
294
end;
329
295
 
330
 
{ TChartAxisTitle }
331
 
 
332
 
procedure TChartAxisTitle.Assign(Source: TPersistent);
333
 
begin
334
 
  if Source is TChartAxisTitle then
335
 
    with TChartAxisTitle(Source) do
336
 
      FCaption := Caption;
337
 
  inherited Assign(Source);
338
 
end;
339
 
 
340
 
constructor TChartAxisTitle.Create(AOwner: TCustomChart);
341
 
begin
342
 
  inherited Create(AOwner);
343
 
  FDistance := DEF_TITLE_DISTANCE;
344
 
  FFrame.Style := psClear;
345
 
  FLabelBrush.Style := bsClear;
346
 
  FVisible := false;
347
 
end;
348
 
 
349
 
function TChartAxisTitle.GetFont: TFont;
350
 
begin
351
 
  Result := LabelFont;
352
 
end;
353
 
 
354
 
procedure TChartAxisTitle.SetCaption(AValue: String);
355
 
begin
356
 
  if FCaption = AValue then exit;
357
 
  FCaption := AValue;
358
 
  StyleChanged(Self);
359
 
end;
360
 
 
361
 
procedure TChartAxisTitle.SetFont(AValue: TFont);
362
 
begin
363
 
  LabelFont := AValue;
364
 
end;
365
 
 
366
 
{ TChartAxisMarks }
367
 
 
368
 
constructor TChartAxisMarks.Create(AOwner: TCustomChart);
369
 
begin
370
 
  inherited Create(AOwner);
371
 
  FDefaultSource := TIntervalChartSource.Create(AOwner);
372
 
  FDistance := 1;
373
 
  FFrame.Style := psClear;
374
 
  FLabelBrush.Style := bsClear;
375
 
  FListener := TListener.Create(@FSource, @StyleChanged);
376
 
  FStyle := smsValue;
377
 
  FFormat := SERIES_MARK_FORMATS[FStyle];
378
 
end;
379
 
 
380
 
destructor TChartAxisMarks.Destroy;
381
 
begin
382
 
  FreeAndNil(FListener);
383
 
  FreeAndNil(FDefaultSource);
384
 
  inherited;
385
 
end;
386
 
 
387
 
function TChartAxisMarks.IsFormatStored: Boolean;
388
 
begin
389
 
  Result := FStyle <> smsValue;
390
 
end;
391
 
 
392
 
procedure TChartAxisMarks.SetAtDataOnly(AValue: Boolean);
393
 
begin
394
 
  if FAtDataOnly = AValue then exit;
395
 
  FAtDataOnly := AValue;
396
 
  StyleChanged(Self);
397
 
end;
398
 
 
399
 
procedure TChartAxisMarks.SetSource(AValue: TCustomChartSource);
400
 
begin
401
 
  if FSource = AValue then exit;
402
 
  if FListener.IsListening then
403
 
    FSource.Broadcaster.Unsubscribe(FListener);
404
 
  FSource := AValue;
405
 
  if FSource <> nil then
406
 
    FSource.Broadcaster.Subscribe(FListener);
407
 
  StyleChanged(Self);
408
 
end;
409
 
 
410
 
function TChartAxisMarks.SourceDef: TCustomChartSource;
411
 
begin
412
 
  Result := FSource;
413
 
  if Result = nil then
414
 
    Result := FDefaultSource;
 
296
procedure UpdateBoundsByAxisRange(
 
297
  AAxisList: TChartAxisList; AIndex: Integer; var AMin, AMax: Double);
 
298
begin
 
299
  if not InRange(AIndex, 0, AAxisList.Count - 1) then exit;
 
300
  AAxisList[AIndex].UpdateBounds(AMin, AMax);
 
301
end;
 
302
 
 
303
{ TChartAxisEnumerator }
 
304
 
 
305
function TChartAxisEnumerator.GetCurrent: TChartAxis;
 
306
begin
 
307
  Result := TChartAxis(inherited GetCurrent);
 
308
end;
 
309
 
 
310
{ TChartMinorAxis }
 
311
 
 
312
constructor TChartMinorAxis.Create(ACollection: TCollection);
 
313
begin
 
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];
 
319
    MinLength := 5;
 
320
  end;
 
321
  TickLength := DEF_TICK_LENGTH div 2;
 
322
end;
 
323
 
 
324
function TChartMinorAxis.GetAlignment: TChartAxisAlignment;
 
325
begin
 
326
  Result := (Collection.Owner as TChartAxis).Alignment;
 
327
end;
 
328
 
 
329
function TChartMinorAxis.GetDisplayName: String;
 
330
begin
 
331
  Result := 'M';
 
332
end;
 
333
 
 
334
function TChartMinorAxis.GetMarks: TChartMinorAxisMarks;
 
335
begin
 
336
  Result := TChartMinorAxisMarks(inherited Marks);
 
337
end;
 
338
 
 
339
function TChartMinorAxis.GetMarkValues(AMin, AMax: Double): TChartValueTextArray;
 
340
var
 
341
  vp: TValuesInRangeParams;
 
342
begin
 
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);
 
348
end;
 
349
 
 
350
procedure TChartMinorAxis.SetAlignment(AValue: TChartAxisAlignment);
 
351
begin
 
352
  Unused(AValue);
 
353
  raise EChartError.Create('TChartMinorAxis.SetAlignment');
 
354
end;
 
355
 
 
356
procedure TChartMinorAxis.SetMarks(AValue: TChartMinorAxisMarks);
 
357
begin
 
358
  inherited Marks := AValue;
 
359
end;
 
360
 
 
361
procedure TChartMinorAxis.StyleChanged(ASender: TObject);
 
362
begin
 
363
  (Collection.Owner as TChartAxis).StyleChanged(ASender);
 
364
end;
 
365
 
 
366
{ TChartMinorAxisList }
 
367
 
 
368
function TChartMinorAxisList.Add: TChartMinorAxis;
 
369
begin
 
370
  Result := TChartMinorAxis(inherited Add);
 
371
end;
 
372
 
 
373
constructor TChartMinorAxisList.Create(AOwner: TChartAxis);
 
374
begin
 
375
  inherited Create(TChartMinorAxis);
 
376
  FAxis := AOwner;
 
377
end;
 
378
 
 
379
function TChartMinorAxisList.GetAxes(AIndex: Integer): TChartMinorAxis;
 
380
begin
 
381
  Result := TChartMinorAxis(Items[AIndex]);
 
382
end;
 
383
 
 
384
function TChartMinorAxisList.GetChart: TCustomChart;
 
385
begin
 
386
  Result := FAxis.GetChart;
 
387
end;
 
388
 
 
389
function TChartMinorAxisList.GetOwner: TPersistent;
 
390
begin
 
391
  Result := FAxis;
 
392
end;
 
393
 
 
394
procedure TChartMinorAxisList.Update(AItem: TCollectionItem);
 
395
begin
 
396
  FAxis.StyleChanged(AItem);
415
397
end;
416
398
 
417
399
{ TChartAxis }
420
402
begin
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;
433
413
 
434
414
      Self.FOnMarkToText := OnMarkToText;
435
 
    end
436
 
  else
437
 
    inherited Assign(ASource);
 
415
    end;
 
416
  inherited Assign(ASource);
438
417
end;
439
418
 
440
419
constructor TChartAxis.Create(ACollection: TCollection);
441
420
begin
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);
451
 
  FVisible := true;
 
430
  FMarginsForMarks := true;
452
431
end;
453
432
 
454
433
destructor TChartAxis.Destroy;
455
434
begin
456
435
  FreeAndNil(FTitle);
457
 
  FreeAndNil(FMarks);
 
436
  FreeAndNil(FRange);
 
437
  FreeAndNil(FMinors);
458
438
  FreeAndNil(FListener);
459
 
  FreeAndNil(FGrid);
 
439
  FreeAndNil(FHelper);
 
440
  FreeAndNil(FAxisPen);
460
441
  inherited;
461
442
end;
462
443
 
463
 
procedure TChartAxis.Draw(
464
 
  ACanvas: TCanvas; const AExtent: TDoubleRect;
465
 
  const ATransf: ICoordTransformer; const AZOffset: TPoint);
466
 
 
467
 
var
468
 
  prevLabelPoly: TPointArray = nil;
469
 
 
470
 
 
471
 
  procedure LineZ(AP1, AP2: TPoint);
472
 
  begin
473
 
    ACanvas.Line(AP1 + AZOffset, AP2 + AZOffset);
474
 
  end;
475
 
 
476
 
  procedure DrawLabelAndTick(
477
 
    ALabelCenter: TPoint; const ATickRect: TRect; const AText: String);
478
 
  begin
479
 
    PrepareSimplePen(ACanvas, TickColor);
480
 
    LineZ(ATickRect.TopLeft, ATickRect.BottomRight);
481
 
    ALabelCenter += AZOffset;
482
 
    Marks.DrawLabel(ACanvas, ALabelCenter, ALabelCenter, AText, prevLabelPoly);
483
 
  end;
484
 
 
485
 
  procedure DrawXMark(AY: Integer; AMark: Double; const AText: String);
486
 
  var
487
 
    x, d: Integer;
488
 
  begin
489
 
    x := ATransf.XGraphToImage(AMark);
490
 
 
491
 
    if Grid.Visible then begin
492
 
      ACanvas.Pen.Assign(Grid);
493
 
      ACanvas.Brush.Style := bsClear;
494
 
      LineZ(
495
 
        Point(x, ATransf.YGraphToImage(AExtent.a.Y)),
496
 
        Point(x, ATransf.YGraphToImage(AExtent.b.Y)));
497
 
    end;
498
 
 
499
 
    if Marks.Visible then begin
500
 
      d := TickLength + Marks.CenterOffset(ACanvas, AText).cy;
501
 
      if Alignment = calTop then
502
 
        d := -d;
503
 
      DrawLabelAndTick(
504
 
        Point(x, AY + d), Rect(x, AY - TickLength, x, AY + TickLength), AText);
505
 
    end;
506
 
  end;
507
 
 
508
 
  procedure DrawYMark(AX: Integer; AMark: Double; const AText: String);
509
 
  var
510
 
    y, d: Integer;
511
 
  begin
512
 
    y := ATransf.YGraphToImage(AMark);
513
 
 
514
 
    if Grid.Visible then begin
515
 
      ACanvas.Pen.Assign(Grid);
516
 
      ACanvas.Brush.Style := bsClear;
517
 
      LineZ(
518
 
        Point(ATransf.XGraphToImage(AExtent.a.X), y),
519
 
        Point(ATransf.XGraphToImage(AExtent.b.X), y));
520
 
    end;
521
 
 
522
 
    if Marks.Visible then begin
523
 
      d := TickLength + Marks.CenterOffset(ACanvas, AText).cx;
524
 
      if Alignment = calLeft then
525
 
        d := -d;
526
 
      DrawLabelAndTick(
527
 
        Point(AX + d, y), Rect(AX - TickLength, y, AX + TickLength, y), AText);
528
 
    end;
529
 
  end;
530
 
 
531
 
var
532
 
  i, coord: Integer;
533
 
  v: Double;
 
444
procedure TChartAxis.Draw;
 
445
 
 
446
  procedure DrawMinors(AFixedCoord: Integer; AMin, AMax: Double);
 
447
  const
 
448
    EPS = 1e-6;
 
449
  var
 
450
    j: Integer;
 
451
    minorMarks: TChartValueTextArray;
 
452
    m: TChartValueText;
 
453
  begin
 
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
 
459
        FAxis := Minors[j];
 
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;
 
466
        try
 
467
          BeginDrawing;
 
468
          for m in minorMarks do
 
469
            DrawMark(AFixedCoord, FHelper.FAxisTransf(m.FValue), m.FText);
 
470
          EndDrawing;
 
471
        finally
 
472
          Free;
 
473
        end;
 
474
      end;
 
475
    end;
 
476
  end;
 
477
 
 
478
var
 
479
  fixedCoord: Integer;
 
480
  pv, v: Double;
 
481
  t: TChartValueText;
534
482
begin
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]);
540
 
    if IsVertical then
541
 
      DrawYMark(coord, v, FMarkTexts[i])
542
 
    else
543
 
      DrawXMark(coord, v, FMarkTexts[i]);
 
484
  if Marks.Visible then
 
485
    FHelper.FDrawer.Font := Marks.LabelFont;
 
486
  fixedCoord := TChartAxisMargins(FAxisRect)[Alignment];
 
487
  pv := SafeNaN;
 
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);
 
494
    pv := t.FValue;
544
495
  end;
 
496
  FHelper.EndDrawing;
545
497
end;
546
498
 
547
 
procedure TChartAxis.DrawTitle(
548
 
  ACanvas: TCanvas; const ACenter, AZOffset: TPoint; ASize: Integer);
 
499
procedure TChartAxis.DrawTitle(ASize: Integer);
549
500
var
550
501
  p: TPoint;
551
502
  dummy: TPointArray = nil;
552
503
  d: Integer;
553
504
begin
554
 
  if not Visible or (ASize = 0) then exit;
555
 
  p := ACenter;
556
 
  d := (ASize + Title.Distance) div 2;
 
505
  if not Visible or (ASize = 0) or (FTitlePos = MaxInt) then exit;
 
506
  if Title.DistanceToCenter then
 
507
    d := Title.Distance
 
508
  else
 
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;
562
515
  end;
563
 
  p += AZOffset;
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);
 
519
end;
 
520
 
 
521
function TChartAxis.GetAlignment: TChartAxisAlignment;
 
522
begin
 
523
  Result := FAlignment;
 
524
end;
 
525
 
 
526
function TChartAxis.GetChart: TCustomChart;
 
527
begin
 
528
  Result := Collection.Owner as TCustomChart;
565
529
end;
566
530
 
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)';
574
537
begin
575
538
  Result :=
576
 
    SIDE_NAME[Alignment] + VISIBLE_NAME[Visible] + INVERTED_NAME[Inverted];
577
 
  if Title.Caption <> '' then
578
 
    Result += Format(CAPTION_FMT, [Title.Caption]);
579
 
end;
580
 
 
581
 
procedure TChartAxis.GetMarkValues(AMin, AMax: Double);
 
539
    SIDE_NAME[Alignment] + VISIBLE_NAME[Visible] + INVERTED_NAME[Inverted] +
 
540
    FormatIfNotEmpty(' (%s)', Title.Caption);
 
541
end;
 
542
 
 
543
function TChartAxis.GetMarks: TChartAxisMarks;
 
544
begin
 
545
  Result := TChartAxisMarks(inherited Marks);
 
546
end;
 
547
 
 
548
procedure TChartAxis.GetMarkValues;
582
549
var
583
550
  i: Integer;
584
 
  d: TAxisDataExtent;
 
551
  d: TValuesInRangeParams;
585
552
  vis: TChartOnVisitSources;
 
553
  t: TChartValueText;
 
554
  axisMin, axisMax: Double;
586
555
begin
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);
 
559
  end;
 
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
594
 
    d.FMin := AMin;
595
 
    d.FMax := AMax;
596
566
    vis(@VisitSource, Self, d);
 
567
    // FIXME: Intersect axisMin/Max with the union of series extents.
597
568
  end
598
569
  else
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);
 
574
  end;
601
575
  if Inverted then
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]);
 
577
      t := FMarkValues[i];
 
578
      FMarkValues[i] := FMarkValues[High(FMarkValues) - i];
 
579
      FMarkValues[High(FMarkValues) - i] := t;
605
580
    end;
606
581
 
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);
610
585
end;
611
586
 
612
587
function TChartAxis.GetTransform: TChartAxisTransformations;
621
596
  Result := Alignment in [calLeft, calRight];
622
597
end;
623
598
 
 
599
function TChartAxis.MakeValuesInRangeParams(
 
600
  AMin, AMax: Double): TValuesInRangeParams;
 
601
begin
 
602
  Result.FMin := AMin;
 
603
  Result.FMax := AMax;
 
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;
 
612
end;
 
613
 
624
614
procedure TChartAxis.Measure(
625
 
  ACanvas: TCanvas; const AExtent: TDoubleRect;
626
 
  AFirstPass: Boolean; var AMeasureData: TChartAxisGroup);
627
 
 
628
 
  function CalcMarksSize(AMin, AMax: Double): TSize;
629
 
  const
630
 
    SOME_DIGIT = '0';
 
615
  const AExtent: TDoubleRect; var AMeasureData: TChartAxisGroup);
 
616
var
 
617
  v: Boolean;
 
618
  d: IChartDrawer;
 
619
 
 
620
  function TitleSize: Integer;
 
621
  begin
 
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);
 
629
  end;
 
630
 
 
631
  procedure UpdateFirstLast(ACoord, AIndex, ARMin, ARMax: Integer);
631
632
  var
632
 
    i, d: Integer;
633
 
    t: String;
 
633
    sz, fm, lm: Integer;
634
634
  begin
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.
645
 
      t := FMarkTexts[i];
646
 
      if AFirstPass then
647
 
        t += SOME_DIGIT;
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);
652
 
      end;
 
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;
 
641
    if v then
 
642
      Exchange(fm, lm);
 
643
    with AMeasureData do begin
 
644
      FFirstMark := Max(fm, FFirstMark);
 
645
      FLastMark := Max(lm, FLastMark);
653
646
    end;
654
647
  end;
655
648
 
656
 
  function CalcTitleSize: Integer;
657
 
  var
658
 
    sz: TSize;
659
 
  begin
660
 
    if not Title.Visible or (Title.Caption = '') then
661
 
      exit(0);
662
 
    sz := Title.MeasureLabel(ACanvas, Title.Caption);
663
 
 
664
 
    Result := IfThen(IsVertical, sz.cx, sz.cy) + Title.Distance;
665
 
  end;
666
 
 
667
649
var
668
 
  sz: Integer;
 
650
  sz, rmin, rmax, c, i, j, minc, maxc, mini, maxi: Integer;
 
651
  minorValues: TChartValueTextArray;
 
652
  pv: Double = NaN;
 
653
  cv: Double;
669
654
begin
670
655
  if not Visible then exit;
671
 
  if IsVertical then
672
 
    sz := CalcMarksSize(AExtent.a.Y, AExtent.b.Y).cx
673
 
  else
674
 
    sz := CalcMarksSize(AExtent.a.X, AExtent.b.X).cy;
675
 
  if sz > 0 then
676
 
    sz += TickLength + Marks.Distance;
 
656
  v := IsVertical;
 
657
  d := FHelper.FDrawer;
 
658
  FHelper.FValueMin := TDoublePointBoolArr(AExtent.a)[v];
 
659
  FHelper.FValueMax := TDoublePointBoolArr(AExtent.b)[v];
 
660
  GetMarkValues;
 
661
  sz := Marks.Measure(d, not v, TickLength, FMarkValues);
 
662
  FHelper.GetClipRange(rmin, rmax);
 
663
  minc := MaxInt;
 
664
  maxc := -MaxInt;
 
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);
 
672
        end;
 
673
    end;
 
674
    pv := cv;
 
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
 
679
      minc := c;
 
680
      mini := i;
 
681
    end;
 
682
    if c > maxc then begin
 
683
      maxc := c;
 
684
      maxi := i;
 
685
    end;
 
686
  end;
677
687
  with AMeasureData do begin
678
688
    FSize := Max(sz, FSize);
679
 
    FTitleSize := Max(CalcTitleSize, FTitleSize);
680
 
  end;
 
689
    FTitleSize := Max(TitleSize, FTitleSize);
 
690
    FMargin := Max(Margin, FMargin);
 
691
  end;
 
692
  if minc < MaxInt then begin
 
693
    UpdateFirstLast(minc, mini, rmin, rmax);
 
694
    UpdateFirstLast(maxc, maxi, rmin, rmax);
 
695
  end;
 
696
  if not Title.PositionOnMarks then
 
697
    FTitlePos := (rmin + rmax) div 2
 
698
  else if minc < MaxInt then
 
699
    FTitlePos := (maxc + minc) div 2
 
700
  else
 
701
    FTitlePos := MaxInt;
 
702
 
 
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);
 
707
    end;
 
708
end;
 
709
 
 
710
procedure TChartAxis.PrepareHelper(
 
711
  ADrawer: IChartDrawer; const ATransf: ICoordTransformer;
 
712
  AClipRect: PRect; AMaxZPosition: Integer);
 
713
begin
 
714
  FreeAndNil(FHelper);
 
715
  if IsVertical then
 
716
    FHelper := TAxisDrawHelperY.Create
 
717
  else
 
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;
681
726
end;
682
727
 
683
728
procedure TChartAxis.SetAlignment(AValue: TChartAxisAlignment);
687
732
  StyleChanged(Self);
688
733
end;
689
734
 
690
 
procedure TChartAxis.SetGrid(AValue: TChartAxisPen);
 
735
procedure TChartAxis.SetAxisPen(AValue: TChartAxisPen);
691
736
begin
692
 
  FGrid.Assign(AValue);
 
737
  FAxisPen.Assign(AValue);
693
738
  StyleChanged(Self);
694
739
end;
695
740
 
707
752
  StyleChanged(Self);
708
753
end;
709
754
 
710
 
procedure TChartAxis.SetMarks(const AValue: TChartAxisMarks);
711
 
begin
712
 
  if FMarks = AValue then exit;
713
 
  FMarks := AValue;
714
 
  StyleChanged(Self);
715
 
end;
716
 
 
717
 
procedure TChartAxis.SetOnMarkToText(const AValue: TChartAxisMarkToTextEvent);
 
755
procedure TChartAxis.SetMargin(AValue: TChartDistance);
 
756
begin
 
757
  if FMargin = AValue then exit;
 
758
  FMargin := AValue;
 
759
  StyleChanged(Self);
 
760
end;
 
761
 
 
762
procedure TChartAxis.SetMarginsForMarks(AValue: Boolean);
 
763
begin
 
764
  if FMarginsForMarks = AValue then exit;
 
765
  FMarginsForMarks := AValue;
 
766
  StyleChanged(Self);
 
767
end;
 
768
 
 
769
procedure TChartAxis.SetMarks(AValue: TChartAxisMarks);
 
770
begin
 
771
  inherited Marks := AValue;
 
772
end;
 
773
 
 
774
procedure TChartAxis.SetMinors(AValue: TChartMinorAxisList);
 
775
begin
 
776
  FMinors.Assign(AValue);
 
777
  StyleChanged(Self);
 
778
end;
 
779
 
 
780
procedure TChartAxis.SetOnMarkToText(AValue: TChartAxisMarkToTextEvent);
718
781
begin
719
782
  if TMethod(FOnMarkToText) = TMethod(AValue) then exit;
720
783
  FOnMarkToText := AValue;
721
784
  StyleChanged(Self);
722
785
end;
723
786
 
724
 
procedure TChartAxis.SetTickColor(AValue: TColor);
725
 
begin
726
 
  if FTickColor = AValue then exit;
727
 
  FTickColor := AValue;
728
 
  StyleChanged(Self);
729
 
end;
730
 
 
731
 
procedure TChartAxis.SetTickLength(AValue: Integer);
732
 
begin
733
 
  if FTickLength = AValue then exit;
734
 
  FTickLength := AValue;
735
 
  StyleChanged(Self);
 
787
procedure TChartAxis.SetRange(AValue: TChartRange);
 
788
begin
 
789
  if FRange = AValue then exit;
 
790
  FRange.Assign(AValue);
 
791
  StyleChanged(Range);
736
792
end;
737
793
 
738
794
procedure TChartAxis.SetTitle(AValue: TChartAxisTitle);
752
808
  StyleChanged(AValue);
753
809
end;
754
810
 
755
 
procedure TChartAxis.SetVisible(const AValue: Boolean);
756
 
begin
757
 
  if FVisible = AValue then exit;
758
 
  FVisible := AValue;
759
 
  StyleChanged(Self);
760
 
end;
761
 
 
762
 
procedure TChartAxis.SetZPosition(const AValue: TChartDistance);
 
811
procedure TChartAxis.SetZPosition(AValue: TChartDistance);
763
812
begin
764
813
  if FZPosition = AValue then exit;
765
814
  FZPosition := AValue;
768
817
 
769
818
procedure TChartAxis.StyleChanged(ASender: TObject);
770
819
begin
771
 
  with Collection.Owner as TCustomChart do begin
 
820
  with GetChart do begin
772
821
    // Transformation change could have invalidated the current extent,
773
822
    // so revert to full extent for now.
774
823
    if (ASender is TAxisTransform) or (ASender is TChartAxisTransformations) then
777
826
  end;
778
827
end;
779
828
 
 
829
procedure TChartAxis.UpdateBounds(var AMin, AMax: Double);
 
830
begin
 
831
  with Range do begin
 
832
    if UseMin then
 
833
      AMin := Min;
 
834
    if UseMax then
 
835
      AMax := Max;
 
836
  end;
 
837
end;
 
838
 
780
839
procedure TChartAxis.VisitSource(ASource: TCustomChartSource; var AData);
781
840
var
782
 
  lmin, lmax: Double;
783
841
  ext: TDoubleRect;
 
842
  p: TValuesInRangeParams absolute AData;
784
843
begin
785
844
  ext := ASource.Extent;
786
 
  with TAxisDataExtent(AData) do begin
787
 
    if IsVertical then begin
788
 
      lmin := Max(ext.a.Y, FMin);
789
 
      lmax := Min(ext.b.Y, FMax);
790
 
    end
791
 
    else begin
792
 
      lmin := Max(ext.a.X, FMin);
793
 
      lmax := Min(ext.b.X, FMax);
794
 
    end;
795
 
    Marks.SourceDef.ValuesInRange(
796
 
      lmin, lmax, Marks.Format, IsVertical, FMarkValues, FMarkTexts);
797
 
  end;
 
845
  //p := TValuesInRangeParams(AData);
 
846
  p.FMin := Max(TDoublePointBoolArr(ext.a)[IsVertical], p.FMin);
 
847
  p.FMax := Min(TDoublePointBoolArr(ext.b)[IsVertical], p.FMax);
 
848
  Marks.SourceDef.ValuesInRange(p, FMarkValues);
798
849
end;
799
850
 
800
851
const
822
873
  inherited Destroy;
823
874
end;
824
875
 
825
 
procedure TChartAxisList.Draw(
826
 
  ACanvas: TCanvas; const AExtent: TDoubleRect;
827
 
  const ATransf: ICoordTransformer; ACurrentZ, AMaxZ: Integer;
828
 
  var AIndex: Integer);
829
 
var
830
 
  zoffset: TPoint;
 
876
procedure TChartAxisList.Draw(ACurrentZ: Integer; var AIndex: Integer);
831
877
begin
832
878
  while AIndex < FZOrder.Count do
833
879
    with TChartAxis(FZOrder[AIndex]) do begin
834
880
      if ACurrentZ < ZPosition then break;
835
 
      zoffset.Y := Min(ZPosition, AMaxZ);
836
 
      zoffset.X := - zoffset.Y;
837
 
      Draw(ACanvas, AExtent, ATransf, zoffset);
838
 
      DrawTitle(ACanvas, FCenterPoint, zoffset, FGroups[FGroupIndex].FTitleSize);
 
881
      try
 
882
        Draw;
 
883
        DrawTitle(FGroups[FGroupIndex].FTitleSize);
 
884
      except
 
885
        Visible := false;
 
886
        raise;
 
887
      end;
839
888
      AIndex += 1;
840
889
    end;
841
890
end;
846
895
end;
847
896
 
848
897
function TChartAxisList.GetAxis(AIndex: Integer): TChartAxis;
849
 
var
850
 
  i: Integer;
851
898
begin
852
 
  for i := 0 to Count - 1 do
853
 
    if Axes[i].Alignment = AXIS_INDEX[AIndex] then
854
 
      exit(Axes[i]);
 
899
  for Result in Self do
 
900
    if Result.Alignment = AXIS_INDEX[AIndex] then exit;
855
901
  Result := nil;
856
902
end;
857
903
 
 
904
function TChartAxisList.GetEnumerator: TChartAxisEnumerator;
 
905
begin
 
906
  Result := TChartAxisEnumerator.Create(Self);
 
907
end;
 
908
 
858
909
function TChartAxisList.GetOwner: TPersistent;
859
910
begin
860
911
  Result := FChart;
863
914
procedure TChartAxisList.InitAndSort(
864
915
  AList: TFPList; ACompare: TListSortCompare);
865
916
var
866
 
  i: Integer;
 
917
  a: TChartAxis;
867
918
begin
868
919
  AList.Clear;
869
 
  for i := 0 to Count - 1 do
870
 
    AList.Add(Pointer(Axes[i]));
 
920
  for a in Self do
 
921
    AList.Add(Pointer(a));
871
922
  AList.Sort(ACompare);
872
923
end;
873
924
 
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;
 
927
var
 
928
  g: ^TChartAxisGroup;
 
929
 
 
930
  procedure UpdateMarginsForMarks(AFirst, ALast: TChartAxisAlignment);
 
931
  begin
 
932
    Result[AFirst] := Max(Result[AFirst], g^.FFirstMark);
 
933
    Result[ALast] := Max(Result[ALast], g^.FLastMark);
 
934
  end;
 
935
 
 
936
const
 
937
  ALIGN_TO_ZDIR: array [TChartAxisAlignment] of Integer = (1, -1, -1, 1);
877
938
var
878
939
  i, j, ai: Integer;
879
940
  axis: TChartAxis;
880
 
  g: ^TChartAxisGroup;
881
941
begin
 
942
  FillChar(Result, SizeOf(Result), 0);
882
943
  ai := 0;
883
944
  for i := 0 to High(FGroups) do begin
884
945
    g := @FGroups[i];
 
946
    g^.FFirstMark := 0;
 
947
    g^.FLastMark := 0;
 
948
    g^.FMargin := 0;
885
949
    g^.FSize := 0;
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^);
 
953
      try
 
954
        axis.Measure(AExtent, g^);
 
955
      except
 
956
        axis.Visible := false;
 
957
        raise;
 
958
      end;
890
959
      ai += 1;
891
960
    end;
892
 
    if AFirstPass then
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));
 
965
  end;
 
966
  ai := 0;
 
967
  for i := 0 to High(FGroups) do begin
 
968
    g := @FGroups[i];
 
969
    if TChartAxis(FGroupOrder[ai]).IsVertical then
 
970
      UpdateMarginsForMarks(calBottom, calTop)
 
971
    else
 
972
      UpdateMarginsForMarks(calLeft, calRight);
 
973
    ai += g^.FCount;
894
974
  end;
895
975
end;
896
976
 
897
977
procedure TChartAxisList.Prepare(ARect: TRect);
898
978
var
899
 
  i, j, ai: Integer;
 
979
  i, ai: Integer;
900
980
  axis: TChartAxis;
901
 
  g: ^TChartAxisGroup;
 
981
  g: TChartAxisGroup;
 
982
  axisRect: TRect;
902
983
begin
903
 
  FCenterPoint := CenterPoint(ARect);
904
984
  ai := 0;
905
 
  for i := 0 to High(FGroups) do begin
906
 
    g := @FGroups[i];
907
 
    for j := 0 to g^.FCount - 1 do begin
908
 
      axis := TChartAxis(FGroupOrder[ai + j]);
909
 
      axis.FAxisRect := ARect;
910
 
    end;
911
 
    SideByAlignment(ARect, axis.Alignment, g^.FSize);
912
 
    for j := 0 to g^.FCount - 1 do begin
 
985
  for g in FGroups do begin
 
986
    axisRect := ARect;
 
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;
915
992
      ai += 1;
916
993
    end;
917
 
    SideByAlignment(ARect, axis.Alignment, g^.FTitleSize);
 
994
    SideByAlignment(ARect, axis.Alignment, g.FTitleSize + g.FMargin);
918
995
  end;
919
996
  InitAndSort(FZOrder, @AxisZCompare);
920
997
end;
949
1026
  if a = nil then
950
1027
    a := Add;
951
1028
  a.Assign(AValue);
952
 
  a.FAlignment := AXIS_INDEX[AIndex];
 
1029
  a.Alignment := AXIS_INDEX[AIndex];
953
1030
end;
954
1031
 
955
1032
procedure TChartAxisList.Update(AItem: TCollectionItem);
967
1044
  FAxis := AAxis;
968
1045
  FImageLo := AImageLo;
969
1046
  FImageHi := AImageHi;
970
 
  FMarginLo := AMarginLo;
971
 
  FMarginHi := AMarginHi;
972
1047
  FMin := AMin;
973
1048
  FMax := AMax;
974
 
  FLo := FImageLo + FMarginLo;
975
 
  FHi := FImageHi + FMarginHi;
 
1049
  FLo := FImageLo + AMarginLo;
 
1050
  FHi := FImageHi + AMarginHi;
976
1051
end;
977
1052
 
978
1053
function TAxisCoeffHelper.CalcScale(ASign: Integer): Double;
1003
1078
  RegisterPropertyToSkip(TChartAxis, 'Offset', TRANSFORM_NOTE, '');
1004
1079
  RegisterPropertyToSkip(TChartAxis, 'Scale', TRANSFORM_NOTE, '');
1005
1080
  RegisterPropertyToSkip(TChartAxis, 'Transformation', TRANSFORM_NOTE, '');
1006
 
  RegisterPropertyEditor(
1007
 
    TypeInfo(TFont), TChartAxisTitle, 'Font', THiddenPropertyEditor);
1008
1081
end;
1009
1082
 
1010
1083
initialization