/******************************************************************* * Unit Graph2D.cpp Version 1.1, 15/01/1997 * * for Visual C++ 1.52 Copyright J-P Moreau * * (www.jpmoreau.fr) * * ---------------------------------------------------------------- * * Functions to draw a 2D curve y=f(x) with automatic scaling in * * linear or logarithmic scales, in a window defined by Fenetre(). * * * * Main global variables: * * * * xgclot,xdclot,ybcot,yhclot : limits in pixels of the window * * defined by Fenetre(). * * MaxX, MaxY : screen resolution in X and Y * * ( Ex : MaxX=639 MaxY=349 ). * * Log_X, Log_Y : log. scale if = 1 * * Ech_auto : automatic scaling if = 1 * * If Ech_auto = 0, scales in X and Y are defined by: * * X_mini, X_maxi, Y_mini et Y_maxi. * * * * ---------------------------------------------------------------- * * Version 1.1: added blue line for screen output and thicker black * * line for printer output in CourbeXY(). * *******************************************************************/ #include "graph2d.h" //implemented by main program void DrawPixel(int,int); void DrawLine(int,int,int,int); void OutText(int,int,char *); void Fenetre(int i1,int i2,int i3,int i4) // défine a framed graphic zone in pixels { int ep; // thickness of frame xgclot=i1; // left margin (pixels) xdclot=i2; // right margin ybclot=i3; // lower margin yhclot=i4; // upper margin // draw a frame of thickness ep if (!id_imprim) ep=1; else ep=2; DrawLine(i1,MaxY-i4,i2,MaxY-i4); DrawLine(i2,MaxY-i4,i2,MaxY-i3); DrawLine(i2,MaxY-i3,i1,MaxY-i3); DrawLine(i1,MaxY-i3,i1,MaxY-i4); id_out=0; } void Fenetre10() // open window n° 10 (entire graphic zone, external graduations) { Fenetre(10*Bord,MaxX-2*Bord,6*Bord,MaxY-6*Bord); } void PleinEcran() // open entire client zone for graph { xgclot=0; xdclot=MaxX; ybclot=0; yhclot=MaxY; } // Choose a scaling factor among [10,5,2.5,1.25,1] double Echelle(double ech) { const double wval[5] = {10.0,5.0,2.5,1.25,1.0}; int i,ixpo; double wl,wlp,wlm,LogEch; wlp = wval[0]; LogEch=log10(ech); ixpo = (int) floor(LogEch); wl = ech/pow(10.0,ixpo); for (i=0;i<4;i++) { wlm = wlp; wlp = wval[i+1]; if ((wl-wlm)*(wl-wlp) <= 0) goto fin; } wlm = 1.0; fin: wl = wlm; ech = wl * pow(10.0,ixpo); return ech; } // Echelle() // seek Ox scaling to have cm when printing double EchelleX(void) { double ech; dx=xmax-xmin; if (dx <= 0) return 0; else { ech=dx/Cxmx; return Echelle(ech); } } // EchelleX() // seek Oy scaling to have cm when printing double EchelleY(void) { double ech; dy=ymax-ymin; if (dy <= 0) return 0; else { ech=dy/Cymx; return Echelle(ech); } } // EchelleY() /***************************************************************** * Convert physical coordinates in pixels in the window opened by * * Initwindow(). Used by MoveXY() and LineXY() * *****************************************************************/ void Conversion(double xx,double yy,int *Ix,int *Iy) { double xcm,ycm; int i,j; double Macheps=1.2E-16; xcm=xo; ycm=yo; if (Log_X==0) xcm=xo+(xx-xmin)/Echx; else {if (fabs(xx) > Macheps) xcm=xo+(log10(fabs(xx))-xmin)/Echx;} if (Log_Y==0) ycm=yo+(yy-ymin)/Echy; else {if (fabs(yy) > Macheps) ycm=yo+(log10(fabs(yy))-ymin)/Echy;} i = ixmn + (int) floor(xratio*xcm); j = iymx - (int) floor(yratio*ycm); *Ix=i; *Iy=j; } // Conversion() /************************************ * Draw axes Ox and Oy, if visible. * * Function called by Initwindow(). * ************************************/ void Tracer_les_axes() { int ix1,iy1; double xcm,ycm; xcm = xo-xmin/Echx; ix1 = ixmn + (int) ceil(xratio*xcm); ycm = yo-ymin/Echy; iy1 = iymx - (int) ceil(yratio*ycm); if (ix1 > xgclot-1 && ix1 < xdclot+1) DrawLine(ix1,iymn,ix1,iymx); if (iy1 > MaxY-yhclot-1 && iy1 < MaxY-ybclot+1) DrawLine(ixmn,iy1,ixmx,iy1); } // Tracer_les_axes() /***************************************************** * Adjust number of displayed figures with respect to * * number magnitude and its variation between two * * graduations. * * From Pascal routine by J-P Dumont * *****************************************************/ void Ajuster_format ( double xs, double dx_par_graduation, double xmax, int *n, int *m ) { double test_zero,test_precision; int ng; if (fabs(xs)<0.000001*fabs(xmax)) test_zero=0; else test_zero=log10(fabs(xs)); if (test_zero==0) { *n=1; *m=0; return; } if (test_zero<0) ng=1; else ng=(int) floor(test_zero)+1; test_precision= log10(fabs(dx_par_graduation)); if (test_precision>=0) *m=0; else *m=1+(int)floor(fabs(test_precision)-0.01); *n=ng+*m+2; // 2: sign and decimal point } /**************************************************** * Automatic graduations of axes Ox, Oy in linear * * (Log_X=0 and/or Log_Y=0) or in logarithmic scales * * ( Log_X=1 and/or Log_Y=1 ). * * From a Pascal routine by J-P Dumont, modified by * * J-P Moreau. * * ------------------------------------------------- * * Function called by Initwindow(). * ****************************************************/ void Graduer_les_axes() { const cm_par_grad_x = 4, cm_par_grad_y = 2, grad_x = 4, grad_y = 4; const double petit=0.001; int nx,comptx,ny,compty,sous_grad,i,ii,imin,imax,ixxg,ixxg0,ixxg1, iyyg,iyyg0,iyyg1,grid,npar,mpar,trait,ligne,raccord; double xcm,ycm,xg,yg,Xlim,Ylim, dx_par_graduation,dy_par_graduation; char mot[13],mot1[1],mot2[1],flag[1]; // Xlim,Ylim= physical coord. of left lower corner // red pen for grid HPEN redpen,oldpen; redpen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); dx_par_graduation=Echx*cm_par_grad_x; dy_par_graduation=Echy*cm_par_grad_y; if (Log_X==0) // linear graduations { Xlim=xmin-xo*Echx; Ylim=ymin-yo*Echy; nx= (int) ceil(Xlim/Echx)-1; comptx = nx % cm_par_grad_x; if (comptx<0) comptx = cm_par_grad_x + comptx; do { comptx++; if (comptx == cm_par_grad_x) comptx =0; nx++; xg = nx *Echx; xcm = xo +(xg-xmin)/Echx; ixxg = ixmn + (int) ceil(xratio * xcm); if (wind==11) trait = 5*grad_x; else trait=grad_x; if (comptx==0) if (wind==11) trait = 10*grad_x; else trait=2*grad_x; if (ixxg >= ixmn && ixxg <= ixmx) { DrawLine(ixxg,iymx,ixxg,iymx-trait); if (comptx==0) { Ajuster_format(xg,dx_par_graduation,xmax,&npar,&mpar); gcvt(xg,npar,mot); if (wind==10) OutText(ixxg-10,MaxY-25,mot); else if (wind==11) OutText(ixxg-10,MaxY-60,mot); else { ligne=iymx+(int) (0.025*(iymx-iymn)); OutText(ixxg,ligne,mot); } } DrawLine(ixxg,iymn,ixxg,iymn+trait); if (!id_imprim) grid=2; else if (id_couleur) grid=3; else grid=2; imin=1; imax=(int) ceil((iymx-iymn)/grid); for (i=imin;i<=imax;i++) { ii=i*grid; if (!id_imprim || id_couleur) oldpen = SelectObject(hdc,redpen); DrawPixel(ixxg,iymn+ii); if (!id_imprim || id_couleur) SelectObject(hdc,oldpen); } } } while (xcm < Cxmx); } else // log. graduations in X { Xlim=xmin-xo*Echx; Ylim=ymin-yo*Echy; nx= (int) ceil(Xlim/Echx)-1; comptx = nx % cm_par_grad_x; sous_grad=0; if (comptx<0) comptx = cm_par_grad_x + comptx; strcpy(flag,"*"); do { comptx++; sous_grad++; if (comptx == cm_par_grad_x) { comptx =0; sous_grad=0; strcpy(mot1," "); strcpy(flag," "); } nx++; xg = nx *Echx; xcm = xo +(xg-xmin)/Echx; ixxg = ixmn + (int) ceil(xratio * xcm); if (comptx==0) ixxg0=ixxg; if (sous_grad==1) { ixxg=ixxg0+ (int) ceil((ixxg-ixxg0)*1.204); strcpy(mot1,"2"); strcpy(mot2,"3"); ixxg1=ixxg0+ (int) ceil((ixxg-ixxg0)*1.585); } if (sous_grad==2) { ixxg=ixxg0+ (int) ceil((ixxg-ixxg0)*1.364); strcpy(mot1,"5"); } if (sous_grad==3) { ixxg=ixxg0+ (int) ceil((ixxg-ixxg0)*1.14); strcpy(mot1,"7"); } if (wind==11) trait = 5*grad_x; else trait=grad_x; if (comptx==0) if (wind==11) trait = 10*grad_x; else trait=2*grad_x; if (ixxg >= ixmn && flag != "*" && ixxg <= ixmx) { raccord=1; DrawLine(ixxg,iymx,ixxg,iymx-trait); if (wind>9) OutText(ixxg,iymx-(int) (0.04*(iymx-iymn)),mot1); else { ligne=iymx-20; OutText(ixxg,ligne,mot1); } if (sous_grad==1) { DrawLine(ixxg1,iymx,ixxg1,iymx-trait); if (wind>9) { OutText(ixxg,iymx-(int) (0.04*(iymx-iymn)),"2"); OutText(ixxg1,iymx-(int) (0.04*(iymx-iymn)),mot2); } else { ligne=iymx-20; OutText(ixxg1,ligne,mot2); } } if (comptx==0) { if (fabs(ceil(xg)-xg) < petit) { xg=pow(10.0,ceil(xg)); Ajuster_format(xg,dx_par_graduation,xmax,&npar,&mpar); gcvt(xg,npar,mot); if (wind>9) OutText(ixxg,MaxY-25,mot); else { ligne=iymx-20; OutText(ixxg,ligne,mot); } } } raccord=1; DrawLine(ixxg,iymn,ixxg,iymn+trait); if (!id_imprim) grid=2; else grid=2; imin=1; imax=(int) ceil((iymx-iymn)/grid); for (i=imin;i<=imax;i++) { if (!id_imprim) { ii=i*grid; DrawPixel(ixxg,iymn+ii); } else { ii=i*grid; DrawPixel(ixxg,iymn+ii); } } if (sous_grad==1) { DrawLine(ixxg1,iymn,ixxg1,iymn+trait); for (i=imin;i<=imax;i++) { if (!id_imprim) { ii=i*grid; DrawPixel(ixxg1,iymn+ii); } else { ii=i*grid; DrawPixel(ixxg1,iymn+ii); } } } } } while (xcm < 0.92*Cxmx); } // end of else if (Log_Y==0) // linear graduations in Y { ny = (int) ceil(Ylim/Echy)-1; compty = ny % cm_par_grad_y; if (compty<0) compty=cm_par_grad_y+compty; do { compty++; if (compty==cm_par_grad_y) compty =0; ny++; yg = ny*Echy; ycm = yo +(yg-ymin)/Echy; iyyg = iymx - (int) ceil(yratio * ycm); if (wind==11) trait=5*grad_y; else trait=grad_y; if (compty==0) if (wind==11) trait = 10*grad_y; else trait=2*grad_y; if (iyyg >= iymn && iyyg<=iymx) { DrawLine(xgclot,iyyg,xgclot+trait,iyyg); if (compty==0) { Ajuster_format(yg,dy_par_graduation,ymax,&npar,&mpar); gcvt(yg,npar,mot); if (wind==10) OutText(10,iyyg-10,mot); else if (wind==11) OutText(140,iyyg-10,mot); else { if (!id_imprim) raccord=xgclot-35; else raccord=xgclot-175; OutText(raccord,iyyg-10,mot); } } DrawLine(xdclot,iyyg,xdclot-trait,iyyg); if (!id_imprim) grid=3; else if (id_couleur) grid=4; else grid=3; imin=1; imax=(int) ceil((ixmx-ixmn)/grid); for (i=imin;i<=imax;i++) { ii=grid*i-1; if (!id_imprim || id_couleur) oldpen = SelectObject(hdc,redpen); DrawPixel(ixmn+ii,iyyg); if (!id_imprim || id_couleur) SelectObject(hdc,oldpen); } } } while (ycm < Cymx); } else // log. graduations in Y { dy_par_graduation=Echy*cm_par_grad_x; ny= (int) ceil(Ylim/Echy)-1; compty = ny % cm_par_grad_x; if (compty<0) compty = cm_par_grad_x + compty; strcpy(flag,"*"); do { compty++; sous_grad++; ny++; yg=ny*Echy; ycm=yo+(yg-ymin)/Echy; iyyg=iymx-(int) ceil(yratio*ycm); if (compty == cm_par_grad_x) { compty =0; sous_grad=0; strcpy(mot1," "); strcpy(flag," "); } if (compty==0) iyyg0=iyyg; if (sous_grad==1) { iyyg=iyyg0+ (int) ceil((iyyg-iyyg0)*1.204); strcpy(mot1,"2"); strcpy(mot2,"3"); iyyg1=iyyg0+ (int) ceil((iyyg-iyyg0)*1.585); } if (sous_grad==2) { iyyg=iyyg0+ (int) ceil((iyyg-iyyg0)*1.364); strcpy(mot1,"5"); } if (sous_grad==3) { iyyg=iyyg0+ (int) ceil((iyyg-iyyg0)*1.14); strcpy(mot1,"7"); } if (wind==11) trait = 5*grad_y; else trait=grad_y; if (compty==0) if (wind==11) trait = 10*grad_y; else trait=2*grad_y; if (iyyg >= iymn && flag != "*" && iyyg < iymx) { raccord=1; DrawLine(ixmn,iyyg,ixmn+trait,iyyg); OutText(ixmn+10,iyyg-10,mot1); if (sous_grad==1) { DrawLine(ixmn,iyyg1,ixmn+trait,iyyg1); OutText(ixmn+10,iyyg-10,"2"); OutText(ixmn+10,iyyg1-10,mot2); } if (compty==0) { if (fabs(ceil(yg)-yg) < petit) { yg=pow(10.0,ceil(yg)); Ajuster_format(yg,dy_par_graduation,ymax,&npar,&mpar); gcvt(yg,npar,mot); if (wind>9) OutText(5,iyyg-10,mot); else OutText(ixmn+10,iyyg-10,mot); } } DrawLine(ixmx,iyyg,ixmx-trait,iyyg); if (!id_imprim) grid=3; else grid=3; imin=1; imax=(int) ceil((ixmx-ixmn)/grid); for (i=imin;i<=imax;i++) { if (!id_imprim) { ii=grid*i-1; DrawPixel(ixmn+ii,iyyg); } else { ii=grid*i-1; DrawPixel(ixmn+ii,iyyg); } } if (sous_grad==1) { DrawLine(ixmx,iyyg1,ixmx-trait,iyyg1); for (i=imin;i<=imax;i++) { if (!id_imprim) { ii=grid*i-1; DrawPixel(ixmn+ii,iyyg1); } else { ii=grid*i-1; DrawPixel(ixmn+ii,iyyg1); } } } } } while (ycm < 0.92*Cymx); } // end of else DeleteObject(redpen); } //Graduer_les_Axes() /**************************************************************** * Initialize a 2D graphic window of number fntr and define the * * physical range in Ox and Oy. The pixel zone of drawing is * * monitored by MaxX and MaxY parameters. * * ------------------------------------------------------------- * * fntr zone * * ---- ---- * * 1 left upper quarter * * 2 right " " * * 3 lower half * * 4 all the client zone (with internal * * graduations and caption) * * 5 upper half * * 6 left lower quarter * * 7 right " " * * 8 left half * * 9 right half * * 10 idem 4 with external graduations * * and upper caption (most used) * * 11 idem 10, adapted for printer. * ****************************************************************/ void Initwindow(int fntr,double xmn,double xmx,double ymn,double ymx) { int bord=Bord, bord1=5*Bord; wind=fntr; if (id_EPSON) { xratio=XrEPSON; yratio=YrEPSON; } if (id_HP) { xratio=XrHP; yratio=YrHP; } if(!id_imprim) { xratio=XrIBM; yratio=YrIBM; } else { bord=7*Bord; bord1=bord;} if (wind==0) // user defined window { ixmn = xgclot; ixmx = xdclot; iymn = MaxY-yhclot; iymx = MaxY-ybclot; } else // one of the 11 predefined windows { xmin1=(MaxX/2)-bord; xmax1=(MaxX/2)+8*bord; ymin1=(MaxY/2)-bord; ymax1=(MaxY/2)+4*bord; switch(wind) { // 1-9: same for screen or printer case 1: Fenetre(8*bord,xmin1,ymax1,MaxY-2*bord); break; case 2: Fenetre(xmax1,MaxX-bord,ymax1,MaxY-2*bord); break; case 3: Fenetre(8*bord,MaxX-bord,bord1,ymin1); break; case 4: Fenetre(8*bord,MaxX-bord,bord1,MaxY-2*bord); break; case 5: Fenetre(8*bord,MaxX-bord,ymax1,MaxY-2*bord); break; case 6: Fenetre(8*bord,xmin1,bord1,ymin1); break; case 7: Fenetre(xmax1,MaxX-bord,bord1,ymin1); break; case 8: Fenetre(8*bord,xmin1,bord1,MaxY-2*bord); break; case 9: Fenetre(xmax1,MaxX-bord,bord1,MaxY-2*bord); break; // specific for SVGA screen case 10: Fenetre(12*bord,MaxX-6*bord,6*bord,MaxY-6*bord); break; // black & white laser or color ink jet printer case 11: Fenetre(14*bord,MaxX-bord,4*bord,MaxY-6*bord); } ixmn = xgclot; ixmx = xdclot; iymn = MaxY-yhclot; iymx = MaxY-ybclot; } Cxmx=(ixmx-ixmn)/xratio; Cymx=(iymx-iymn)/yratio; // max. lenght (screen cm) available for the drawing if (Ech_auto==1) // automatic scales { xmin=xmn; xmax=xmx; ymin=ymn; ymax=ymx; } else // manual scales (calling program must define { // X_mini,X_maxi,Y_mini and Y_maxi) xmin=X_mini; xmax=X_maxi; ymin=Y_mini; ymax=Y_maxi; } if (Log_X==1) // logarithmic scale in Ox { if (xdclot-xgclot > MaxX/2) xmin=xmax/10000.0; else xmin=xmax/100.0; xmax=log10(xmax); xmin=log10(xmin); } if (Log_Y==1) // logarithmic scale in Oy { if (yhclot-ybclot > MaxY/2) ymin=ymax/1000.0; else ymin=ymax/10.0; ymax=log10(ymax); ymin=log10(ymin); } Echx=EchelleX(); Echy=EchelleY(); xo = 0.5 * (Cxmx - dx/Echx); if (xmin==0) { Xc=xmin-xo*Echx; if ( Xc<0 ) xo=xmin/Echx; } yo = 0.5 * (Cymx - dy/Echy); if (ymin==0) { Yc=ymin-yo*Echy; if (Yc<0 ) yo=ymin/Echy; } Graduer_les_axes(); Tracer_les_axes(); // thick blue pen for frame HPEN doublepen, oldpen; doublepen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255)); oldpen = SelectObject(hdc,doublepen); // restore horizontal lines of frame DrawLine(ixmn,iymn,ixmx,iymn); DrawLine(ixmn,iymx,ixmx,iymx); // restore current pen SelectObject(hdc,oldpen); DeleteObject(doublepen); } // Initwindow() /************************************************************** * Move invisible pen to physical point (x,y). If new location * * is not visible, set id_out to 1. * * Prerequisite: call Initwindow() before. * **************************************************************/ void MoveXY(double x, double y) { // extern ix,iy Conversion(x,y,&ix,&iy); if (ix>=ixmn && ix<=ixmx && iy>=iymn && iy<=iymx) id_out=0; else id_out=1; } /************************************************************** * Draw a line from current physical point to physical point * * (x,y), if id_out=0 (the current point is visible). * * Prerequisite: call Initwindow() before. * **************************************************************/ void LineXY(double x, double y) { // extern ix,iy (current point) int ix1,iy1; Conversion(x,y,&ix1,&iy1); if (ix>=ixmn && ix<=ixmx && iy>=iymn && iy<=iymx) { if (!id_out) DrawLine(ix,iy,ix1,iy1); ix=ix1; iy=iy1; id_out=0; } else id_out=1; } /*********************************************************** * Print curve caption and names of axes with a special font* * for large windows (caption only) * * Prerequisite: call Initwindow() before. * ***********************************************************/ void Legendes (int wind,char *tr,char *nx,char *ny, int lx1,int ly1,char *leg1,int lx2, int ly2,char *leg2) // print caption, names of axes and two legends { int xtitre,ytitre,l,h; int xnx,ynx,xny,yny; int hauteur,largeur; l=xdclot-xgclot; h=yhclot-ybclot; xtitre=xgclot+l/5; ytitre=MaxY-yhclot+h/12; xnx=xgclot+ (int) (0.7*l); if (h > MaxY / 2) ynx=MaxY-ybclot-(int) (0.075*h); else ynx=MaxY-ybclot-(int) (0.15*h); xny=xgclot+(int) (0.03*l); yny=MaxY-yhclot+(int) (0.03*h); if (l > MaxX / 2) { HFONT myfont, oldfont; if (!id_imprim) { hauteur=25; largeur=12; } else { hauteur=100; largeur=50; } myfont = CreateFont(hauteur, // graph caption font 0, 0, 0, 700, // gras ( 400 = normal ) FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial"); oldfont = SelectObject(hdc,myfont); xnx=xgclot+ (int) (0.85*l); xtitre=xgclot+(l-8*strlen(tr))/2; if (wind==10) ytitre=8; else if (wind==11) ytitre=0; xtitre=xgclot+(l-largeur*strlen(tr))/2; OutText(xtitre,ytitre,tr); SelectObject(hdc,oldfont); } else OutText(xtitre,ytitre,tr); OutText(xnx,ynx,nx); OutText(xny,yny,ny); // print two legends OutText(lx1,ly1,leg1); OutText(lx2,ly2,leg2); } //Legendes() /*********************************************************** * Return minimum and maximum values of a table Y of size n * * in ymin et ymax. * ***********************************************************/ void MinMax(int n, float *Y, double *ymin, double *ymax) { int i; float y1,y2; y1=Y[0]; y2=y1; for (i=1; i y2) y2=Y[i]; } *ymin=y1; *ymax=y2; } /************************************************************** * Draw a curve 2D in a graphic zone number fntr. X increment * * is constant. * * ----------------------------------------------------------- * * n : number of points to draw (int) * * Y : pointer to table of Y values (float) * * xn : start x value (double) * * xm : end x value (double) * * ----------------------------------------------------------- * * Takes in charge calling Initwindow(). * **************************************************************/ void CourbeXY(int n,int fntr,float * Y,double xn,double xm) { int i; double x,dx,y1,y2,temp; dx=(xm-xn)/(n-1); y1=Y[0]; y2=y1; for (i=1; i y2) y2=Y[i]; } Initwindow(fntr,xn,xm,y1,y2); x=xn; temp=(double) Y[0]; MoveXY(x,temp); for (i=1; i