unit Graf;
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, Spin,

  FEvalLib, Newton;

const
  POSUN = 20; {okraje grafu}

 {minimalni a maxilalni zvetseni} 
  MAX_ZOOM = 8192;
  MIN_ZOOM = 1/128;

  MARK_DIST = 70; {priblizna vzdalenost znacek}

type
  TFormGraph = class(TForm)
    GroupBox1: TGroupBox;
    RBShowNothing: TRadioButton;
    RBShowHalf: TRadioButton;
    RBShowNewton: TRadioButton;
    GroupBox2: TGroupBox;
    Img: TImage;
    ButtonGraphHide: TButton;
    GroupBox3: TGroupBox;
    LabelZoom: TLabel;
    Label2: TLabel;
    procedure ButtonGraphHideClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);

    procedure SetNewton(arr: TArrResult; cnt: integer);
    procedure SetBorderPoints(apar, bpar: extended);

    procedure FormShow(Sender: TObject);
    procedure ImgMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ImgMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure RBShowNewtonClick(Sender: TObject);


  private
    AproxNewton: TArrResult;
    CountNewton: integer;

    a, b: extended;
    Zoom: extended;
    x0, x1, ymax: extended;

    isDrawn: boolean;

  public
    procedure DrawGraph;

    procedure SetZoom(z: real);

  private
    procedure DrawAxes;
    procedure DrawFunction;

    procedure DrawNewton;

    function PixToValX(i: integer): extended;
    function ValToPixX(r: extended): integer;

    function ValToPixY(r: extended): integer;

  private
    Func: TFunctionEval;
  end;

var
  FormGraph: TFormGraph;

implementation
uses
  Main, Math, Errors;
{$R *.dfm}
{$R Lupa.res}

const
  curLupa = 5;



procedure TFormGraph.SetZoom(z: real);
begin
  Zoom:= z;

  if z>=1 then
    LabelZoom.Caption:= FloatToStr(Zoom)
  else
    LabelZoom.Caption:= '1/' + FloatToStr(trunc(1/Zoom));
end;


procedure TFormGraph.ButtonGraphHideClick(Sender: TObject);
begin
  FormGraph.Hide;
end;

procedure TFormGraph.FormCreate(Sender: TObject);
begin
  CountNewton:= -1;
  a:= 0; b:= 0;
  SetZoom(1);
  x0:= 0; x1:= 0; ymax:= 0;

  isDrawn:= false;

  Img.Canvas.Brush.Color:= clWhite;
  Img.Canvas.FillRect(Img.ClientRect);

  Screen.Cursors[curLupa] := LoadCursor(HInstance, 'LUPA');

  Func:= MainForm.Newton.GetFunc;
end;

procedure TFormGraph.SetNewton(arr: TArrResult; cnt: integer);
begin
  AproxNewton:= arr;
  CountNewton:= cnt;
end;


procedure TFormGraph.SetBorderPoints(apar, bpar: extended);
begin
  a:= apar;
  b:= bpar;
  x0:= a - (b-a) / 4;
  x1:= b + (b-a) / 4;

  ymax:= (x1- x0) / Img.Width * Img.Height/2; 
end;


procedure TFormGraph.FormShow(Sender: TObject);
begin
  if (a <> 0) or (b<>0) then
    DrawGraph;
end;


procedure TFormGraph.DrawAxes;

procedure DrawMarkX(Val: extended);
var
  pos: integer;
begin
  pos:= ValToPixX(Val);
  Img.Canvas.MoveTo( pos, Img.Height - POSUN - 2);
  Img.Canvas.LineTo( pos, Img.Height - POSUN + 2);
end;

procedure DrawValueX(Val: extended);
var
  pos: integer;
begin
  pos:= ValToPixX(Val);
  Img.Canvas.TextOut(pos, Img.Height - Posun + 3,
    FloatToStrF(Val, ffGeneral, 5, 5));
end;


