unit Drawings;

interface
uses ExtCtrls,graphics,common,forms,sysutils,types,StdCtrls,math,porto;
type
    TSmallIntFile = file of smallInt;

                var                                        PCrange, PCrange0,cpstrVizXrange : word;
                                                          startOsc,dataBlockLength,count_cp : longWord;
                  f1wspx2,f1wspy2,f1wspx4,f1wspy4,wspx1,wspy1,wspx2,wspy2,wspx3,wspy3,wspx4,
  f1wspx6,f1wspy6,wspy4,wspx5,wspy5,wspx6,wspy6,wspx8,wspy8,f2Panel2Stretch,f2Panel1Stretch : double;        //współczynniki proporcjonalności dla poszczególnych paneli
                f1centr2,f1centr4,centr1,centr2,centr3,centr4,centr5,centr6,centr8,f1centr6 : integer;        //centra pionowe dla poszczególnych paneli
                     back1,next1,back10,next10,decwspx1, incWspx1,begWave,backWave,nextWave,
                                                                     wholeWave,rep,exitWave : boolean;       //waveShow wait biass, etc.
                                                                             panel2Decimals : shortInt;
                                                                               T0dashLength : byte=5;
                                                                                spongeShift : word;
                                                                                    WSstart : boolean=false; //18022024
                                                                                  GraphAgain : boolean=false;
                                                                             prevHighBorder : word;          //18022024 , 23022024
                                                                                    T0Clear : boolean;
procedure linReg(const ArrX,ArrY:array of double;ext:word; out A,B:extended);
procedure SearchLimits(out lowerLimit,upperLimit:smallInt; const edit:Tedit);
procedure markSoundsBorders(const k:dword;lborderAddr,hborderAddr:int64;fontColor:longint;callChain:string);
procedure label7Show;
procedure FramesRepaint(spcFrameCount:word; const T0Clear:boolean);
procedure frame(Panel:TPanel;canvas:tcanvas;const dataBlockLength:longWord; const stretch:double;
 const decimals:shortInt; out wspx,wspy:double; out centr:integer; const half,revers:boolean);
procedure waveShow(canvas:Tcanvas;Panel:Tpanel;dataBlockLength:longWord; var waveFile:TsmallIntFile);
procedure OscDraw(panel:tpanel;canvas:tcanvas; const y:TdbArr; wspx,wspy : double; centr:integer;
  const count:longWord; const clear:boolean;const chartColor:longInt; it:longWord; var annotation:boolean);
procedure SpcDraw(panel:tpanel;canvas:tcanvas; const y:TdbArr; wspx,wspy:double; const centr:integer;
  dataLength:Word; clear:boolean; const chartColor:longint; const clip:boolean);
procedure FilterDraw(filter:array of double;form:tform;panel:tpanel;label0:Tlabel; penCol:longint; out wspx,wspy:double; out centr:integer);
procedure WeightsDraw(var weights:array of double;form:tform;panel:tpanel;labelx:Tlabel; radio:Tradiogroup;out wspx,wspy:double; out centr:integer);
procedure lifterLine(cpstrPoint:word;wspx,wspy:double;lineColor:tcolor;goOn:boolean; panel:Tpanel);
function T0annotate(panel:Tpanel;chartColor:longint;var zn1,zn2:char):boolean;
procedure HorizTic(Panel:TPanel;canvas:tcanvas;const wspx:double; const xStretch,Xrefr:dword;Yrefr,Drawcolor:integer);
procedure T0plot(const iw:dword; T0:word;callChain:string);

implementation

uses newSpectrum,visualization,unit1,dialogs;

var
   temp,tempus : variant;
 T0ColorChange : boolean;
       T0Color : longInt;
       exLeft : boolean=true;

procedure linReg(const ArrX,ArrY:array of double; ext:word; out A,B:extended);
 (* computes  linear regression equation coefficients of transformation ArrX into ArrY, i.e. ArrY=A*ArrX+B
  A={S[(Xi-meanX)*(Yi-meanY)]/S(Xi-meanX)**2};
   B=meanY-A*meanX;
 *)
 var i:word; sxy,sx,sy,sqrx,sqsx:extended;
 Begin
  A:=0; B:=0; sxy:=0; sx:=0; sy:=0; sqrx:=0;
  for i:=0 to ext do
   begin
    sxy:=sxy+ArrX[i]*ArrY[i];
    sx:=sx+ArrX[i];
    sy:=sy+ArrY[i];
    sqrx:=sqrx+sqr(ArrX[i]);
   end;
   inc(ext);
   A:=(ext*sxy-sx*sy)/(ext*sqrx-sqr(sx));
   B:= sy/ext-A*sx/ext;
 End;{linReg}

procedure SearchLimits(out lowerLimit,upperLimit:smallInt; const edit:Tedit);
  var
  l,k:byte;
  s:shortString;
  Begin
   k:=pos(',',edit.Text);
   l:=length(edit.Text);
   s:=copy(edit.Text,k+1,l-k+1);
   upperLimit:=strToInt(s); if upperLimit<=0 then upperLimit:=-upperLimit;
   s:=copy(edit.Text,0,k-1);
   lowerLimit:=strToInt(s);
   End;{SearchLimits}

