where : ibrtses delphi

plotting with a grid and labeled axis

disclaimer

the source code of this page may not appear correctly in certain browsers
due to special characters. Have a look at the source of this HTML page
with notepad instead


Based on the TMapwin, this component has a crude frame around the plot.
It allows labels for the axis and range values as shown :

Above, the complex plot type is shown.

 

the code


{
 provides a frame around a plotwindow,


create:
 
gw:=TGridWin.create(image1);    // assumes image1 exists
gw.xlow:=0; gw.xhigh:=pi; 
gw.ylow:=-1.1; gw.yhigh:=1.1;
gw.hlabel:='X axis';
gw.vlabel:='Y axis';
gw.clear;
gw.drawframe;
gw.drawgrid;

plot :

procedure TForm1.Button2Click(Sender: TObject);
var x,y:integer;
sx,sy:single;
begin
 for x:=gw.left to gw.left+gw.width do begin
  sx:=gw.getX(x);
  sy:=sin(sx);
  gw.putpixel(sx,sy,clred);
 end;
end;

a cursor : // set as cross in the image1, a label :mouselabel exists

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var xs,ys:float;
begin
 gw.screentofloat(x,y,xs,ys);
 mouselabel.caption:=format('%5.5g,%5.5g',[xs,ys]);
end;

}
unit GridWin;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  extctrls,ComplexRec,Plotwin;

type
  TGridWin = class(TMapWin)
  private
    { Private declarations }
    flborder,frborder,ftborder,fbborder:integer;
    fhlabel,fvlabel:string;
  protected
    { Protected declarations }
   procedure setleftborder(i:integer);
   procedure setrightborder(i:integer);
   procedure settopborder(i:integer);
   procedure setbottomborder(i:integer);
   procedure sethlabel(s:string);
   procedure setvlabel(s:string);
   function significantdigits(a,b:extended):integer;
  public
    { Public declarations }
   constructor create(image:TImage);
   destructor destroy;
   procedure drawframe;
   procedure drawgrid;
  published
    { Published declarations }
   property leftborder:integer read flborder write setleftborder;
   property rightborder:integer read frborder write setrightborder;
   property topborder:integer read ftborder write settopborder;
   property bottomborder:integer read fbborder write setbottomborder;
   property hlabel:string read fhlabel write sethlabel;
   property vlabel:string read fvlabel write setvlabel;
  end;

 cwmodetype=(saAP,saRI);   // real/imag or abs/phase

 TComplexGridWin=class(TObject)
 private
    { Private declarations }
   fimage:TImage;
   fmw1,fmw2:TMapWin; 
   flborder,frborder,ftborder,fbborder:integer;
   fhlabel,fvlabel:string;
   ftlx,ftly,fbrx,fbry,fwidth,fheight:integer;
   fflow,ffhigh,fmina,fmaxa:float;
   fmode:cwmodetype;
   fxlog:boolean;
  protected
    { Protected declarations }
   procedure setleftborder(i:integer);
   procedure setrightborder(i:integer);
   procedure settopborder(i:integer);
   procedure setbottomborder(i:integer);
   procedure sethlabel(s:string);
   procedure setvlabel(s:string);
   function significantdigits(a,b:extended):integer;
   procedure setimage(i:TImage);
   procedure setTLX(u:integer);
   procedure setTLY(u:integer);
   procedure setWidth(u:integer);
   procedure setHeight(u:integer);
   procedure setflow(f:float);
   procedure setfhigh(f:float);
   procedure setmina(a:float);
   procedure setmaxa(a:float);
   procedure setxlog(b:boolean);
  public
    { Public declarations }
   constructor create(image:TImage);
   destructor destroy;
   procedure drawframe;
   procedure drawgrid;
   procedure ComplexToScreen(z:Complex;f:float;var xs,ya,yp:integer); virtual;
   procedure ScreenToFloat(xs,ys:integer;var xf,yf:float); virtual;
   procedure clear;
   procedure putpixel(z:Complex;f:float;color:TColor); virtual;
   procedure VLine(f:float;color:TColor); virtual;   //vertical line
   procedure HALine(f:float;color:TColor); virtual;  // horizontal line amplitude
   procedure HPLine(f:float;color:TColor); virtual;  // horizontal line phase
   function getX(i:integer):float;             // i:=0..width-1
  published
    { Published declarations }
   property image:TImage read fimage write setimage;
   property left:integer read ftlx write setTLX;     // with respect to image
   property top:integer read ftly write setTLY;     // with respect to image
   property width:integer read fwidth write setWidth;     // with respect to image
   property height:integer read fheight write setHeight;     // with respect to image
   property mode:cwmodetype read fmode write fmode;
   property flow:float read fflow write setflow;        // frequency
   property fhigh:float read ffhigh write setfhigh;      // frequency
   property mina:float read fmina write setmina;        // absolute value
   property maxa:float read fmaxa write setmaxa;        // absolute value    
   property leftborder:integer read flborder write setleftborder;
   property rightborder:integer read frborder write setrightborder;
   property topborder:integer read ftborder write settopborder;
   property bottomborder:integer read fbborder write setbottomborder;
   property hlabel:string read fhlabel write sethlabel;
   property vlabel:string read fvlabel write setvlabel;
   property xlog:boolean read fxlog write setxlog;
  end; 