procedure DrawMarkY(Val: extended);
var
  pos: integer;
begin
  pos:= ValToPixY(Val);
  Img.Canvas.MoveTo(POSUN - 2, pos);
  Img.Canvas.LineTo(POSUN + 2, pos);
end;

procedure DrawValueY(Val: extended);
var
  pos: integer;
begin
  pos:= ValToPixY(Val);
  Img.Canvas.TextOut(POSUN + 3, pos + 1,
    FloatToStrF(Val, ffGeneral, 5, 5));
end;

var
  x0pom, x1pom: extended;
  xValDist, xM0: extended;
  i, Iwidth, xMCnt: integer;
  xFirstPom: extended;

  yMCnt, yValDist: extended;
begin
    with Img.Canvas do begin
    Pen.Color:= clBlue;
    Pen.Style:= psSolid;


   {svisla osa}
    MoveTo( POSUN, POSUN );
    LineTo( POSUN, Img.Height - (POSUN div 2));

   {vodorovna}
    MoveTo( POSUN div 2, Img.Height - POSUN);
    LineTo(Img.Width - POSUN, Img.Height - POSUN);

   {osa y=0}
    Pen.Color:= clBlue;
    MoveTo(POSUN, Img.Height div 2);
    LineTo(Img.Width - POSUN, Img.Height div 2);

    Pen.Color:= clBlack;
  end;
  Img.Canvas.Font.Name:= 'MS Sans Serif';
  Img.Canvas.Font.Size:= 6;
  Img.Canvas.Font.Color:= clBlack;


 {!!!!  znacky}

   {x-ove}
    Iwidth:= Img.Width - 2*Posun;

    x0pom:= x0;
    x1pom:= x1;

  {pomocna cisla}
    xMCnt:= Iwidth div MARK_DIST; {pocet znacek}


  {vzdalenost hodnot, zjisteni priblizne cele hodnoty}
    xValDist:= (x1pom - x0pom) / xMCnt;

