{
 /***************************************************************************
                               TASeries.pp
                               ----------
                Component Library Standard Graph Series


 ***************************************************************************/

 *****************************************************************************
 *                                                                           *
 *  See the file COPYING.modifiedLGPL.txt, included in this distribution,        *
 *  for details about the copyright.                                         *
 *                                                                           *
 *  This program is distributed in the hope that it will be useful,          *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     *
 *                                                                           *
 *****************************************************************************

Authors: Luís Rodrigues and Philippe Martinole

}

unit TASeries;

{$IFDEF fpc}
{$MODE DELPHI}{$H+}
{$ENDIF}

interface

uses
 {$IFDEF fpc}
  {$ELSE}
  Windows,
  {$ENDIF}
  classes, graphics, tagraph, taengine, sysutils, dialogs;

Const clTAColor = clScrollBar;

type

  ChartCoord = record
    x, y: double;
    Color: tcolor;
    Text: string;
  end;
  PChartCoord = ^ChartCoord;

  TSeriesPointerStyle=(psRectangle,psCircle,psCross,psDiagCross,psStar);

  BarException=class(Exception);

  TChartSeries = class(TComponent)
  private
    // Graph = coordinates in the graph
    FXGraphMin,FYGraphMin:Double;                // Max Graph value of points
    FXGraphMax,FYGraphMax:Double;
    FSeriesColor: TColor;
    FTitle: String;
    FCoordList: TList;
    FActive:Boolean;
    FMarks: TSeriesMarksStyle;
    FShowInLegend: Boolean;

    procedure SetActive(Value:Boolean);
    procedure SetMarks(Value:TSeriesMarksStyle);
    function GetXMinVal: Integer;
    procedure SetShowInLegend(Value: Boolean);
  public
    ParentChart:TChart;
    procedure Draw(ACanvas : TCanvas); virtual; abstract;

    constructor Create(AOwner:TComponent); override;
    destructor Destroy; override;

    property XGraphMin: Double read FXGraphMin  write FXGraphMin;
    property YGraphMin: Double read FYGraphMin  write FYGraphMin;
    property XGraphMax: Double read FXGraphMax  write FXGraphMax;
    property YGraphMax: Double read FYGraphMax  write FYGraphMax;
    property SeriesColor : TColor read FSeriesColor write FSeriesColor default clTAColor;
    property Title: String read FTitle write FTitle;

    function Count:Integer;
    function AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint; virtual;
    function Add(aValue: Double; XLabel: String; Color: TColor) : Longint; virtual;
    procedure Delete(Index:Integer); virtual;
    procedure Clear;

    property Coord: TList read FCoordList;
  published
      property MarksStyle: TSeriesMarksStyle read FMarks write SetMarks; //this should be an object
      property Active:Boolean read FActive write SetActive;
      property ShowInLegend:Boolean read FShowInLegend write SetShowInLegend;
  end;

  TSeriesPointer=class(TPersistent)
  private
    FHorizSize,
    FVertSize:Integer;
    FStyle:TSeriesPointerStyle;
    FPen:TChartPen;
    FBrush:TBrush;
    FVisible:Boolean;
    FOwner:TChartSeries;
    FChanged: TNotifyEvent;

    Procedure SetVisible(Value:Boolean);
    Procedure SetStyle(Value:TSeriesPointerStyle);
    Procedure SetPen(Value:TChartPen);
    Procedure SetBrush(Value:TBrush);
    Procedure SetHorizSize(Value:Integer);
    Procedure SetVertSize(Value:Integer);
  protected
  public
    Constructor Create(AOwner:TChartSeries);
    Destructor Destroy; override;
    Procedure Draw(ACanvas : TCanvas; px,py:Integer; SeriesColor: TColor);

    property ParentSeries:TChartSeries read FOwner;
    Procedure Assign(Source:TPersistent); override;
  published
    property Brush:TBrush read FBrush write SetBrush;
    property Pen:TChartPen read FPen write SetPen;
    Property Style:TSeriesPointerStyle read FStyle write SetStyle;
    Property Visible:Boolean read FVisible write SetVisible;
    property OnChange: TNotifyEvent read FChanged write FChanged;
    property HorizSize:Integer read FHorizSize write SetHorizSize default 4;
    property VertSize:Integer read FVertSize write SetVertSize default 4;

  end;


  TLineStyle=(lsVertical,lsHorizontal);

  TBarSeries = class(TChartSeries)
  private
    FBarBrush: TBrush;
    FBarPen: TPen;

    FBarWidthPercent: Integer;
    
    FSeriesNumber: Integer;

    Procedure SetBarWidthPercent(Value:Integer);
    procedure SetBarBrush(Value:TBrush);
    procedure SetBarPen(Value:TPen);
  protected
    procedure StyleChanged(Sender:TObject);
  public
    constructor Create(AOwner:TComponent); override;
    destructor  Destroy; override;

    {from parent}
    procedure Draw(ACanvas : TCanvas); override;
    function AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint; override;
    //        function AddBar(Value: Double; Text: String; Color: TColor) : Longint;
  published
    property BarWidthPercent:Integer read FBarWidthPercent write SetBarWidthPercent default 70;
    property BarBrush: TBrush read FBarBrush write SetBarBrush;
    property BarPen: TPen read FBarPen write SetBarPen;
    property Title;
    property Active;
    property SeriesNumber: Integer read FSeriesNumber write FSeriesNumber;
  end;

  TPieSeries = class(TChartSeries)
  private
      ColorIndex: Integer;

      FPiePen: TPen;
      procedure SetPiePen(Value:TPen);
  protected
    procedure StyleChanged(Sender:TObject);
  public
    constructor Create(AOwner:TComponent); override;
    destructor  Destroy; override;

    {from parent}
    procedure Draw(ACanvas : TCanvas); override;
    function AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint; override;
    function AddPie(Value: Double; Text: String; Color: TColor) : Longint;
  published
    property PiePen: TPen read FPiePen write SetPiePen;
    property Title;
    property Active;
  end;


  TAreaSeries = class(TChartSeries)
  private
{      ColorIndex: Integer;

      FPiePen: TPen;
      procedure SetPiePen(Value:TPen);
      }
      FAreaLinesPen: TChartPen;
      FAreaBrush: TBrush;
      FStairs: boolean;
      FInvertedStairs: boolean;

      procedure SetAreaBrush(value: TBrush);
      procedure SetStairs(value: Boolean);
      procedure SetInvertedStairs(value: Boolean);
  protected
    procedure StyleChanged(Sender:TObject);
  public
    constructor Create(AOwner:TComponent); override;
    destructor  Destroy; override;

    {from parent}
    procedure Draw(ACanvas : TCanvas); override;
    function AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint; override;
  published
     property AreaLinesPen : TChartPen read FAreaLinesPen write FAreaLinesPen;
     property AreaBrush : TBrush read FAreaBrush write SetAreaBrush;
     property Stairs: boolean read FStairs write setStairs;
     property InvertedStairs: boolean read FInvertedStairs  write setInvertedStairs;
     property Title;
     property Active;
  end;




  TSerie = class(TChartSeries)
  private
    FPointer:TSeriesPointer;

    XOfYGraphMin,XOfYGraphMax:Double;          // X max value of points
    FShowPoints:Boolean;
    FShowLines:Boolean;

    UpdateInProgress:Boolean;
    procedure SetShowPoints(Value:Boolean);
    procedure SetShowLines(Value:Boolean);
    procedure SetPointer(Value:TSeriesPointer);
  protected
    { Déclarations protégées }
  public

    { Déclarations publiques }
    FStyle : TPenStyle;
    constructor Create(AOwner:TComponent); override;
    destructor  Destroy; override;

    procedure StyleChanged(Sender:TObject);
    procedure Draw(ACanvas : TCanvas); override;
    function AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint; override;
    function  GetXValue(Index:Integer):Double;
    function  GetYValue(Index:Integer):Double;
    procedure SetXValue(Index:Integer;Value:Double);
    procedure SetYValue(Index:Integer;Value:Double);
    function  GetXImgValue(Index:Integer):Integer;
    function  GetYImgValue(Index:Integer):Integer;
    procedure GetMin(var X,Y:Double);
    procedure GetMax(var X,Y:Double);
    function  GetXMin:Double;
    function  GetXMax:Double;
    function  GetYMin:Double;
    function  GetYMax:Double;
    procedure SetColor(Index:Integer;_Color:TColor);
    function  GetColor(Index:Integer):TColor;

    procedure BeginUpdate;
    procedure EndUpdate;

    property XGraphMin;
    property YGraphMin;
    property XGraphMax;
    property YGraphMax;
  published
    property Title;
    property Active;

    property ShowPoints:Boolean read FShowPoints write SetShowPoints;
    property ShowLines:Boolean read FShowLines write SetShowLines default True;
    property Pointer:TSeriesPointer read FPointer write SetPointer;
  end;

  TLine = class(TChartSeries)
  private
    FStyle:TLineStyle;

    PosImage:Integer;                     // Image coordinates of line
    PosGraph:Double;                      // Graph coordinates of line

    FPen:TPen;

    procedure SetPos(Value:Double);
    procedure SetPen(Value:TPen);
    procedure SetStyle(Value:TLineStyle);
  protected
    { Déclarations protégées }
  public
    { Déclarations publiques }
    constructor Create(AOwner:TComponent); override;
    destructor  Destroy; override;

    procedure Draw(ACanvas : TCanvas); override;
    procedure StyleChanged(Sender:TObject);

    property  LineStyle:TLineStyle read FStyle write SetStyle;

  published
    property Pen:TPen read FPen write SetPen;
    property Position:Double read PosGraph write SetPos;
    property Active;
  end;