procedure Register;

implementation

uses math;

const
 defaultleftborder=30;
 defaultrightborder=0;
 defaulttopborder=0;
 defaultbottomborder=20;


constructor TGridWin.create(image:TImage);
begin
 inherited create(image);
 flborder:=defaultleftborder;
 frborder:=defaultrightborder;
 ftborder:=defaulttopborder;
 fbborder:=defaultbottomborder;
 top:=defaulttopborder;
 left:=defaultleftborder;
 width:=image.width-defaultleftborder-defaultrightborder;
 height:=image.height-defaulttopborder-defaultbottomborder;
 fhlabel:='';
 fvlabel:='';
end;

destructor TGridWin.destroy;
begin
 inherited destroy;
end;



procedure TGridWin.setleftborder(i:integer);
begin
 left:=i;
 width:=image.width-i-frborder;
 flborder:=i;
end;
procedure TGridWin.setrightborder(i:integer);
begin
 frborder:=i;
 width:=image.width-flborder-frborder;
end;
procedure TGridWin.settopborder(i:integer);
begin
 top:=i;
 ftborder:=i;
 height:=image.height-ftborder-fbborder;
end;
procedure TGridWin.setbottomborder(i:integer);
begin
 fbborder:=i;
 height:=image.height-ftborder-fbborder;
end;

procedure TGridWin.sethlabel(s:string);
begin
 fhlabel:=s;
end;
procedure TGridWin.setvlabel(s:string);
begin
 fvlabel:=s;
end;

function TGridWin.significantdigits(a,b:extended):integer;
var r:extended;
 astr,bstr:string;
begin
 if abs(a)<1e-60 then result:=2
 else
  if abs(b)<1e-60 then result:=2
  else
   begin
    r:=abs(a-b)/abs(a);
    if r<1 then r:=1/r;
    result:=2;
    while (r<1.1)do
     begin
      r:=10*(r-1); inc(result);
     end;
   end;
end;

procedure TGridWin.drawframe;
var x1,x2,y1,y2:integer;
var ex:TSize;
 st,n:integer;
 num:string;
  tmpF : HFONT;
  tmpL : TLogFont;
  oldFont : HFONT;
  tmpTFont : TFont;
  offSetX : integer;
