1 {
2  /***************************************************************************
3                                TATypes.pas
4                                -----------
5               Component Library Standard Graph Element Types
6 
7 
8  ***************************************************************************/
9 
10  *****************************************************************************
11   See the file COPYING.modifiedLGPL.txt, included in this distribution,
12   for details about the license.
13  *****************************************************************************
14 
15   Authors: Luнs Rodrigues, Philippe Martinole, Alexander Klenin
16 
17 }
18 unit TATypes;
19 
20 {$H+}
21 
22 interface
23 
24 uses
25   Classes, Graphics, Controls, FPCanvas,
26   TAChartUtils, TADrawUtils;
27 
28 const
29   DEF_MARGIN = 4;
30   DEF_MIN_DATA_SPACE = 10;
31   DEF_MARKS_DISTANCE = 20;
32   DEF_POINTER_SIZE = 4;
33   MARKS_YINDEX_ALL = -1;
34   DEF_ARROW_LENGTH = 10;
35   DEF_ARROW_WIDTH = 5;
36   DEF_SHADOW_OFFSET = 8;
37   DEF_SHADOW_TRANSPARENCY = 128;
38   CMD_QUERY_SERIESEXTENT = 0;
39 
40 
41 type
42   TCustomChart = class(TCustomControl)
43   public
44     procedure Notify(ACommand: Integer; AParam1, AParam2: Pointer; var Data); virtual; abstract;
45     procedure StyleChanged(Sender: TObject); virtual; abstract;
46     procedure ZoomFull(AImmediateRecalc: Boolean = false); virtual; abstract;
47   end;
48 
49   { TChartPen }
50 
51   TChartPen = class(TPen)
52   strict private
53     FVisible: Boolean;
54     procedure SetVisible(AValue: Boolean);
55   public
56     constructor Create; override;
57   public
58     procedure Assign(ASource: TPersistent); override;
EffVisiblenull59     function EffVisible: Boolean; inline;
60   published
61     property Color default clDefault;
62     property Visible: Boolean read FVisible write SetVisible default true;
63   end;
64 
65   TClearBrush = class(TBrush)
66   public
67     constructor Create; override;
68   published
69    // property Style default bsClear;
70   { wp: Removed because bsClear would no longer work - TBrush.SetColor switches
71         Style to clSolid if Style is bsClear }
72   end;
73 
74   TFPCanvasHelperClass = class of TFPCanvasHelper;
75 
76   { TChartElement }
77 
78   TChartElement = class(TPersistent)
79   strict protected
80     FOwner: TCustomChart;
81     FVisible: Boolean;
82     procedure InitHelper(var AResult; AClass: TFPCanvasHelperClass);
83     procedure SetVisible(AValue: Boolean);
84     procedure StyleChanged(Sender: TObject); virtual;
85   protected
86   public
87     constructor Create(AOwner: TCustomChart);
88   public
89     procedure Assign(ASource: TPersistent); override;
GetOwnernull90     function GetOwner: TPersistent; override;
91     procedure SetOwner(AOwner: TCustomChart);
92     property Visible: Boolean read FVisible write SetVisible;
93   end;
94 
95   TSeriesPointerStyle = (
96     psNone, psRectangle, psCircle, psCross, psDiagCross, psStar,
97     psLowBracket, psHighBracket, psLeftBracket, psRightBracket, psDiamond,
98     psTriangle, psLeftTriangle, psRightTriangle, psVertBar, psHorBar, psPoint,
99     psDownTriangle, psHexagon, psFullStar);
100 
101   { TSeriesPointer }
102 
103   TSeriesPointer = class(TChartElement)
104   strict private
105     FBrush: TBrush;
106     FHorizSize: Integer;
107     FOverrideColor: TOverrideColors;
108     FPen: TChartPen;
109     FStyle: TSeriesPointerStyle;
110     FVertSize: Integer;
111 
112     procedure SetBrush(AValue: TBrush);
113     procedure SetHorizSize(AValue: Integer);
114     procedure SetOverrideColor(AValue: TOverrideColors);
115     procedure SetPen(AValue: TChartPen);
116     procedure SetStyle(AValue: TSeriesPointerStyle);
117     procedure SetVertSize(AValue: Integer);
118   public
119     constructor Create(AOwner: TCustomChart);
120     destructor Destroy; override;
121   public
122     procedure Assign(Source: TPersistent); override;
123     procedure Draw(ADrawer: IChartDrawer; ACenter: TPoint; AColor: TColor;
124       ABrushAlreadySet: Boolean = false);
125     procedure DrawSize(ADrawer: IChartDrawer; ACenter, ASize: TPoint;
126       AColor: TColor; AAngle: Double = 0.0; ABrushAlreadySet: Boolean = false);
127   published
128     property Brush: TBrush read FBrush write SetBrush;
129     property HorizSize: Integer read FHorizSize write SetHorizSize default DEF_POINTER_SIZE;
130     property OverrideColor: TOverrideColors
131       read FOverrideColor write SetOverrideColor default [ocBrush];
132     property Pen: TChartPen read FPen write SetPen;
133     property Style: TSeriesPointerStyle read FStyle write SetStyle default psRectangle;
134     property VertSize: Integer read FVertSize write SetVertSize default DEF_POINTER_SIZE;
135     property Visible default false;
136   end;
137 
138   EExtentError = class(EChartError);
139 
140   TChartRange = class(TChartElement)
141   strict private
142     FBounds: array [1..2] of Double;
143     FUseBounds: array [1..2] of Boolean;
144 
GetBoundsnull145     function GetBounds(AIndex: Integer): Double;
GetUseBoundsnull146     function GetUseBounds(AIndex: integer): Boolean;
IsBoundsStorednull147     function IsBoundsStored(AIndex: Integer): Boolean;
148     procedure SetBounds(AIndex: Integer; const AValue: Double);
149     procedure SetUseBounds(AIndex: Integer; AValue: Boolean);
150   public
151     procedure Assign(ASource: TPersistent); override;
152     procedure CheckBoundsOrder;
153     procedure Intersect(var AMin, AMax: Double);
154   published
155     property Max: Double index 2 read GetBounds write SetBounds stored IsBoundsStored;
156     property Min: Double index 1 read GetBounds write SetBounds stored IsBoundsStored;
157     property UseMax: Boolean index 2 read GetUseBounds write SetUseBounds default false;
158     property UseMin: Boolean index 1 read GetUseBounds write SetUseBounds default false;
159   end;
160 
161   { TChartExtent }
162 
163   TChartExtent = class(TChartElement)
164   strict private
165     FExtent: TDoubleRect;
166     FUseBounds: array [1..4] of Boolean;
167 
GetBoundsnull168     function GetBounds(AIndex: Integer): Double;
GetUseBoundsnull169     function GetUseBounds(AIndex: integer): Boolean;
IsBoundsStorednull170     function IsBoundsStored(AIndex: Integer): Boolean;
171     procedure SetBounds(AIndex: Integer; const AValue: Double);
172     procedure SetUseBounds(AIndex: Integer; AValue: Boolean);
173   public
174     procedure Assign(ASource: TPersistent); override;
175     procedure CheckBoundsOrder;
176     procedure FixTo(const ABounds: TDoubleRect);
177   published
178     property UseXMax: Boolean index 3 read GetUseBounds write SetUseBounds default false;
179     property UseXMin: Boolean index 1 read GetUseBounds write SetUseBounds default false;
180     property UseYMax: Boolean index 4 read GetUseBounds write SetUseBounds default false;
181     property UseYMin: Boolean index 2 read GetUseBounds write SetUseBounds default false;
182     property XMax: Double index 3 read GetBounds write SetBounds stored IsBoundsStored;
183     property XMin: Double index 1 read GetBounds write SetBounds stored IsBoundsStored;
184     property YMax: Double index 4 read GetBounds write SetBounds stored IsBoundsStored;
185     property YMin: Double index 2 read GetBounds write SetBounds stored IsBoundsStored;
186   end;
187 
188   TChartExtentHistory = specialize THistory<TDoubleRect>;
189 
190   TRectArray = array [1..4] of Integer;
191 
192   { TChartMargins }
193 
194   TChartMargins = class(TChartElement)
195   strict private
196     FData: record
197       case Integer of
198         0: (FRect: TRect;);
199         1: (FCoords: TRectArray;);
200       end;
201   public
202     constructor Create(AOwner: TCustomChart);
203 
GetValuenull204     function GetValue(AIndex: Integer): Integer;
205     procedure SetValue(AIndex: Integer; AValue: TChartDistance);
206   public
207     procedure Assign(Source: TPersistent); override;
208     procedure ExpandRectScaled(ADrawer: IChartDrawer; var ARect: TRect);
209     property Data: TRect read FData.FRect;
210   published
211     property Left: TChartDistance index 1 read GetValue write SetValue default DEF_MARGIN;
212     property Top: TChartDistance index 2 read GetValue write SetValue default DEF_MARGIN;
213     property Right: TChartDistance index 3 read GetValue write SetValue default DEF_MARGIN;
214     property Bottom: TChartDistance index 4 read GetValue write SetValue default DEF_MARGIN;
215   end;
216 
217   TChartArrow = class(TChartElement)
218   strict private
219     FBaseLength: TChartDistance;
220     FInverted: Boolean;
221     FLength: TChartDistance;
222     FWidth: TChartDistance;
223     procedure SetBaseLength(AValue: TChartDistance);
224     procedure SetInverted(AValue: Boolean);
225     procedure SetLength(AValue: TChartDistance);
226     procedure SetWidth(AValue: TChartDistance);
227   public
228     constructor Create(AOwner: TCustomChart);
229   public
230     procedure Assign(ASource: TPersistent); override;
231     procedure Draw(
232       ADrawer: IChartDrawer; const AEndPos: TPoint; AAngle: Double;
233       APen: TFPCustomPen);
234   published
235     property BaseLength: TChartDistance
236       read FBaseLength write SetBaseLength default 0;
237     property Inverted: Boolean
238       read FInverted write SetInverted default false;
239     property Length: TChartDistance
240       read FLength write SetLength default DEF_ARROW_LENGTH;
241     property Visible default false;
242     property Width: TChartDistance
243       read FWidth write SetWidth default DEF_ARROW_WIDTH;
244   end;
245 
246   TChartShadow = class(TChartElement)
247   strict private
248     FColor: TColor;
249     FOffset: TPoint;
250     FTransparency: TChartTransparency;
251     procedure SetColor(AValue: TColor);
252     procedure SetOffsetX(AValue: Integer);
253     procedure SetOffsetY(AValue: Integer);
254     procedure SetTransparency(AValue: TChartTransparency);
255   public
256     constructor Create(AOwner: TCustomChart);
257   public
258     procedure Assign(ASource: TPersistent); override;
259   published
260     property Color: TColor read FColor write SetColor default clBlack;
261     property OffsetX: Integer read FOffset.X write SetOffsetX default DEF_SHADOW_OFFSET;
262     property OffsetY: Integer read FOffset.Y write SetOffsetY default DEF_SHADOW_OFFSET;
263     property Transparency: TChartTransparency
264       read FTransparency write SetTransparency default DEF_SHADOW_TRANSPARENCY;
265     property Visible default false;
266   end;
267 
268   TChartErrorBar = class(TChartElement)
269   private
270     FWidth: Integer;
271     FPen: TPen;
272     procedure SetPen(const AValue: TPen);
273     procedure SetWidth(const AValue: Integer);
274   public
275     constructor Create(AOwner: TCustomChart);
276     destructor Destroy; override;
277     procedure Assign(ASource: TPersistent); override;
278   published
279     property Pen: TPen read FPen write SetPen;
280     property Visible default false;
281     property Width: Integer read FWidth write SetWidth default -1;
282   end;
283 
284 implementation
285 
286 uses
287   Math, SysUtils,
288   TAGeometry;
289 
290 { TChartPen }
291 
292 procedure TChartPen.Assign(ASource: TPersistent);
293 begin
294   if ASource is TChartPen then
295     FVisible := TChartPen(ASource).Visible;
296   inherited Assign(ASource);
297 end;
298 
299 constructor TChartPen.Create;
300 begin
301   inherited Create;
302   SetPropDefaults(Self, ['Color', 'Style', 'Visible']);
303 end;
304 
EffVisiblenull305 function TChartPen.EffVisible: Boolean;
306 begin
307   Result := Visible and (Style <> psClear);
308 end;
309 
310 procedure TChartPen.SetVisible(AValue: Boolean);
311 begin
312   FVisible := AValue;
313   if Assigned(OnChange) then OnChange(Self);
314 end;
315 
316 { TClearBrush }
317 
318 constructor TClearBrush.Create;
319 begin
320   inherited;
321   Style := bsClear;
322 end;
323 
324 { TChartElement }
325 
326 procedure TChartElement.Assign(ASource: TPersistent);
327 begin
328   if ASource is TChartElement then
329     with TChartElement(ASource) do begin
330       Self.FVisible := FVisible;
331       Self.FOwner := FOwner;
332     end;
333 end;
334 
335 constructor TChartElement.Create(AOwner: TCustomChart);
336 begin
337   inherited Create;
338   FOwner := AOwner;
339 end;
340 
TChartElement.GetOwnernull341 function TChartElement.GetOwner: TPersistent;
342 begin
343   Result := FOwner;
344 end;
345 
346 procedure TChartElement.InitHelper(var AResult; AClass: TFPCanvasHelperClass);
347 begin
348   TFPCanvasHelper(AResult) := AClass.Create;
349   TFPCanvasHelper(AResult).OnChange := @StyleChanged;
350 end;
351 
352 procedure TChartElement.SetOwner(AOwner: TCustomChart);
353 begin
354   FOwner := AOwner;
355 end;
356 
357 procedure TChartElement.SetVisible(AValue: Boolean);
358 begin
359   if FVisible = AValue then exit;
360   FVisible := AValue;
361   StyleChanged(Self);
362 end;
363 
364 procedure TChartElement.StyleChanged(Sender: TObject);
365 begin
366   if FOwner <> nil then
367     FOwner.StyleChanged(Sender);
368 end;
369 
370 { TSeriesPointer }
371 
372 procedure TSeriesPointer.Assign(Source: TPersistent);
373 begin
374   if Source is TSeriesPointer then
375     with TSeriesPointer(Source) do begin
376       Self.FBrush.Assign(Brush);
377       Self.FHorizSize := HorizSize;
378       Self.FOverrideColor := OverrideColor;
379       Self.FPen.Assign(Pen);
380       Self.FStyle := Style;
381       Self.FVertSize := VertSize;
382     end;
383   inherited Assign(Source);
384 end;
385 
386 constructor TSeriesPointer.Create(AOwner: TCustomChart);
387 begin
388   inherited Create(AOwner);
389 
390   InitHelper(FBrush, TBrush);
391   InitHelper(FPen, TChartPen);
392 
393   FHorizSize := DEF_POINTER_SIZE;
394   SetPropDefaults(Self, ['OverrideColor', 'Style']);
395   FVertSize := DEF_POINTER_SIZE;
396   FVisible := false;
397 end;
398 
399 destructor TSeriesPointer.Destroy;
400 begin
401   FreeAndNil(FBrush);
402   FreeAndNil(FPen);
403   inherited;
404 end;
405 
406 { Draws the pointer.
407   If ABrushAlreadySet is true then the method assumes that the brush already has
408   been set by the calling routine and does not change it any further (needed
409   for applying ChartStyle brush). }
410 procedure TSeriesPointer.Draw(ADrawer: IChartDrawer; ACenter: TPoint;
411   AColor: TColor; ABrushAlreadySet: Boolean = false);
412 begin
413   DrawSize(ADrawer,
414     ACenter,
415     Point(ADrawer.Scale(HorizSize), ADrawer.Scale(VertSize)),
416     AColor,
417     0,
418     ABrushAlreadySet
419   );
420 end;
421 
422 procedure TSeriesPointer.DrawSize(ADrawer: IChartDrawer;
423   ACenter, ASize: TPoint; AColor: TColor; AAngle: Double; ABrushAlreadySet: Boolean);
424 
PointByIndexnull425   function PointByIndex(AIndex: Char): TPoint; inline;
426   // 7--8--9
427   // 4  5  6
428   // 1--2--3
429   const
430     V: array ['1'..'9'] of -1..1 = (1, 1, 1, 0, 0, 0, -1, -1, -1);
431     H: array ['1'..'9'] of -1..1 = (-1, 0, 1, -1, 0, 1, -1, 0, 1);
432   begin
433     Result := ACenter + RotatePoint(
434       Point(H[AIndex] * ASize.X, V[AIndex] * ASize.Y), AAngle);
435   end;
436 
437   procedure DrawByString(const AStr: String);
438   var
439     pts: array of TPoint = nil;
440     i: Integer;
441     j: Integer = 0;
442     notClosed: Boolean;
443   begin
444     notClosed := (AStr[1] <> AStr[Length(AStr) - 1]);
445     SetLength(pts, Length(AStr));
446     for i := 1 to Length(AStr) do begin
447       if AStr[i] = ' ' then begin
448         if (Brush.Style = bsClear) or notClosed then
449           ADrawer.Polyline(pts, 0, j)
450         else
451           ADrawer.Polygon(pts, 0, j); // Winding?
452         j := 0;
453       end
454       else begin
455         pts[j] := PointByIndex(AStr[i]);
456         Inc(j);
457       end;
458     end;
459   end;
460 
461   procedure DrawPolygon(AStyle: TSeriesPointerStyle);
462   const
463     INNER = 0.5;
464   var
465     p: array of TPoint = nil;
466     pt: TDoublePoint;
467     phi, sinphi, cosphi, dPhi: Math.float;
468     i: Integer;
469   begin
470     case AStyle of
471       psHexagon  : begin dPhi := pi / 3; SetLength(p, 7); end;
472       psFullStar : begin dPhi := pi / 6; SetLength(p, 13); end;
473       else ;
474     end;
475     phi := 0;
476     for i := 0 to High(p) do
477     begin
478       SinCos(phi, sinphi, cosphi);
479       pt := DoublePoint(ASize.X * cosPhi, ASize.Y * sinPhi);
480       if odd(i) and (AStyle = psFullStar) then
481         pt := pt * INNER;
482       p[i] := ACenter + RoundPoint(RotatePoint(pt, AAngle));
483       phi += dPhi;
484     end;
485     ADrawer.Polygon(p, 0, Length(p));
486   end;
487 
488 
489 const
490   DRAW_STRINGS: array [TSeriesPointerStyle] of String = (
491     // psNone, psRectangle, psCircle, psCross, psDiagCross, psStar,
492     // psLowBracket, psHighBracket, psLeftBracket, psRightBracket, psDiamond,
493     // psTriangle, psLeftTriangle, psRightTriangle, psVertBar, psHorBar,
494     // psPoint, pwDownTriangle, psHexagon, psFullStar
495     '', '17931', '', '28 46', '19 73', '', //28 46 19 73',
496     '41236', '47896', '87412', '89632', '84268',
497     '1831', '8428', '8628', '82', '46',
498     '', '7927', '', '');
499 begin
500   if not ABrushAlreadySet then begin
501     ADrawer.Brush := Brush;
502     if (ocBrush in OverrideColor) and (AColor <> clTAColor) then
503       ADrawer.BrushColor := AColor;
504   end;
505 
506   ADrawer.Pen := Pen;
507   if (ocPen in OverrideColor) and (AColor <> clTAColor) then
508     ADrawer.SetPenParams(Pen.Style, AColor);
509 
510   case Style of
511     psNone    : ;
512     psPoint   : ADrawer.PutPixel(ACenter.X, ACenter.Y, Pen.Color);
513     psCircle  : ADrawer.Ellipse(
514                   ACenter.X - ASize.X, ACenter.Y - ASize.Y,
515                   ACenter.X + ASize.X + 1, ACenter.Y + ASize.Y + 1);
516     psHexagon : DrawPolygon(psHexagon);
517     psStar    : begin
518                   DrawByString(DRAW_STRINGS[psCross] + ' ');
519                   ASize := Point(ASize.X * 7 div 10, ASize.Y * 7 div 10);
520                   DrawByString(DRAW_STRINGS[psDiagCross] + ' ');
521                 end;
522     psFullStar: DrawPolygon(psFullStar);
523     else        DrawByString(DRAW_STRINGS[Style] + ' ');
524   end;
525 end;
526 
527 procedure TSeriesPointer.SetBrush(AValue: TBrush);
528 begin
529   FBrush.Assign(AValue);
530   StyleChanged(Self);
531 end;
532 
533 procedure TSeriesPointer.SetHorizSize(AValue: Integer);
534 begin
535   if FHorizSize = AValue then exit;
536   FHorizSize := AValue;
537   StyleChanged(Self);
538 end;
539 
540 procedure TSeriesPointer.SetOverrideColor(AValue: TOverrideColors);
541 begin
542   if FOverrideColor = AValue then exit;
543   FOverrideColor := AValue;
544   StyleChanged(Self);
545 end;
546 
547 procedure TSeriesPointer.SetPen(AValue: TChartPen);
548 begin
549   FPen.Assign(AValue);
550   StyleChanged(Self);
551 end;
552 
553 procedure TSeriesPointer.SetStyle(AValue: TSeriesPointerStyle);
554 begin
555   if FStyle = AValue then exit;
556   FStyle := AValue;
557   StyleChanged(Self);
558 end;
559 
560 procedure TSeriesPointer.SetVertSize(AValue: Integer);
561 begin
562   if FVertSize = AValue then exit;
563   FVertSize := AValue;
564   StyleChanged(Self);
565 end;
566 
567 
568 { TChartRange }
569 
570 procedure TChartRange.Assign(ASource: TPersistent);
571 begin
572   if ASource is TChartRange then
573     with TChartRange(ASource) do begin
574       Self.FBounds := FBounds;
575       Self.FUseBounds := FUseBounds;
576     end;
577   inherited Assign(ASource);
578 end;
579 
580 procedure TChartRange.CheckBoundsOrder;
581 begin
582   if UseMin and UseMax and (Min >= Max) then begin
583     UseMin := false;
584     UseMax := false;
585     raise EExtentError.Create('ChartRange: Min >= Max');
586   end;
587 end;
588 
GetBoundsnull589 function TChartRange.GetBounds(AIndex: Integer): Double;
590 begin
591   Result := FBounds[AIndex];
592 end;
593 
GetUseBoundsnull594 function TChartRange.GetUseBounds(AIndex: integer): Boolean;
595 begin
596   Result := FUseBounds[AIndex];
597 end;
598 
599 procedure TChartRange.Intersect(var AMin, AMax: Double);
600 begin
601   if UseMin and (Min > AMin) then
602     AMin := Min;
603   if UseMax and (Max < AMax)then
604     AMax := Max;
605 end;
606 
IsBoundsStorednull607 function TChartRange.IsBoundsStored(AIndex: Integer): Boolean;
608 begin
609   Result := not SameValue(FBounds[AIndex], 0);
610 end;
611 
612 procedure TChartRange.SetBounds(AIndex: Integer; const AValue: Double);
613 begin
614   FBounds[AIndex] := AValue;
615   StyleChanged(Self);
616 end;
617 
618 procedure TChartRange.SetUseBounds(AIndex: Integer; AValue: Boolean);
619 begin
620   FUseBounds[AIndex] := AValue;
621   StyleChanged(Self);
622 end;
623 
624 { TChartExtent }
625 
626 procedure TChartExtent.Assign(ASource: TPersistent);
627 begin
628   if ASource is TChartExtent then
629     with TChartExtent(ASource) do begin
630       Self.FExtent := FExtent;
631       Self.FUseBounds := FUseBounds;
632     end;
633   inherited Assign(ASource);
634 end;
635 
636 procedure TChartExtent.CheckBoundsOrder;
637 begin
638   if UseXMin and UseXMax and (XMin >= XMax) then begin
639     UseXMin := false;
640     UseXMax := false;
641     raise EExtentError.Create('ChartExtent: XMin >= XMax');
642   end;
643   if UseYMin and UseYMax and (YMin >= YMax) then begin
644     UseYMin := false;
645     UseYMax := false;
646     raise EExtentError.Create('ChartExtent: YMin >= YMax');
647   end;
648 end;
649 
650 procedure TChartExtent.FixTo(const ABounds: TDoubleRect);
651 begin
652   FExtent := ABounds;
653   FillChar(FUseBounds, SizeOf(FUseBounds), true);
654   StyleChanged(Self);
655 end;
656 
GetBoundsnull657 function TChartExtent.GetBounds(AIndex: Integer): Double;
658 begin
659   Result := FExtent.coords[AIndex];
660 end;
661 
GetUseBoundsnull662 function TChartExtent.GetUseBounds(AIndex: Integer): Boolean;
663 begin
664   Result := FUseBounds[AIndex];
665 end;
666 
IsBoundsStorednull667 function TChartExtent.IsBoundsStored(AIndex: Integer): Boolean;
668 begin
669   Result := not SameValue(FExtent.coords[AIndex], 0.0);
670 end;
671 
672 procedure TChartExtent.SetBounds(AIndex: Integer; const AValue: Double);
673 begin
674   FExtent.coords[AIndex] := AValue;
675   StyleChanged(Self);
676 end;
677 
678 procedure TChartExtent.SetUseBounds(AIndex: Integer; AValue: Boolean);
679 begin
680   FUseBounds[AIndex] := AValue;
681   StyleChanged(Self);
682 end;
683 
684 { TChartMargins }
685 
686 procedure TChartMargins.Assign(Source: TPersistent);
687 begin
688   if Source is TChartMargins then
689     Self.FData.FRect := TChartMargins(Source).FData.FRect;
690   inherited Assign(Source);
691 end;
692 
693 constructor TChartMargins.Create(AOwner: TCustomChart);
694 begin
695   inherited Create(AOwner);
696   SetPropDefaults(Self, ['Left', 'Top', 'Right', 'Bottom']);
697 end;
698 
699 procedure TChartMargins.ExpandRectScaled(
700   ADrawer: IChartDrawer; var ARect: TRect);
701 begin
702   ARect.TopLeft -= Point(ADrawer.Scale(Left), ADrawer.Scale(Top));
703   ARect.BottomRight += Point(ADrawer.Scale(Right), ADrawer.Scale(Bottom));
704 end;
705 
GetValuenull706 function TChartMargins.GetValue(AIndex: Integer): Integer;
707 begin
708   Result := FData.FCoords[AIndex];
709 end;
710 
711 procedure TChartMargins.SetValue(AIndex: Integer; AValue: TChartDistance);
712 begin
713   if FData.FCoords[AIndex] = AValue then exit;
714   FData.FCoords[AIndex] := AValue;
715   StyleChanged(Self);
716 end;
717 
718 { TChartArrow }
719 
720 procedure TChartArrow.Assign(ASource: TPersistent);
721 begin
722   if ASource is TChartArrow then
723     with TChartArrow(ASource) do begin
724       Self.FBaseLength := FBaseLength;
725       Self.FLength := FLength;
726       Self.FWidth := FWidth;
727     end;
728   inherited Assign(ASource);
729 end;
730 
731 constructor TChartArrow.Create(AOwner: TCustomChart);
732 begin
733   inherited Create(AOwner);
734   FLength := DEF_ARROW_LENGTH;
735   FVisible := false;
736   FWidth := DEF_ARROW_WIDTH;
737 end;
738 
739 procedure TChartArrow.Draw(
740   ADrawer: IChartDrawer; const AEndPos: TPoint; AAngle: Double;
741   APen: TFPCustomPen);
742 var
743   da: Double;
744   diag: Integer;
745   pt1, pt2, ptBase: TPoint;
746   sgn: Integer;
747 begin
748   if not Visible then exit;
749   da := ArcTan2(Width, Length);
750 
751   sgn := Math.IfThen(FInverted, -1, +1);
752   diag := -ADrawer.Scale(Round(Sqrt(Sqr(Length) + Sqr(Width))));
753   pt1 := AEndPos + RotatePointX(diag, AAngle - da)*sgn;
754   pt2 := AEndPos + RotatePointX(diag, AAngle + da)*sgn;
755   ADrawer.SetPenParams(psSolid, FPColorToTColor(APen.FPColor), APen.Width);
756   if BaseLength > 0 then begin
757     ptBase := AEndPos + RotatePointX(-ADrawer.Scale(BaseLength), AAngle)*sgn;
758     ADrawer.SetBrushParams(bsSolid, FPColorToChartColor(APen.FPColor));
759     ADrawer.Polygon([pt1, AEndPos, pt2, ptBase], 0, 4);
760   end
761   else
762     ADrawer.Polyline([pt1, AEndPos, pt2], 0, 3);
763 end;
764 
765 procedure TChartArrow.SetBaseLength(AValue: TChartDistance);
766 begin
767   if FBaseLength = AValue then exit;
768   FBaseLength := AValue;
769   StyleChanged(Self);
770 end;
771 
772 procedure TChartArrow.SetInverted(AValue: Boolean);
773 begin
774   if FInverted = AValue then exit;
775   FInverted := AValue;
776   StyleChanged(Self);
777 end;
778 
779 procedure TChartArrow.SetLength(AValue: TChartDistance);
780 begin
781   if FLength = AValue then exit;
782   FLength := AValue;
783   StyleChanged(Self);
784 end;
785 
786 procedure TChartArrow.SetWidth(AValue: TChartDistance);
787 begin
788   if FWidth = AValue then exit;
789   FWidth := AValue;
790   StyleChanged(Self);
791 end;
792 
793 { TChartShadow }
794 
795 procedure TChartShadow.Assign(ASource: TPersistent);
796 begin
797   if ASource is TChartShadow then
798     with TChartShadow(ASource) do begin
799       Self.FColor := Color;
800       Self.FOffset := FOffset;
801       Self.FTransparency := Transparency;
802     end;
803   inherited Assign(ASource);
804 end;
805 
806 constructor TChartShadow.Create(AOwner: TCustomChart);
807 begin
808   inherited Create(AOwner);
809   FColor := clBlack;
810   FOffset := Point(DEF_SHADOW_OFFSET, DEF_SHADOW_OFFSET);
811   FTransparency := DEF_SHADOW_TRANSPARENCY;
812 end;
813 
814 procedure TChartShadow.SetColor(AValue: TColor);
815 begin
816   if FColor = AValue then exit;
817   FColor := AValue;
818   StyleChanged(Self);
819 end;
820 
821 procedure TChartShadow.SetOffsetX(AValue: Integer);
822 begin
823   if FOffset.X = AValue then exit;
824   FOffset.X := AValue;
825   StyleChanged(Self);
826 end;
827 
828 procedure TChartShadow.SetOffsetY(AValue: Integer);
829 begin
830   if FOffset.Y = AValue then exit;
831   FOffset.Y := AValue;
832   StyleChanged(Self);
833 end;
834 
835 procedure TChartShadow.SetTransparency(AValue: TChartTransparency);
836 begin
837   if FTransparency = AValue then exit;
838   FTransparency := AValue;
839   StyleChanged(Self);
840 end;
841 
842 { TChartErrorBar }
843 
844 constructor TChartErrorBar.Create(AOwner: TCustomChart);
845 begin
846   inherited Create(AOwner);
847   FWidth := -1;     // -1 = same width as series pointer
848   FPen := TPen.Create;
849   FPen.OnChange := @StyleChanged;
850   FVisible := false;
851 end;
852 
853 destructor TChartErrorBar.Destroy;
854 begin
855   FPen.Free;
856   inherited Destroy;
857 end;
858 
859 procedure TChartErrorBar.Assign(ASource: TPersistent);
860 begin
861   if ASource is TChartErrorBar then begin
862     FPen.Assign(TChartErrorBar(ASource).Pen);
863     FWidth := TChartErrorBar(ASource).Width;
864   end;
865   inherited;
866 end;
867 
868 procedure TChartErrorBar.SetPen(const AValue: TPen);
869 begin
870   FPen.Assign(AValue);
871   StyleChanged(Self);
872 end;
873 
874 procedure TChartErrorBar.SetWidth(const AValue: Integer);
875 begin
876   if FWidth = AValue then exit;
877   FWidth := AValue;
878   StyleChanged(self);
879 end;
880 
881 end.
882 
883