implementation


constructor TChartSeries.Create(AOwner:TComponent);
begin
     inherited Create(AOwner);

     XGraphMin:=MaxDouble;
     YGraphMin:=MaxDouble;
     XGraphMax:=MinDouble;
     YGraphMax:=MinDouble;

     FActive := True;
     FShowInLegend := True;
     FMarks := smsLabel;
     FCoordList := TList.Create;
end;

destructor TChartSeries.Destroy;
var i: integer;
begin
     for i := 0 to FCoordList.Count - 1 do begin
         Dispose( PChartCoord(FCoordList.Items[i]) );
     end;
     FCoordList.Free;

     inherited Destroy;
end;

function TChartSeries.GetXMinVal : Integer;
begin
     if count > 0 then
        result := round(PChartCoord(FCoordList[FCoordList.Count-1])^.x)
     else result := 0;
end;

function TChartSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint;
var
   coordn: PChartCoord;
   i: integer;
begin
   //save the coord and color
   New(Coordn);
   Coordn^.x := X;
   Coordn^.y := Y;
   Coordn^.Color := Color;
   Coordn^.Text := XLabel;

   //add in order or at end
   i := 0;
   while (i < FCoordList.Count) and (PChartCoord(FCoordList.Items[i])^.x <= X) do inc(i);
   if i = FCoordList.Count then FCoordList.Add(Coordn)
   else  FCoordList.Insert(i, Coordn);

   result := FCoordList.IndexOf( Coordn );
end;

function TChartSeries.Add(aValue: Double; XLabel: String; Color: TColor) : Longint;
var XVal: Integer;
begin
    if FCoordList.Count = 0 then
      XVal := 0
    else
      XVal :=  Round(PChartCoord(FCoordList.Items[ FCoordList.Count-1 ])^.x);
     AddXY(XVal+1, aValue, XLabel, Color);
end;


procedure TChartSeries.Delete(Index:Integer);
begin
     Dispose( PChartCoord(FCoordList.Items[Index]) );
     FCoordList.Delete( Index );
     if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TChartSeries.Clear;
begin
     FCoordList.Clear;

     XGraphMin:=MaxDouble;
     YGraphMin:=MaxDouble;
     XGraphMax:=MinDouble;
     YGraphMax:=MinDouble;

     if ParentChart <> nil then ParentChart.Invalidate;
end;

function TChartSeries.Count:Integer;
begin
   Result := FCoordList.count;
end;

procedure TChartSeries.SetActive(Value:Boolean);
begin
     FActive:=Value;
     if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TChartSeries.SetShowInLegend(Value:Boolean);
begin
     FShowInLegend:=Value;
     if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TChartSeries.SetMarks(Value:TSeriesMarksStyle);
begin
     FMarks := Value;
     if ParentChart <> nil then ParentChart.Invalidate;
end;

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

Procedure TSeriesPointer.SetVisible(Value:Boolean);
begin
     FVisible := Value;
     if assigned( FChanged ) then FChanged(Self);
end;

Procedure TSeriesPointer.SetStyle(Value:TSeriesPointerStyle);
begin
     FStyle := Value;
     if assigned( FChanged ) then FChanged(Self);
end;

Procedure TSeriesPointer.SetPen(Value:TChartPen);
begin
     FPen.Assign(Value);
     if assigned( FChanged ) then FChanged(Self);
end;

Procedure TSeriesPointer.SetBrush(Value:TBrush);
begin
     FBrush.Assign(Value);
     if assigned( FChanged ) then FChanged(Self);
end;

Procedure TSeriesPointer.SetHorizSize(Value:Integer);
Begin
   FHorizSize := Value;
   if assigned( FChanged ) then FChanged(Self);
end;

Procedure TSeriesPointer.SetVertSize(Value:Integer);
Begin
  FVertSize := Value;
  if assigned( FChanged ) then FChanged(Self);
end;