{!!!!!!! predelat na cykly !!!!!!!!!}

    if abs(xValDist) > 100000 then                  {5}
      xValDist:= 10000 * round(xValDist / 10000)
    else if abs(xValDist) > 10000 then                  {4}
      xValDist:= 1000 * round(xValDist / 1000)
    else if abs(xValDist) > 1000 then              {3}
      xValDist:= 100 * round(xValDist / 100)
    else if abs(xValDist) > 100 then              {2}
      xValDist:= 10 * round(xValDist / 10)
    else if abs(xValDist) > 10 then               {1}
      xValDist:= 1 * round(xValDist / 1)

    else if abs(xValDist) < 0.000001 then            {-5}
      xValDist:= 0.0000001 * round(xValDist / 0.0000001)
    else if abs(xValDist) < 0.00001 then            {-4}
      xValDist:= 0.000001 * round(xValDist / 0.000001)
    else if abs(xValDist) < 0.0001 then            {-5}
      xValDist:= 0.00001 * round(xValDist / 0.00001)
    else if abs(xValDist) < 0.001 then            {-3}
      xValDist:= 0.0001 * round(xValDist / 0.0001)
    else if abs(xValDist) < 0.01 then             {-2}
      xValDist:= 0.001 * round(xValDist / 0.001)
    else if abs(xValDist) < 0.1 then              {-1}
      xValDist:= 0.01 * round(xValDist / 0.01)
    else if abs(xValDist) < 1 then                {0}
      xValDist:= 0.1 * round(xValDist / 0.1)

    else                                          {--}
      xValDist:= round(xValDist);

  { nalezeni prvni pravidelne znacky}
    xFirstPom := x0pom / xValDist;

    if abs(xFirstPom) < 0.00001 then            {-5}
      xFirstPom:= trunc(xFirstPom * 1000000)
    else if abs(xFirstPom) < 0.0001 then            {-4}
      xFirstPom:= trunc(xFirstPom * 100000)
    else if abs(xFirstPom) < 0.001 then            {-3}
      xFirstPom:= trunc(xFirstPom * 10000)
    else if abs(xFirstPom) < 0.01 then             {-2}
      xFirstPom:= trunc(xFirstPom * 1000)
    else if abs(xFirstPom) < 0.1 then              {-1}
      xFirstPom:= trunc(xFirstPom * 100)
    else if abs(xFirstPom) < 1 then                {0}
      xFirstPom:= trunc(xFirstPom * 10)
    else                                          {--}
      xFirstPom:= trunc(xFirstPom);

    xM0:= xFirstPom  * xValDist;


   {pravidelne znacky}
    i:=0;
    while xM0 + xValDist*i <= x1 do begin
      DrawMarkX(xM0 + xValDist*i);
      DrawValueX(xM0 + xValDist*i);

      inc(i);
    end;

   {znacky pro a, b, x0}
    DrawMarkX(x0);
    DrawMarkX(x1);
    if (a > x0) and (a < x1) then
      DrawMarkX(a);
    if (b > x0) and (b < x1) then
      DrawMarkX(b);

  {y-ove znacky}
    yMCnt:= Img.Height div MARK_DIST;
    yValDist:= ymax * 2 / yMCnt;

    if abs(yValDist) > 100000 then                  {5}
      yValDist:= 10000 * round(yValDist / 10000)
    else if abs(yValDist) > 10000 then                  {4}
      yValDist:= 1000 * round(yValDist / 1000)
    else if abs(yValDist) > 1000 then              {3}
      yValDist:= 100 * round(yValDist / 100)
    else if abs(yValDist) > 100 then              {2}
      yValDist:= 10 * round(yValDist / 10)
    else if abs(yValDist) > 10 then               {1}
      yValDist:= 1 * round(yValDist / 1)

    else if abs(yValDist) < 0.000001 then            {-5}
      yValDist:= 0.0000001 * round(yValDist / 0.0000001)
    else if abs(yValDist) < 0.00001 then            {-4}
      yValDist:= 0.000001 * round(yValDist / 0.000001)
    else if abs(yValDist) < 0.0001 then            {-5}
      yValDist:= 0.00001 * round(yValDist / 0.00001)
    else if abs(yValDist) < 0.001 then            {-3}
      yValDist:= 0.0001 * round(yValDist / 0.0001)
    else if abs(yValDist) < 0.01 then             {-2}
      yValDist:= 0.001 * round(yValDist / 0.001)
    else if abs(yValDist) < 0.1 then              {-1}
      yValDist:= 0.01 * round(yValDist / 0.01)
    else if abs(yValDist) < 1 then                {0}
      yValDist:= 0.1 * round(yValDist / 0.1)

    else                                          {--}
      yValDist:= round(yValDist);


   {pravidelne znacky}
    i:= 0;
    while yValDist * i < ymax do begin
      DrawMarkY(yValDist * i);
      DrawValueY(yValDist * i);

      if i <> 0 then begin
        DrawValueY(-yValDist);
        DrawMarkY(-yValDist);
      end;

      inc(i);


    end;

end;


procedure TFormGraph.DrawFunction;



var
  xplus, x, y: extended;
  bMoved: boolean;
begin
  xplus:= (x1 - x0) / Img.Width;

  x:= x0;
  bMoved:= false;


  while x<= x1 do begin

    y:= Func.EvalExpr(x);
    if GetLastErrorCode = ERR_OK then begin
      if not bmoved then begin
        Img.Canvas.MoveTo(ValToPixX(x), ValToPixY(y));
        bMoved:= true;
      end
      else
        Img.Canvas.LineTo(ValToPixX(x), ValToPixY(y));
    end;

    x:= x + xplus;
  end;

end;


procedure TFormGraph.DrawNewton;
var
  i: integer;
  y: extended;