begin
 if leftborder<>0 then x1:=flborder-1 else x1:=0;
 if rightborder<>0 then x2:=image.width-frborder+1 else x2:=image.width-1;
 if topborder<>0 then y1:=topborder-1 else y1:=0;
 if bottomborder<>0 then y2:=image.height-fbborder+1 else y2:=image.height-1;
 image.canvas.pen.color:=clblack;
 if leftborder<>0 then begin 
  image.canvas.moveto(x1,y1);
  image.canvas.lineto(x1,y2);
  end;
 if bottomborder<>0 then begin
  image.canvas.moveto(x1,y2);
  image.canvas.lineto(x2,y2);
 end;
 if rightborder<>0 then begin
  image.canvas.moveto(x2,y2);
  image.canvas.lineto(x2,y1);
 end;
 if topborder<>0 then begin
  image.canvas.moveto(x2,y1);
  image.canvas.lineto(x1,y1);
 end;
 if (bottomborder<>0)and(hlabel<>'') then begin
   image.canvas.font.name:='MS Sans Serif';
   image.canvas.Font.Size:=8;
  { the units }
   n:=significantdigits(xlow,xhigh);
   num:=format('%'+inttostr(n)+'g',[xlow]);
   image.canvas.textout(leftborder,image.height-12,num);
   num:=format('%'+inttostr(n+1)+'.'+inttostr(n)+'g',[xhigh]);
   ex:=image.Canvas.TextExtent(num);
   image.canvas.textout(image.width-ex.cx,image.height-12,num);
  { the label }
   //canvas.font.style.[fsItalic]:=true;
   ex:=image.Canvas.TextExtent(fhlabel);
   st:=(width-ex.cx)div 2;
   image.canvas.TextOut(st,image.height-12,fhlabel);
  end;
 if (leftborder<>0)and(vlabel<>'') then begin
   image.canvas.font.name:='Courier New';
   image.canvas.Font.Size:=8;
  { the units }
   n:=significantdigits(ylow,yhigh);
   num:=format('%'+inttostr(n)+'g',[ylow]);
   image.canvas.textout(1,image.height-bottomborder-12,num);
   num:=format('%'+inttostr(n)+'g',[yhigh]);
   ex:=image.Canvas.TextExtent(num);
   image.canvas.textout(1,0,num);
  { the label }
   //canvas.font.style.[fsItalic]:=true;
   //ex:=Canvas.TextExtent(fvaxislabel);
   //st:=(height-ex.cx)div 2;
   //canvas.TextOut(1,st,fvaxislabel);
  tmpTFont := TFont.Create;
  tmpTFont.Assign( image.canvas.font );
  GetObject( image.canvas.Font.Handle,SizeOf(TLogFont),@tmpL);
  tmpL.lfEscapement := 900;
  tmpL.lfOrientation := 900;
  tmpF := CreateFontIndirect(tmpL);
  image.canvas.Font.Handle := tmpF;
  SetBkMode( image.canvas.Handle, windows.TRANSPARENT );
  Windows.TextOut( image.canvas.handle, 2 , Height div 2 ,pChar(vlabel), length(vlabel) );
  SetBkMode( image.canvas.Handle, OPAQUE );
  image.canvas.Font.assign(tmpTFont);
  DeleteObject(tmpF);
  tmpTFont.Free;

 end;
end;

procedure TGridWin.drawgrid;
var d:float;
 i:integer;
begin
 // is zero part of it ?
 if (not xlog)and(xlow<=0)and(xhigh>=0) then vline(0,clred);
 if (not ylog)and(ylow<=0)and(yhigh>=0) then hline(0,clred);
 // log grid
 if (xlog) then begin
  for i:=-20 to 20 do begin
   d:=intpower(10,i);
   if (d>=xlow)and(d<=xhigh) then 
    vline(d,clgray);
   end;
  end;
 if (ylog) then begin
  for i:=-20 to 20 do begin
   d:=intpower(10,i);
   if (d>=ylow)and(d<=yhigh) then 
    hline(d,clgray);
   end;
  end;  
  
end;



//#############################################################
procedure TComplexGridWin.setimage(i:TImage);
begin
 fimage:=i;
 fmw1.image:=i;
 fmw2.image:=i;
end;
procedure TComplexGridWin.setTLX(u:integer);
begin ftlx:=u; end;
procedure TComplexGridWin.setTLY(u:integer);
begin ftly:=u; end;
procedure TComplexGridWin.setWidth(u:integer);
begin fbrx:=ftlx+u; fwidth:=u; end;
procedure TComplexGridWin.setHeight(u:integer);
begin fbry:=ftly+u; fheight:=u; end;
procedure TComplexGridWin.setflow(f:float);
begin
 fflow:=f;
 fmw1.xlow:=f;
 fmw2.xlow:=f;
