50
49
FDepth: TChartDistance;
52
50
FZPosition: TChartDistance;
54
52
procedure AfterAdd; virtual; abstract;
55
53
procedure AfterDraw; virtual;
56
54
procedure BeforeDraw; virtual;
57
// Set series bounds in axis coordinates.
58
// Some or all bounds may be left unset, in which case they will be ignored.
59
procedure GetBounds(var ABounds: TDoubleRect); virtual; abstract;
60
55
procedure GetGraphBounds(var ABounds: TDoubleRect); virtual; abstract;
61
56
procedure GetLegendItemsBasic(AItems: TChartLegendItems); virtual; abstract;
62
57
function GetShowInLegend: Boolean; virtual; abstract;
63
58
procedure SetActive(AValue: Boolean); virtual; abstract;
64
59
procedure SetDepth(AValue: TChartDistance); virtual; abstract;
65
procedure SetTitle(const AValue: String); virtual; abstract;
60
procedure SetShowInLegend(AValue: Boolean); virtual; abstract;
66
61
procedure SetZPosition(AValue: TChartDistance); virtual; abstract;
67
procedure UpdateMargins(ACanvas: TCanvas; var AMargins: TRect); virtual;
62
procedure UpdateMargins(ADrawer: IChartDrawer; var AMargins: TRect); virtual;
68
63
procedure VisitSources(
69
64
AVisitor: TChartOnSourceVisitor; AAxis: TChartAxis; var AData); virtual;
72
67
function AxisToGraphX(AX: Double): Double; virtual;
73
68
function AxisToGraphY(AY: Double): Double; virtual;
74
69
function GraphToAxisX(AX: Double): Double; virtual;
75
70
function GraphToAxisY(AY: Double): Double; virtual;
73
procedure Assign(Source: TPersistent); override;
78
74
destructor Destroy; override;
81
procedure Draw(ACanvas: TCanvas); virtual; abstract;
77
procedure Draw(ADrawer: IChartDrawer); virtual; abstract;
82
78
function IsEmpty: Boolean; virtual; abstract;
83
procedure MovePoint(var AIndex: Integer; const ANewPos: TPoint); virtual;
79
procedure MovePoint(var AIndex: Integer; const ANewPos: TPoint); overload; inline;
80
procedure MovePoint(var AIndex: Integer; const ANewPos: TDoublePoint); overload; virtual;
85
82
property Active: Boolean read FActive write SetActive default true;
86
83
property Depth: TChartDistance read FDepth write SetDepth default 0;
87
84
property ParentChart: TChart read FChart;
88
property Title: String read FTitle write SetTitle;
89
85
property ZPosition: TChartDistance read FZPosition write SetZPosition default 0;
92
88
TSeriesClass = class of TBasicChartSeries;
94
TChartToolEvent = procedure (AChart: TChart; AX, AY: Integer) of object;
96
90
{ TBasicСhartTool }
98
92
TBasicChartTool = class(TIndexedComponent)
128
133
function Count: Integer;
134
function GetEnumerator: TBasicChartSeriesEnumerator;
130
136
property Items[AIndex: Integer]: TBasicChartSeries read GetItem; default;
131
property List: TFPList read FList;
137
property List: TIndexedComponentList read FList;
140
TChartAfterDrawEvent = procedure (
141
ASender: TChart; ACanvas: TCanvas; const ARect: TRect) of object;
142
TChartBeforeDrawEvent = procedure (
143
ASender: TChart; ACanvas: TCanvas; const ARect: TRect;
144
var ADoDefaultDrawing: Boolean) of object;
145
TChartEvent = procedure (ASender: TChart) of object;
146
TChartPaintEvent = procedure (
147
ASender: TChart; const ARect: TRect;
148
var ADoDefaultDrawing: Boolean) of object;
150
TChartRenderingParams = record
153
FLogicalExtent, FPrevLogicalExtent: TDoubleRect;
154
FScale, FOffset: TDoublePoint;
136
159
TChart = class(TCustomChart, ICoordTransformer)
137
private // Property fields
160
strict private // Property fields
138
161
FAllowZoom: Boolean;
162
FAntialiasingMode: TChartAntialiasingMode;
139
163
FAxisList: TChartAxisList;
140
164
FAxisVisible: Boolean;
141
165
FBackColor: TColor;
142
166
FDepth: TChartDistance;
143
167
FExpandPercentage: Integer;
144
168
FExtent: TChartExtent;
169
FExtentSizeLimit: TChartExtent;
145
170
FFoot: TChartTitle;
146
171
FFrame: TChartPen;
147
172
FGraphBrush: TBrush;
148
173
FLegend: TChartLegend;
149
174
FLogicalExtent: TDoubleRect;
150
175
FMargins: TChartMargins;
176
FMarginsExternal: TChartMargins;
177
FOnAfterDrawBackground: TChartAfterDrawEvent;
178
FOnAfterDrawBackWall: TChartAfterDrawEvent;
179
FOnBeforeDrawBackground: TChartBeforeDrawEvent;
180
FOnBeforeDrawBackWall: TChartBeforeDrawEvent;
181
FOnChartPaint: TChartPaintEvent;
151
182
FOnDrawReticule: TDrawReticuleEvent;
183
FProportional: Boolean;
152
184
FSeries: TChartSeriesList;
153
185
FTitle: TChartTitle;
154
186
FToolset: TBasicChartToolset;
188
function ClipRectWithoutFrame(AZPosition: TChartDistance): TRect;
157
190
FActiveToolIndex: Integer;
158
191
FBroadcaster: TBroadcaster;
159
192
FBuiltinToolset: TBasicChartToolset;
160
193
FClipRect: TRect;
161
194
FCurrentExtent: TDoubleRect;
195
FDisableRedrawingCounter: Integer;
196
FDrawer: IChartDrawer;
197
FExtentBroadcaster: TBroadcaster;
162
198
FIsZoomed: Boolean;
163
199
FOffset: TDoublePoint; // Coordinates transformation
164
FProportional: Boolean;
200
FOnAfterPaint: TChartEvent;
201
FOnExtentChanged: TChartEvent;
202
FPrevLogicalExtent: TDoubleRect;
165
203
FReticuleMode: TReticuleMode;
166
204
FReticulePos: TPoint;
167
205
FScale: TDoublePoint; // Coordinates transformation
169
207
procedure CalculateTransformationCoeffs(const AMargin: TRect);
170
procedure DrawReticule(ACanvas: TCanvas);
171
function GetAxis(AIndex: integer): TChartAxis; inline;
208
procedure DrawReticule(ADrawer: IChartDrawer);
209
procedure FindComponentClass(
210
AReader: TReader; const AClassName: String; var AClass: TComponentClass);
211
function GetAxis(AIndex: Integer): TChartAxis;
172
212
function GetChartHeight: Integer;
173
213
function GetChartWidth: Integer;
174
function GetMargins(ACanvas: TCanvas): TRect;
214
function GetMargins(ADrawer: IChartDrawer): TRect;
215
function GetRenderingParams: TChartRenderingParams;
175
216
function GetSeriesCount: Integer;
176
217
function GetToolset: TBasicChartToolset;
177
218
procedure HideReticule;
220
procedure SetAntialiasingMode(AValue: TChartAntialiasingMode);
179
221
procedure SetAxis(AIndex: Integer; AValue: TChartAxis);
180
222
procedure SetAxisList(AValue: TChartAxisList);
181
223
procedure SetAxisVisible(Value: Boolean);
182
procedure SetBackColor(const AValue: TColor);
224
procedure SetBackColor(AValue: TColor);
183
225
procedure SetDepth(AValue: TChartDistance);
184
226
procedure SetExpandPercentage(AValue: Integer);
185
procedure SetExtent(const AValue: TChartExtent);
227
procedure SetExtent(AValue: TChartExtent);
228
procedure SetExtentSizeLimit(AValue: TChartExtent);
186
229
procedure SetFoot(Value: TChartTitle);
187
230
procedure SetFrame(Value: TChartPen);
188
231
procedure SetGraphBrush(Value: TBrush);
189
232
procedure SetLegend(Value: TChartLegend);
190
233
procedure SetLogicalExtent(const AValue: TDoubleRect);
191
234
procedure SetMargins(AValue: TChartMargins);
235
procedure SetMarginsExternal(AValue: TChartMargins);
236
procedure SetOnAfterDrawBackground(AValue: TChartAfterDrawEvent);
237
procedure SetOnAfterDrawBackWall(AValue: TChartAfterDrawEvent);
238
procedure SetOnBeforeDrawBackground(AValue: TChartBeforeDrawEvent);
239
procedure SetOnBeforeDrawBackWall(AValue: TChartBeforeDrawEvent);
240
procedure SetOnChartPaint(AValue: TChartPaintEvent);
241
procedure SetOnDrawReticule(AValue: TDrawReticuleEvent);
192
242
procedure SetProportional(AValue: Boolean);
193
procedure SetReticuleMode(const AValue: TReticuleMode);
243
procedure SetRenderingParams(AValue: TChartRenderingParams);
244
procedure SetReticuleMode(AValue: TReticuleMode);
194
245
procedure SetReticulePos(const AValue: TPoint);
195
246
procedure SetTitle(Value: TChartTitle);
196
247
procedure SetToolset(AValue: TBasicChartToolset);
197
248
procedure VisitSources(
198
249
AVisitor: TChartOnSourceVisitor; AAxis: TChartAxis; var AData);
200
procedure Clean(ACanvas: TCanvas; ARect: TRect);
201
procedure DisplaySeries(ACanvas: TCanvas);
202
procedure DrawBackground(const ACanvas: TCanvas);
203
procedure DrawTitleFoot(ACanvas: TCanvas);
251
function DoMouseWheel(
252
AShift: TShiftState; AWheelDelta: Integer;
253
AMousePos: TPoint): Boolean; override;
204
254
procedure MouseDown(
205
255
Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
206
256
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
207
257
procedure MouseUp(
208
258
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override;
260
procedure Clear(ADrawer: IChartDrawer; const ARect: TRect);
261
procedure DisplaySeries(ADrawer: IChartDrawer);
262
procedure DrawBackWall(ADrawer: IChartDrawer);
263
procedure KeyDownAfterInterface(var AKey: Word; AShift: TShiftState); override;
264
procedure KeyUpAfterInterface(var AKey: Word; AShift: TShiftState); override;
210
266
procedure DoOnResize; override;
212
268
procedure Notification(
213
269
AComponent: TComponent; AOperation: TOperation); override;
214
procedure PrepareAxis(ACanvas: TCanvas);
215
procedure PrepareLegend(
216
ACanvas: TCanvas; out ALegendItems: TChartLegendItems;
217
var AClipRect: TRect; out ALegendRect: TRect);
270
procedure PrepareAxis(ADrawer: IChartDrawer);
271
function PrepareLegend(
272
ADrawer: IChartDrawer; var AClipRect: TRect): TChartLegendDrawingData;
273
procedure SetName(const AValue: TComponentName); override;
219
275
constructor Create(AOwner: TComponent); override;
220
276
destructor Destroy; override;
354
procedure RegisterSeriesClass(ASeriesClass: TSeriesClass; const ACaption: string);
441
procedure RegisterSeriesClass(
442
ASeriesClass: TSeriesClass; const ACaption: String);
356
444
if SeriesClassRegistry.IndexOfObject(TObject(ASeriesClass)) < 0 then
357
445
SeriesClassRegistry.AddObject(ACaption, TObject(ASeriesClass));
448
procedure WriteComponentToStream(AStream: TStream; AComponent: TComponent);
451
destroyDriver: Boolean = false;
453
writer := CreateLRSWriter(AStream, destroyDriver);
455
writer.Root := AComponent.Owner;
456
writer.WriteComponent(AComponent);
458
if destroyDriver then
464
{ TBasicChartSeriesEnumerator }
466
function TBasicChartSeriesEnumerator.GetCurrent: TBasicChartSeries;
468
Result := TBasicChartSeries(inherited GetCurrent);
473
procedure TChart.AddSeries(ASeries: TBasicChartSeries);
475
if ASeries.FChart = Self then exit;
476
if ASeries.FChart <> nil then
477
ASeries.FChart.DeleteSeries(ASeries);
479
Series.FList.Add(ASeries);
480
ASeries.FChart := Self;
482
StyleChanged(ASeries);
485
procedure TChart.CalculateTransformationCoeffs(const AMargin: TRect);
487
rX, rY: TAxisCoeffHelper;
490
BottomAxis, FClipRect.Left, FClipRect.Right, AMargin.Left, -AMargin.Right,
491
@FCurrentExtent.a.X, @FCurrentExtent.b.X);
493
LeftAxis, FClipRect.Bottom, FClipRect.Top, -AMargin.Bottom, AMargin.Top,
494
@FCurrentExtent.a.Y, @FCurrentExtent.b.Y);
495
FScale.X := rX.CalcScale(1);
496
FScale.Y := rY.CalcScale(-1);
497
if Proportional then begin
498
if Abs(FScale.X) > Abs(FScale.Y) then
499
FScale.X := Abs(FScale.Y) * Sign(FScale.X)
501
FScale.Y := Abs(FScale.X) * Sign(FScale.Y);
503
FOffset.X := rX.CalcOffset(FScale.X);
504
FOffset.Y := rY.CalcOffset(FScale.Y);
505
rX.UpdateMinMax(@XImageToGraph);
506
rY.UpdateMinMax(@YImageToGraph);
509
procedure TChart.Clear(ADrawer: IChartDrawer; const ARect: TRect);
511
defaultDrawing: Boolean = true;
512
ic: IChartTCanvasDrawer;
514
ADrawer.PrepareSimplePen(Color);
515
ADrawer.SetBrushParams(bsSolid, Color);
516
if Supports(ADrawer, IChartTCanvasDrawer, ic) and Assigned(OnBeforeDrawBackground) then
517
OnBeforeDrawBackground(Self, ic.Canvas, ARect, defaultDrawing);
518
if defaultDrawing then
519
ADrawer.Rectangle(ARect);
520
if Supports(ADrawer, IChartTCanvasDrawer, ic) and Assigned(OnAfterDrawBackground) then
521
OnAfterDrawBackground(Self, ic.Canvas, ARect);
524
procedure TChart.ClearSeries;
530
function TChart.ClipRectWithoutFrame(AZPosition: TChartDistance): TRect;
533
if (AZPosition > 0) or not Frame.Visible or (Frame.Style = psClear) then exit;
534
Result.Left += (Frame.Width + 1) div 2;
535
Result.Top += (Frame.Width + 1) div 2;
536
Result.Bottom -= Frame.Width div 2;
537
Result.Right -= Frame.Width div 2;
540
function TChart.Clone: TChart;
543
cloned: TComponent = nil;
545
ms := TMemoryStream.Create;
547
WriteComponentToStream(ms, Self);
548
ms.Seek(0, soBeginning);
549
ReadComponentFromBinaryStream(
550
ms, cloned, @FindComponentClass, Owner, Parent, Owner);
551
Result := cloned as TChart;
557
procedure TChart.CopyToClipboardBitmap;
559
with SaveToImage(TBitmap) do
561
SaveToClipboardFormat(RegisterClipboardFormat(MimeType));
362
567
constructor TChart.Create(AOwner: TComponent);
364
569
DEFAULT_CHART_WIDTH = 300;
424
649
FreeAndNil(FAxisList);
425
650
FreeAndNil(FFrame);
426
651
FreeAndNil(FExtent);
652
FreeAndNil(FExtentSizeLimit);
427
653
FreeAndNil(FMargins);
654
FreeAndNil(FMarginsExternal);
428
655
FreeAndNil(FBuiltinToolset);
429
656
FreeAndNil(FBroadcaster);
657
FreeAndNil(FExtentBroadcaster);
431
659
DrawData.DeleteByChart(Self);
436
procedure TChart.DoOnResize;
439
// FIXME: GTK does not invalidate the control on resizing, do it manually
444
procedure TChart.EraseBackground(DC: HDC);
446
// do not erase, since we will paint over it anyway
450
function TChart.GetAxis(AIndex: integer): TChartAxis;
452
Result := FAxisList.GetAxis(AIndex);
455
procedure TChart.StyleChanged(Sender: TObject);
457
if Sender is TChartExtent then
460
Broadcaster.Broadcast(Sender);
463
procedure TChart.Paint;
465
PaintOnCanvas(Canvas, GetClientRect);
468
procedure TChart.PaintOnCanvas(ACanvas: TCanvas; ARect: TRect);
471
legendItems: TChartLegendItems = nil;
474
Clean(ACanvas, ARect);
477
InflateRect(FClipRect, -2, -2);
479
for i := 0 to AxisList.Count - 1 do
481
if Transformations <> nil then
482
Transformations.SetChart(Self);
483
for i := 0 to SeriesCount - 1 do
484
Series[i].BeforeDraw;
486
if not FIsZoomed then
487
FLogicalExtent := GetFullExtent;
488
FCurrentExtent := FLogicalExtent;
489
DrawTitleFoot(ACanvas);
490
if Legend.Visible then
491
PrepareLegend(ACanvas, legendItems, FClipRect, legendRect);
493
PrepareAxis(ACanvas);
494
DrawBackground(ACanvas);
495
DisplaySeries(ACanvas);
496
if Legend.Visible then
497
Legend.Draw(ACanvas, legendItems, legendRect);
501
DrawReticule(ACanvas);
503
for i := 0 to SeriesCount - 1 do
507
procedure TChart.PrepareLegend(
508
ACanvas: TCanvas; out ALegendItems: TChartLegendItems;
509
var AClipRect: TRect; out ALegendRect: TRect);
513
ALegendItems := TChartLegendItems.Create;
515
for i := 0 to SeriesCount - 1 do
517
if Active and GetShowInLegend then
518
GetLegendItemsBasic(ALegendItems);
519
ALegendRect := Legend.Prepare(ACanvas, ALegendItems, AClipRect);
521
FreeAndNil(ALegendItems);
526
procedure TChart.DrawBackground(const ACanvas: TCanvas);
528
with ACanvas do begin
529
if FFrame.Visible then
532
Pen.Style := psClear;
533
Brush.Color := BackColor;
535
Rectangle(Left, Top, Right + 1, Bottom + 1);
541
ACanvas.Line(Left, Bottom, Left - Depth, Bottom + Depth);
544
procedure TChart.HideReticule;
546
// Hide reticule - - it will be drawn again in the next MouseMove.
547
FReticulePos := Point( - 1, - 1);
550
procedure TChart.CalculateTransformationCoeffs(const AMargin: TRect);
552
rX, rY: TAxisCoeffHelper;
555
BottomAxis, FClipRect.Left, FClipRect.Right, AMargin.Left, -AMargin.Right,
556
@FCurrentExtent.a.X, @FCurrentExtent.b.X);
558
LeftAxis, FClipRect.Bottom, FClipRect.Top, -AMargin.Bottom, AMargin.Top,
559
@FCurrentExtent.a.Y, @FCurrentExtent.b.Y);
560
FScale.X := rX.CalcScale(1);
561
FScale.Y := rY.CalcScale(-1);
562
if Proportional then begin
563
if Abs(FScale.X) > Abs(FScale.Y) then
564
FScale.X := Abs(FScale.Y) * Sign(FScale.X)
566
FScale.Y := Abs(FScale.X) * Sign(FScale.Y);
568
FOffset.X := rX.CalcOffset(FScale.X);
569
FOffset.Y := rY.CalcOffset(FScale.Y);
570
rX.UpdateMinMax(@XImageToGraph);
571
rY.UpdateMinMax(@YImageToGraph);
574
procedure TChart.Clean(ACanvas: TCanvas; ARect: TRect);
576
PrepareSimplePen(ACanvas, Color);
577
ACanvas.Brush.Color := Color;
578
ACanvas.Brush.Style := bsSolid;
579
ACanvas.Rectangle(ARect);
582
procedure TChart.ClearSeries;
588
procedure TChart.DrawTitleFoot(ACanvas: TCanvas);
590
function AlignedTextPos(AAlign: TAlignment; const AText: String): TSize;
592
Result := ACanvas.TextExtent(AText);
595
Result.cx := FClipRect.Left;
597
Result.cx := (FClipRect.Left + FClipRect.Right - Result.cx) div 2;
599
Result.cx := FClipRect.Right - Result.cx;
606
pbf: TPenBrushFontRecall;
608
pbf := TPenBrushFontRecall.Create(ACanvas, [pbfBrush, pbfFont]);
611
if Visible and (Text.Count > 0) then begin
612
ACanvas.Brush.Assign(Brush);
613
ACanvas.Font.Assign(Font);
614
for i := 0 to Text.Count - 1 do begin
615
sz := AlignedTextPos(Alignment, Text[i]);
616
ACanvas.TextOut(sz.cx, FClipRect.Top, Text[i]);
617
FClipRect.Top += sz.cy;
619
FClipRect.Top += Margin;
622
if Visible and (Text.Count > 0) then begin
623
ACanvas.Brush.Assign(Brush);
624
ACanvas.Font.Assign(Font);
625
for i := Text.Count - 1 downto 0 do begin
626
sz := AlignedTextPos(Alignment, Text[i]);
627
FClipRect.Bottom -= sz.cy;
628
ACanvas.TextOut(sz.cx, FClipRect.Bottom, Text[i]);
630
FClipRect.Bottom -= Margin;
637
procedure TChart.PrepareAxis(ACanvas: TCanvas);
639
axisMargin: TChartAxisMargins = (0, 0, 0, 0);
640
a: TChartAxisAlignment;
642
if not AxisVisible then begin
643
FClipRect.Left += Depth;
644
FClipRect.Bottom -= Depth;
645
CalculateTransformationCoeffs(GetMargins(ACanvas));
649
AxisList.PrepareGroups;
650
AxisList.Measure(ACanvas, CurrentExtent, true, axisMargin);
651
axisMargin[calLeft] := Max(axisMargin[calLeft], Depth);
652
axisMargin[calBottom] := Max(axisMargin[calBottom], Depth);
653
for a := Low(a) to High(a) do
654
SideByAlignment(FClipRect, a, -axisMargin[a]);
656
CalculateTransformationCoeffs(GetMargins(ACanvas));
657
AxisList.Measure(ACanvas, CurrentExtent, false, axisMargin);
658
AxisList.Prepare(FClipRect);
661
procedure TChart.DrawLegendOn(ACanvas: TCanvas; var ARect: TRect);
663
legendItems: TChartLegendItems = nil;
667
PrepareLegend(ACanvas, legendItems, ARect, legendRect);
668
Legend.Draw(ACanvas, legendItems, legendRect);
674
procedure TChart.DrawLineHoriz(ACanvas: TCanvas; AY: Integer);
676
if (FClipRect.Top < AY) and (AY < FClipRect.Bottom) then
677
ACanvas.Line(FClipRect.Left, AY, FClipRect.Right, AY);
680
procedure TChart.DrawLineVert(ACanvas: TCanvas; AX: Integer);
682
if (FClipRect.Left < AX) and (AX < FClipRect.Right) then
683
ACanvas.Line(AX, FClipRect.Top, AX, FClipRect.Bottom);
686
procedure TChart.SetReticuleMode(const AValue: TReticuleMode);
688
if FReticuleMode = AValue then exit;
689
FReticuleMode := AValue;
693
procedure TChart.SetReticulePos(const AValue: TPoint);
695
if FReticulePos = AValue then exit;
696
DrawReticule(Canvas);
697
FReticulePos := AValue;
698
DrawReticule(Canvas);
701
procedure TChart.SetTitle(Value: TChartTitle);
703
FTitle.Assign(Value);
707
procedure TChart.SetToolset(AValue: TBasicChartToolset);
709
if FToolset = AValue then exit;
710
if FToolset <> nil then
711
RemoveFreeNotification(FToolset);
713
if FToolset <> nil then
714
FreeNotification(FToolset);
715
FActiveToolIndex := -1;
718
procedure TChart.SetFoot(Value: TChartTitle);
725
function TChart.GetMargins(ACanvas: TCanvas): TRect;
729
Result := FMargins.Data;
730
for i := 0 to SeriesCount - 1 do
731
if Series[i].Active then
732
Series[i].UpdateMargins(ACanvas, Result);
735
procedure TChart.SetGraphBrush(Value: TBrush);
737
FGraphBrush.Assign(Value);
740
procedure TChart.AddSeries(ASeries: TBasicChartSeries);
742
if ASeries.FChart = Self then exit;
743
if ASeries.FChart <> nil then
744
ASeries.FChart.DeleteSeries(ASeries);
745
DrawReticule(Canvas);
746
Series.FList.Add(ASeries);
747
ASeries.FChart := Self;
752
procedure TChart.DeleteSeries(ASeries: TBasicChartSeries);
756
i := FSeries.FList.IndexOf(ASeries);
758
FSeries.FList.Delete(i);
759
ASeries.FChart := nil;
763
function TChart.XGraphToImage(AX: Double): Integer;
765
Result := RoundChecked(FScale.X * AX + FOffset.X);
768
function TChart.YGraphToImage(AY: Double): Integer;
770
Result := RoundChecked(FScale.Y * AY + FOffset.Y);
773
function TChart.GraphToImage(const AGraphPoint: TDoublePoint): TPoint;
775
Result := Point(XGraphToImage(AGraphPoint.X), YGraphToImage(AGraphPoint.Y));
778
function TChart.XImageToGraph(AX: Integer): Double;
780
Result := (AX - FOffset.X) / FScale.X;
783
function TChart.YImageToGraph(AY: Integer): Double;
785
Result := (AY - FOffset.Y) / FScale.Y;
788
function TChart.ImageToGraph(const APoint: TPoint): TDoublePoint;
790
Result.X := XImageToGraph(APoint.X);
791
Result.Y := YImageToGraph(APoint.Y);
794
function TChart.IsPointInViewPort(const AP: TDoublePoint): Boolean;
797
InRange(AP.X, XGraphMin, XGraphMax) and InRange(AP.Y, YGraphMin, YGraphMax);
800
procedure TChart.SaveToBitmapFile(const AFileName: String);
802
SaveToFile(TBitmap, AFileName);
805
procedure TChart.SaveToFile(AClass: TRasterImageClass; const AFileName: String);
807
with SaveToImage(AClass) do
809
SaveToFile(AFileName);
815
function TChart.SaveToImage(AClass: TRasterImageClass): TRasterImage;
817
Result := AClass.Create;
819
Result.Width := Width;
820
Result.Height := Height;
821
PaintOnCanvas(Result.Canvas, Rect(0, 0, Width, Height));
828
procedure TChart.SetAxis(AIndex: Integer; AValue: TChartAxis);
830
FAxisList.SetAxis(AIndex, AValue);
834
procedure TChart.SetAxisList(AValue: TChartAxisList);
836
FAxisList.Assign(AValue);
840
procedure TChart.CopyToClipboardBitmap;
842
with SaveToImage(TBitmap) do
844
SaveToClipboardFormat(RegisterClipboardFormat(MimeType));
850
procedure TChart.DrawOnCanvas(Rect: TRect; ACanvas: TCanvas);
852
PaintOnCanvas(ACanvas, Rect);
855
procedure TChart.DisplaySeries(ACanvas: TCanvas);
663
procedure TChart.DisableRedrawing;
665
FDisableRedrawingCounter += 1;
668
procedure TChart.DisplaySeries(ADrawer: IChartDrawer);
857
670
procedure OffsetDrawArea(AZPos, ADepth: Integer);
903
715
if AxisVisible then
904
AxisList.Draw(ACanvas, CurrentExtent, Self, MaxInt, d, axisIndex);
907
procedure TChart.DrawReticule(ACanvas: TCanvas);
909
PrepareXorPen(ACanvas);
716
AxisList.Draw(MaxInt, axisIndex);
719
function TChart.DoMouseWheel(
720
AShift: TShiftState; AWheelDelta: Integer; AMousePos: TPoint): Boolean;
722
EV: array [Boolean] of TChartToolEventId = (
723
evidMouseWheelDown, evidMouseWheelUp);
726
GetToolset.Dispatch(Self, EV[AWheelDelta > 0], AShift, AMousePos) or
727
inherited DoMouseWheel(AShift, AWheelDelta, AMousePos);
731
procedure TChart.DoOnResize;
734
// FIXME: GTK does not invalidate the control on resizing, do it manually
739
procedure TChart.Draw(ADrawer: IChartDrawer; const ARect: TRect);
741
ldd: TChartLegendDrawingData;
742
s: TBasicChartSeries;
746
ADrawer.DrawingBegin(ARect);
747
ADrawer.SetAntialiasingMode(AntialiasingMode);
748
Clear(ADrawer, ARect);
751
with MarginsExternal do begin
752
FClipRect.Left += Left;
753
FClipRect.Top += Top;
754
FClipRect.Right -= Right;
755
FClipRect.Bottom -= Bottom;
758
with ClipRect do begin;
759
FTitle.Draw(ADrawer, 1, Left, Right, Top);
760
FFoot.Draw(ADrawer, -1, Left, Right, Bottom);
764
if Legend.Visible then
765
ldd := PrepareLegend(ADrawer, FClipRect);
767
PrepareAxis(ADrawer);
768
DrawBackWall(ADrawer);
769
DisplaySeries(ADrawer);
770
if Legend.Visible then
775
DrawReticule(ADrawer);
776
GetToolset.Draw(Self, ADrawer);
782
if FPrevLogicalExtent <> FLogicalExtent then begin
783
FExtentBroadcaster.Broadcast(Self);
784
if Assigned(OnExtentChanged) then
785
OnExtentChanged(Self);
786
FPrevLogicalExtent := FLogicalExtent;
790
procedure TChart.DrawBackWall(ADrawer: IChartDrawer);
792
defaultDrawing: Boolean = true;
793
ic: IChartTCanvasDrawer;
795
if Supports(ADrawer, IChartTCanvasDrawer, ic) and Assigned(OnBeforeDrawBackWall) then
796
OnBeforeDrawBackWall(Self, ic.Canvas, FClipRect, defaultDrawing);
797
if defaultDrawing then
798
with ADrawer do begin
799
if FFrame.Visible then
802
SetPenParams(psClear, clTAColor);
803
SetBrushParams(bsSolid, BackColor);
805
Rectangle(Left, Top, Right + 1, Bottom + 1);
807
if Supports(ADrawer, IChartTCanvasDrawer, ic) and Assigned(OnAfterDrawBackWall) then
808
OnAfterDrawBackWall(Self, ic.Canvas, FClipRect);
811
if (Depth > 0) and FFrame.Visible then begin
812
ADrawer.Pen := FFrame;
814
ADrawer.Line(Left, Bottom, Left - Depth, Bottom + Depth);
818
procedure TChart.DrawLegendOn(ACanvas: TCanvas; var ARect: TRect);
820
ldd: TChartLegendDrawingData;
822
ldd := PrepareLegend(TCanvasDrawer.Create(ACanvas), ARect);
830
procedure TChart.DrawLineHoriz(ADrawer: IChartDrawer; AY: Integer);
832
if (FClipRect.Top < AY) and (AY < FClipRect.Bottom) then
833
ADrawer.Line(FClipRect.Left, AY, FClipRect.Right, AY);
836
procedure TChart.DrawLineVert(ADrawer: IChartDrawer; AX: Integer);
838
if (FClipRect.Left < AX) and (AX < FClipRect.Right) then
839
ADrawer.Line(AX, FClipRect.Top, AX, FClipRect.Bottom);
842
procedure TChart.DrawOnCanvas(Rect: TRect; ACanvas: TCanvas);
844
PaintOnCanvas(ACanvas, Rect);
847
procedure TChart.DrawReticule(ADrawer: IChartDrawer);
849
ic: IChartTCanvasDrawer;
851
if not Supports(ADrawer, IChartTCanvasDrawer, ic) then exit;
852
PrepareXorPen(ic.Canvas);
910
853
if ReticuleMode in [rmVertical, rmCross] then
911
DrawLineVert(ACanvas, FReticulePos.X);
854
DrawLineVert(ADrawer, FReticulePos.X);
912
855
if ReticuleMode in [rmHorizontal, rmCross] then
913
DrawLineHoriz(ACanvas, FReticulePos.Y);
916
procedure TChart.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
919
PtInRect(FClipRect, Point(X, Y)) and
920
GetToolset.Dispatch(Self, evidMouseDown, Shift, Point(X, Y))
926
procedure TChart.MouseMove(Shift: TShiftState; X, Y: Integer);
928
if GetToolset.Dispatch(Self, evidMouseMove, Shift, Point(X, Y)) then exit;
932
procedure TChart.MouseUp(
933
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer);
935
MOUSE_BUTTON_TO_SHIFT: array [TMouseButton] of TShiftStateEnum = (
936
ssLeft, ssRight, ssMiddle, ssExtra1, ssExtra2);
938
// To find a tool, toolset must see the shift state with the button still down.
939
Include(AShift, MOUSE_BUTTON_TO_SHIFT[AButton]);
940
if GetToolset.Dispatch(Self, evidMouseUp, AShift, Point(AX, AY)) then exit;
944
procedure TChart.Notification(AComponent: TComponent; AOperation: TOperation);
946
if (AOperation = opRemove) and (AComponent = Toolset) then
948
inherited Notification(AComponent, AOperation);
951
procedure TChart.SetLegend(Value: TChartLegend);
953
FLegend.Assign(Value);
957
procedure TChart.SetLogicalExtent(const AValue: TDoubleRect);
960
FLogicalExtent := AValue;
965
procedure TChart.SetMargins(AValue: TChartMargins);
967
FMargins.Assign(AValue);
971
procedure TChart.SetProportional(AValue: Boolean);
973
if FProportional = AValue then exit;
974
FProportional := AValue;
978
procedure TChart.SetChildOrder(Child: TComponent; Order: Integer);
856
DrawLineHoriz(ADrawer, FReticulePos.Y);
859
procedure TChart.EnableRedrawing;
861
FDisableRedrawingCounter -= 1;
864
procedure TChart.EraseBackground(DC: HDC);
866
// do not erase, since we will paint over it anyway
870
procedure TChart.FindComponentClass(
871
AReader: TReader; const AClassName: String; var AClass: TComponentClass);
982
i := Series.FList.IndexOf(Child);
984
Series.FList.Move(i, Order);
987
procedure TChart.SetDepth(AValue: TChartDistance);
989
if FDepth = AValue then exit;
994
procedure TChart.SetExpandPercentage(AValue: Integer);
996
if FExpandPercentage = AValue then exit;
997
FExpandPercentage := AValue;
1001
procedure TChart.SetExtent(const AValue: TChartExtent);
1003
FExtent.Assign(AValue);
1007
procedure TChart.SetFrame(Value: TChartPen);
1009
FFrame.Assign(Value);
1013
procedure TChart.SetAxisVisible(Value: Boolean);
1015
FAxisVisible := Value;
1019
procedure TChart.SetBackColor(const AValue: TColor);
1021
FBackColor:= AValue;
876
if AClassName = ClassName then begin
880
for i := 0 to SeriesClassRegistry.Count - 1 do begin
881
AClass := TSeriesClass(SeriesClassRegistry.Objects[i]);
882
if AClass.ClassNameIs(AClassName) then exit;
887
function TChart.GetAxis(AIndex: Integer): TChartAxis;
889
Result := FAxisList.GetAxis(AIndex);
1025
892
function TChart.GetChartHeight: Integer;
1121
1045
Result := FBuiltinToolset;
1048
function TChart.GraphToImage(const AGraphPoint: TDoublePoint): TPoint;
1050
Result := Point(XGraphToImage(AGraphPoint.X), YGraphToImage(AGraphPoint.Y));
1053
procedure TChart.HideReticule;
1055
// Hide reticule - - it will be drawn again in the next MouseMove.
1056
FReticulePos := Point( - 1, - 1);
1059
function TChart.ImageToGraph(const APoint: TPoint): TDoublePoint;
1061
Result.X := XImageToGraph(APoint.X);
1062
Result.Y := YImageToGraph(APoint.Y);
1065
function TChart.IsPointInViewPort(const AP: TDoublePoint): Boolean;
1069
InRange(AP.X, XGraphMin, XGraphMax) and InRange(AP.Y, YGraphMin, YGraphMax);
1072
procedure TChart.KeyDownAfterInterface(var AKey: Word; AShift: TShiftState);
1076
p := ScreenToClient(Mouse.CursorPos);
1077
if GetToolset.Dispatch(Self, evidKeyDown, AShift, p) then exit;
1081
procedure TChart.KeyUpAfterInterface(var AKey: Word; AShift: TShiftState);
1085
p := ScreenToClient(Mouse.CursorPos);
1086
// To find a tool, toolset must see the shift state with the key still down.
1088
VK_CONTROL: AShift += [ssCtrl];
1089
VK_MENU: AShift += [ssAlt];
1090
VK_SHIFT: AShift += [ssShift];
1092
if GetToolset.Dispatch(Self, evidKeyUp, AShift, p) then exit;
1096
procedure TChart.MouseDown(
1097
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1100
PtInRect(FClipRect, Point(X, Y)) and
1101
GetToolset.Dispatch(Self, evidMouseDown, Shift, Point(X, Y))
1107
procedure TChart.MouseMove(Shift: TShiftState; X, Y: Integer);
1109
if GetToolset.Dispatch(Self, evidMouseMove, Shift, Point(X, Y)) then exit;
1113
procedure TChart.MouseUp(
1114
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer);
1116
MOUSE_BUTTON_TO_SHIFT: array [TMouseButton] of TShiftStateEnum = (
1117
ssLeft, ssRight, ssMiddle, ssExtra1, ssExtra2);
1119
// To find a tool, toolset must see the shift state with the button still down.
1120
Include(AShift, MOUSE_BUTTON_TO_SHIFT[AButton]);
1121
if GetToolset.Dispatch(Self, evidMouseUp, AShift, Point(AX, AY)) then exit;
1125
procedure TChart.Notification(AComponent: TComponent; AOperation: TOperation);
1127
if (AOperation = opRemove) and (AComponent = Toolset) then
1129
inherited Notification(AComponent, AOperation);
1132
procedure TChart.Paint;
1134
defaultDrawing: Boolean = true;
1137
if Assigned(OnChartPaint) then
1138
OnChartPaint(Self, GetClientRect, defaultDrawing);
1140
if defaultDrawing then
1141
Draw(FDrawer, GetClientRect);
1142
if Assigned(OnAfterPaint) then
1146
procedure TChart.PaintOnAuxCanvas(ACanvas: TCanvas; ARect: TRect);
1148
rp: TChartRenderingParams;
1150
rp := RenderingParams;
1151
ExtentBroadcaster.Locked := true;
1154
PaintOnCanvas(ACanvas, ARect);
1156
RenderingParams := rp;
1157
ExtentBroadcaster.Locked := false;
1161
procedure TChart.PaintOnCanvas(ACanvas: TCanvas; ARect: TRect);
1163
Draw(TCanvasDrawer.Create(ACanvas), ARect);
1166
procedure TChart.PrepareAxis(ADrawer: IChartDrawer);
1168
axisMargin: TChartAxisMargins;
1169
aa: TChartAxisAlignment;
1172
prevExt: TDoubleRect;
1175
if not AxisVisible then begin
1176
FClipRect.Left += Depth;
1177
FClipRect.Bottom -= Depth;
1178
CalculateTransformationCoeffs(GetMargins(ADrawer));
1182
AxisList.PrepareGroups;
1183
for axis in AxisList do
1184
axis.PrepareHelper(ADrawer, Self, @FClipRect, Depth);
1186
// There is a cyclic dependency: extent -> visible marks -> margins.
1187
// We recalculate them iteratively hoping that the process converges.
1188
CalculateTransformationCoeffs(Rect(0, 0, 0, 0));
1190
for tries := 1 to 10 do begin
1191
axisMargin := AxisList.Measure(CurrentExtent, Depth);
1192
axisMargin[calLeft] := Max(axisMargin[calLeft], Depth);
1193
axisMargin[calBottom] := Max(axisMargin[calBottom], Depth);
1195
for aa := Low(aa) to High(aa) do
1196
SideByAlignment(FClipRect, aa, -axisMargin[aa]);
1197
prevExt := FCurrentExtent;
1198
FCurrentExtent := FLogicalExtent;
1199
CalculateTransformationCoeffs(GetMargins(ADrawer));
1200
if prevExt = FCurrentExtent then break;
1201
prevExt := FCurrentExtent;
1204
AxisList.Prepare(FClipRect);
1207
procedure TChart.Prepare;
1210
s: TBasicChartSeries;
1212
for a in AxisList do
1213
if a.Transformations <> nil then
1214
a.Transformations.SetChart(Self);
1218
if not FIsZoomed then
1219
FLogicalExtent := GetFullExtent;
1220
FCurrentExtent := FLogicalExtent;
1223
function TChart.PrepareLegend(
1224
ADrawer: IChartDrawer; var AClipRect: TRect): TChartLegendDrawingData;
1226
Result.FDrawer := ADrawer;
1227
Result.FItems := GetLegendItems;
1229
Legend.SortItemsByOrder(Result.FItems);
1230
Legend.AddGroups(Result.FItems);
1231
Legend.Prepare(Result, AClipRect);
1233
FreeAndNil(Result.FItems);
1238
procedure TChart.SaveToBitmapFile(const AFileName: String);
1240
SaveToFile(TBitmap, AFileName);
1243
procedure TChart.SaveToFile(AClass: TRasterImageClass; AFileName: String);
1245
with SaveToImage(AClass) do
1247
SaveToFile(AFileName);
1253
function TChart.SaveToImage(AClass: TRasterImageClass): TRasterImage;
1255
Result := AClass.Create;
1257
Result.Width := Width;
1258
Result.Height := Height;
1259
PaintOnCanvas(Result.Canvas, Rect(0, 0, Width, Height));
1266
procedure TChart.SetAntialiasingMode(AValue: TChartAntialiasingMode);
1268
if FAntialiasingMode = AValue then exit;
1269
FAntialiasingMode := AValue;
1273
procedure TChart.SetAxis(AIndex: Integer; AValue: TChartAxis);
1275
FAxisList.SetAxis(AIndex, AValue);
1276
StyleChanged(AValue);
1279
procedure TChart.SetAxisList(AValue: TChartAxisList);
1281
FAxisList.Assign(AValue);
1285
procedure TChart.SetAxisVisible(Value: Boolean);
1287
FAxisVisible := Value;
1291
procedure TChart.SetBackColor(AValue: TColor);
1293
FBackColor:= AValue;
1297
procedure TChart.SetChildOrder(Child: TComponent; Order: Integer);
1301
i := Series.FList.IndexOf(Child);
1303
Series.FList.Move(i, Order);
1306
procedure TChart.SetDepth(AValue: TChartDistance);
1308
if FDepth = AValue then exit;
1313
procedure TChart.SetExpandPercentage(AValue: Integer);
1315
if FExpandPercentage = AValue then exit;
1316
FExpandPercentage := AValue;
1320
procedure TChart.SetExtent(AValue: TChartExtent);
1322
FExtent.Assign(AValue);
1326
procedure TChart.SetExtentSizeLimit(AValue: TChartExtent);
1328
if FExtentSizeLimit = AValue then exit;
1329
FExtentSizeLimit.Assign(AValue);
1333
procedure TChart.SetFoot(Value: TChartTitle);
1335
FFoot.Assign(Value);
1339
procedure TChart.SetFrame(Value: TChartPen);
1341
FFrame.Assign(Value);
1345
procedure TChart.SetGraphBrush(Value: TBrush);
1347
FGraphBrush.Assign(Value);
1350
procedure TChart.SetLegend(Value: TChartLegend);
1352
FLegend.Assign(Value);
1356
procedure TChart.SetLogicalExtent(const AValue: TDoubleRect);
1360
if FLogicalExtent = AValue then exit;
1361
w := Abs(AValue.a.X - AValue.b.X);
1362
h := Abs(AValue.a.Y - AValue.b.Y);
1363
with ExtentSizeLimit do
1365
UseXMin and (w < XMin) or UseXMax and (w > XMax) or
1366
UseYMin and (h < YMin) or UseYMax and (h > YMax)
1370
FLogicalExtent := AValue;
1375
procedure TChart.SetMargins(AValue: TChartMargins);
1377
FMargins.Assign(AValue);
1381
procedure TChart.SetMarginsExternal(AValue: TChartMargins);
1383
if FMarginsExternal = AValue then exit;
1384
FMarginsExternal.Assign(AValue);
1388
procedure TChart.SetName(const AValue: TComponentName);
1392
if Name = AValue then exit;
1394
inherited SetName(AValue);
1395
if csDesigning in ComponentState then
1396
Series.List.ChangeNamePrefix(oldName, AValue);
1399
procedure TChart.SetOnAfterDrawBackground(AValue: TChartAfterDrawEvent);
1401
if TMethod(FOnAfterDrawBackground) = TMEthod(AValue) then exit;
1402
FOnAfterDrawBackground := AValue;
1406
procedure TChart.SetOnAfterDrawBackWall(AValue: TChartAfterDrawEvent);
1408
if TMethod(FOnAfterDrawBackWall) = TMethod(AValue) then exit;
1409
FOnAfterDrawBackWall := AValue;
1413
procedure TChart.SetOnBeforeDrawBackground(AValue: TChartBeforeDrawEvent);
1415
if TMethod(FOnBeforeDrawBackground) = TMethod(AValue) then exit;
1416
FOnBeforeDrawBackground := AValue;
1420
procedure TChart.SetOnBeforeDrawBackWall(AValue: TChartBeforeDrawEvent);
1422
if TMethod(FOnBeforeDrawBackWall) = TMethod(AValue) then exit;
1423
FOnBeforeDrawBackWall := AValue;
1427
procedure TChart.SetOnChartPaint(AValue: TChartPaintEvent);
1429
if TMethod(FOnChartPaint) = TMethod(AValue) then exit;
1430
FOnChartPaint := AValue;
1434
procedure TChart.SetOnDrawReticule(AValue: TDrawReticuleEvent);
1436
if TMethod(FOnDrawReticule) = TMethod(AValue) then exit;
1437
FOnDrawReticule := AValue;
1441
procedure TChart.SetProportional(AValue: Boolean);
1443
if FProportional = AValue then exit;
1444
FProportional := AValue;
1448
procedure TChart.SetRenderingParams(AValue: TChartRenderingParams);
1450
FScale := AValue.FScale;
1451
FOffset := AValue.FOffset;
1452
FClipRect := AValue.FClipRect;
1453
FLogicalExtent := AValue.FLogicalExtent;
1454
FPrevLogicalExtent := AValue.FPrevLogicalExtent;
1455
FIsZoomed := AValue.FIsZoomed;
1458
procedure TChart.SetReticuleMode(AValue: TReticuleMode);
1460
if FReticuleMode = AValue then exit;
1461
FReticuleMode := AValue;
1465
procedure TChart.SetReticulePos(const AValue: TPoint);
1467
if FReticulePos = AValue then exit;
1468
DrawReticule(FDrawer);
1469
FReticulePos := AValue;
1470
DrawReticule(FDrawer);
1473
procedure TChart.SetTitle(Value: TChartTitle);
1475
FTitle.Assign(Value);
1479
procedure TChart.SetToolset(AValue: TBasicChartToolset);
1481
if FToolset = AValue then exit;
1482
if FToolset <> nil then
1483
RemoveFreeNotification(FToolset);
1485
FActiveToolIndex := -1;
1486
if FToolset <> nil then
1487
FreeNotification(FToolset);
1490
procedure TChart.StyleChanged(Sender: TObject);
1492
if FDisableRedrawingCounter > 0 then exit;
1493
if Sender is TChartExtent then
1496
Broadcaster.Broadcast(Sender);
1124
1499
procedure TChart.VisitSources(
1125
1500
AVisitor: TChartOnSourceVisitor; AAxis: TChartAxis; var AData);
1129
for i := 0 to SeriesCount - 1 do
1132
VisitSources(AVisitor, AAxis, AData);
1502
s: TBasicChartSeries;
1506
s.VisitSources(AVisitor, AAxis, AData);
1509
function TChart.XGraphToImage(AX: Double): Integer;
1511
Result := RoundChecked(FScale.X * AX + FOffset.X);
1514
function TChart.XImageToGraph(AX: Integer): Double;
1516
Result := (AX - FOffset.X) / FScale.X;
1519
function TChart.YGraphToImage(AY: Double): Integer;
1521
Result := RoundChecked(FScale.Y * AY + FOffset.Y);
1524
function TChart.YImageToGraph(AY: Integer): Double;
1526
Result := (AY - FOffset.Y) / FScale.Y;
1135
1529
procedure TChart.ZoomFull;