unit Axisu;
{$I DEFINE.PAS}
interface

uses
  MyType, MyLib, UlanType, Spectrum, Plotu,
  Stru, {$IFDEF WIN32} SysUtils, {$ENDIF}
  {$IFDEF DEBUG}
  DebugFrm,
  {$ENDIF}
  ShowMsg, Math;

function CreateAxisPlotting(var ApexPoints:TAcqData;
  const AUserLimit: TUserViewLimit; const AScreenDisp: TScreenDisp;
  var APlotting:TPlotting):boolean;

{function CreateAxisPlotting(var ApexPoints: TAcqData; const AScreenDisp: TScreenDisp;
  var APlotting: TPlotting): boolean;
}
const
  XMaxLen:integer = 6;
  XMaxDec:integer = 3;
  XMinDec:integer = 0;
  XMaxNonEOrder = 3;
  XMinNonEOrder = -3;


  YMaxLen:integer = 7;
  YMaxDec:integer = 5;
  YMinDec:integer = 2;
  YMaxNonEOrder = 1;
  YMinNonEOrder = -5;
implementation

type

  TAxisOpt=record
    {primary:}
    UserViewLimit:TUserViewLimit;
      { Rozsah grafu }
    ScreenSize:TScreenPoint;
      { Rozsah okna kam se kresli osy x=width, y=height }
    ScreenOrigin:TScreenPoint;
      { Left top corner of screen window coordinates }
    {calculated:}
    UserFirstSec: TUserPoint;
      { First axis dash value, >= UserViewLimit.Min }
    UserViewSize: TUserPoint;
    UserSecSize: TUserPoint;
    ScreenSecSize: TScreenPoint;
    SecCount: TScreenPoint;
      { Count of grid sections }
    SecondYAxis:boolean;
    PRX, PRY:byte;
      { What percentage of axis length is the length of the section
        dash (=1% by default) }
  end;

const

  DefXAxisName: PChar = 'Time';
  DefYAxisName: PChar = 'AU';

(*
rectangle
procedure COutText(st:string);{clear text area, outtext}
var UserFirstSec.Y,x,y:integer;
begin
  x:=textwidth(st);
  y:=textheight(st);
  UserFirstSec.X := APlotting.GetX;
  UserFirstSec.Y := gety;
  with viewport do begin
    setviewport(x1+UserFirstSec.X,y1+UserFirstSec.Y,x1+UserFirstSec.X+x,y1+UserFirstSec.Y+y,true);
    clearviewport;
  end;
  with viewport do
    setviewport(x1,y1,x2,y2,true);
  MoveTo(UserFirstSec.X,UserFirstSec.Y);
  outtext(st);
end;
*)

procedure RoundUserPars(var AUserFirstSec: single; var AUserViewSize: single;
  var AUserSecSize: single; var ASecCount:integer);
{
vstup:
  AUserFirstSec, (hodnota prvniho dilku na ose (puvodne leva hranice grafu))
  AUserViewSize, (rozsah grafu, osy)
  ASecCount;(pocet dilku, na nez se deli osa(minimalne 3),
    muze byt zmeneno, je urcite zmeneno kdyz < 3)

vystup:
  zaokrouhli se AUserFirstSec (podle ASecCount a AUserViewSize),
  AUserSecSize (velikost jednoho dilku osy)
  ASecCount  pocet dilku na ose
}

const
  ars=3;
  allowedSecSizes: array[1..ars] of byte = (1, 2, 5);{(1, 2, 4, 5, 8);}
   {povolene velikosti dilku}

var
  r, {rg, }r1, {r2,} mg, m: real;
  i, {j, k, }l, lg: integer;
{  en: boolean;}
  OUserFirstSec{, OUserSecSize}: real; {original AUserFirstSec and OUserSecSize}