Constructor TSeriesPointer.Create(AOwner:TChartSeries);
begin
     FBrush := TBrush.Create;
     FBrush.Color := clLime;
     FPen := TChartPen.Create;
     FOwner := AOwner;

     FHorizSize := 4;
     FVertSize  := 4;
end;

Destructor TSeriesPointer.Destroy;
begin
   FBrush.Free;
   FPen.Free;
   inherited Destroy;
end;

Procedure TSeriesPointer.Draw(ACanvas : TCanvas; px,py:Integer; SeriesColor: TColor);
begin
     with FOwner do begin
        ACanvas.Brush.Assign( FBrush );
        ACanvas.Pen.Assign( FPen );

        case FStyle of
            psRectangle: begin
               ACanvas.Brush.Color := SeriesColor;
               ACanvas.Rectangle(px-FHorizSize,py-FVertSize,px+FHorizSize+1,py+FVertSize+1);
            end;
            psCross: begin
               ACanvas.Pen.Color := SeriesColor;
               ACanvas.MoveTo(px-FHorizSize,py);
               ACanvas.LineTo(px+FHorizSize+1,py);
               ACanvas.MoveTo(px,py-FVertSize);
               ACanvas.LineTo(px,py+FVertSize+1);
            end;
            psDiagCross: begin
               ACanvas.Pen.Color := SeriesColor;
               ACanvas.MoveTo(px-FHorizSize,py-FVertSize);
               ACanvas.LineTo(px+FHorizSize+1,py+FVertSize+1);
               ACanvas.MoveTo(px-FHorizSize,py+FVertSize+1);
               ACanvas.LineTo(px+FHorizSize+1,py-FVertSize);
            end;
            psStar: begin
               ACanvas.Pen.Color := SeriesColor;
               ACanvas.MoveTo(px-FHorizSize,py);
               ACanvas.LineTo(px+FHorizSize+1,py);
               ACanvas.MoveTo(px,py-FVertSize);
               ACanvas.LineTo(px,py+FVertSize+1);

               ACanvas.MoveTo(px-FHorizSize,py-FVertSize);
               ACanvas.LineTo(px+FHorizSize+1,py+FVertSize+1);
               ACanvas.MoveTo(px-FHorizSize,py+FVertSize+1);
               ACanvas.LineTo(px+FHorizSize+1,py-FVertSize);
            end;
            psCircle: begin
               ACanvas.Brush.Color := SeriesColor;
               ACanvas.Ellipse(px-FHorizSize,py-FVertSize,px+FHorizSize+1,py+FVertSize+1);
            end;
        end;
     end;
end;

Procedure TSeriesPointer.Assign(Source:TPersistent);
begin
     if Source is TSeriesPointer then
        with TSeriesPointer( Source ) do begin
             FBrush.Assign(Brush);
             FPen.Assign(Pen);
             FStyle := Style;
             FVisible := Visible;
        end;
     inherited Assign( Source );
end;


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

constructor TSerie.Create(AOwner:TComponent);
begin
   inherited Create(AOwner);

   FPointer := TSeriesPointer.Create( Self );
   FPointer.FStyle := psCross;
   FPointer.OnChange := StyleChanged;

   FStyle:=psSolid;

   ShowPoints:=False;
   ShowLines:=True;

   UpdateInProgress:=False;
end;

destructor TSerie.Destroy;
begin
   inherited Destroy;
end;

procedure TSerie.StyleChanged(Sender:TObject);
begin
     if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TSerie.SetPointer(Value:TSeriesPointer);
begin
     FPointer.Assign(Value);
     if ParentChart<>nil then ParentChart.Invalidate;
end;

procedure TSerie.Draw(ACanvas : TCanvas);
var
   i,j:Integer;

   xi1,yi1,xi2,yi2:Integer;
   xg1,yg1,xg2,yg2:Double;
   Min,Max,a,b:Double;
   Inside1,Inside2:Boolean;
   Chart1:TChart;
   YLeft,YRight,XBottom,XTop:Double;
   XLine,YLine:array[1..2] of Integer;
   BLeft,BRight,BBottom,BTop:Boolean;
   Temp:Double;
   dx,dy,dxy,qx,rx,qy,ry,u1,u2,u3,u4:Double;
   OK:Boolean;
   XMin,XMax,Ymin,Ymax,TempI:Integer;
   graphCoord: PChartCoord;

   label Points;
