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.
{ 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.