end;
procedure TComplexGridWin.setfhigh(f:float);
begin
 ffhigh:=f;
 fmw1.xhigh:=f;
 fmw2.xhigh:=f;
end;
procedure TComplexGridWin.setmina(a:float);
begin
 fmina:=a;
 fmw1.ylow:=a;
end;
procedure TComplexGridWin.setmaxa(a:float);
begin
 fmaxa:=a;
 fmw1.yhigh:=a;
end;
procedure TComplexGridWin.setxlog(b:boolean);
begin
 fmw1.xlog:=b;
 fmw2.xlog:=b;
 fxlog:=b;
end;

procedure TComplexGridWin.setleftborder(i:integer);
begin
 left:=i;
 width:=image.width-i-frborder;
 flborder:=i;
end;
procedure TComplexGridWin.setrightborder(i:integer);
begin
 frborder:=i;
 width:=image.width-flborder-frborder;
end;
procedure TComplexGridWin.settopborder(i:integer);
begin
 top:=i;
 ftborder:=i;
 height:=image.height-ftborder-fbborder;
end;
procedure TComplexGridWin.setbottomborder(i:integer);
begin
 fbborder:=i;
 height:=image.height-ftborder-fbborder;
end;

procedure TComplexGridWin.sethlabel(s:string);
begin
 fhlabel:=s;
end;
procedure TComplexGridWin.setvlabel(s:string);
begin
 fvlabel:=s;
end;

function TComplexGridWin.significantdigits(a,b:extended):integer;
var r:extended;
 astr,bstr:string;
begin
 if abs(a)<1e-60 then result:=2
 else
  if abs(b)<1e-60 then result:=2
  else
   begin
    r:=abs(a-b)/abs(a);
    if r<1 then r:=1/r;
    result:=2;
   { while (r<1.1)do
     begin
      r:=10*(r-1); inc(result);
     end; }
   end;
end;

constructor TComplexGridWin.create(image:TImage);
begin
 inherited create;
 fimage:=image;
 fmw1:=TMapWin.create(image);
 fmw2:=TMapWin.create(image);
 flborder:=defaultleftborder;
 frborder:=defaultrightborder;
 ftborder:=defaulttopborder;
 fbborder:=defaultbottomborder;
 fmw1.top:=defaulttopborder;
 fmw1.left:=defaultleftborder;
 fmw2.top:=(image.height-defaulttopborder-defaultbottomborder) div 2;
 fmw2.left:=defaultleftborder;
 fmw1.width:=image.width-defaultleftborder-defaultrightborder;
 fmw2.width:=fmw1.width;
 fmw1.height:=(image.height-defaulttopborder-defaultbottomborder)div 2;
 fmw2.height:=fmw1.height;
 fheight:=(image.height-defaulttopborder-defaultbottomborder);
 fhlabel:='frequency';
 fvlabel:='amplitude';
 flow:=0.1; fhigh:=100;
 fmina:=1e-9; fmaxa:=1;
 fmw1.xlow:=flow;           fmw2.xlow:=flow;
 fmw1.xhigh:=fhigh;         fmw2.xhigh:=fhigh;
 fmw1.YLow:=fmina;          fmw2.YLow:=-pi;
 fmw1.YHigh:=fmaxa;             fmw2.YHigh:=pi;
 fmw1.xlog:=true;           fmw2.xlog:=true;
 fmw1.ylog:=true;           fmw2.ylog:=false;
 fmw1.protectlog:=true;
end;

 
destructor TComplexGridWin.destroy;
begin
 fmw1.destroy;
 fmw2.destroy;
 inherited destroy;
end;

procedure TComplexGridWin.ComplexToScreen(z:Complex;f:float;var xs,ya,yp:integer);
var ax,ay,bx,by:integer;
begin
 if fmode=saAP then begin   // z:=a,p
  fmw1.floattoscreen(f,z.a,ax,ay);
  fmw2.floattoscreen(f,z.b,bx,by);
  xs:=ax; ya:=ay; yp:=by;
 end;