begin
   if count=0 then Exit;

   with ParentChart do begin
      XMin:=XImageMin;
      XMax:=XImageMax;
      YMin:=YImageMin;
      YMax:=YImageMax;
   end;

   if XMin>XMax then begin
      TempI:=XMin;
      XMin:=XMax;
      XMax:=TempI;
   end;
   if YMin>YMax then begin
      TempI:=YMin;
      YMin:=YMax;
      YMax:=TempI;
   end;

      ACanvas.Pen.Mode:=pmCopy;
      ACanvas.Pen.Style:=psSolid;
      ACanvas.Pen.Width:=1;

   Min:=ParentChart.XGraphMin;
   Max:=ParentChart.XGraphMax;


   for i:=0 to count-2 do begin
      graphCoord := FCoordList.Items[i];
      xg1 := graphCoord^.x;
      yg1 := graphCoord^.y;
      ParentChart.GraphToImage(xg1, yg1, xi1, yi1);
      graphCoord := FCoordList.Items[i+1];
      xg2 := graphCoord^.x;
      yg2 := graphCoord^.y;
      ParentChart.GraphToImage(xg2, yg2, xi2, yi2);


      ACanvas.Pen.Style:=FStyle;
      ACanvas.Pen.Color:= graphCoord^.Color;

      if FShowLines then begin
         if (xg1>ParentChart.XGraphMin) and (xg2>ParentChart.XGraphMin) and (xg1<ParentChart.XGraphMax) and (xg2<ParentChart.XGraphMax) and
            (yg1>ParentChart.YGraphMin) and (yg2>ParentChart.YGraphMin) and (yg1<ParentChart.YGraphMax) and (yg2<ParentChart.YGraphMax) then
            begin
               ACanvas.MoveTo(xi1,yi1);
               ACanvas.LineTo(xi2,yi2);
               goto Points;
            end;

         if ((xg1<ParentChart.XGraphMin) and (xg2<ParentChart.XGraphMin)) or ((xg1>ParentChart.XGraphMax) and (xg2>ParentChart.XGraphMax)) or
            ((yg1<ParentChart.YGraphMin) and (yg2<ParentChart.YGraphMin)) or ((yg1>ParentChart.YGraphMax) and (yg2>ParentChart.YGraphMax)) then
            goto Points;

         if yg1>yg2 then begin
            Temp:=xg1; xg1:=xg2; xg2:=Temp;
            Temp:=yg1; yg1:=yg2; yg2:=Temp;
         end;

         if yg1=yg2 then begin
            if xg1>xg2 then begin
               Temp:=xg1; xg1:=xg2; xg2:=Temp;
               Temp:=yg1; yg1:=yg2; yg2:=Temp;
            end;
            if xg1<ParentChart.XGraphMin then xi1:=ParentChart.XImageMin;
            if xg2>ParentChart.XGraphMax then xi2:=ParentChart.XImageMax;
            ACanvas.MoveTo(xi1,yi1);
            ACanvas.LineTo(xi2,yi2);
            goto Points;
         end;

         if xg1=xg2 then begin
            if yg1<ParentChart.YGraphMin then yi1:=ParentChart.YImageMin;
            if yg2>ParentChart.YGraphMax then yi2:=ParentChart.YImageMax;
            ACanvas.MoveTo(xi1,yi1);
            ACanvas.LineTo(xi2,yi2);
            goto Points;
         end;

         dy:=yg1-yg2;
         dx:=xg1-xg2;
         dxy:=xg1*yg2-yg1*xg2;
         qx:=ParentChart.XGraphMin*dy;
         rx:=ParentChart.XGraphMax*dy;
         qy:=ParentChart.YGraphMin*dx;
         ry:=ParentChart.YGraphMax*dx;
         u1:=qx-qy+dxy;
         u2:=qx-ry+dxy;
         u3:=rx-ry+dxy;
         u4:=rx-qy+dxy;

         OK:=False;
         if u1*u2<0 then begin
            OK:=True;
            if xg1<ParentChart.XGraphMin then begin
               yg1:=(ParentChart.XGraphMin*dy+dxy)/dx;
               xg1:=ParentChart.XGraphMin;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
            if xg2<ParentChart.XGraphMin then begin
               yg2:=(ParentChart.XGraphMin*dy+dxy)/dx;
               xg2:=ParentChart.XGraphMin;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if u2*u3<0 then begin
            OK:=True;
            if yg2>ParentChart.YGraphMax then begin
               xg2:=(ParentChart.YGraphMax*dx-dxy)/dy;
               yg2:=ParentChart.YGraphMax;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if u3*u4<0 then begin
            OK:=True;
            if xg1>ParentChart.XGraphMax then begin
               yg1:=(ParentChart.XGraphMax*dy+dxy)/dx;
               xg1:=ParentChart.XGraphMax;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
            if xg2>ParentChart.XGraphMax then begin
               yg2:=(ParentChart.XGraphMax*dy+dxy)/dx;
               xg2:=ParentChart.XGraphMax;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if u4*u1<0 then begin
            OK:=True;
            if yg1<ParentChart.YGraphMin then begin
               xg1:=(ParentChart.YGraphMin*dx-dxy)/dy;
               yg1:=ParentChart.YGraphMin;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if OK then begin
            ParentChart.XGraphToImage(xg1,xi1);
            ParentChart.YGraphToImage(yg1,yi1);
            ParentChart.XGraphToImage(xg2,xi2);
            ParentChart.YGraphToImage(yg2,yi2);

            ACanvas.MoveTo(xi1,yi1);
            ACanvas.LineTo(xi2,yi2);

         end;

      end;

      Points:
        if FShowPoints and (yi1>=YMin) and (yi1<=YMax)
           and (xi1>=XMin) and (xi1<=XMax) then begin
           FPointer.Draw(ACanvas, xi1, yi1, SeriesColor);
         end;

   end;

   // Draw last point
   graphCoord := FCoordList.items[ Count - 1 ];
   xg1 := graphCoord^.x;
   yg1 := graphCoord^.y;
   ParentChart.GraphToImage(xg1, yg1, xi1, yi1);

   if FShowPoints and (yi1>=YMin) and
      (yi1<=YMax) and (xi1>=XMin) and (xi1<=XMax) then begin
         FPointer.draw(ACanvas, xi1, yi1, SeriesColor );
   end;
end;


function TSerie.AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint;
begin
     if Color = clTAColor then
       Color := SeriesColor;

     inherited AddXY(X, Y, XLabel, Color);

     // Update max
     if X>XGraphMax then XGraphMax:=X;
     if X<XGraphMin then XGraphMin:=X;
     if Y>YGraphMax then begin
        YGraphMax:=Y;
        XOfYGraphMax:=X;
     end;
     if Y<YGraphMin then begin
        YGraphMin:=Y;
        XOfYGraphMin:=X;
     end;

     if ParentChart <> nil then ParentChart.Invalidate;
end;

function TSerie.GetXValue(Index:Integer):Double;
begin
     Result:=PChartCoord(FCoordList.Items[Index])^.x;
end;

function TSerie.GetYValue(Index:Integer):Double;
begin
     Result:=PChartCoord(FCoordList.Items[Index])^.y;
end;

procedure TSerie.SetXValue(Index:Integer;Value:Double);
var
   i:Integer;
   Val:Double;
begin
if not(UpdateInProgress) then begin
   if Value<XGraphMin then XGraphMin:=Value
   else if Value>XGraphMax then XGraphMax:=Value
   else begin
     if PChartCoord(FCoordList.Items[Index])^.x=XGraphMax then begin
        PChartCoord(FCoordList.Items[Index])^.x:=Value;
        if Value<XGraphMax then begin
           XGraphMax:=MinDouble;
           for i:=0 to FCoordList.Count-1 do begin
               Val:=PChartCoord(FCoordList.Items[Index])^.x;
               if Val>XGraphMax then XGraphMax:=Val;
           end;
        end;
     end
     else if PChartCoord(FCoordList.Items[Index])^.x=XGraphMin then begin
       PChartCoord(FCoordList.Items[Index])^.x:=Value;
       if Value>XGraphMin then begin
          XGraphMin:=MaxDouble;
          for i:=0 to FCoordList.Count-1 do begin
              Val:=PChartCoord(FCoordList.Items[Index])^.x;
              if Val<XGraphMin then XGraphMin:=Val;
          end;
       end;
     end;
   end;
end;

PChartCoord(FCoordList.Items[Index])^.x:=Value;

if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TSerie.SetYValue(Index:Integer;Value:Double);
var
   i:Integer;
   Val:Double;
begin
if not(UpdateInProgress) then begin
   if Value<YGraphMin then YGraphMin:=Value
   else if Value>YGraphMax then YGraphMax:=Value
   else begin
      if PChartCoord(FCoordList.Items[Index])^.y=YGraphMax then begin
         PChartCoord(FCoordList.Items[Index])^.y:=Value;
         if Value<YGraphMax then begin
            YGraphMax:=MinDouble;
            for i:=0 to FCoordList.Count-1 do begin
               Val:=PChartCoord(FCoordList.Items[Index])^.y;
               if Val>YGraphMax then YGraphMax:=Val;
            end;
         end;
      end
      else if PChartCoord(FCoordList.Items[Index])^.y=YGraphMin then begin
         PChartCoord(FCoordList.Items[Index])^.y:=Value;
         if Value>YGraphMin then begin
            YGraphMin:=MaxDouble;
            for i:=0 to FCoordList.Count-1 do begin
               Val:=PChartCoord(FCoordList.Items[Index])^.y;
               if Val<YGraphMin then YGraphMin:=Val;
            end;
         end;
      end;
   end;
