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