begin
{  en := false;}
  OUserFirstSec := AUserFirstSec;
{  OUserSecSize := AUserViewSize;}
  if (ASecCount < 3) or (ASecCount > 100) then begin
    ASecCount := 10;
  end;

  r := AUserViewSize / ASecCount;
  GetManLog(r, m, l);{m mantisa of the z range, l order of the z range}
  GetManLog(AUserFirstSec, mg, lg);{mg mantisa of th z start value,
    lg order of the z start value}
  {i :=1;}
  r1 := 0;{ just to satisfy compiler }

  for i := 1 to ars do begin
    if i = ars then begin
      l := succ(l);
      r1 := allowedSecSizes[1];
      break;
    end;
    r1 := allowedSecSizes[i];
    if abs(m) <= (r1*1.2) then begin
      break;{en:=true}
    end;
  end;
  {
  while not en do begin
    r1 := allowedSecSizes[i];
    if abs(m) <= r1 then
      en:=true
    else begin
      i:=succ(i);
      if i = succ(ars) then begin
        en := true;
        l := succ(l);
        i := 1;
      end;
    end;
    r1 := allowedSecSizes[i];
  end;
  }

  {
  else begin
    r1:=abs(graphscale);
    if m>r1 then
      l:=succ(l);
  end
  }
  ;
  AUserSecSize := r1 * exp10(l);
  r1 := exp10(lg-l);
  AUserFirstSec := exp10(l) * int(mg*r1) - AUserSecSize;
  while AUserFirstSec < OUserFirstSec do
    AUserFirstSec := AUserFirstSec + AUserSecSize;
  ASecCount := round((AUserViewSize -
    (AUserFirstSec - OUserFirstSec)) / AUserSecSize)
  ;
    {don't get the axis start out of graphic range}

{
  AUserViewSize:=AUserSecSize*ASecCount;
  while AUserFirstSec + AUserViewSize > OUserSecSize + OUserFirstSec do
    AUserViewSize := AUserViewSize - AUserSecSize;
}
{$IFDEF DEBUG}
  DebLog('RoundUserPars: UsrFrstSec:' + FloatToStr(AUserFirstSec) + ' ViewSize:' +
    FloatToStr(AUserViewSize) + ' -> SecSize:' + FloattoStr(AUserSecSize) + ' SecCnt:' +
    IntToStr(ASecCount));
{$ENDIF}
end;


function CreateAxisPlotting(var ApexPoints:TAcqData;
  const AUserLimit: TUserViewLimit; const AScreenDisp: TScreenDisp;
  var APlotting:TPlotting):boolean;

{UserFirstSec.X,UserFirstSec.Y:real;xname,yname:pathstr}
{kresli osy x,y, prusecik v poloze UserFirstSec.X,UserFirstSec.Y, v aktivnim viewportu,
 podle ScreenAxisOpt a UserAxisOpt;SecCount.X,SecCount.Y useky-delka kolmych carek na ose x-prx procent
 celkove delky osy y,podobne pry; popisy  jmen osy,hodnoty pruseciku,
 max hodnot os}

var
  AxisOpt:TAxisOpt;
  th:TScreenY;
  {tw:TScreenX;}

  procedure GetAxisOpt;
  begin
    {primary:}
    AxisOpt.UserViewLimit := AUserLimit;{ApexPoints.SpectrumOpt.UserLimit;}
    AxisOpt.ScreenSize.X := AScreenDisp.Right - AScreenDisp.Left + RectInclusive;{AScreenDisp.Width;}
    AxisOpt.ScreenSize.Y := AScreenDisp.Bottom - AScreenDisp.Top + RectInclusive;{AScreenDisp.Height;}
    AxisOpt.ScreenOrigin.X := AScreenDisp.Left;
    AxisOpt.ScreenOrigin.Y := AScreenDisp.Top;

    {calculated:}
    AxisOpt.UserFirstSec := AxisOpt.UserViewLimit.Min;
      {.. will be adjusted (rounded) by RoundUserPars}

    AxisOpt.UserViewSize.X := AxisOpt.UserViewLimit.Max.X -
      AxisOpt.UserViewLimit.Min.X;
    AxisOpt.UserViewSize.Y := AxisOpt.UserViewLimit.Max.Y -
      AxisOpt.UserViewLimit.Min.Y;
      {.. just for faster calculations}

    AxisOpt.UserSecSize.X := 0;
    AxisOpt.UserSecSize.Y := 0;
      {.. will be set by RoundUserPars}

    AxisOpt.ScreenSecSize.X := 0;
    AxisOpt.ScreenSecSize.Y := 0;
      {.. will be set by RoundUserPars}

    AxisOpt.SecCount.X := 0;
    AxisOpt.SecCount.Y := 0;
      {.. will be set by RoundUserPars}

    AxisOpt.PRX := 1;
    AxisOpt.PRY := 1;
      {.. defaults mylib}

    RoundUserPars(AxisOpt.UserFirstSec.X, AxisOpt.UserViewSize.X,
      AxisOpt.UserSecSize.X, AxisOpt.SecCount.X);

    RoundUserPars(AxisOpt.UserFirstSec.Y, AxisOpt.UserViewSize.Y,
      AxisOpt.UserSecSize.Y, AxisOpt.SecCount.Y);

    ApexPoints.UserSizeToScreenSize(AxisOpt.UserSecSize, AScreenDisp, AxisOpt.ScreenSecSize);
  end;


  procedure WriteBelowXAxis(AText:string; var ALastTextRightEdge:TScreenX;
    X:TScreenX; CanWriteOver:boolean);
  {psani textu pod osu x zleva, kontrola na prepsani,
   posl. pozice: ALastTextRightEdge; }
  var
    {w,h,
    dx, dy,}
    tw,{th,}
    y:integer;
    {v0.09}
    tx:integer;
    ttw:integer;
    {/v0.09}
  begin
    {v0.09}
    ttw := APlotting.TextWidth(trim(AText));
    {/v0.09}
    AText:= trim(AText)+ ' ';
    y := APlotting.GetY;
    tw := APlotting.TextWidth(AText);
    {th := APlotting.TextHeight(AText);}
    {v0.09}
    tx := x - (ttw div 2);
    {/v0.09 tx := x;}

    if tx < 0 then
      tx := 0;
    if (tx + tw) >= (AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.X) then
      tx := AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.X - 1;
    if tx < 0 then
      tx := 0;

    if y < 0 then
      y := 0;
    {if y > dy - th then
      y := dy - th;}
    if tx < ALastTextRightEdge then begin
      if not CanWriteOver then
        exit
      else
        AText:=' '+AText;
    end;
    ALastTextRightEdge := tx + tw;
    APlotting.MoveTo(tx,y);
    APlotting.ClearTextOut(tx, y, AText);
  end;

  procedure AxisNumToStr(r:real; Len, Dec:integer; var st:string);
  {var {i,j,l,k:integer; st1:string;}
  begin
     st := FloatToStrF(r, ffFixed, len, dec);
     {
     r:=r*100;
     if r > 0 then
       r:=r + 0.5
     else
       r:=r - 0.5;
     r := int(r) / 100;
     str(r:5:2, st);}
  end;

  procedure CutAxisStr(var st:string);
  {var i,j:integer;}
  begin
    if pos('E',st)>0 then
      st:=copy(st,1,length(st)-4);
  end;

  procedure WriteNextToYAxis(AText:string; var ALastTextUpperEdge:TScreenY;
    Y:TScreenY; CanWriteOver:boolean);
  {psani textu podel osy y odspoda, kontrola na prepsani, posl. pozice: ALastTextUpperEdge}
  var
    tw, x: TScreenX;
    {th: TScreenY;}
  begin
    AText := AText;
    x := APlotting.GetX;
    tw := APlotting.TextWidth(AText);
    {th := APlotting.TextHeight(AText);}
    if x < 0 then
      x := 0;
    if (x + tw) >= (AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.X) then
      x := (AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.X - tw);
    if x < 0 then
      x := 0;

    if y < 0 then
      y:=0;
    if y > (AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.Y - 1) then
      y := AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.Y - 1;
    if (y + th) > ALastTextUpperEdge then begin
      if not CanWriteOver then
        exit;
    end;
    ALastTextUpperEdge := y;
    APlotting.MoveTo(x,y);
    APlotting.ClearTextOut(x, y, AText);{textout}
  end;

  procedure UserXYToScreenXY(x:TUserX; y:TUserY; var gx:TScreenX; var gy:TScreenY);
  var
    userPoint:TuserPoint;
    screenPoint:TScreenPoint;
  begin
    userPoint.X := x;
    userPoint.Y := y;
    gx := 0;
    gy := 0;
    if not ApexPoints.UserToScreen(userPoint, AScreenDisp, screenPoint) then
      exit;
    gx := screenPoint.x;
    gy := screenPoint.y;
  end;

  procedure DrawXAxis;
  var
    x:TUserX;
    y:TUserY;

    i:TScreenX;
    j:TScreenY;
    m, iy1, iy2:TScreenY;
    LastTextRightEdge:TScreenX;
    secman,uman:real;
    seclog,ulog, curdec:integer;
    UserValueOrder:real;
    st:string;
  begin
    GetManLog(AxisOpt.UserViewLimit.Max.X, uman, ulog);
    if (ulog <= XMaxNonEOrder) and (ulog >= XMinNonEOrder) then
      ulog := 0;
    {if (ulog = 1) or (ulog = -1) then
      ulog := 0;}
    GetManLog(AxisOpt.UserSecSize.X, secman, seclog);
    {curdec := XMaxDec;}
    if seclog < 0 then begin
      curdec := -seclog;
    end else begin
      curdec := 0;
    end;
     if curdec > XMaxDec then
      curdec := XMaxDec;
    if curdec < XMinDec then
      curdec := XMinDec;

    UserValueOrder := exp10(ulog);
    LastTextRightEdge := 0;

    m := round(AxisOpt.ScreenSize.Y / 100 * AxisOpt.PRX);
    x := AxisOpt.UserFirstSec.X;
    y := AxisOpt.UserViewLimit.Min.Y;{UserFirstSec.Y;}

    iy1 := AxisOpt.ScreenOrigin.Y + AxisOpt.ScreenSize.Y  + m;
    iy2 := AxisOpt.ScreenOrigin.Y + AxisOpt.ScreenSize.Y - 1;

    while x <= AxisOpt.UserViewLimit.Max.X do begin
      {draw sections on X axis:}
      UserXYToScreenXY(x,y,i,j);
      APlotting.Line(i,iy1,i,iy2);
      if x = AxisOpt.UserFirstSec.X then begin
        AxisNumToStr(AxisOpt.UserFirstSec.X / UserValueOrder, XMaxLen, curDec, st);
        APlotting.MoveTo(i, AxisOpt.ScreenOrigin.Y + AxisOpt.ScreenSize.Y
          + 2* m);
        WriteBelowXAxis(st, LastTextRightEdge, i, true);
      end;

      if x > AxisOpt.UserFirstSec.X then {if x<>(UserViewSize.X+UserViewLimit.Min.X) then}
      begin
        AxisNumToStr(x{re+ AxisOpt.UserFirstSec.X)}/UserValueOrder, XMaxLen, curDec, st);
        CutAxisStr(st);
        APlotting.MoveTo(i, AxisOpt.ScreenOrigin.Y + AxisOpt.ScreenSize.Y + 2*m);
        {re:= re + r;}
        WriteBelowXAxis(st,LastTextRightEdge,i,false);
      end;
      x := x + AxisOpt.UserSecSize.X;
    end;

    x := AxisOpt.UserViewLimit.Max.X - AxisOpt.UserSecSize.X;
    UserXYToScreenXY(x,y,i,j);
    APlotting.SetLineStyle(0,0,0);
    if ulog <> 0 then begin
      str(ulog:3,st);
      st:=' (*10E'+trim(st)+')';
    end else
      st:='';
    st:={xname+}st + ' ';
    i := AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.X - APlotting.TextWidth(st);
    APlotting.MoveTo(i, AxisOpt.ScreenOrigin.Y + AxisOpt.ScreenSize.Y + th + m);
    WriteBelowXAxis(st, LastTextRightEdge, i, true);
  end;

  procedure DrawYAxis;
  var
    {textx:TScreenX;}

    x,tx: TUserX;
    y:TUserY;

    tofs,m,i,ix1,ix2:TScreenX;
    j{,th}:TScreenY;

    ulog, seclog:integer;
    uman, secman:real;
    curdec:integer;

    LastTextUpperEdge:TScreenY;
    UserValueOrder:real;
    st:string;
    cw:TScreenX;
  begin
    GetManLog(AxisOpt.UserViewLimit.Max.Y, uman, ulog);
    if (ulog <= YMaxNonEOrder) and (ulog >= YMinNonEOrder) then
      ulog := 0;
    UserValueOrder := exp10(ulog);
    GetManLog(AxisOpt.UserSecSize.Y, secman, seclog);
    curdec := YMaxDec;
    if seclog < 0 then begin
      curdec := -seclog;
      if curdec > YMaxDec then
        curdec := YMaxDec;
      if curdec < YMinDec then
        curdec := YMinDec;
    end;


    LastTextUpperEdge := AxisOpt.ScreenSize.Y + AxisOpt.ScreenOrigin.Y;


    m := round(AxisOpt.ScreenSize.X / 100 * AxisOpt.PRY);
    x := AxisOpt.UserViewLimit.Min.X;
    tx := x + AxisOpt.UserSecSize.X;
      { to be sure the x will be inside the region }

    ix1 := AxisOpt.ScreenOrigin.X - m;
    ix2 := AxisOpt.ScreenOrigin.X;

    {th := APlotting.TextHeight(' ');}
    cw := APlotting.TextWidth(' ');
    {if ix2 > ScreenSize.X then
      ix2 := ScreenSize.X;}
    {
    GetManLog(AxisOpt.UserSecSize.Y, re, lg);
    re := round(re) * exp10(lg);
    r := re;
    if pry > 10 then
      APlotting.SetLineStyle(1, 0, 0);
    }

    y := AxisOpt.UserFirstSec.Y;
    while y <= AxisOpt.UserViewLimit.Max.Y do begin
      UserXYToScreenXY(tx, y, i, j);
      APlotting.Line(ix1, j, ix2, j);
      j := j - th div 2;

      if y = AxisOpt.UserFirstSec.Y then begin
        AxisNumToStr(AxisOpt.UserFirstSec.Y / UserValueOrder, YMaxLen, curdec{YMaxDec}, st);
        if y < 0 then
          tofs := 0{cw}
        else
          tofs := 0;

        APlotting.MoveTo(AxisOpt.ScreenOrigin.X - m - cw - tofs -
          APlotting.TextWidth(st), j);
        WriteNextToYAxis(st, LastTextUpperEdge, j, true);
      end;

      if y > AxisOpt.UserFirstSec.Y then begin
        if y <> (AxisOpt.UserViewSize.Y + AxisOpt.UserViewLimit.Min.Y) then
        begin
          AxisNumToStr(y / UserValueOrder, YMaxLen, curDec, st);
          CutAxisStr(st);
          if y < 0 then
            tofs := 0{cw}
          else
            tofs := 0;
          APlotting.MoveTo(AxisOpt.ScreenOrigin.X - m - cw - tofs -
            APlotting.TextWidth(st), j);
          WriteNextToYAxis(st, LastTextUpperEdge, j, false);
        end;
      end;
      y := y + AxisOpt.UserSecSize.Y;
    end;
    {y := y - AxisOpt.UserSecSize.Y;
    {y := AxisOpt.UserViewLimit.Max.Y;
    UserXYToScreenXY(x,y,i,j);
    APlotting.SetLineStyle(0, 0, 0);}

    i := AxisOpt.ScreenOrigin.X;
    j := AxisOpt.ScreenOrigin.Y;
    if ulog <> 0 then begin
      str(ulog, st);
      st:=' (*10E' + trim(st) + ')';
    end else
      st:='';
    st := {yname + }st + ' ';
    i := i - APlotting.TextWidth(st) - m;
    APlotting.MoveTo(i, j);
    WriteNextToYAxis(st, LastTextUpperEdge, j, true);
  end;

begin
  CreateAxisPlotting := false;
  APlotting.Clear;
   {xname := 'X';
  yname := 'Y';}
  th := APlotting.TextHeight('M');
  {tw := APlotting.TextWidth(' ');}
  {rlen := 8;
  tofs := 3;}

  GetAxisOpt;

  if ((AxisOpt.UserSecSize.X <= 0) or
      (AxisOpt.UserViewSize.Y <= 0) or
      (AxisOpt.UserSecSize.Y <= 0) or
      (AxisOpt.UserViewSize.X <= 0)) then
  begin
    ShowMessage(' Incorrect graph. parameters (in CreateAxisPlotting)',smError,0);
    exit;
  end;
  APlotting.Rectangle(
    AxisOpt.ScreenOrigin.X,
    AxisOpt.ScreenOrigin.Y,
    AxisOpt.ScreenOrigin.X + AxisOpt.ScreenSize.X - RectInclusive,
    AxisOpt.ScreenOrigin.Y + AxisOpt.ScreenSize.Y - RectInclusive
  );

  DrawXAxis;
  DrawYAxis;
  CreateAxisPlotting := true;
end;

end.