begin
 try

  with Img.Canvas do begin
    Pen.Color:= clRed;
    Pen.Style:= psSolid;

    MoveTo(ValToPixX(AproxNewton[0]), ValToPixY(0));
    for i:= 1 to CountNewton do begin
      y:= Func.EvalExpr(AproxNewton[i-1]);
  {     if Sqrt( ValToPixX(AproxNewton[i-1]) * ValToPixX(AproxNewton[i]) +
                 Sqr(ValToPixY(y)) ) >= 5 then begin}

        LineTo(ValToPixX(AproxNewton[i-1]), ValToPixY(y));
        LineTo(ValToPixX(AproxNewton[i]), ValToPixY(0));
{      end
      else
        break;}
    end;
   end;
  except
    on EInvalidOp do;

  end;

end;




procedure TFormGraph.DrawGraph;
begin
  if Func.GetLoadedExpr <> '' then begin
    isDrawn:= true;

   {vymazat puvodni}
    Img.Canvas.Brush.Color:= clWhite;
    Img.Canvas.FillRect(Img.ClientRect);

    DrawAxes;

    DrawFunction;

    if RBShowNewton.Checked then
      DrawNewton
    else if RBShowHalf.Checked then
      {DrawHalf};
  end;
end;



function TFormGraph.ValToPixX(r: extended): integer;
{prevod z hodnoty na pozici na obrazovce}
begin
  if isDrawn then
    ValToPixX:= round((r - x0) / (x1 - x0) * (Img.Width- 2*POSUN) + POSUN)
  else
    ValToPixX:= 0;
end;


function TFormGraph.PixToValX(i: integer): extended;
{prevod z pozice na obrazovce na hodnotu}
begin
  if isDrawn then
    PixToValX:= (i - POSUN) * (x1 - x0) / (Img.Width - 2*POSUN) + x0
  else
    PixToValX:= 0;
end;

function TFormGraph.ValToPixY(r: extended): integer;
begin
  if isDrawn then
    ValToPixY:= round((Img.Height - 2*POSUN) * (ymax -r) /(2*ymax)) + POSUN
  else
    ValToPixY:= 0;
end;


procedure TFormGraph.ImgMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
{zoomovani grafu}

var
  NewX, Newx0, Newx1: extended;
begin
  if isDrawn and (Button in [mbLeft, mbRight]) then begin
    if Button = mbLeft then  {leve tlacitko}
      if ((Zoom *2) <= MAX_ZOOM)  then begin  {zvetsit Zoom}
        NewX:= PixToValX(X); {budouci stred osy}

        if Zoom = 1 then
          if NewX > 3/4*(x1-x0) then
            NewX:= 3/4*(x1-x0)
          else if NewX < -3/4*(x1-x0) then
            NewX:= -3/4*(x1-x0);

        Newx0:= NewX - (x1-x0)/4; {nastaveni novych krajnich bodu}
        Newx1:= NewX + (x1-x0)/4;

        x0:= Newx0;
        x1:= Newx1;

        ymax:= ymax / 2;

        SetZoom(Zoom *2);

        DrawGraph; {prekreslit graf}
      end else

    else  {prave tlacitko}
      if (Zoom > MIN_ZOOM) then begin  {zmensit zoom}
        SetZoom(Zoom / 2);
{        if Zoom = 1 then begin
          x0:= a - (b - a)/4;
          x1:= b + (b - a)/4;
{        end
        else begin             }
          Newx0:= x0 - (x1 - x0)/2 ;
          Newx1:= x1 + (x1 - x0)/2 ;

          x0:= Newx0;
          x1:= Newx1;
//    end;
        ymax:= ymax * 2;

        DrawGraph; {prekreslit graf}
      end;

  end;

end;


procedure TFormGraph.ImgMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Screen.Cursor:= curLupa;
end;

procedure TFormGraph.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Screen.Cursor:= crDefault;
end;

procedure TFormGraph.RBShowNewtonClick(Sender: TObject);
begin
  DrawGraph;
end;

end.