end;

PChartCoord(FCoordList.Items[Index])^.y := Value;

if ParentChart <> nil then ParentChart.Invalidate;
end;

function TSerie.GetXImgValue(Index:Integer):Integer;
begin
     ParentChart.XGraphToImage( PChartCoord(FCoordList.Items[Index])^.x, Result );
end;

function TSerie.GetYImgValue(Index:Integer):Integer;
begin
     ParentChart.YGraphToImage( PChartCoord(FCoordList.Items[Index])^.y, Result );
end;


function TSerie.GetXMin:Double;
begin
     Result:=XGraphMin;
end;

function TSerie.GetXMax:Double;
begin
     Result:=XGraphMax;
end;

function TSerie.GetYMin:Double;
begin
     Result:=YGraphMin;
end;

function TSerie.GetYMax:Double;
begin
     Result:=YGraphMax;
end;

procedure TSerie.GetMax(var X,Y:Double);
begin
     X:=XOfYGraphMax;
     Y:=YGraphMax;
end;

procedure TSerie.GetMin(var X,Y:Double);
begin
     X:=XOfYGraphMin;
     Y:=YGraphMin;
end;

procedure TSerie.SetColor(Index:Integer;_Color:TColor);
begin
     PChartCoord(FCoordList.items[Index])^.color := _Color;
end;

function TSerie.GetColor(Index:Integer):TColor;
begin
     result := PChartCoord(FCoordList.items[Index])^.color;
end;

procedure TSerie.SetShowPoints(Value:Boolean);
begin
     FShowPoints:=Value;
     if ParentChart<>nil then ParentChart.Invalidate;
end;

procedure TSerie.SetShowLines(Value:Boolean);
begin
     FShowLines:=Value;
     if ParentChart<>nil then ParentChart.Invalidate;
end;

procedure TSerie.BeginUpdate;
begin
     UpdateInProgress:=True;
end;

procedure TSerie.EndUpdate;
var
   i:Integer;
   Val:Double;
begin
     UpdateInProgress:=False;

     XGraphMax:=MinDouble;
     XGraphMin:=MaxDouble;
     for i:=0 to Count-1 do begin
         Val:= PChartCoord(FCoordList.Items[i])^.x;
         if Val>XGraphMax then XGraphMax:=Val;
         if Val<XGraphMin then XGraphMin:=Val;
     end;

     YGraphMax:=MinDouble;
     YGraphMin:=MaxDouble;
     for i:=0 to Count-1 do begin
         Val:= PChartCoord(FCoordList.Items[i])^.y;
         if Val>YGraphMax then YGraphMax:=Val;
         if Val<YGraphMin then YGraphMin:=Val;
     end;

     if ParentChart <> nil then ParentChart.Invalidate;
end;






constructor TLine.Create(AOwner:TComponent);
begin
     inherited Create(AOwner);

     FPen:=TPen.Create;
     FPen.OnChange:=StyleChanged;


     LineStyle:=lsHorizontal;
end;

destructor TLine.Destroy;
begin
   inherited Destroy;
   FPen.Free;
end;

procedure TLine.StyleChanged(Sender:TObject);
begin
     if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TLine.SetPen(Value:TPen);
begin
   FPen.Assign(Value);
end;

procedure TLine.SetStyle(Value:TLineStyle);
begin
     if FStyle<>Value then begin
        FStyle:=Value;
        case LineStyle of
           lsHorizontal: begin YGraphMin := PosGraph; YGraphMax := PosGraph; end;
           lsVertical:  begin XGraphMin := PosGraph; XGraphMax := PosGraph; end;
        end;
        if ParentChart<>nil then ParentChart.Invalidate;
     end;
end;

procedure TLine.SetPos(Value:Double);
begin
     PosGraph:=Value;

     //FIXME: not the best way of doing this
         {if Visible then begin
               NBPointsMax:=NBPointsMax+1;
               case LineStyle of
                  lsHorizontal:
                     begin
                     if Position<YMinSeries then YMinSeries:=Position;
                     if Position>YMaxSeries then YMaxSeries:=Position;
                     end;
                  lsVertical:
                     begin
                     if Position<XMinSeries then XMinSeries:=Position;
                     if Position>XMaxSeries then XMaxSeries:=Position;
                     end;
                  end;
               end;
            end;}
     case LineStyle of
        lsHorizontal: begin YGraphMin := PosGraph; YGraphMax := PosGraph; end;
        lsVertical:  begin XGraphMin := PosGraph; XGraphMax := PosGraph; end;
      end;

     if ParentChart<>nil then ParentChart.Invalidate;
end;

procedure TLine.Draw;
var
   XMin,XMax,Ymin,Ymax,TempI:Integer;
   label Points;
begin

with ParentChart as TChart do
   begin
   XMin:=XImageMin;
   XMax:=XImageMax;
   YMin:=YImageMin;
   YMax:=YImageMax;
   end;

if XMin>XMax then
   begin
   TempI:=XMin;
   XMin:=XMax;
   XMax:=TempI;
   end;
if YMin>YMax then
   begin
   TempI:=YMin;
   YMin:=YMax;
   YMax:=TempI;
   end;

// Draw
ACanvas.Pen.Assign(FPen);

case LineStyle of
   lsHorizontal:
      if (PosGraph < ParentChart.XGraphMax) and (PosGraph > ParentChart.XGraphMin) then begin
         ParentChart.YGraphToImage(PosGraph,PosImage);
         ACanvas.MoveTo(XMin,PosImage);
         ACanvas.LineTo(XMax,PosImage);
      end;
   lsVertical:
      if (PosGraph < ParentChart.YGraphMax) and (PosGraph > ParentChart.YGraphMin) then begin
         ParentChart.XGraphToImage(PosGraph,PosImage);
         ACanvas.MoveTo(PosImage,YMin);
         ACanvas.LineTo(PosImage,YMax);
      end;
end;

end;



constructor TBarSeries.Create(AOwner:TComponent);
begin
     inherited Create(AOwner);
     FBarWidthPercent := 70; //70%

     FBarBrush := TBrush.Create;
     FBarBrush.OnChange := StyleChanged;

     FBarPen := TPen.Create;
     FBarPen.OnChange := StyleChanged;
     FBarPen.Mode:=pmCopy;
     FBarPen.Style:=psSolid;
     FBarPen.Width:=1;
     FBarPen.Color := clBlack;
     FBarBrush.Color := clRed;