procedure markSoundsBorders(const k:dword;lborderAddr,hborderAddr:int64;fontColor:longint;callChain:string);
 var L, txtWide,U,X0:longint;
  Begin
   callChain:=callChain+'>markSoundsBorders';
   try
   L:=(LborderAddr div JumpNbrSamples);   if L<0 then L:=0;                            //number of slices
   X0:=L-(L mod (form2.panel1.width div T0dashLength));  //15022024;
   if not form2.checkBox1.checked and not form2.checkBox94.checked then exit;      //13022024 u.a. in order to dodge erronous computation of the   T0sliceNbrGlob    block 25032024
   txtWide:=form2.Canvas.TextWidth(intToStr(k));      //01032024
   with form2.Panel1, form2.Canvas  do         //a sound begining marker
    begin
     font.color:=fontColor;   //28022024
     HborderAddr:=left+(round((HborderAddr div jumpNbrSamples)*wspx1) mod width)+2*form2.Panel9.width;
     LborderAddr:=left+round(L*wspx1) mod width;
     if LborderAddr>HborderAddr then    //turning border point
     begin
      Pen.Color:=clBtnShadow;
      rectangle(prevHighBorder+1+txtWide,top+1,LborderAddr-1,top+height-1); //28022024, clear between windows space
      Pen.Color:=clRed;
      rectangle(LborderAddr+1,top+1,width+left-1,top+height-1);//swing (przepływ)
      application.ProcessMessages;                           //debug prp 07022024
      rectangle(left+1,top+1,HborderAddr,top+height-1);
      Pen.Color:=clBtnShadow;                                           //forspace clearing czyszczenie przedpola
      rectangle(HborderAddr,top+1,HborderAddr+txtWide+2,top+height-1); //forspace clearing czyszczenie przedpola
      textOut(HborderAddr+2,top+height+font.Height-2,intToStr(k));
      textOut(LborderAddr-2-txtWide,top+height+font.Height-2,intToStr(k));
      application.ProcessMessages;                                  //debug prp 07022024
      Pen.Color:=clWhite;
      moveTo(LborderAddr,(top+height) div 2);
      lineTo(width+left,(top+height) div 2);
      moveTo(left,(top+height) div 2);
      lineTo(HborderAddr,(top+height) div 2);
     end
     else
     begin
      form2.Canvas.Pen.Color:=clBtnShadow;
      if prevHighBorder>LborderAddr then
       begin
        rectangle(prevHighBorder+1+txtWide,top+1,width+left-1,top+height-1); // 1.clear between prev. sound frame end and previous T0 plot windows end      20022024
        Pen.Color:=clBtnShadow;                                               //forspace clearing czyszczenie przedpola
        rectangle(HborderAddr,top+1,HborderAddr+txtWide+2,top+height-1); //forspace clearing  czyszczenie przedpola
        textOut(HborderAddr+2,top+height+font.Height-2,intToStr(k)); application.ProcessMessages;                               //debug prp 01032024
        textOut(LborderAddr-2-txtWide,top+height+font.Height-2,intToStr(k)); application.ProcessMessages;                           //debug prp 01032024
        rectangle(left+1,top+1,LborderAddr-1,top+height-1); // 2.clear between new T0 plot windows begining and current sound frame space   20022024
       end
      else
       rectangle(prevHighBorder+1+txtWide,top+1,LborderAddr-1,top+height-1); //18022024, clear between windows space;
      Pen.Color:=fontColor;
      rectangle(LborderAddr,top+1,HborderAddr,top+height-1);
      Pen.Color:=clBtnShadow;                                               //forspace clearing czyszczenie przedpola
      rectangle(HborderAddr,top+1,HborderAddr+txtWide+2,top+height-1); //forspace clearing czyszczenie przedpola
      textOut(HborderAddr+2,top+height+font.Height-2,intToStr(k));                                                            //debug prp
      textOut(LborderAddr-2-txtWide,top+height+font.Height-2,intToStr(k)); application.ProcessMessages;                           //debug prp 01032024
      Pen.Color:=clWhite;
      moveTo(LborderAddr,(top+height) div 2);
      lineTo(HborderAddr,(top+height) div 2);
     end;
     prevHighBorder:=HborderAddr;   zn1:=#0;                          //18022024, clear between windows space; annotate new section  in T0 plot
    end;
   if X0 mod (form2.panel1.width div T0dashLength)<>0 then showMessage(callChain+#13#10'X0b='+intToStr(X0)+' mod (panel1.width div T0dashLength)<>0, ='+intToStr( X0 mod (form2.panel1.width div T0dashLength)));
   //try
   except
    ShowMessage('Some Error occured in '+callChain);
   end;
   application.ProcessMessages;                                      //width=nbr_of_slices_on_the_T0_plot=panel.width div dash_length
  End; {markSoundsBorders}                                           //start mod width = 0 - is a must!

  procedure HorizTic(Panel:TPanel;canvas:tcanvas;const wspx:double; const xStretch,Xrefr:dword;Yrefr,Drawcolor:integer);
  { Yrefr - polozenie podpisu w pionie
    Xrefr - odniesienie poczatkowe opisu wsp X
  }
   var d:double; i:byte; x:integer; s:shortString;
   Begin
     d:=xStretch/20;
    with Panel,canvas do
     begin
      pen.Color:=DrawColor;  font.Color:=DrawColor;
      for i:=0 to 20 do
       begin
        x:=round(left+round(i*d)*wspx);
        if i mod 2=0 then
         begin
          moveTo(x,Yrefr);
          lineTo(x,Yrefr+10);  application.processmessages;           //tic
          s:=intToStr(round(i*d)+Xrefr);                             //nbr
          TextOut(x,Yrefr-6-Font.Height,s);                          //write nbr
         end
        else
         begin
          moveTo(x,Yrefr);
          lineTo(x,Yrefr+6);  application.processmessages;        //small tic
         end;
       end;
     end;{with}
   End;{HorizTic}

 procedure T0plot(const iw:dword; T0:word;callChain:string);
 {
 The plot outside source parameters:
 iw             - the time sample index in a wav file
 T0             - fundamental frequency period
 jumpNbrSamples - step of spectral analyses
 T0sliceNbrGlob - T0 plot X axis start point slice nbr

 math rules:
  start=addr-addr mod width
  width=nbr_of_slices_on_the_T0_plot=panel.width div dash_length
  T0_plot_X_addr=sliceNbr*dash_length*wspx
  sliceNbr=(iw div jumpNbrSamples);  rule:
  start mod width = 0 - is a must to actualise X axis description
  =T0sliceNbrGlob mod (panel1.width div T0dashLength)
 }
   var x,X0,xiw,y: longint;

   Begin   //-----------------T0Plot-------------------
    callChain:=callChain+'>T0plot';
    if windowsSearching and not form2.CheckBox75.Checked and timeBack then exit; //do not plot T0 in the windows searching mode when "show averaging" is unchecked and averaging is pending, but plot it for different windows after averaging is done.
    with form2.Panel1, form2.Canvas  do
      begin
       xiw:=(iw div jumpNbrSamples);   //number of slices till up to now
       x:=round(xiw*wspx1)mod width;           //(iw div paramRec.pJump) provides index  of a current slice (a number), while iw itself is a sample time index
       X0:=xiw-(xiw mod (form2.panel1.width div T0dashLength));
       if T0sliceNbrGlob<>X0 then
        with form2.Panel1 do   //************************** **************************************
         begin
     {!}  T0sliceNbrGlob:=X0;                                            //21032024; xiw-(xiw mod (form2.panel1.width div T0dashLength));

          zn1:=#0;                     //1312018
          form2.Canvas.Pen.Color:=clBtnShadow;
          with form2.Panel1 do Rectangle(left,top+Height+3, left+Width+9, -form2.canvas.font.Height+top+Height+8);                     //31122023 clear previous X axis description
          form2.Canvas.Pen.Color:=clWhite;
          application.ProcessMessages;

          if T0timeBack  then //it is case, when X axis  changes as a result of a "Back" operation (taste or averaging visualisation)
            T0timeBack:=false
          else
           begin   //for the case, when X axis does not change as a result of a "Back" operation, but as a result of reaching the right end of the T0 plot's window
            if WSstart then xiw:=0;
            if windowsSearching then  form2.canvas.Pen.Color:=clwhite;
           end;{if T0timeBack}

          T0ColorChange:= not T0ColorChange;
          if T0ColorChange then T0Color:=clLime else T0Color:=clblue;
          horizTic(form2.panel1,form2.canvas,wspx1,form2.panel1.Width div T0dashLength,T0sliceNbrGlob,form2.panel1.top+form2.panel1.height,T0Color);  //24122023  T0sliceNbrGlob 03032024
          form2.Panel9.Left:=left+1;
          form2.canvas.moveTo(left+width,top);
          form2.Canvas.Pen.Color:=clWhite;
          form2.Canvas.LineTo(left+width,top+height+1);
          form2.Canvas.Pen.Color:=clred;
          application.ProcessMessages;          //without it the back plot line is cleared! (bez tego linia zamykaj. jest kasowana!!!)
         end; {if xtr=0} //************************** **************************************

       y:=round(T0*wspy1+top);   //*1000/rate 12012024
       if -spongeShift+x+form2.Panel1.left+T0dashLength+1>=form2.Panel1.left then   //09012025
        form2.Panel9.Left:=x+left+T0dashLength+1-spongeShift; //sponge (gąbka)
       form2.canvas.Pen.Color:=T0Color;
       moveTo(x+left+1,y);
       lineTo(x+left+T0dashLength,y); //draw horizontal mark T0dashLength; pts long
      end;{with panel1}
     with form2 do T0annotate(panel1,Canvas.Pen.Color,zn1,cath.TcPhon);//T0 plot annotation
     WSstart:=false;
   End; {T0plot}

procedure label7Show;
Begin
  with form2 do
  begin   //function *1E-4
   if (checkBox83.Checked ) and not (checkBox82.Checked ) and checkBox7.Checked and checkBox8.Checked then  //{08122023}    27012024
    begin
     label7.Visible:=false;  //27012024
     label7.Visible:=true
    end
   else
     label7.Visible:=false;
  end;{with}
 Application.ProcessMessages 
End;

procedure FramesRepaint(spcFrameCount:word; const T0Clear:boolean);
  //repaints drawings frames
 Begin
  with form2 do
  Begin

  //{ with F0panelDecHalfRev do} Frame(form2.Panel1,form2.canvas,form2.panel1.Width div T0dashLength,{y1range}1000*F0count/(2*rate),{r}Decimals,wspx1,wspy1,centr1,{r}Half,{r}Rev);
  // with form2 do HorizTic(Panel2,canvas,wspx1,form2.panel1.Width div T0dashLength,0,panel1.top+panel1.height);
  //panelSet:=[]; T0sliceNbrGlob:=0;

   if spcFrameCount>=count then spcFrameCount:=count-1;
   if T0Clear then
    begin
     with form2 do  //02112024
      Begin
       case RadioGroup8.ItemIndex of //T0 limit [ms]
        0: f2Panel1Stretch:=10;
        1: f2Panel1Stretch:=20;
        2: f2Panel1Stretch:=1000*count/(2*rate);
       end;{case}
       Frame(Panel1,canvas,panel1.Width div T0dashLength,f2Panel1Stretch,1,wspx1,wspy1,centr1,true,true);   //02112024                //T0 <=20 ms
      End;{with}
     with form2 do HorizTic(Panel2,canvas,wspx1,form2.panel1.Width div T0dashLength, T0sliceNbrGlob, panel1.top+panel1.height,clBlack);
     wspy1:=wspy1*1000/rate;
    end;{if Clear}

   Frame(Panel2,canvas,count-1,f2Panel2Stretch,panel2Decimals,wspx2,wspy2,centr2,false,false);   //wav
    HorizTic(Panel2,canvas,wspx2,count-1,0,panel2.top+panel2.height,clBlack);

   Frame(Panel3,canvas,spcFrameCount,PSrange,3,wspx3,wspy3,centr3,false,false);                  //PS
   HorizTic(Panel2,canvas,wspx3,spcFrameCount,0,panel3.top+panel3.height,clBlack);

   if not checkBox95.Checked then
    begin
     Frame(Panel4,canvas,spcFrameCount,PCrange,2,wspx4,wspy4,centr4,false,false);                  //PC
     HorizTic(Panel2,canvas,wspx4,spcFrameCount,0,panel4.top+panel4.height,clBlack);
    end
   else
    begin
     Frame(Panel4,canvas,cpstrPoint+1,PCrange,2,wspx4,wspy4,centr4,false,false);
     HorizTic(Panel2,canvas,wspx4,cpstrPoint,0,panel4.top+panel4.height,clBlack);
    end;

   Frame(Panel5,canvas,spcFrameCount,3,3,wspx5,wspy5,centr5,false,false);                       //angle
   HorizTic(Panel2,canvas,wspx5,spcFrameCount,0,panel5.top+panel5.height,clBlack);
   label7Show;
  End;
 End;{FramesRepaint}
               //   1            2                  3                        4
procedure frame(Panel:TPanel;canvas:tcanvas;const dataBlockLength:longWord; const stretch:double;
 const decimals:shortInt; out wspx,wspy:double; out centr:integer; const half,revers:boolean);
 //       5                    6     7                8                    9     10
  //Wykreśla ramki pokazujące pole wykresu
  //wspx,wspy - współczynniki proporcjonalności dla okna
  //centr - środek w pionie pola rysunku
  //intervalsAmount - liczba przedziałów zmiennej na półosi (zależna od wysokości cyfr)
  var i,j,k,intervalsAmount:integer; d:integer; s:shortString;

  procedure tick(const sign:shortInt);
  Begin
   k:=j;
   with panel,canvas do
    begin
     if half then
      if revers then j:=top+sign*round(i*height/intervalsAmount)
      else j:=centr-sign*round(i*height/intervalsAmount)
     else j:=centr-sign*round(i*height/(2*intervalsAmount));
     if revers and half then d:=d-j+k
     else d:=d+j-k;                     //d increases until a tick number is writen down, then d:=0
     moveTo(left-3,j);
     lineTo(left,j); application.processMessages;     //tick
    end;
  End;{tick}

  procedure number(const sign:shortInt);
   var m:Integer;
   begin
    with panel,canvas do
     begin
      if sign*d<Font.Height then
      begin
       s:=floatToStrF(sign*(i*stretch/intervalsAmount),ffFixed,8,decimals);
       for m:=1 to 7-length(s) do s:=' '+s;  //equalizing of the description length to 8 by adding spaces fom front
       canvas.TextOut(left-6*font.Size,j+Font.Height,s);   application.processMessages;
       moveTo(left-5,j);
       lineTo(left+1,j); application.processMessages;     // numbers tick
       d:=0;                             //d increases until a tick number is writen down, then d:=0
      end;{if}
     end;{with}
   end;{number}

  Begin  //--------------------------------Frame---------------------------
   with Panel,canvas do
    begin
     panel.visible:=false;
     panel.Font.Height:=8;
     if half then
      begin
       intervalsAmount:=height div 16;   //tick interval
       centr:=top+height;
       wspy:=height/stretch;
      end
     else
      begin
       intervalsAmount:=height div 16;   //tick interval
       centr:=round(top+height/2);
       wspy:=0.5*height/stretch;
      end;
     wspx:=width/dataBlockLength;   //-1 091218 -0 161218
     Pen.Color:=clred;                                         //------------------------------------a ten jest!
     Rectangle(2,top-5,left+Width+10,  top+Height-font.Height+10); application.processMessages;  //big rectangle
     Pen.Color:=clwhite;
     Rectangle(left,top,left+Width, top+Height+2); application.processmessages;                  //exact rectangle
     moveTo(left,centr);                                           //--------------------ten kolor nie jest aktywowany!
     lineTo(left+width,centr);    application.processmessages;
     d:=0; if revers and half then j:=top else j:=centr;
     for i:=1 to intervalsAmount do  // positive part of dependent axis
      begin
       tick(1);
       number(1);        //<, because font.height is <0
      end;
     d:=0; j:=centr;
     if not half then
     for i:=1 to intervalsAmount do    // negative part of dependent axis
      begin
       tick(-1);
       number(-1);
      end;
      moveTo(left,centr);
    end;
   application.processmessages;
  End;{frame}

 function annotate(form:tform;panel:Tpanel;chartColor:longint;var zn1:char):boolean;
 var
      tp:Tpoint;
  Begin
   annotate:=true;
   try
   read(cathFile,cath);
   except
    annotate:=false;
   end;
   if (zn1<>cath.TcPhon) then                 //opis fonematyczny małego oscylogramu
  with form,panel do
    begin
     Canvas.Pen.Color:=clyellow;
     tp:=canvas.penPos;
     canvas.MoveTo(tp.X,top);
     canvas.LineTo(tp.X,top+24);     //tick
     canvas.TextOut(tp.X,top+1,cath.TcPhon);
     application.processMessages;
     canvas.MoveTo(tp.X,tp.y);
     zn1:=cath.TcPhon;
     Canvas.Pen.Color:=chartColor;
    end; {opis fonem.}
  End; {annotate}


 function T0annotate(panel:Tpanel;chartColor:longint;var zn1,zn2:char):boolean;
 var
      tp:Tpoint;
  Begin
   result:=true;
   if (zn1<>zn2) then                 //phonemic description of T0 plot
  with form2,panel do
    begin
     form2.canvas.font.Color:=clBlack;
     Canvas.Pen.Color:=clyellow;
     tp:=canvas.penPos;
     canvas.TextOut(tp.X-8,top+2,zn2);
     canvas.MoveTo(tp.X-1,top+2);
     canvas.LineTo(tp.X-1,top+24);
     canvas.MoveTo(tp.X,tp.y);
     zn1:=zn2;
     form1.Canvas.Pen.Color:=chartColor; //set previous color
    end {opis fonem.}
   else result:=false;
  End;{T0annotate}


procedure SmallIntWaveDraw(panel:tpanel;canvas:tcanvas; const dataBlockLength:longWord; wspx,wspy : double; centr:integer; var waveFile:TsmallIntFile);
//---------------------oscylogram danych wave
//seek(waveFile, j+22) because wave file header size equals 22 bytes
var
            i,j,m : int64;  //i - index of a point of the plot, j - index of a samle in the file
                k : smallInt;
        prevX,l,r : longInt;
               zn : char;
       annotation : boolean;
               xy : tpoint;

  BEGIN
   j:=filePos(waveFile); //debug prp., j=22
   with form1 do begin label66.top:=label65.top;  label66.left:=panel2.Left+panel2.Width-30 end;
   oscRep:=false;    //oscRep:=false in order to prevent draw repeatition because oscRep:-True at the start of launch of this procedure; the oscRep:-True results from OnResize event which is evoked in the initiation processes of the program
   m:=(dataBlockLength+1) div 2-1;
   Canvas.Pen.Color:=clBlue;
   application.processMessages;                 //jeśli tego nie ma, to końcowy processMessages kasuje narysowany oscylogram
   i:=0;  zn:=#0; annotation:=true;  j:=0;  prevX:=0;
   nextWave:=false;  wholeWave:=false; exitWave:=false; backWave:=false; decWspx1:=false; incWspx1:=false;
   back1:=false; back10:=false; next1:=false;next10:=false;
   with panel do
    begin
     xy:=canvas.PenPos;    //bo textOut przesuwa pozycje piora
     form1.label46.caption:=intToStr(j)+'pts';
     form1.label65.caption:=intToStr(filePos(waveFile)-22)+'pts';
     Application.ProcessMessages;
     canvas.MoveTo(xy.X,xy.Y)
    end;{with}
   while not (eof(waveFile) or (j>=m)) do
    BEGIN
     read(waveFile,k);
     if {BreakWholeWave or} (round(i*wspx)>=panel.Width) then  //draw from begin, "WAIT" block
      with panel do
      Begin
       form1.label46.caption:=intToStr(j-i)+'pts'; form1.label65.caption:=intToStr(filePos(waveFile)-22-i-1)+'pts';

       repeat   //wait till the "Next" or other command will be clicked ("WAIT" loop)
        form1.label66.caption:=intToStr(filePos(waveFile)-22-1)+'pts';//intToStr(j-1);  //End of the plot
        application.ProcessMessages;
        if exitWave then exit;
       until begWave or backWave or nextWave or wholeWave or decWspx1 or incWspx1 or back1 or next1 or oscRep or back10 or next10;

       if decWspx1 then
        if j>=i then
        begin
         decWspx1:=false; wspx:=wspx/2;
         j:=j-i;  seek(waveFile,j+22);
         seek(cathFile,j);
        end;
       if incWspx1 then
        if j>=i then
        begin
         incWspx1:=false; wspx:=wspx*2; j:=j-i;
         seek(waveFile,j+22);
         seek(cathFile,j);
        end;

        if back10 then //back 10 pts; button 13
        if j>=i+10 then
         begin
          j:=j-i-10;
          seek(waveFile,j+22);
          seek(cathFile,j);
          back10:=false;
          if j=0 then
           with form1 do
            begin
             button13.Enabled:=false; //<10
             button10.Enabled:=false; //Begin
             button9.Enabled:=false;  //Back
            end; {with}
         end
        else form1.button13.Enabled:=false; //button "<10"

        if next10 then //next 10 pts; button 14
         if j>=i-10 then
         begin
          j:=j-i+10;
          seek(waveFile,j+22);
          seek(cathFile,j);
          next10:=false; //button14=">10"
         end;

       if back1 then //back 10 pts; button 13
        if j>=i+1 then
         begin
          j:=j-i-1;
          seek(waveFile,j+22);
          seek(cathFile,j);
          back1:=false;
          if j=0 then
           with form1 do
            begin
             button13.Enabled:=false; //<10
             button10.Enabled:=false; //Begin
             button9.Enabled:=false;  //Back
            end; {with}
         end
        else form1.button13.Enabled:=false; //button "<10"

        if next1 then //next 10 pts; button 14
         if j>=i-1 then
         begin
          j:=j-i+1;
          seek(waveFile,j+22);
          seek(cathFile,j);
          next1:=false; //button14=">10"
         end;


       if backWave then //back
        if j>=2*(i) then
         begin
          j:=j-2*i;
          seek(waveFile,j+22);
          seek(cathFile,j);
          backWave:=false;
          if j=0 then
           with form1 do
            begin
             button13.Enabled:=false; //<10
             button10.Enabled:=false; //Begin
             button9.Enabled:=false;  //Back
            end; {with}
         end
        else form1.button9.Enabled:=false;//button

        if oscRep then //Repeat
        if j>=i then
         begin
          j:=j-i;
          Frame(Panel,canvas,i,high(smallInt),0,wspx,wspy,centr,false,false); //div 2, bo każda próbka zajmuje 2 bajty!!
          seek(waveFile,j+22);
          seek(cathFile,j);
          oscRep:=false; wholeWave:=false;
         end;

       if begWave then //go to begin of signal
        if j>i then
         begin
          j:=0;
          seek(waveFile,j+22);
          seek(cathFile,j);
          begWave:=false;
         end
        else form1.button10.Enabled:=false;

        Canvas.Pen.Color:=clWhite;
        canvas.moveTo(left,top);
        canvas.lineTo(left,top+height);         //clear rubbish at the begining on the Y axis
        canvas.moveTo(left+width,top);
        canvas.lineTo(left+width,top+height);   //close back of the frame
        if not wholeWave then  with panel do
         begin
          canvas.rectangle(left,top,left+width,top+height+2);//==================clear plot Area=========================
          Canvas.moveTo(left,centr);
          Canvas.lineTo(left+width,centr);
          Canvas.moveTo(left,centr);
          Canvas.Pen.Color:=clBlue;
         end;{if}
       xy:=canvas.PenPos;
       form1.label46.caption:=intToStr(j)+'pts';                    application.processMessages;
       form1.label65.caption:=intToStr(filePos(waveFile)-22)+'pts'; application.processMessages;
       if not(nextWave or wholeWave) then read(waveFile,k);  //it is the must, because of that, that filePos was changed (seek(waveFile,j) i some commands
       canvas.MoveTo(left,round(-k*wspy+centr));
       nextWave:=false;
       i:=0; StartOsc:=j;  //StartOsc is needed in the OnMouseMove event
       with form1 do begin label66.top:=label65.top;  label66.left:=panel2.Left+panel2.Width-20 end;
      End;{if round(i*wspx+left)>=panel.Width - "Draw from begin" i.e. "WAIT" block}

     if wholeWave then //subsequently clear previous plot
      with Panel do
      begin
       if prevX<round(i*wspx+left)+1 then //draw a vertical line clearing previous plot pixel by pixel
        begin
         xy:=canvas.PenPos;
         Canvas.Pen.Color:=clMedGray;
         r:=round(i*wspx+left)+1;
         for l:=prevX to r do
          begin
           canvas.moveTo(l,top+1);
           canvas.lineTo(l,top+height);
          end;{for}
         canvas.MoveTo(xy.X,xy.Y);Canvas.Pen.Color:=clBlue;
        end;{if}
      end;{with}

     if annotation then
      with form1 do  annotation:=annotate(form1,panel,Canvas.Pen.Color,zn);//podpis oscylogramu  (oscillogram annotation
     with Panel do canvas.LineTo(round(i*wspx+left),round(-k*wspy+centr));
     with Panel do prevX:=round(i*wspx+left)+1;
     inc(i); inc(j);
    END;{while}
   application.processMessages;                  //Uwaga! skasuje narysowany oscylogram, jeśli wcześniej nie wywołano jeszcze processmessages
  end;{SmallIntWaveDraw}


 procedure waveShow(canvas:Tcanvas;panel:TPanel;dataBlockLength:longWord; var waveFile:TsmallIntFile);
  var  i:longWord;
  Begin  //-----------------------------------------------waveShow------------------------------------------
   showButtons;
   if form1.button8.CanFocus then form1.button8.SetFocus;
   //with form do
    begin
     panel.Left:=16+5*form1.Canvas.font.Size-form1.Left+2-8;
     panel.width:=form1.Width-10-panel.Left-40;
     i:=(dataBlockLength+1) div 2-1+count;
     if i>panel.Width then i:=panel.Width;
     Frame(Panel,canvas,i,high(smallInt),0,f1wspx2,f1wspy2,f1centr2,false,false); //div 2, because each sample occupies 2 bytes
     SmallIntWaveDraw(Panel,canvas,dataBlockLength,f1wspx2,f1wspy2,f1centr2,waveFile);
    end;{with}
   close(waveFile);
  End;{waveShow}

procedure OscDraw(panel:tpanel;canvas:tcanvas; const y:TdbArr; wspx,wspy : double; centr:integer;
  const count:longWord; const clear:boolean;const chartColor:longInt; it:longWord; var annotation:boolean);
//---------------------oscylogram danych wave
//it - portion number
var
      i : longWord;
      j : longint;
    zn1 : char;
    label e1;
  procedure clipper;
   Begin
    with panel do
     begin
      if j<top then j:=top;
      if j>top+Height then j:=top+Height
     end {with}
   End;{clipper}

  Begin if not form2.CheckBox1.Checked then exit;//pokaż grafikę
  application.ProcessMessages;        //because T0 in panel1 was cleared - now no longer debug: czemu T0 w panel1 ulega wyczyszczeniu? - po wstawieniu tej instrukcji TO podczas wyszukiwania najlepszego wycinka jest pokazywane, a nie kasowane jak bez tej instrukcji
  if not rep and clear then
   begin
    with panel,canvas do
     begin
      pen.Color:=clWhite;
      rectangle(Left,Top,left+Width,Top+Height+2);

      canvas.Pen.Color:=clOlive;        //clear rubish at the end of the graph
      canvas.moveTo(left+width,top);
      canvas.LineTo(left+width,top+height);

     end;{with}
    end;{not rep and clear}
   zn1:=#0;
   with panel do
    begin
     j:=centr;
     if j<=top then goto e1;
     if j>=top+Height then goto e1;
     form2.Canvas.Pen.Color:=clwhite;
     form2.Canvas.moveTo(left,centr);                                           //--------------------ten kolor nie jest aktywowany!
     form2.Canvas.lineTo(left+width,centr);
e1:  form2.Canvas.moveTo(left,centr);
     form2.Canvas.Pen.Color:=chartColor;
    end;
   application.processMessages;
   if it>=1 then i:=it-1
   else i:=0;
   seek(cathFile,i);
   with panel do
    begin
     j:=round(-y[0]*wspy+centr);
     clipper;
     canvas.moveTo(left,j);
    end;
   for i:=1 to count-1 do with Panel do
    begin                                                                      
     if annotation then
      if it+i>-1 then annotation:=annotate(form2,Panel,chartColor,zn1);//opis fonematyczny małego oscylogramu
     j:=round(-y[i]*wspy+centr);
     clipper;
     canvas.LineTo(round(i*wspx+left),j);
     application.processMessages;                  //Uwaga! skasuje narysowany oscylogram, jeśli wcześniej nie wywołano jeszcze processmessages
    end;{for}
  end;{OscDraw}

procedure lifterLine(cpstrPoint:word;wspx,wspy:double;LineColor:tcolor;goOn:boolean; panel:Tpanel);
 begin
  with Panel,form2.canvas do
    if form2.CheckBox14.checked and goOn then   //goOn = stop return in stepWiseManager ((OutPutRecord.sliceNbr=savedRec))
   begin
   form2.Canvas.Pen.Color:=lineColor;
   form2.Canvas.moveTo(round(cpstrPoint*wspx+left),top);
   form2.canvas.LineTo(round(cpstrPoint*wspx+left),top+height);
  end;
 end; {lifterLine}

procedure SpcDraw(panel:tpanel;canvas:tcanvas; const y:TdbArr; wspx,wspy:double; const centr:integer;
  dataLength:Word; clear:boolean; const chartColor:longint; const clip:boolean);

var
    i : longWord;
    j : int64;
    label e1;

 procedure clipper;
  Begin
   with panel do
    begin
     if j<top then j:=top;
     if j>top+Height then j:=top+Height;
    end;
  End;{clipper}

  Begin  //----------------SpcDraw-------------------------------
   application.processMessages;
   if dataLength>=count then dataLength:=count-1;
   if not form2.CheckBox1.Checked then exit;//pokaż grafikę
   if not rep and clear then
    begin
     with panel do
      begin
       form2.Canvas.pen.Color:=clWhite;
       form2.Canvas.rectangle(Left,Top,left+Width,Top+Height+2);

       canvas.Pen.Color:=clOlive;   //clear rubish at the end of the graph
       canvas.moveTo(left+width,top);
       canvas.LineTo(left+width,top+height);

      end;{with}
   end; {if not rep and clear}
   with panel do
    begin
     j:=centr;
     if j<top then goto e1;
     if j>top+Height then goto e1;
     form2.Canvas.Pen.Color:=clwhite;
     form2.Canvas.moveTo(left,centr);                                           //--------------------ten kolor nie jest aktywowany!
     form2.Canvas.lineTo(left+width,centr);
e1:  form2.Canvas.moveTo(left,centr);
     form2.Canvas.Pen.Color:=chartColor;
    end;
   with panel do
    begin
     j:=round(-y[0]*wspy+centr);
     if clip then clipper;
     canvas.moveTo(panel.Left,j);
    end;
   application.processMessages;
   for i:=1 to dataLength do with Panel do
    begin
     j:=round(-y[i]*wspy+centr);
     if clip then clipper;
     canvas.LineTo(round(i*wspx+left),j);
     application.processMessages;                  //Uwaga! skasuje narysowany oscylogram, jeśli wcześniej nie wywołano jeszcze processmessages
    end;
   Canvas.Pen.Color:=clLime;
  End;{SpcDraw}

 procedure synchroKreutz(form:tform;panel:tpanel);
   Begin
    form.Canvas.Pen.Color:=clgreen;
    with form, Panel do   //synchro kreutz
     begin
      canvas.moveTo(left,round(top+height/2));
      Canvas.LineTo(round(left+width),round(top+height/2));
      canvas.moveTo(round(left+width/2),top);
      Canvas.LineTo(round(left+width/2),round(top+height));
     end;
   End;{synchroKreutz}

procedure FilterDraw(filter:array of double;form:tform;panel:tpanel;label0:Tlabel; penCol:longint; out wspx,wspy:double; out centr:integer);
//---------------------draws filter shape
var
  i,m : longWord;
    j : integer;
    min,max:double;
    temp,temp1,temp2:extended;
  label  tryOnceMore;
  Begin
   //if not spcSave then exit;
   if form2.checkbox79.Checked then m:=count div 8-1 else m:=count div 2-1;
   if form2.CheckBox65.Checked then   //dB
    begin
    for i:=0 to m do
     try
      filter[i]:=4.34294482*ln(filter[i])//20*log10(filter[i]);
     except
     // Writeln(reportFile,#13#10'Proc. "FilterDraw", except processing. Error occured at i='+intToStr(i)+'; the "'+form2.label18.caption+'" (rms spectrum or filter) at this point='+floatTostr(filter[i])+'. This value will be overwriten with a next>0');
      flush(reportFile);
      j:=i;
      repeat
       inc(j);
       filter[i]:=filter[j];
      until (filter[j]>0) or (j>=count div 2-1);
      try            //double except processing
      filter[i]:=4.34294482*ln(filter[i])
      except
      // Writeln(reportFile,#13#10'Proc. "FilterDraw", double except processing. Error occured at j='+intToStr(j)+'; the "'+form2.label18.caption+'" (rms spectrum or filter) at this point='+floatTostr(filter[j])+'. This value will be overwriten with a next>0');
       flush(reportFile);
       repeat
       inc(j);
       filter[i]:=filter[j];
      until (filter[j]>0) or (j>=count div 2-1);
      try         //triple except processing
      filter[i]:=4.34294482*ln(filter[i])
      except
       on Exception : EConvertError do
       Writeln(reportFile,'Proc. "FilterDraw", third exception processing', Exception.Message);
      end; //3-th  except processing
      flush(reportFile);
      end //2-th
     end; //1-th except processing
   end;{if}
    min:=maxDouble; max:=-maxDouble;
   for i:=0 to m do
    begin
     if filter[i]<min then min:=filter[i];
     if filter[i]>max then max:=filter[i];
    end;
   with form do
    begin
     Label0.Visible:=True;
     Canvas.Pen.Color:=clwhite;                                  application.processMessages;
     with panel do
      begin
       Canvas.Rectangle(left,top,left+width,top+height);
       application.processMessages;
       if abs(max-min)>=height/maxDouble then
        wspy:=height/(max-min)
       else
        if max<>0 then wspy:=abs(height/max)
        else wspy:=maxDouble;
       wspx:=width/(m+1);
      end;
     synchroKreutz(form,panel);
     Canvas.Pen.Color:=penCol;//clblack;
     application.processMessages;
     with panel do canvas.moveTo(left,round(-(filter[0]-min)*wspy+top+height));  //filter[0]=1 in order not to affect the constant contribution
     for i:=1 to m do
     with Panel do
      begin
       j:=round(-(filter[i]-min)*wspy+top+height);
       Canvas.LineTo(round(i*wspx+left),j);
       application.processMessages;                  //Uwaga! skasuje narysowany oscylogram, jeśli wcześniej nie wywołano jeszcze processmessages
      end;
   end;{with form}
   centr:=0;
  End;{filterDraw}

 procedure WeightsDraw(var weights:array of double;form:tform;panel:tpanel;labelx:Tlabel; radio:Tradiogroup;out wspx,wspy:double; out centr:integer);
//---------------------draws weighting windows shape
 var
    i: longWord;
    j : integer;
    min,max:double;
  Begin
   if form2.checkbox72.Checked then exit;
   min:=maxDouble; max:=-maxDouble;
   for i:=0 to count-1 do
    begin
     if weights[i]<min then min:=weights[i];
    if weights[i]>max then max:=weights[i];
    end;
   with form do
    begin
     Labelx.Visible:=True;
     labelx.Caption:=Radio.Items[Radio.ItemIndex];
     Canvas.Pen.Color:=clblue;                                  application.processMessages;
     with panel do
      begin
       visible:=false;
       Canvas.Rectangle(left,top,left+width,top+height);
       application.processMessages;
       wspx:=width/(count);
       if (abs(max-min)>=height/maxDouble) and (form2.CheckBox77.Checked or (form2.RadioGroup7.ItemIndex=3))  then wspy:=height/(max-min)
       else begin wspy:=abs(height/max); min:=0 end;//min:=0 because full rectangle window is bad showed: as bottom line in its graph
      end;
     synchroKreutz(form,panel);
     Canvas.Pen.Color:=clyellow;
     application.processMessages;
     with Panel do
      canvas.moveTo(left,round(-(weights[0]-min)*wspy+top+height));
     for i:=1 to count -1 do
     with Panel do
      begin
       j:=round(-(weights[i]-min)*wspy+top+height);
       Canvas.LineTo(round(i*wspx+left),j);
       application.processMessages;                  //Uwaga! skasuje narysowany oscylogram, jeśli wcześniej nie wywołano jeszcze processmessages
      end;
   end;{with form}
   centr:=round(1000*min*wspy);
  End;{WeightsDraw}
begin
  begWave:=false; backWave:=false; nextWave:=false; wholeWave:=false; exitWave:=false; decWspx1:=false;
  incWspx1:=false; back10:=false; next10:=false;
end.



from HorizTic:

Implementation
var exLeft:boolean=true;

if panel.Left<0 then
 begin
  if exLeft then
   begin
   showMessage('Panel.left <0 (i.e.='+intToStr(Panel.left)+'), so horizontal scales will not be created');
   exLeft:=false
  end;
  exit
 end
 else exLeft:=true;
d:=xStretch/20;
