Back to index

texmacs  1.0.7.15
graphics_boxes.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : graphics.cpp
00004 * DESCRIPTION: Boxes for graphics
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
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 "env.hpp"
00013 #include "Boxes/graphics.hpp"
00014 #include "Boxes/composite.hpp"
00015 #include "Boxes/construct.hpp"
00016 #include "Graphics/math_util.hpp"
00017 
00018 /******************************************************************************
00019 * Graphics boxes
00020 ******************************************************************************/
00021 
00022 struct graphics_box_rep: public composite_box_rep {
00023   frame f;
00024   grid g;
00025   point lim1, lim2;
00026   SI old_clip_x1, old_clip_x2, old_clip_y1, old_clip_y2;
00027   graphics_box_rep (
00028     path ip, array<box> bs, frame f, grid g, point lim1, point lim2);
00029   frame get_frame ();
00030   grid get_grid ();
00031   void  get_limits (point& lim1, point& lim2);
00032   operator tree () { return "graphics"; }
00033   void pre_display (renderer &ren);
00034   void post_display (renderer &ren);
00035   int reindex (int i, int item, int n);
00036   virtual int find_child (SI x, SI y, SI delta, bool force);
00037   gr_selections graphical_select (SI x, SI y, SI dist);
00038   gr_selections graphical_select (SI x1, SI y1, SI x2, SI y2);
00039 };
00040 
00041 graphics_box_rep::graphics_box_rep (
00042   path ip2, array<box> bs2, frame f2, grid g2, point lim1b, point lim2b):
00043   composite_box_rep (ip2, bs2), f (f2), g (g2), lim1 (lim1b), lim2 (lim2b)
00044 {
00045   point flim1= f(lim1), flim2= f(lim2);
00046   x1= (SI) min (flim1[0], flim2[0]);
00047   y1= (SI) min (flim1[1], flim2[1]);
00048   x2= (SI) max (flim1[0], flim2[0]);
00049   y2= (SI) max (flim1[1], flim2[1]);
00050   x3= max (x1, x3);
00051   y3= max (y1, y3);
00052   x4= min (x2, x4);
00053   y4= min (y2, y4);
00054   finalize ();
00055 }
00056 
00057 frame
00058 graphics_box_rep::get_frame () {
00059   return f;
00060 }
00061 
00062 grid
00063 graphics_box_rep::get_grid () {
00064   return g;
00065 }
00066 
00067 void
00068 graphics_box_rep::get_limits (point& lim1b, point& lim2b) {
00069   lim1b= lim1;
00070   lim2b= lim2;
00071 }
00072 
00073 void
00074 graphics_box_rep::pre_display (renderer &ren) {
00075   ren->get_clipping (old_clip_x1, old_clip_y1, old_clip_x2, old_clip_y2);
00076   ren->extra_clipping (x1, y1, x2, y2);
00077 }
00078 
00079 void
00080 graphics_box_rep::post_display (renderer &ren) {
00081   ren->set_clipping (
00082     old_clip_x1, old_clip_y1, old_clip_x2, old_clip_y2, true);
00083 }
00084 
00085 int
00086 graphics_box_rep::reindex (int i, int item, int n) {
00087   (void) item; (void) n;
00088   return i;
00089 }
00090 
00091 int
00092 graphics_box_rep::find_child (SI x, SI y, SI delta, bool force) {
00093   int m= composite_box_rep::find_child (x, y, delta, force);
00094   if (m==-1) return -1;
00095   int i, n= subnr();
00096   for (i=0; i<n; i++)
00097     if (distance (i, x, y, delta)==0) {
00098       tree ty= (tree)bs[i];
00099       if ((bs[i]->accessible () || force) && is_tuple (ty) && ty[0]=="text-at")
00100         return i;
00101     }
00102   return m;
00103 }
00104 
00105 /*NOTE: It seems that the dimensions of the boxes that inherit from
00106   composite_box are not calculated correctly (namely : one can find
00107   points inside the box that are outside the rectangle (x1, y1, x2, y2)
00108   that defines the border of the box). As a consequence, we use a traversal
00109   routine that doesn't tests contains_rectangle(). When this problem
00110   will have been corrected, the method of composite_box should work,
00111   and consequently, its more specific implementation below should be
00112   removed (this is the same in concat_boxes and stack_boxes). */
00113 
00114 gr_selections
00115 graphics_box_rep::graphical_select (SI x, SI y, SI dist) {
00116   gr_selections res;
00117   int i, n= subnr();
00118   for (i=n-1; i>=0; i--)
00119     res << bs[i]->graphical_select (x- sx(i), y- sy(i), dist);
00120   return res;
00121 }
00122 
00123 gr_selections
00124 graphics_box_rep::graphical_select (SI x1, SI y1, SI x2, SI y2) {
00125   gr_selections res;
00126   int i, n= subnr();
00127   for (i=n-1; i>=0; i--)
00128     res << bs[i]->graphical_select (x1- sx(i), y1- sy(i),
00129                                 x2- sx(i), y2- sy(i));
00130   return res;
00131 }
00132 
00133 /******************************************************************************
00134 * Group boxes
00135 ******************************************************************************/
00136 
00137 struct graphics_group_box_rep: public composite_box_rep {
00138   graphics_group_box_rep (path ip, array<box> bs):
00139     composite_box_rep (ip, bs, true) { finalize (); }
00140   bool access_allowed () { return false; }
00141   operator tree () { return "graphics_group"; }
00142   path find_lip () { return path (-1); }
00143   path find_rip () { return path (-1); }
00144   gr_selections graphical_select (SI x, SI y, SI dist);
00145   gr_selections graphical_select (SI x1, SI y1, SI x2, SI y2);
00146   int reindex (int i, int item, int n);
00147 };
00148 
00149 gr_selections
00150 graphics_group_box_rep::graphical_select (SI x, SI y, SI dist) {
00151   gr_selections res;
00152   if (graphical_distance (x, y) <= dist) {
00153     gr_selection gs;
00154     gs->type= "group";
00155     gs->dist= graphical_distance (x, y);
00156   //gs->p= point (x, y); // The cursor moves freely inside the box
00157     gs->cp << reverse (path (0, ip));
00158     gs->pts= array<point> (0);
00159     gs->c= curve ();
00160     res << gs;
00161   }
00162   return res;
00163 }
00164 
00165 gr_selections
00166 graphics_group_box_rep::graphical_select (SI x1, SI y1, SI x2, SI y2) {
00167   gr_selections res;
00168   if (in_rectangle (x1, y1, x2, y2)) {
00169     gr_selection gs;
00170     gs->type= "group";
00171     gs->dist= graphical_distance (x1, y1);
00172     gs->cp << reverse (path (0, ip));
00173     gs->pts= array<point> (0);
00174     gs->c= curve ();
00175     res << gs;
00176   }
00177   return res;
00178 }
00179 
00180 int
00181 graphics_group_box_rep::reindex (int i, int item, int n) {
00182   (void) item; (void) n;
00183   return i;
00184 }
00185 
00186 /******************************************************************************
00187 * Point boxes
00188 ******************************************************************************/
00189 
00190 struct point_box_rep: public box_rep {
00191   point p;
00192   SI r;
00193   color col;
00194   int fill;
00195   color fill_col;
00196   string style;
00197   point_box_rep (
00198     path ip, point p, SI radius, color col,
00199     int fill, color fill_col, string style);
00200   SI graphical_distance (SI x, SI y) { return (SI)norm (p - point (x, y)); }
00201   gr_selections graphical_select (SI x, SI y, SI dist);
00202   void display (renderer ren);
00203   operator tree () { return "point"; }
00204 };
00205 
00206 point_box_rep::point_box_rep (
00207   path ip2, point p2, SI r2, color col2,
00208   int fill2, color fill_col2, string style2):
00209     box_rep (ip2), p (p2), r (r2), col (col2),
00210     fill (fill2), fill_col (fill_col2), style (style2)
00211 {
00212   x1= x3= ((SI) p[0]) - r;
00213   y1= y3= ((SI) p[1]) - r;
00214   x2= x4= ((SI) p[0]) + r;
00215   y2= y4= ((SI) p[1]) + r;
00216 }
00217 
00218 gr_selections
00219 point_box_rep::graphical_select (SI x, SI y, SI dist) {
00220   gr_selections res;
00221   if (graphical_distance (x, y) <= dist) {
00222     gr_selection gs;
00223     gs->type= "point";
00224     gs->dist= graphical_distance (x, y);
00225     gs->p= p;
00226     gs->cp << reverse (path (0, ip));
00227     gs->pts << p;
00228     gs->c= curve ();
00229     res << gs;
00230   }
00231   return res;
00232 }
00233 
00234 void
00235 point_box_rep::display (renderer ren) {
00236   array<SI> x (4), y (4);
00237   x[0]= ((SI) p[0]) - r;
00238   y[0]= ((SI) p[1]) - r;
00239   x[1]= ((SI) p[0]) - r;
00240   y[1]= ((SI) p[1]) + r;
00241   x[2]= ((SI) p[0]) + r;
00242   y[2]= ((SI) p[1]) + r;
00243   x[3]= ((SI) p[0]) + r;
00244   y[3]= ((SI) p[1]) - r;
00245   ren->set_line_style (PIXEL);
00246   if (style == "square") {
00247     if (fill == FILL_MODE_INSIDE || fill == FILL_MODE_BOTH) {
00248       ren->set_color (fill_col);
00249       ren->line (x[0], y[0], x[1], y[1]);
00250       ren->line (x[1], y[1], x[2], y[2]);
00251       ren->line (x[2], y[2], x[3], y[3]);
00252       ren->line (x[3], y[3], x[0], y[0]);
00253       ren->polygon (x, y, false);
00254     }
00255     if (fill == FILL_MODE_NONE || fill == FILL_MODE_BOTH) {
00256       ren->set_color (col);
00257       ren->line (x[0], y[0], x[1], y[1]);
00258       ren->line (x[1], y[1], x[2], y[2]);
00259       ren->line (x[2], y[2], x[3], y[3]);
00260       ren->line (x[3], y[3], x[0], y[0]);
00261     }
00262   }
00263   else {
00264     if (style == "disk"
00265      || fill == FILL_MODE_INSIDE || fill == FILL_MODE_BOTH) {
00266       ren->set_color (style == "disk" ? col : fill_col);
00267       ren->arc (x[0], y[0]+ren->pixel, x[2], y[2]+ren->pixel, 0, 64*360);
00268       ren->fill_arc (x[0], y[0]+ren->pixel, x[2], y[2]+ren->pixel, 0, 64*360);
00269     }
00270     if (fill == FILL_MODE_NONE || fill == FILL_MODE_BOTH) {
00271       ren->set_color (col);
00272       ren->arc (x[0], y[0]+ren->pixel, x[2], y[2]+ren->pixel, 0, 64*360);
00273     }
00274   }
00275 }
00276 
00277 /******************************************************************************
00278 * Curve boxes
00279 ******************************************************************************/
00280 
00281 struct curve_box_rep: public box_rep {
00282   array<point> a;
00283   SI width;
00284   color col;
00285   curve c;
00286   array<bool> style;
00287   SI style_unit;
00288   array<SI> styled_n;
00289   int fill;
00290   color fill_col;
00291   array<box> arrows;
00292   curve_box_rep (path ip, curve c, SI width, color col,
00293                array<bool> style, SI style_unit,
00294                int fill, color fill_col,
00295                array<box> arrows);
00296   box transform (frame fr);
00297   SI graphical_distance (SI x, SI y);
00298   gr_selections graphical_select (SI x, SI y, SI dist);
00299   gr_selections graphical_select (SI x1, SI y1, SI x2, SI y2);
00300   void display (renderer ren);
00301   operator tree () { return "curve"; }
00302   SI length ();
00303   void apply_style ();
00304 };
00305 
00306 curve_box_rep::curve_box_rep (path ip2, curve c2, SI W, color C,
00307   array<bool> style2, SI style_unit2, int fill2, color fill_col2,
00308   array<box> arrows2)
00309   :
00310   box_rep (ip2), width (W), col (C), c (c2),
00311   style (style2), style_unit (style_unit2),
00312   fill (fill2), fill_col (fill_col2)
00313 {
00314   a= c->rectify (PIXEL);
00315   int i, n= N(a);
00316   x1= y1= x3= y3= MAX_SI;
00317   x2= y2= x4= y4= -MAX_SI;
00318   for (i=0; i<(n-1); i++) {
00319     x1= min (x1, min ((SI) a[i][0], (SI) a[i+1][0]));
00320     y1= min (y1, min ((SI) a[i][1], (SI) a[i+1][1]));
00321     x2= max (x2, max ((SI) a[i][0], (SI) a[i+1][0]));
00322     y2= max (y2, max ((SI) a[i][1], (SI) a[i+1][1]));
00323   }
00324   apply_style ();
00325   arrows= array<box>(2);
00326   point p1, p2;
00327   bool error;
00328   if (N(arrows2)>0 && !is_nil (arrows2[0])) {
00329     point tg= c->grad (0.0, error);
00330     if (!error) {
00331       frame fr= scaling (1.0, a[0]) *
00332               rotation_2D (point (0.0, 0.0), arg (tg));
00333       arrows[0]= arrows2[0]->transform (fr);
00334       if (!is_nil (arrows[0])) {
00335        x1= min (x1, arrows[0]->x1);
00336        y1= min (y1, arrows[0]->y1);
00337        x2= max (x2, arrows[0]->x2);
00338        y2= max (y2, arrows[0]->y2);
00339       }
00340     }
00341   }
00342   if (N(arrows2)>1 && !is_nil (arrows2[1])) {
00343     point tg= c->grad (1.0, error);
00344     if (!error) {
00345       frame fr= scaling (1.0, a[N(a)-1]) *
00346               rotation_2D (point (0.0, 0.0), arg (tg));
00347       arrows[1]= arrows2[1]->transform (fr);
00348       if (!is_nil (arrows[1])) {
00349        x1= min (x1, arrows[1]->x1);
00350        y1= min (y1, arrows[1]->y1);
00351        x2= max (x2, arrows[1]->x2);
00352        y2= max (y2, arrows[1]->y2);
00353       }
00354     }
00355   }
00356   x3= x1 - (width>>1); y3= y1 - (width>>1); 
00357   x4= x2 + (width>>1); y4= y2 + (width>>1);
00358 }
00359 
00360 box
00361 curve_box_rep::transform (frame fr) {
00362   return curve_box (ip, fr (c), width, col,
00363     style, style_unit, fill, fill_col, arrows);
00364 }
00365 
00366 SI
00367 curve_box_rep::graphical_distance (SI x, SI y) {
00368   SI gd= MAX_SI;
00369   point p (x, y);
00370   int i;
00371   for (i=0; i<N(a)-1; i++) {
00372     axis ax;
00373     ax.p0= a[i];
00374     ax.p1= a[i+1];
00375     gd= min (gd, (SI)seg_dist (ax, p));
00376   }
00377   return gd;
00378 }
00379 
00380 gr_selections
00381 curve_box_rep::graphical_select (SI x, SI y, SI dist) {
00382   gr_selections res;
00383   if (graphical_distance (x, y) <= dist) {
00384     array<double> abs;
00385     array<point> pts;
00386     array<path> paths;
00387     int np= c->get_control_points (abs, pts, paths);
00388     point p (x, y);
00389     int i;
00390     for (i=0; i<N(pts); i++) {
00391       SI n= (SI)norm (p - pts[i]);
00392       if (n <= dist) {
00393        gr_selection gs;
00394        gs->type= "curve-handle";
00395        gs->dist= n;
00396        gs->p= pts[i];
00397        gs->cp << reverse (paths[i]);
00398        gs->pts << pts[i];
00399        gs->c= c;
00400        res << gs;
00401       }
00402     }
00403     if (N(res) != 0) return res;
00404     int ne= np-1;
00405     if (np>1 && (abs[0]!=0.0 || abs[np-1]!=1.0))
00406       ne++;
00407     for (i=0; i<ne; i++) {
00408       bool b;
00409       double t= c->find_closest_point (abs[i], abs[(i+1)%np], p, PIXEL, b);
00410       if (b) {
00411        point p2= c->evaluate (t);
00412        SI n= (SI)norm (p - p2);
00413        if (n <= dist) {
00414          gr_selection gs;
00415          gs->type= "curve-point";
00416          gs->dist= n;
00417          gs->p= p2;
00418          gs->cp << reverse (paths[i]);
00419          gs->cp << reverse (paths[(i+1)%np]);
00420          gs->pts << pts[i];
00421          gs->pts << pts[(i+1)%np];
00422          gs->c= c;
00423          res << gs;
00424        }
00425       }
00426     }
00427   }
00428   return res;
00429 }
00430 
00431 gr_selections
00432 curve_box_rep::graphical_select (SI x1, SI y1, SI x2, SI y2) {
00433   gr_selections res;
00434   if (in_rectangle (x1, y1, x2, y2)) {
00435     gr_selection gs;
00436     gs->type= "curve";
00437     gs->dist= graphical_distance (x1, y1);
00438     gs->cp << reverse (path (0, ip));
00439     gs->pts= array<point> (0);
00440     gs->c= c;
00441     res << gs;
00442   }
00443   return res;
00444 }
00445 
00446 void
00447 curve_box_rep::display (renderer ren) {
00448   int i, n;
00449   if (fill == FILL_MODE_INSIDE || fill == FILL_MODE_BOTH) {
00450     ren->set_color (fill_col);
00451     n= N(a);
00452     array<SI> x (n), y (n);
00453     for (i=0; i<n; i++) {
00454       x[i]= (SI)a[i][0];
00455       y[i]= (SI)a[i][1];
00456     }
00457     ren->polygon (x, y, false);
00458   }
00459   if (fill == FILL_MODE_NONE || fill == FILL_MODE_BOTH) {
00460     ren->set_color (col);
00461     ren->set_line_style (width, 0, false);
00462  // TODO: Add options for handling round/nonround joins & line ends
00463     if (N (style) == 0) {
00464       n= N(a);
00465       array<SI> x (n), y (n);
00466       for (i=0; i<n; i++) {
00467        x[i]= (SI) a[i][0];
00468        y[i]= (SI) a[i][1];
00469       }
00470       ren->lines (x, y);
00471     }
00472     else {
00473       SI li=0, o=0;
00474       i=0;
00475       int no;
00476       point prec= a[0];
00477       for (no=0; no<N(styled_n); no++) {
00478        array<SI> x, y;
00479        point seg= a[i+1]-a[i];
00480        while (fnull (norm(seg),1e-6) && i+2<N(a)) {
00481          i++;
00482          seg= a[i+1]-a[i];
00483        }
00484        if (fnull (norm(seg),1e-6) && i+2>=N(a))
00485          break;
00486        SI lno= styled_n[no]*style_unit,
00487           len= li+(SI)norm(seg);
00488        while (i+2<N(a) && lno>len) {
00489          li= len;
00490          if (no%2!=0) {
00491          // 1st subsegment of a dash, along with the next ones
00492            x << (SI) prec[0];
00493            y << (SI) prec[1];
00494            prec= a[i+1];
00495          }
00496          i++;
00497          seg= a[i+1]-a[i];
00498          len= li+(SI)norm(seg);
00499        }
00500        if (N(x)>0 && no%2!=0) {
00501          x << (SI) prec[0];
00502          y << (SI) prec[1];
00503        }
00504        o= lno-li;
00505        if (i<N(a)) {
00506          point b= a[i] + o*(seg/norm(seg));
00507          if (no%2==0)
00508            prec= b;
00509          else {
00510         // Last subsegment of a dash
00511            if (N(x)==0) {
00512              x << (SI) prec[0];
00513              y << (SI) prec[1];
00514            }
00515            x << (SI) b[0];
00516            y << (SI) b[1];
00517          }
00518        }
00519        ren->lines (x, y);
00520       }
00521     }
00522   }
00523 
00524   rectangles ll;
00525   if (!is_nil (arrows[0])) arrows[0]->redraw (ren, path (), ll);
00526   if (!is_nil (arrows[1])) arrows[1]->redraw (ren, path (), ll);
00527 }
00528 
00529 SI
00530 curve_box_rep::length () {
00531   int i, n= N(a);
00532   SI res= 0;
00533   for (i=1; i<n; i++)
00534     res+= (SI)norm (a[i] - a[i-1]);
00535   return res;
00536 }
00537 
00538 void
00539 curve_box_rep::apply_style () {
00540   int n= N(style);
00541   if (n<=0 || fnull (style_unit,1e-6)) return;
00542   int i;
00543   bool all0=true, all1=true;
00544   for (i=0; i<n; i++) {
00545     if (style[i]) all0= false;
00546     if (!style[i]) all1= false;
00547   }
00548   if (all1) style= array<bool>(0);
00549   if (all0 || all1) return;
00550 
00551   int n2= 0;
00552   i= 0;
00553   while (!style[i]) i++;
00554   while (i<N(style)) {
00555     if (style[i]) n2++; else break;
00556     i++;
00557   }
00558 
00559   if (a[0]==a[N(a)-1]) n2= n; // Closed curves
00560 
00561   SI l= length (), l1= n*style_unit, n1= l/l1 + 1, l2= n2*style_unit;
00562   l1= (SI)((((double)l)*((double)l1)) / ((double)(n1*l1 + l2)));
00563   style_unit= l1/n;
00564   l2= n2*style_unit;
00565 
00566   int nfrag=0, prevfrag=-1;
00567   for (i=0; i<n; i++) {
00568     int frag= style[i]?1:0;
00569     if (frag!=prevfrag && frag==1) nfrag++;
00570     prevfrag= frag;
00571   }
00572 
00573   int nfrag2=0;
00574   prevfrag=-1;
00575   for (i=0; i<n2; i++) {
00576     int frag= style[i]?1:0;
00577     if (frag!=prevfrag && frag==1) nfrag2++;
00578     prevfrag= frag;
00579   }
00580 
00581   bool common_frag= style[0] && style[n-1] && n1>1;
00582   if (common_frag) nfrag--;
00583   styled_n= array<SI>(2*(nfrag*n1 + nfrag2));
00584 
00585   int no=0, nbu=0;
00586   prevfrag=-1;
00587   for (i=0; i<n1+1; i++) {
00588     int j;
00589     for (j=0; j<(i==n1?n2:n); j++) {
00590       int frag= style[j]?1:0;
00591       if (frag!=prevfrag) {
00592        if (frag==1) {
00593          styled_n[no]= nbu;
00594          no++;
00595        }
00596        else
00597        if (frag==0 && prevfrag!=-1) {
00598          styled_n[no]= nbu;
00599          no++;
00600        }
00601       }
00602       prevfrag= frag;
00603       nbu++;
00604     }
00605   }
00606   if (style[n2-1]) styled_n[N(styled_n)-1]= nbu;
00607 }
00608 
00609 /******************************************************************************
00610 * User interface
00611 ******************************************************************************/
00612 
00613 box
00614 graphics_box (
00615   path ip, array<box> bs, frame f, grid g, point lim1, point lim2)
00616 {
00617   box r= tm_new<graphics_box_rep> (ip, bs, f, g, lim1, lim2);
00618   if (r->x1 != 0) r= move_box (ip, r, -r->x1, 0);
00619   return r;
00620 }
00621 
00622 box
00623 graphics_group_box (path ip, array<box> bs) {
00624   return tm_new<graphics_group_box_rep> (ip, bs);
00625 }
00626 
00627 box
00628 point_box (
00629   path ip, point p, SI r, color col, int fill, color fill_col, string style) {
00630   return tm_new<point_box_rep> (ip, p, r, col, fill, fill_col, style);
00631 }
00632 
00633 box
00634 curve_box (path ip, curve c, SI width, color col,
00635   array<bool> style, SI style_unit,
00636   int fill, color fill_col,
00637   array<box> arrows)
00638 {
00639   return tm_new<curve_box_rep> (ip, c, width, col,
00640                                 style, style_unit, fill, fill_col, arrows);
00641 }