Back to index

texmacs  1.0.7.15
grid.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : grid.cpp
00004 * DESCRIPTION: grids for the graphics
00005 * COPYRIGHT  : (C) 2003  Henri Lesourd
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "grid.hpp"
00013 #include "math_util.hpp"
00014 #include "curve.hpp"
00015 
00016 /******************************************************************************
00017 * General routines
00018 ******************************************************************************/
00019 
00020 void
00021 grid_rep::set_aspect (tree aspect) {
00022   subd= array<SI> (2);
00023   subd[0]= 0;
00024   subd[1]= 1;
00025   col= array<string> (2);
00026   col[0]= "#808080";
00027   col[1]= "#c0c0c0";
00028   if (is_tuple (aspect)) {
00029     int i;
00030     bool b= false;
00031     subd= array<SI> (N(aspect));
00032     col= array<string> (N(aspect));
00033     for (i=0; i<N(aspect); i++) {
00034        if (is_tuple (aspect[i], "axes", 1)) {
00035          subd[i]= 0;
00036          b= true;
00037        }
00038        else {
00039          subd[i]= as_int (aspect[i][0]);
00040        }
00041        col[i]= as_string (aspect[i][1]);
00042     }
00043     if (!b) {
00044       array<SI> subd0 (1);
00045       array<string> col0 (1);
00046       subd0[0]= 0;
00047       col0[0]= "#e0e0ff";
00048       subd= subd0 << subd;
00049       col= col0 << col;
00050     }
00051     do {
00052       b= true;
00053       for (i=1; i<N(subd); i++)
00054          if (subd[i-1]>subd[i]) {
00055            SI j;
00056            string c;
00057            j= subd[i-1];subd[i-1]= subd[i];subd[i]= j;
00058            c= col[i-1];col[i-1]= col[i];col[i]= c;
00059            b= false;
00060          }
00061     }
00062     while (!b);
00063   }
00064 }
00065 
00066 array<grid_curve>
00067 grid_rep::get_curves_around (point p, double delta, frame f) {
00068   point p2= f (p);
00069   point pmin, pmax;
00070   pmin= f[point (p2[0]-delta, p2[1]-delta)];
00071   pmax= f[point (p2[0]+delta, p2[1]+delta)];
00072   return get_curves (pmin, pmax, true);
00073 }
00074 
00075 point
00076 grid_rep::find_point_around (point p, double delta, frame f) {
00077   point p2= f (p);
00078   point pmin, pmax;
00079   pmin= f[point (p2[0]-delta, p2[1]-delta)];
00080   pmax= f[point (p2[0]+delta, p2[1]+delta)];
00081   return find_closest_point (p, pmin, pmax);
00082 }
00083 
00084 /******************************************************************************
00085 * The empty grid
00086 ******************************************************************************/
00087 
00088 struct empty_grid_rep: public grid_rep {
00089   empty_grid_rep ():
00090     grid_rep () {}
00091   array<grid_curve> get_curves (point lim1, point lim2, bool b) {
00092     (void) lim1; (void) lim2; (void) b;
00093     array<grid_curve> res;
00094     return res; }
00095   operator tree () { return "empty_grid"; }
00096   point find_closest_point (point p, point pmin, point pmax);
00097 };
00098 
00099 point
00100 empty_grid_rep::find_closest_point (point p, point pmin, point pmax) {
00101   (void) pmin; (void) pmax;
00102 /*double x= floor (10.0*p[0] + 0.5);
00103   double y= floor (10.0*p[1] + 0.5);
00104   return point (x / 10.0, y / 10.0);*/
00105   return p;
00106 }
00107 
00108 grid
00109 empty_grid () {
00110   return tm_new<empty_grid_rep> ();
00111 }
00112 
00113 /******************************************************************************
00114 * Cartesian grids
00115 ******************************************************************************/
00116 
00117 struct cartesian_rep: public grid_rep {
00118   double step; // Unit length
00119   cartesian_rep (array<SI> subd, array<string> col, point o, double st):
00120     grid_rep (subd, col, o), step (st) {}
00121   operator tree () { return "cartesian"; }
00122   array<grid_curve> get_curves (point lim1, point lim2, bool b);
00123   array<grid_curve> get_curves_around (point p, double delta, frame f);
00124   point find_closest_point (point p, point pmin, point pmax);
00125 };
00126 
00127 static grid_curve
00128 create_line (double x1, double y1, double x2, double y2, string col) {
00129   array<point> a(2);
00130   a[0]= point (x1, y1);
00131   a[1]= point (x2, y2);
00132   array<path> cip(2);
00133   grid_curve res= grid_curve (col, poly_segment (a, cip));
00134   return res;
00135 }
00136 
00137 array<grid_curve>
00138 cartesian_rep::get_curves (point lim1, point lim2, bool b) {
00139   array<grid_curve> res;
00140   if (N(subd)<1) return res;
00141   double x1= min (lim1[0], lim2[0]);
00142   double y1= min (lim1[1], lim2[1]);
00143   double x2= max (lim1[0], lim2[0]);
00144   double y2= max (lim1[1], lim2[1]);
00145   double xo= center[0];
00146   double yo= center[1];
00147   int i;
00148   for (i= N(subd)-1; i>=1; i--) {
00149      SI nsub;
00150      nsub= subd[i];
00151      if (nsub!=0) {
00152        double x, y, s;
00153        s= max (step/nsub, 1.0e-6);
00154        for (x= xo; x<=x2; x+=s)
00155          if (x>=x1) {
00156            res << create_line (x, y1, x, y2, col[i]);
00157            if (b) break;
00158          }
00159        for (x= xo-s; x>=x1; x-=s)
00160          if (x<=x2) {
00161            res << create_line (x, y1, x, y2, col[i]);
00162            if (b) break;
00163          }
00164        for (y= yo; y<=y2; y+=s)
00165          if (y>=y1) {
00166            res << create_line (x1, y, x2, y, col[i]);
00167            if (b) break;
00168          }
00169        for (y= yo-s; y>=y1; y-=s)
00170          if (y<=y2) {
00171            res << create_line (x1, y, x2, y, col[i]);
00172            if (b) break;
00173          }
00174      }
00175   }
00176   if (!b && yo>=y1 && yo<=y2)
00177     res << create_line (x1, yo, x2, yo, col[0]);
00178   if (!b && xo>=x1 && xo<=x2)
00179     res << create_line (xo, y1, xo, y2, col[0]);
00180   return res;
00181 }
00182 
00183 array<grid_curve>
00184 cartesian_rep::get_curves_around (point p, double delta, frame f) {
00185   double nsub= 0;
00186   string c;
00187   for (int i=0; i<N(subd); i++)
00188     if (subd[i] != 0) { nsub= subd[i]; c= col[i]; }
00189   if (nsub == 0) return array<grid_curve> (0);
00190 
00191   point p2  = f (p);
00192   point lim1= f[point (p2[0]-delta, p2[1]-delta)];
00193   point lim2= f[point (p2[0]+delta, p2[1]+delta)];
00194  
00195   double x1= min (lim1[0], lim2[0]);
00196   double y1= min (lim1[1], lim2[1]);
00197   double x2= max (lim1[0], lim2[0]);
00198   double y2= max (lim1[1], lim2[1]);
00199   double xo= center[0];
00200   double yo= center[1];
00201   double s = step / nsub;
00202 
00203   double X1= xo + floor ((p[0] - xo) / s) * s;
00204   double Y1= yo + floor ((p[1] - yo) / s) * s;
00205   double X2= xo + ceil  ((p[0] - xo) / s) * s;
00206   double Y2= yo + ceil  ((p[1] - yo) / s) * s;
00207   x1= min (x1, X1 - s / 10);
00208   y1= min (y1, Y1 - s / 10);
00209   x2= max (x2, X2 + s / 10);
00210   y2= max (y2, Y2 + s / 10);
00211 
00212   array<grid_curve> res;
00213   res << create_line (X1, y1, X1, y2, c);
00214   res << create_line (x1, Y1, x2, Y1, c);
00215   if (X2 > X1) res << create_line (X2, y1, X2, y2, c);
00216   if (Y2 > Y1) res << create_line (x1, Y2, x2, Y2, c);
00217   return res;
00218 }
00219 
00220 point
00221 cartesian_rep::find_closest_point (point p, point pmin, point pmax) {
00222   double x, y, oldx=0, oldy= 0, ssubd;
00223   point res= p;
00224   p= p-center;
00225   int i;
00226   for (i=1; i<N(subd); i++) {
00227     ssubd= ((double) subd[i]) / step;
00228     if (ssubd==0) continue;
00229     x= nearest (p[0]*ssubd);
00230     y= nearest (p[1]*ssubd);
00231     res= center + point (x/ssubd, y/ssubd);
00232     if (i!=1) {
00233       if (inside_rectangle (point (oldx, res[1]), pmin, pmax))
00234         return point (oldx, res[1]);
00235       if (inside_rectangle (point (res[0], oldy), pmin, pmax))
00236         return point (res[0], oldy);
00237     }
00238     oldx= res[0];
00239     oldy= res[1];
00240     if (inside_rectangle (res, pmin, pmax))
00241       return res;
00242   }
00243   return res;
00244 }
00245 
00246 grid
00247 cartesian (array<SI> subd, array<string> col, point o, double step) {
00248   return tm_new<cartesian_rep> (subd, col, o, step);
00249 }
00250 
00251 /******************************************************************************
00252 * Polar grids
00253 ******************************************************************************/
00254 
00255 struct polar_rep: public grid_rep {
00256   double step;  // Radial unit length
00257   SI astep;     // # angles
00258   polar_rep (array<SI> subd, array<string> col, point o, double st, SI ast):
00259     grid_rep (subd, col, o), step (st), astep (ast) {}
00260   operator tree () { return "polar"; }
00261   array<grid_curve> get_curves (point lim1, point lim2, bool b);
00262   point find_closest_point (point p, point pmin, point pmax);
00263 };
00264 
00265 static grid_curve
00266 create_arc (
00267   double x1, double y1, double x2, double y2, double x3, double y3, string col)
00268 {
00269   array<point> a(3);
00270   a[0]= point (x1, y1);
00271   a[1]= point (x2, y2);
00272   a[2]= point (x3, y3);
00273   array<path> cip(3);
00274   grid_curve res= grid_curve (col, arc (a, cip, true));
00275   return res;
00276 }
00277 
00278 array<grid_curve>
00279 polar_rep::get_curves (point lim1, point lim2, bool b) {
00280   (void) b;
00281   array<grid_curve> res;
00282   if (N(subd)<1) return res;
00283   double x1= min (lim1[0], lim2[0]);
00284   double y1= min (lim1[1], lim2[1]);
00285   double x2= max (lim1[0], lim2[0]);
00286   double y2= max (lim1[1], lim2[1]);
00287   double xo= center[0];
00288   double yo= center[1];
00289   point P1, P2;
00290   if (x1<=0 && y1<=0 && x2>=0 && y2>=0) {
00291     P1= point (0, 0);
00292     P2= point (x2, y2) - point (x1, y1);
00293   }
00294   else {
00295     double ox= (x1 + x2) / 2;
00296     double oy= (y1 + y2) / 2;
00297     if (oy>=0)
00298       P1= point (0, y1>=0 ? y1 : 0);
00299     else
00300       P1= point (0, y2<=0 ? y2 : 0);
00301 
00302     if (ox>=0 && oy>=0)
00303       P2= point (x2, y2);
00304     else
00305     if (ox<=0 && oy>=0)
00306       P2= point (x1, y2);
00307     else
00308     if (ox<=0 && oy<=0)
00309       P2= point (x1, y1);
00310     else
00311     if (ox>=0 && oy<=0)
00312       P2= point (x2, y1);
00313   }
00314   double r, R1= norm (P1), R2= norm (P2);
00315   int i;
00316   for (i= N(subd)-1; i>=1; i--) {
00317     SI nsub;
00318     nsub= subd[i];
00319     if (nsub!=0) {
00320       SI j;
00321       double s= max (step/nsub, 1.0e-6);
00322       for (r=0; r<=R2; r+=s)
00323        if (r>=R1)
00324          res << create_arc (xo+r, yo, xo, yo+r, xo-r, yo, col[i]);
00325       for (j=0; j<astep*nsub; j++)
00326         res << create_line (xo, yo, xo+R2*cos((2*tm_PI*j)/(astep*nsub)),
00327                                     yo+R2*sin((2*tm_PI*j)/(astep*nsub)),
00328                                     col[i]);
00329     }
00330   }
00331   res << create_line (x1, yo, x2, yo, col[0]);
00332   res << create_line (xo, y1, xo, y2, col[0]);
00333   return res;
00334 }
00335 
00336 point
00337 polar_rep::find_closest_point (point p, point pmin, point pmax) {
00338   double n, a, oldn= 0, olda= 0, ssubd;
00339   point res= p;
00340   p= p-center;
00341   int i;
00342   for (i=1; i<N(subd); i++) {
00343     ssubd= (double)subd[i];
00344     if (ssubd==0) continue;
00345     n= nearest (norm(p)*(ssubd/step));
00346     if (fnull (norm (p), 1.0e-6))
00347       a= 0.0;
00348     else
00349       a= nearest ((arg(p)/(2*tm_PI))*astep*ssubd);
00350     n= n*(step/ssubd);
00351     a= a/(astep*ssubd);
00352     if (i!=1) {
00353       res= center + oldn * point (cos(2*tm_PI*a), sin(2*tm_PI*a));
00354       if (inside_rectangle (res, pmin, pmax))
00355         return res;
00356       res= center + n * point (cos(2*tm_PI*olda), sin(2*tm_PI*olda));
00357       if (inside_rectangle (res, pmin, pmax))
00358         return res;
00359     }
00360     oldn= n;
00361     olda= a;
00362     res= center + n * point (cos(2*tm_PI*a), sin(2*tm_PI*a));
00363     if (inside_rectangle (res, pmin, pmax))
00364       return res;
00365   }
00366   return res;
00367 }
00368 
00369 grid
00370 polar (array<SI> subd, array<string> col, point o, double step, SI astep) {
00371   return tm_new<polar_rep> (subd, col, o, step, astep);
00372 }
00373 
00374 /******************************************************************************
00375 * Logarithmic grids
00376 ******************************************************************************/
00377 
00378 struct logarithmic_rep: public grid_rep {
00379   double step; // Unit length
00380   SI base;
00381   logarithmic_rep (array<SI> subd, array<string> col, point o, double st, SI b):
00382     grid_rep (subd, col, o), step (max (st, 1.0e-6)), base (b) {}
00383   operator tree () { return "logarithmic"; }
00384   array<grid_curve> get_curves (point lim1, point lim2, bool b);
00385   point find_closest_point (point p, point pmin, point pmax);
00386 };
00387 
00388 array<grid_curve>
00389 logarithmic_rep::get_curves (point lim1, point lim2, bool b) {
00390   (void) b;
00391   array<grid_curve> res;
00392   if (N(subd)<1) return res;
00393   double x1= min (lim1[0], lim2[0]);
00394   double y1= min (lim1[1], lim2[1]);
00395   double x2= max (lim1[0], lim2[0]);
00396   double y2= max (lim1[1], lim2[1]);
00397   double xo= center[0];
00398   double yo= center[1];
00399   int i;
00400   double x, y;
00401   if (N(col)>=3) {
00402     for (i=2; i<base; i++) {
00403       double dx, dy;
00404       dx= dy= step*log((double)i)/log((double)base);
00405       for (x=xo; x<=x2; x+=step)
00406         res << create_line (x+dx, y1, x+dx, y2, col[2]);
00407       for (x=xo-step; x>=x1-step; x-=step)
00408         res << create_line (x+dx, y1, x+dx, y2, col[2]);
00409       for (y=yo; y<=y2; y+=step)
00410         res << create_line (x1, y+dy, x2, y+dy, col[2]);
00411       for (y=yo-step; y>=y1-step; y-=step)
00412         res << create_line (x1, y+dy, x2, y+dy, col[2]);
00413     }
00414   }
00415   if (N(col)>=2) {
00416     for (x=xo; x<=x2; x+=step)
00417       res << create_line (x, y1, x, y2, col[1]);
00418     for (x=xo; x>=x1; x-=step)
00419       res << create_line (x, y1, x, y2, col[1]);
00420     for (y=yo; y<=y2; y+=step)
00421       res << create_line (x1, y, x2, y, col[1]);
00422     for (y=yo; y>=y1; y-=step)
00423       res << create_line (x1, y, x2, y, col[1]);
00424   }
00425   res << create_line (x1, yo, x2, yo, col[0]);
00426   res << create_line (xo, y1, xo, y2, col[0]);
00427   return res;
00428 }
00429 
00430 point
00431 logarithmic_rep::find_closest_point (point p, point pmin, point pmax) {
00432   double x, y, ssubd= ((double) subd[1]) / step;
00433   point res= p;
00434   if (ssubd!=0) {
00435     p= p-center;
00436     x= nearest (p[0]*ssubd);
00437     y= nearest (p[1]*ssubd);
00438     res= center + point (x/ssubd, y/ssubd);
00439     if (inside_rectangle (res, pmin, pmax))
00440       return res;
00441     p= center+p;
00442   }
00443   double xo, yo;
00444   xo= center[0];
00445   yo= center[1];
00446   double x1, y1, x2, y2;
00447   x1= xo-step;
00448   y1= yo-step;
00449   x2= xo+step;
00450   y2= yo+step;
00451   p= p-center;
00452   double x0, y0;
00453   x0= (SI)(p[0]/step);
00454   y0= (SI)(p[1]/step);
00455   x0*= step;
00456   y0*= step;
00457   p= p-point(x0,y0);
00458   p= center+p;
00459   double xm, ym;
00460   xm= ym= tm_infinity/2;
00461   int i;
00462   for (i=1; i<base; i++) {
00463     double dx, dy;
00464     dx= dy= step*log((double)i)/log((double)base);
00465     for (x=xo; x<=x2; x+=step)
00466       if (norm(x+dx-p[0])<norm(xm-p[0])) xm= x+dx;
00467     for (x=xo-step; x>=x1-step; x-=step)
00468       if (norm(x+dx-p[0])<norm(xm-p[0])) xm= x+dx;
00469     for (y=yo; y<=y2; y+=step)
00470       if (norm(y+dy-p[1])<norm(ym-p[1])) ym= y+dy;
00471     for (y=yo-step; y>=y1-step; y-=step)
00472       if (norm(y+dy-p[1])<norm(ym-p[1])) ym= y+dy;
00473   }
00474   p= point (x0+xm, y0+ym);
00475   if (ssubd!=0) {
00476     if (inside_rectangle (point (res[0], p[1]), pmin, pmax))
00477       return point (res[0], p[1]);
00478     if (inside_rectangle (point (p[0], res[1]), pmin, pmax))
00479       return point (p[0], res[1]);
00480   }
00481   return p;
00482 }
00483 
00484 grid
00485 logarithmic (array<SI> subd, array<string> col, point o, double step, SI base) {
00486   return tm_new<logarithmic_rep> (subd, col, o, step, base);
00487 }
00488 
00489 /******************************************************************************
00490 * User interface
00491 ******************************************************************************/
00492 
00493 grid
00494 as_grid (tree t) {
00495   array<SI> subd (0, 1);
00496   array<string> col ("black", "black");
00497   grid gr= empty_grid ();
00498   double step= 1.0;
00499   point center= point (0.0, 0.0);
00500   if (is_tuple (t, "empty"))
00501     gr= empty_grid ();
00502   else
00503   if (is_tuple (t, "cartesian")) {
00504     if (is_tuple (t, "cartesian", 0)) ;
00505     else
00506     if (is_tuple (t, "cartesian", 1))
00507       step= as_double (t[1]);
00508     else
00509     if (is_tuple (t, "cartesian", 2)) {
00510       center= as_point (t[1]);
00511       step= as_double (t[2]);
00512     }
00513     gr= cartesian (subd, col, center, step);
00514   }
00515   else
00516   if (is_tuple (t, "polar")) {
00517     SI astep= 8;
00518     if (is_tuple (t, "polar", 0)) ;
00519     else
00520     if (is_tuple (t, "polar", 1))
00521       step= as_double (t[1]);
00522     else
00523     if (is_tuple (t, "polar", 2)) {
00524       step= as_double (t[1]);
00525       astep= as_int (t[2]);
00526     }
00527     else
00528     if (is_tuple (t, "polar", 3)) {
00529       center= as_point (t[1]);
00530       step= as_double (t[2]);
00531       astep= as_int (t[3]);
00532     }
00533     gr=polar (subd, col, center, step, astep);
00534   }
00535   else
00536   if (is_tuple (t, "logarithmic")) {
00537     SI base= 10;
00538     if (is_tuple (t, "logarithmic", 0)) ;
00539     else
00540     if (is_tuple (t, "logarithmic", 1))
00541       step= as_double (t[1]);
00542     else
00543     if (is_tuple (t, "logarithmic", 2)) {
00544       step= as_double (t[1]);
00545       base= as_int (t[2]);
00546     }
00547     else
00548     if (is_tuple (t, "logarithmic", 3)) {
00549       center= as_point (t[1]);
00550       step= as_double (t[2]);
00551       base= as_int (t[3]);
00552     }
00553     gr= logarithmic (subd, col, center, step, base);
00554   }
00555   return gr;
00556 }
00557 
00558 tree
00559 as_tree (grid g) {
00560   return (tree) g;
00561 }