unit Graphu;

interface
uses
  Classes, Graphics, Controls, ExtCtrls, Forms,
  FMath, FSpec, Matrices, PlotVar, WinPlot;


type
  TGraph = class(TComponent)
  private
    { Local parameters that correspond to PlotVar globals
      used to draw graph, copied to PlotVar variables before
      calling plotting functions using LocalToPlotVar method }
    FXAxis: TAxis;
    FYAxis: TAxis;
    FFuncLineParam: TLineParam;
    { Coordinates of the graph window on the Canvas in % of width/height maxima }
    FXwin1: integer;
    FYwin1: integer;
    FXwin2: integer;
    FYwin2: integer;
    {/}
    FCurvParam: TCurvParam;

    { Eventual experimental points for which the dots or crosses should be
      drawn }
    FX: TVector;
    FY: TVector;
    FPointCount: integer;

    { Number of screen points to draw }
    FNpts: integer;

    FFunc: TFunc;
    FControl: TControl;
      FCanvas: TCanvas;
      FWidth: integer;
      FHeight: integer;
    FA, FB: Float;  { Function parameters }
    FModifiedSinceDraw: boolean;
    procedure SetControl(AControl: TControl);
    procedure SetFunc(AFunc: TFunc);

  protected
    procedure LocalToPlotVar;

    property Canvas: TCanvas read FCanvas write FCanvas;
    property Width: integer read FWidth write FWidth;
    property Height: integer read FHeight write FHeight;
  public
    constructor Create(Owner: TComponent); override;
    destructor Destroy; override;
    { Performs all the drawing on the Canvas. }
    procedure Draw;
    { clear the Canvas plotting area }
    procedure Clear;
    { Optional method that allows to copy arrays of experimental values
      X->Y in arrays (of the same size) to find out the axis parameters
      of the graph - min,max,step, eventually to draw the experimental points
      as crosses or dots, ... }
    procedure SetExpPoints(AX: TVector; AY: TVector; APointCount: integer);

    { A Control to which Canvas the graph should be drawn, Width and
      Height of the Control will be used. }
    property Control: TControl read FControl write SetControl;
    { The function that will be drawn on the Canvas. }
    property Func: TFunc read FFunc write SetFunc;
    property XAxis: TAxis read FXAxis write FXAxis;
    property YAxis: TAxis read FYAxis write FYAxis;
  end;


implementation

function Linear(X: Float): Float;
begin
  Result := X;
end;

constructor TGraph.Create(Owner: TComponent);
begin
  inherited;
  FXAxis.Min := 0;
  FXAxis.Max := 5;
  FXAxis.Step := 1;
  FXAxis.MinDiv := 2;
  FXAxis.MaxDiv := 5;

  FYAxis.Min := 0;
  FYAxis.Max := 5;
  FYAxis.Step := 1;
  FYAxis.MinDiv := 2;
  FYAxis.MaxDiv := 5;

  FXwin1 := 15;
  FYwin1 := 15;
  FXwin2 := 85;
  FYwin2 := 85;

  FNpts := 200;

  FCurvParam.PointParam.Symbol := syX; { Symbol index }
  FCurvParam.PointParam.Size := 4; { Symbol size in 1/250 of graphic width }
  FCurvParam.PointParam.Color := clBlue;

  FCurvParam.LineParam.Width := 2;
  FCurvParam.LineParam.Color := clBlue;
  FCurvParam.LineParam.Style := psDash;

  FCurvParam.Legend := '';  { Legend of Curv }
  FCurvParam.Step := 1;      { Plot 1 point every Step points }
  FCurvParam.Connect := false; { Connect points with line? }



  FFuncLineParam.Width := 2; {1..9}
  FFuncLineParam.Color := clRed;
  FFuncLineParam.Style := psSolid;

  FA := 0.5; {>0}
  FB := 0.5; {>0}
  FFunc := Linear;
end;

procedure TGraph.LocalToPlotVar;
begin
  PlotVar.XAxis := FXAxis;
  PlotVar.YAxis := FYAxis;
  PlotVar.Xwin1 := FXwin1;
  PlotVar.Ywin1 := FYwin1;
  PlotVar.Xwin2 := FXwin2;
  PlotVar.Ywin2 := FYwin2;
end;

procedure TGraph.Draw;
begin
  if FCanvas = nil then
    exit;
  LocalToPlotVar;
  if FModifiedSinceDraw then
    Clear;

  { The following calls are using global PlotVar unit's variables }
  WinPlot.InitGraph(FCanvas, FWidth, FHeight);
  WinPlot.PlotXAxis(FCanvas);
  WinPlot.PlotYAxis(FCanvas);
  WinPlot.PlotGrid(FCanvas);
  WinPlot.PlotFunc(FCanvas, FFunc, FXAxis.Min, FXAxis.Max, FNpts, FFuncLineParam);
  if (FX <> nil) and (FY <> nil) then begin
    WinPlot.PlotCurve(FCanvas, FX, FY, 0, FPointCount - 1, FCurvParam);
  end;  

  FModifiedSinceDraw := false;
end;

procedure TGraph.Clear;
begin
  if FCanvas = nil then
    exit;
  FCanvas.FillRect(Rect(0, 0, Width, Height));
end;

procedure TGraph.SetControl(AControl: TControl);
begin
  Width := AControl.Width;
  Height := AControl.Height;
  if AControl is TImage then begin
    Canvas := TImage(AControl).Canvas;
  end else if AControl is TForm then begin
    Canvas := TForm(AControl).Canvas;
  end {else if AControl is TPanel then begin
    Canvas := TPanel(AControl).Canvas;
  end};
end;

procedure TGraph.SetExpPoints(AX: TVector; AY: TVector; APointCount: integer);
var
  i: integer;
{var
  x1, x2, y1, y2: Float;}
begin
{ for i := 0 to APointCount - 1 do begin
    if i = 0 then begin
      x1 := AX[0];
      x2 := AX[APointCount - 1];
      y1 := AY[0];
      y2 := AY[0];
    end else begin
      if AY[i] < y1 then
        y1 := AY[i];
      if AY[i] > y2 then
        y2 := AY[i];
    end;
  end; }
  if APointCount = 0 then begin
    FPointCount := 0;
    FX := nil;
    FY := nil;
    exit;
  end;

  if (APointCount = FPointCount) and (AX = FX) and (AY = FY) then
    exit;
  if APointCount <> FPointCount then begin
    FPointCount := APointCount;
    DimVector(FX, FPointCount);
    DimVector(FY, FPointCount);
  end;
  for i := 0 to FPointCount - 1 do begin
    FX[i] := AX[i];
    FY[i] := AY[i];
  end;

  PlotVar.AutoScale(FX, 0, APointCount - 1, FXAxis);
  PlotVar.AutoScale(FY, 0, APointCount - 1, FYAxis);
  FModifiedSinceDraw := true;
{  Interval(Min(AX, 0, APointCount - 1), Max(AX, 0, APointCount - 1), 5, 10,
    FXAxis.Min, FXAxis.Max, FXAxis.Step);
  Interval(Min(AY, 0, APointCount - 1), Max(AY, 0, APointCount - 1), 5, 10,
    FYAxis.Min, FYAxis.Max, FYAxis.Step);}
end;

procedure TGraph.SetFunc(AFunc: TFunc);
begin
  {if FFunc <> AFunc then}
  begin
    FFunc := AFunc;
    FModifiedSinceDraw := true;
  end;
end;

destructor TGraph.Destroy;
begin
  FX := nil;
  FY := nil;
  inherited;
end;

end.