end;

destructor TBarSeries.Destroy;
var i:integer;
begin
   FBarPen.Free;
   FBarBrush.Free;
   inherited Destroy;
end;

procedure TBarSeries.StyleChanged(Sender:TObject);
begin
     if ParentChart<>nil then ParentChart.Invalidate;
end;

procedure TBarSeries.SetBarBrush(Value:TBrush);
begin
     FSeriesColor := Value.Color;
     FBarBrush.Assign(Value);
end;

procedure TBarSeries.SetBarPen(Value:TPen);
begin
     FBarPen.Assign(Value);
end;

Procedure TBarSeries.SetBarWidthPercent(Value:Integer);
Begin
  if (Value<1) or (Value>100) then
     Raise BarException.Create('Wrong BarWidth Percent')
  else FBarWidthPercent := Value;
end;

function TBarSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint;
begin
   if Color = clTAColor then Color := SeriesColor;

   inherited AddXY(X, Y, XLabel, Color);

   //update the interval - the 0.6 is a hack to allow the bars to have some space appart
   if X > XGraphMax-0.6 then XGraphMax := X+0.6;
   if X < XGraphMin+0.6 then XGraphMin := X-0.6;
   //check if the bar is abouve 0 or not
   if Y >= 0 then begin
      if Y > YGraphMax then YGraphMax := Y;
      if YGraphMin > 0 then YGraphMin := 0;
   end else begin
       if Y < YGraphMin then YGraphMin := Y;
       if YGraphMax < 0 then YGraphMax := 0;
   end;


   if ParentChart <> nil then ParentChart.Invalidate;
   //this has to change
   result := 0;
end;

procedure TBarSeries.Draw(ACanvas : TCanvas);
var
   XMin,XMax,TempI:Integer;
   i: Integer;
   graphCoordTop: ChartCoord;
   graphCoordBottom: ChartCoord;
   topX, topY, bottomY: Integer;
   barWidth, TotalbarWidth: Integer;

   bx1, by1, bx2, by2: integer;


   function BarInViewPort( cTop, cBottom: ChartCoord  ): boolean;
   begin //FIXME make cleaner?
        result :=  ( (cTop.x >= ParentChart.XGraphMin) and (cTop.x <= ParentChart.XGraphMax) )
                    and ( ( (cTop.y > ParentChart.YGraphMax) and (cBottom.y < ParentChart.YGraphMin))
                          or ( (cTop.y < ParentChart.YGraphMax) and (cTop.y > ParentChart.YGraphMin))
                          or ( (cBottom.y < ParentChart.YGraphMax) and (cBottom.y > ParentChart.YGraphMin))
                         );
   end;

begin
   //no elements to draw
   if FCoordList.Count = 0 then exit;

   //get the limits (for the zoom) ??
   XMin := ParentChart.XImageMin;
   XMax := ParentChart.XImageMax;
   if XMin > XMax then begin
      TempI:=XMin;
      XMin:=XMax;
      XMax:=TempI;
   end;

   // Draw the bars
   ACanvas.Pen.Assign( FBarPen );
   ACanvas.Brush.Assign( FBarBrush );

   //calc the single bar width
   TotalbarWidth:=Round((FBarWidthPercent*0.01)*ParentChart.ChartWidth/FCoordList.Count);
   //to use with multibar -- with is on by default
   barWidth:=TotalbarWidth div ParentChart.NumBarSeries;


   for i := 0 to FCoordList.Count - 1 do begin
       //get the top and bottom points
       if ChartCoord(FCoordList.Items[i]^).y >= 0 then begin
          graphCoordTop := ChartCoord(FCoordList.Items[i]^);
          graphCoordBottom.x := graphCoordTop.x;
          graphCoordBottom.y := 0;
       end else begin
           graphCoordBottom := ChartCoord(FCoordList.Items[i]^);
           graphCoordTop.x := graphCoordBottom.x;
           graphCoordTop.y := 0;
       end;

       //check if bar in view port
       if BarInViewPort( graphCoordTop, graphCoordBottom ) then begin
          //only draw to the limits
          if graphCoordTop.y > ParentChart.YGraphMax then graphCoordTop.y := ParentChart.YGraphMax;
          if graphCoordBottom.y < ParentChart.YGraphMin then graphCoordBottom.y := ParentChart.YGraphMin;
          //convert from graph to imgs coords
          ParentChart.GraphToImage(graphCoordTop.x, graphCoordTop.y, topX, topY);
          ParentChart.YGraphToImage(graphCoordBottom.y, bottomY);

          //calc coords for bar
{          bx1 := topX-(barWidth div 2);
          by1 := topY;
          bx2 := topX+(barWidth div 2);
          by2 := bottomY;
}
          bx1 := topX-(TotalbarWidth div 2) + SeriesNumber*barWidth;
          by1 := topY;
          bx2 := topX-(TotalbarWidth div 2) + SeriesNumber*barWidth + barWidth;
          by2 := bottomY;


          //FIXME only draw if bar inside image coord (get a better way of doing this)
          if (bx1 >= XMin)  and (bx2 <= XMax) then
             if by1 = by2 then begin //draw a line when y=0 FIXME (clean)
                ACanvas.Pen.Color := FBarBrush.Color;
                ACanvas.MoveTo(bx1, by1);
                ACanvas.LineTo(bx2, by2);
                ACanvas.Pen.Assign( FBarPen );
             end else ACanvas.Rectangle( bx1, by1, bx2, by2);

       end;
   end;

end;

constructor TPieSeries.Create(AOwner:TComponent);
begin
     inherited Create(AOwner);

     ColorIndex := 1;

     FPiePen := TPen.Create;
     FPiePen.OnChange := StyleChanged;
     FPiePen.Mode:=pmCopy;
     FPiePen.Style:=psSolid;
     FPiePen.Width:=1;
     FPiePen.Color := clBlack;
end;

destructor TPieSeries.Destroy;
begin
   FPiePen.Free;
   inherited Destroy;
end;

procedure TPieSeries.StyleChanged(Sender:TObject);
begin
     if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TPieSeries.SetPiePen(Value:TPen);
begin
     FPiePen.Assign(Value);
end;


function TPieSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint;
begin
   if Color = clTAColor then Color := Colors[ ColorIndex ];
   Inc(ColorIndex); if ColorIndex > MaxColor then ColorIndex := 1;

   inherited AddXY(X, Y, XLabel, Color);


   if ParentChart <> nil then ParentChart.Invalidate;
   //this has to change
   result := 0;
end;

function TPieSeries.AddPie(Value: Double; Text: String; Color: TColor) : Longint;
begin
     result := AddXY( getXMinVal + 1, Value, Text, Color);
end;

procedure conv_angle(nOrigoX, nOrigoY, nLen: Integer; Angle: Double; var PX, PY: Integer);
begin
   PX := nOrigoX+round(Cos(Angle)*nLen);
   PY := nOrigoY-round(Sin(Angle)*nLen);