end;

procedure TComplexGridWin.ScreenToFloat(xs,ys:integer;var xf,yf:float);
begin
 if fmode=saAP then begin
  if (ys>=fheight div 2) then  // in the phase part
   fmw2.ScreenToFloat(xs,ys{-(fheight div 2)},xf,yf)
  else // absolute part
   fmw1.ScreenToFloat(xs,ys,xf,yf);
 end;
end;
procedure TComplexGridWin.clear;
begin
 if assigned(fimage) then begin
  fimage.canvas.Pen.color:=clWhite;
  fimage.canvas.brush.color:=clWhite;
  fimage.canvas.rectangle(0,0,fimage.width-1,fimage.height-1);
  fimage.canvas.Pen.color:=clblack;
  fimage.canvas.moveto(0,fimage.height div 2);
  //fimage.canvas.lineto(fimage.width,fimage.height div 2);
  fimage.canvas.Pen.color:=clWhite;
  end;
end;
procedure TComplexGridWin.putpixel(z:Complex;f:float;color:TColor);
begin
 fmw1.putpixel(f,z.a,color);
 fmw2.putpixel(f,z.b,color);
end;

procedure TComplexGridWin.VLine(f:float;color:TColor);
begin
 fmw1.VLine(f,color);
 fmw2.VLine(f,color);
end;

procedure TComplexGridWin.HALine(f:float;color:TColor);
begin
 fmw1.HLine(f,color);
end;
procedure TComplexGridWin.HPLine(f:float;color:TColor);
begin
 fmw2.HLine(f,color);
end;

function TComplexGridWin.getX(i:integer):float;             // i:=0..width-1
begin
 result:=fmw1.getx(i);
end;


procedure TComplexGridWin.drawframe;
var x1,x2,y1,y2:integer;
var ex:TSize;
 st,n:integer;
 num,s:string;
  tmpF : HFONT;
  tmpL : TLogFont;
  oldFont : HFONT;
  tmpTFont : TFont;
  offSetX : integer;
