unit main; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, TAGraph, TASeries, TAChartUtils, TATools, Types; type { TMainForm } TMainForm = class(TForm) Chart: TChart; ReferenceLine: TConstantLine; Series: TUserDrawnSeries; ChartToolset: TChartToolset; DataPointDragTool: TDataPointDragTool; cbShowDataPoints: TCheckBox; Panel: TPanel; procedure SeriesDraw({%H-}ACanvas: TCanvas; const {%H-}ARect: TRect); procedure SeriesGetBounds(var ABounds: TDoubleRect); procedure cbShowDataPointsChange(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private Pen: TPen; public end; var MainForm: TMainForm; implementation {$R *.lfm} uses Math, TAGeometry; const COLOR_ABOVE = clRed; COLOR_BELOW = clGreen; RADIUS = 4; type TDataRec = record x, y: Double; end; var Data: array of TDataRec; Extent: TDoubleRect; function Intersect(p1, p2: TPoint; y: Integer): Integer; begin if p1.y = p2.y then Result := (p1.x + p2.x) div 2 else Result := round((p2.x - p1.x) / (p2.y - p1.y) * (y - p1.y) + p1.x); end; { TMainForm } procedure TMainForm.SeriesDraw(ACanvas: TCanvas; const ARect: TRect); procedure DrawCircle(p: TPoint; AColor: TColor; R: Integer); begin Chart.Drawer.SetBrushParams(bsSolid, AColor); Chart.Drawer.Ellipse(P.x - R, P.y - R, P.x + R, P.y + R); end; function GetColor(y, yref: Integer): TColor; begin if y > yref then Result := COLOR_BELOW else Result := COLOR_ABOVE; end; function GetLineWidth(y, yref: integer): Integer; begin if y > yref then Result := 3 else Result := 1; end; var i: Integer; gp: TDoublePoint; p1, p2, p3: TPoint; yref: Integer; showDataPoints: Boolean; begin p2 := Default(TPoint); showDataPoints := cbShowDataPoints.Checked; Chart.Drawer.Pen := Pen; yref := Chart.YGraphToImage(ReferenceLine.Position); gp := DoublePoint(Data[0].X, Data[0].Y); p1 := Chart.GraphToImage(gp); if showDataPoints then begin Chart.Drawer.SetPenParams(psSolid, GetColor(p1.y, yref)); DrawCircle(p1, GetColor(p1.y, yref), RADIUS); end; for i := 1 to High(Data)-1 do begin gp := DoublePoint(Data[i].X, Data[i].Y); p2 := Chart.GraphToImage(gp); if (p2.y - yref) * (p1.y - yref) < 0 then begin p3 := Point(Intersect(p1, p2, yref), yref); Chart.Drawer.SetPenParams(psSolid, GetColor(p1.y, yref), GetLinewidth(p1.y, yref)); Chart.Drawer.Line(p1, p3); Chart.Drawer.SetPenParams(psSolid, GetColor(p2.y, yref), GetLineWidth(p2.y, yref)); Chart.Drawer.Line(p3, p2); end else begin Chart.Drawer.SetPenParams(psSolid, GetColor(p2.y, yref), GetlineWidth(p2.y, yref)); Chart.Drawer.Line(p1, p2); end; if showDataPoints then DrawCircle(p2, GetColor(p2.y, yref), RADIUS); p1 := p2; end; end; procedure TMainForm.SeriesGetBounds(var ABounds: TDoubleRect); begin ABounds := Extent; end; procedure TMainForm.cbShowDataPointsChange(Sender: TObject); begin Chart.Invalidate; end; procedure TMainForm.FormCreate(Sender: TObject); const N = 20; XMIN = -10; XMAX = +10; var i: Integer; begin Chart.DoubleBuffered := true; Pen := TPen.Create; Pen.Width := 3; SetLength(Data, N); for i:=0 to N-1 do begin Data[i].x := XMIN + (XMAX - XMIN) / (N - 1) * i; // Data[i].y := sin(Data[i].x); Data[i].y := (Random * 2 - 1) * 100; end; Extent.a.x := Data[0].x; Extent.a.y := Data[0].y; Extent.b.x := Data[0].x; Extent.b.y := Data[0].y; for i:= 1 to High(Data) do begin Extent.a.x := Min(Extent.a.x, Data[i].x); Extent.b.x := Max(Extent.b.x, Data[i].x); Extent.a.y := Min(Extent.a.y, Data[i].y); Extent.b.y := Max(Extent.b.y, Data[i].y); end; end; procedure TMainForm.FormDestroy(Sender: TObject); begin Pen.Free; end; end.