end;

procedure TPieSeries.Draw;
var
   nOrigoX, nOrigoY : LongInt;
   nLen             : integer;
   n100Sum: double;
   i, AX, AY, BX, BY: integer;
   curangle, prevangle, midleangle, prop: double;
   maxtextw: integer;
   CircleRect: TRect;
   markrect: trect;
   W: Integer;
      graphCoord: PChartCoord;
   MarkTxtWidth: integer;
const TAGSMargin = 20;
      MarkYMargin = 2;
      MarkXMargin = 4;
var
      SPACE_InMarks: integer;
begin
     SPACE_InMarks := 40;
   //no elements to draw
   if FCoordList.Count = 0 then exit;


   //center the ParentChart on the canvas
   if ParentChart.XImageMax-ParentChart.XImageMin > ParentChart.YImageMin-ParentChart.YImageMax then begin
      W := ParentChart.YImageMin-ParentChart.YImageMax;
      nOrigoX  := (ParentChart.XImageMax-ParentChart.XImageMin) div 2+ParentChart.XImageMin;
      nOrigoY  := Round((W-1.01)/2)+ParentChart.YImageMax;
   end
   else begin
       W := ParentChart.XImageMax-ParentChart.XImageMin;
       nOrigoX  := Round((W-1.01)/2)+ParentChart.XImageMin;
      nOrigoY  := (ParentChart.YImageMin-ParentChart.YImageMax) div 2 +ParentChart.YImageMax;

   end;


   prevangle := 0;
   maxtextw := 0;
   n100Sum     := 0;

   for i := 0 to FCoordList.Count - 1 do begin
      graphCoord := FCoordList[i];
      n100Sum  := n100Sum + graphCoord^.y;
       if ParentChart.Canvas.TextWidth(graphCoord^.Text) > maxtextw then
          maxtextw := ParentChart.Canvas.TextWidth(graphCoord^.Text);
   end;

   //we can only draw if enought space for text is saved
   //so we remove maxtextw and MarkXMargin
   nLen := Round( (W-(maxtextw)*2) /2);
   CircleRect.Left   := nOrigoX-nLen-TAGSMargin;
   CircleRect.Top    := nOrigoY-nLen-TAGSMargin;
   CircleRect.Right  := nOrigoX+nlen+TAGSMargin;
   CircleRect.Bottom := nOrigoY+nlen+TAGSMargin;

//   ParentChart.Canvas.Rectangle(CircleRect.Left, CircleRect.Top, CircleRect.Right, CircleRect.Bottom);

   for i := 0 to FCoordList.Count - 1 do begin
      graphCoord := FCoordList[i];
      if graphCoord^.y < 0 then graphCoord^.y := -graphCoord^.y;
      //if graphCoord^.y = 0 then graphCoord^.y := 0.1; //just to simulate tchart when y=0

      conv_angle(nOrigoX, nOrigoY, nLen, prevangle, AX, AY);
      prop := graphCoord^.y / n100Sum ;
      curangle := prop * (2*pi)  ;
      conv_angle(nOrigoX, nOrigoY, nLen, prevangle+curangle, BX, BY);

      ParentChart.Canvas.Brush.color := graphCoord^.Color;
      ParentChart.canvas.pie(CircleRect.Left,CircleRect.Top, CircleRect.Right, CircleRect.Bottom, AX,AY,BX,BY);

      if nLen < SPACE_InMarks then
         SPACE_InMarks := nLen;


      //marks
      midleangle := prevangle + (curangle/2);
      conv_angle(nOrigoX, nOrigoY, nLen+SPACE_InMarks, midleangle, BX, BY);
      conv_angle(nOrigoX, nOrigoY, nLen, midleangle, aX, aY);

      ParentChart.canvas.Pen.Color := clWhite;
      ParentChart.canvas.moveto(ax, ay);
      ParentChart.canvas.lineto(bx, by);
      ParentChart.canvas.Pen.Color := clBlack;

       //depends on label type
      case MarksStyle of
           smsLabel: MarkTxtWidth := ParentChart.canvas.TextWidth( graphCoord^.Text );
           smsLabelPercent: MarkTxtWidth := ParentChart.canvas.TextWidth( graphCoord^.Text+' '+format('%1.3g',[prop*100])+'%' );
      end;

      //line from mark to pie
      if Bx < nOrigoX then
         markrect.Left    := BX - MarkTxtWidth - MarkXMargin
      else markrect.Left := BX -MarkXMargin;
      markrect.Right  :=  markrect.Left + MarkTxtWidth +MarkXMargin*2;

      markrect.Top   := BY - MarkYMargin;
      markrect.Bottom := BY + ParentChart.canvas.textheight( graphCoord^.Text )+MarkYMargin;


      ParentChart.canvas.Brush.Color := clYellow;
      ParentChart.Canvas.Rectangle(markrect.Left, markrect.Top, markrect.Right, markrect.Bottom);

      if Bx < nOrigoX then BX := BX - MarkTxtWidth;
      ParentChart.canvas.Brush.Color := clYellow;
      case MarksStyle of
           smsLabel: ParentChart.Canvas.TextOut(BX, BY, graphCoord^.Text);
           smsLabelPercent: ParentChart.Canvas.TextOut(BX, BY, graphCoord^.Text+' '+format('%1.3g',[prop*100])+'%');
      end;

      prevangle := prevangle + curangle;
   end;


end;


constructor TAreaSeries.Create(AOwner:TComponent);
begin
     inherited Create(AOwner);
     FAreaLinesPen := TChartPen.Create;
     FAreaBrush := TBrush.Create;
     FStairs := false;
end;

destructor TAreaSeries.Destroy;
begin
   FAreaLinesPen.Free;
   FAreaBrush.Free;
   inherited Destroy;
end;

function TAreaSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor) : Longint;
begin
     if Color = clTAColor then Color := SeriesColor;

     inherited AddXY(X, Y, XLabel, Color);

     // Update max
     if X>XGraphMax then XGraphMax:=X;
     if X<XGraphMin then XGraphMin:=X;
     if Y>YGraphMax then YGraphMax:=Y;
     if Y<YGraphMin then YGraphMin:=Y;

     if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TAreaSeries.SetAreaBrush(value: TBrush);
begin
   FAreaBrush.Assign( value );
   if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TAreaSeries.SetStairs(value: Boolean);
begin
   FStairs := value;
   if ParentChart <> nil then ParentChart.Invalidate;
end;

procedure TAreaSeries.SetInvertedStairs(value: Boolean);
begin
   FInvertedStairs := value;
   if ParentChart <> nil then ParentChart.Invalidate;
end;


procedure TAreaSeries.StyleChanged(Sender:TObject);
begin
     if ParentChart <> nil then ParentChart.Invalidate;