begin
 if leftborder<>0 then x1:=flborder-1 else x1:=0;
 if rightborder<>0 then x2:=fimage.width-frborder+1 else x2:=fimage.width-1;
 if topborder<>0 then y1:=topborder-1 else y1:=0;
 if bottomborder<>0 then y2:=fimage.height-fbborder+1 else y2:=fimage.height-1;
 fimage.canvas.pen.color:=clblack;
 if leftborder<>0 then begin 
  fimage.canvas.moveto(x1,y1);
  fimage.canvas.lineto(x1,y2);
  end;
 if bottomborder<>0 then begin
  fimage.canvas.moveto(x1,y2);
  fimage.canvas.lineto(x2,y2);
 end;
 if rightborder<>0 then begin
  fimage.canvas.moveto(x2,y2);
  fimage.canvas.lineto(x2,y1);
 end;
 if topborder<>0 then begin
  fimage.canvas.moveto(x2,y1);
  fimage.canvas.lineto(x1,y1);
 end;
 //y1:=(fimage.height-ftborder-fbborder)div 2;
 //fimage.canvas.moveto(x1,y1);
 //fimage.canvas.lineto(x2,y1);
 if (bottomborder<>0)and(hlabel<>'') then begin
   fimage.canvas.font.name:='MS Sans Serif';
   fimage.canvas.Font.Size:=8;
  { the units }
   n:=significantdigits(flow,fhigh);
   num:=format('%'+inttostr(n)+'g',[flow]);
   fimage.canvas.textout(leftborder,fimage.height-12,num);
   num:=format('%'+inttostr(n+1)+'.'+inttostr(n)+'g',[fhigh]);
   ex:=fimage.Canvas.TextExtent(num);
   fimage.canvas.textout(image.width-ex.cx,image.height-12,num);
  { the label }
   //canvas.font.style.[fsItalic]:=true;
   ex:=fimage.Canvas.TextExtent(fhlabel);
   st:=(fimage.width-ex.cx)div 2;
   fimage.canvas.TextOut(st,fimage.height-12,fhlabel);
  end;
 if (leftborder<>0)and(vlabel<>'') then begin
   fimage.canvas.font.name:='Courier New';
   fimage.canvas.Font.Size:=8;
  { the units }
   n:=significantdigits(mina,maxa);
   num:=format('%'+inttostr(n)+'g',[mina]);
   fimage.canvas.textout(1,(fimage.height-bottomborder) div 2-12,num);
   num:=format('%'+inttostr(n)+'g',[maxa]);
   ex:=fimage.Canvas.TextExtent(num);
   fimage.canvas.textout(1,0,num);
   fimage.canvas.textout(1,(fimage.height-bottomborder) div 2,'pi');
   fimage.canvas.textout(1,(fimage.height-bottomborder)-12,'-pi');
  { the label }
   //canvas.font.style.[fsItalic]:=true;
   //ex:=Canvas.TextExtent(fvaxislabel);
   //st:=(height-ex.cx)div 2;
   //canvas.TextOut(1,st,fvaxislabel);
  tmpTFont := TFont.Create;
  tmpTFont.Assign( fimage.canvas.font );
  GetObject( fimage.canvas.Font.Handle,SizeOf(TLogFont),@tmpL);
  tmpL.lfEscapement := 900;
  tmpL.lfOrientation := 900;
  tmpF := CreateFontIndirect(tmpL);
  image.canvas.Font.Handle := tmpF;
  SetBkMode( fimage.canvas.Handle, windows.TRANSPARENT );
  y1:=(fimage.height-bottomborder)div 4;
  y2:=3*(fimage.height-bottomborder)div 4;
  s:='abs '+vlabel;
  ex:=fimage.Canvas.TextExtent(s);
  Windows.TextOut( fimage.canvas.handle, 2 , y1+ex.cx div 2 ,pChar(s), length(s) );
  s:='arg '+vlabel;
  ex:=fimage.Canvas.TextExtent(s);
  Windows.TextOut( fimage.canvas.handle, 2 , y2+ex.cx div 2 ,pChar(s), length(s) );
  SetBkMode( fimage.canvas.Handle, OPAQUE );
  fimage.canvas.Font.assign(tmpTFont);
  DeleteObject(tmpF);
  tmpTFont.Free;

 end;
end;

procedure TComplexGridWin.drawgrid;
var d:float;
 i:integer;
begin
 // is zero part of it ?
 if (not fmw1.xlog)and(flow<=0)and(fhigh>=0) then vline(0,clred);
 if (not fmw1.ylog)and(mina<=0)and(maxa>=0) then haline(0,clred);
 if (not fmw2.xlog)and(flow<=0)and(fhigh>=0) then vline(0,clred);
 if (not fmw2.ylog) then hpline(0,clgray);
 // log grid
 if (fmw1.xlog) then begin
  for i:=-20 to 20 do begin
   d:=intpower(10,i);
   if (d>=flow)and(d<=fhigh) then 
    fmw1.vline(d,clgray);
   end;
  end;
 if (fmw1.ylog) then begin
  for i:=-20 to 20 do begin
   d:=intpower(10,i);
   if (d>=mina)and(d<=maxa) then 
    fmw1.hline(d,clgray);
   end;
  end;  
 if (fmw2.xlog) then begin
  for i:=-20 to 20 do begin
   d:=intpower(10,i);
   if (d>=flow)and(d<=fhigh) then 
    fmw2.vline(d,clgray);
   end;
  end;
 if (fmw2.ylog) then begin
  for i:=-20 to 20 do begin
   d:=intpower(10,i);
   if (d>=mina)and(d<=maxa) then 
    fmw2.hline(d,clgray);
   end;
  end; 
  
end;




procedure Register;
begin
 // RegisterComponents('Samples', [TGridWin]);
end;

end.
 

notes

it may be far from perfect, but it served the purpose. Time does not allow
to improve it at the moment. Any feedback on improvements are welcome.




Feedback is welcome





sponsored links




Delphi
home

last updated: 26.may.00


Copyright (99,2000) Ing.Büro R.Tschaggelar