end;


procedure TAreaSeries.Draw;
const   Larg = 4;
var
   i,j:Integer;

   xi1,yi1,xi2,yi2:Integer;
   xg1,yg1,xg2,yg2:Double;
   Inside1,Inside2:Boolean;
   Chart1:TChart;
   YLeft,YRight,XBottom,XTop:Double;
   XLine,YLine:array[1..2] of Integer;
   BLeft,BRight,BBottom,BTop:Boolean;
   Temp:Double;
   dx,dy,dxy,qx,rx,qy,ry,u1,u2,u3,u4:Double;
   OK:Boolean;
   XMin,XMax,Ymin,Ymax,TempI:Integer;
   graphCoord: PChartCoord;
   iy_min: integer;

begin
   if count=0 then Exit;

   with ParentChart do begin
      XMin:=XImageMin;
      XMax:=XImageMax;
      YMin:=YImageMin;
      YMax:=YImageMax;
   end;

   if XMin>XMax then begin
      TempI:=XMin;
      XMin:=XMax;
      XMax:=TempI;
   end;
   if YMin>YMax then begin
      TempI:=YMin;
      YMin:=YMax;
      YMax:=TempI;
   end;

   with ParentChart do begin
      Canvas.Pen.Mode:=pmCopy;
      Canvas.Pen.Style:=psSolid;
      Canvas.Pen.Width:=1;
   end;


   for i:=0 to count-2 do begin
      graphCoord := FCoordList.Items[i];
      xg1 := graphCoord^.x;
      yg1 := graphCoord^.y;
      ParentChart.GraphToImage(xg1, yg1, xi1, yi1);
      graphCoord := FCoordList.Items[i+1];
      xg2 := graphCoord^.x;
      yg2 := graphCoord^.y;
      ParentChart.GraphToImage(xg2, yg2, xi2, yi2);

      ParentChart.YGraphToImage(ParentChart.YGraphMin, iy_min);
      ParentChart.Canvas.Pen.Color:= clBlack;
      ParentChart.Canvas.Brush.Color:= graphCoord^.Color;


         if (xg1>ParentChart.XGraphMin) and (xg2>ParentChart.XGraphMin) and (xg1<ParentChart.XGraphMax) and (xg2<ParentChart.XGraphMax) and
            (yg1>ParentChart.YGraphMin) and (yg2>ParentChart.YGraphMin) and (yg1<ParentChart.YGraphMax) and (yg2<ParentChart.YGraphMax) then
            begin
               if FStairs then begin
                  if FInvertedStairs then
                     ParentChart.Canvas.Polygon([Point(xi1, iy_min), Point(xi1, yi2), Point(xi2, yi2), Point(xi2, iy_min)])
                  else
                     ParentChart.Canvas.Polygon([Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi1), Point(xi2, iy_min)])
               end else
                  ParentChart.Canvas.Polygon([Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi2), Point(xi2, iy_min)]);
                  
               continue;
            end;

         if ((xg1<ParentChart.XGraphMin) and (xg2<ParentChart.XGraphMin)) or ((xg1>ParentChart.XGraphMax) and (xg2>ParentChart.XGraphMax)) or
            ((yg1<ParentChart.YGraphMin) and (yg2<ParentChart.YGraphMin)) or ((yg1>ParentChart.YGraphMax) and (yg2>ParentChart.YGraphMax)) then
            continue;

         if yg1>yg2 then begin
            Temp:=xg1; xg1:=xg2; xg2:=Temp;
            Temp:=yg1; yg1:=yg2; yg2:=Temp;
         end;

         if yg1=yg2 then begin
            if xg1>xg2 then begin
               Temp:=xg1; xg1:=xg2; xg2:=Temp;
               Temp:=yg1; yg1:=yg2; yg2:=Temp;
            end;
            if xg1<ParentChart.XGraphMin then xi1:=ParentChart.XImageMin;
            if xg2>ParentChart.XGraphMax then xi2:=ParentChart.XImageMax;
            ParentChart.Canvas.Polygon([Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi2), Point(xi2, iy_min)]);
            continue;
         end;

         if xg1=xg2 then begin
            if yg1<ParentChart.YGraphMin then yi1:=ParentChart.YImageMin;
            if yg2>ParentChart.YGraphMax then yi2:=ParentChart.YImageMax;
            ParentChart.Canvas.Polygon([Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi2), Point(xi2, iy_min)]);
            continue;
         end;

         dy:=yg1-yg2;
         dx:=xg1-xg2;
         dxy:=xg1*yg2-yg1*xg2;
         qx:=ParentChart.XGraphMin*dy;
         rx:=ParentChart.XGraphMax*dy;
         qy:=ParentChart.YGraphMin*dx;
         ry:=ParentChart.YGraphMax*dx;
         u1:=qx-qy+dxy;
         u2:=qx-ry+dxy;
         u3:=rx-ry+dxy;
         u4:=rx-qy+dxy;

         OK:=False;
         if u1*u2<0 then begin
            OK:=True;
            if xg1<ParentChart.XGraphMin then begin
               yg1:=(ParentChart.XGraphMin*dy+dxy)/dx;
               xg1:=ParentChart.XGraphMin;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
            if xg2<ParentChart.XGraphMin then begin
               yg2:=(ParentChart.XGraphMin*dy+dxy)/dx;
               xg2:=ParentChart.XGraphMin;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if u2*u3<0 then begin
            OK:=True;
            if yg2>ParentChart.YGraphMax then begin
               xg2:=(ParentChart.YGraphMax*dx-dxy)/dy;
               yg2:=ParentChart.YGraphMax;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if u3*u4<0 then begin
            OK:=True;
            if xg1>ParentChart.XGraphMax then begin
               yg1:=(ParentChart.XGraphMax*dy+dxy)/dx;
               xg1:=ParentChart.XGraphMax;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
            if xg2>ParentChart.XGraphMax then begin
               yg2:=(ParentChart.XGraphMax*dy+dxy)/dx;
               xg2:=ParentChart.XGraphMax;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if u4*u1<0 then begin
            OK:=True;
            if yg1<ParentChart.YGraphMin then begin
               xg1:=(ParentChart.YGraphMin*dx-dxy)/dy;
               yg1:=ParentChart.YGraphMin;
               dy:=yg1-yg2;
               dx:=xg1-xg2;
               dxy:=xg1*yg2-yg1*xg2;
            end;
         end;

         if OK then begin
            ParentChart.XGraphToImage(xg1,xi1);
            ParentChart.YGraphToImage(yg1,yi1);
            ParentChart.XGraphToImage(xg2,xi2);
            ParentChart.YGraphToImage(yg2,yi2);
            ParentChart.Canvas.Polygon([Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi2), Point(xi2, iy_min)]);
         end;
   end;
end;

end.
