Back to index

texmacs  1.0.7.15
concat_graphics.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : concat_graphics.cpp
00004 * DESCRIPTION: Typeset graphics
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven and 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 "concater.hpp"
00013 #include "Boxes/graphics.hpp"
00014 #include "drd_std.hpp"
00015 #include "hashset.hpp"
00016 #include "analyze.hpp"
00017 #include "scheme.hpp"
00018 
00019 #define BEGIN_MAGNIFY                                           \
00020   tree new_mag= as_string (env->magn * env->mgfy);              \
00021   tree old_mfy= env->local_begin (MAGNIFY, "1");                \
00022   tree old_mag= env->local_begin (MAGNIFICATION, new_mag);
00023 
00024 #define END_MAGNIFY                             \
00025   env->local_end (MAGNIFICATION, old_mag);      \
00026   env->local_end (MAGNIFY, old_mfy);
00027 
00028 /******************************************************************************
00029 * Typesetting graphics
00030 ******************************************************************************/
00031 
00032 /* NOTE: We use the ascending order for drawing, like in SVG. The code
00033    below conforms to this convention, which says that the first elements
00034    are painted first, and can be covered by the subsequent ones.
00035  */
00036 /* TODO: Check that the active elements (f.e. the <action> markup) also
00037    adhere to the same convention.
00038  */
00039 
00040 void
00041 notify_graphics_extents (tree t, point lbot, point rtop) {
00042   static hashmap<string,tree> h (UNINIT);
00043   if (is_atomic (t)) {
00044     string id= t->label;
00045     tree val= tuple (as_string (lbot[0]), as_string (lbot[1]),
00046                      as_string (rtop[0]), as_string (rtop[1]));
00047     if (h[id] != val) {
00048       h (id)= val;
00049       array<object> args;
00050       args << object (id)
00051            << object (lbot[0]) << object (lbot[1])
00052            << object (rtop[0]) << object (rtop[1]);
00053       call ("graphics-notify-extents", args);
00054     }
00055   }
00056   else if (is_func (t, GRAPHICS) || is_func (t, GR_GROUP)) {
00057     for (int i=0; i<N(t); i++)
00058       notify_graphics_extents (t[i], lbot, rtop);
00059   }
00060   else if (is_func (t, LOCUS) || is_func (t, WITH)) {
00061     if (is_func (t, LOCUS, 2) &&
00062         is_func (t[0], ID, 1) &&
00063         is_atomic (t[0][0]) &&
00064         starts (t[0][0]->label, "graph-"))
00065       notify_graphics_extents (t[0][0], lbot, rtop);
00066     notify_graphics_extents (t[N(t)-1], lbot, rtop);
00067   }
00068 }
00069 
00070 void
00071 concater_rep::typeset_graphics (tree t, path ip) {
00072 BEGIN_MAGNIFY
00073   grid gr= as_grid (env->read (GR_GRID));
00074   array<box> bs;
00075   gr->set_aspect (env->read (GR_GRID_ASPECT));
00076   bs << grid_box (ip, gr, env->fr, env->as_length ("2ln"),
00077                   env->clip_lim1, env->clip_lim2);
00078   typeset_graphical (bs, t, ip);
00079 
00080   point lim1= env->clip_lim1;
00081   point lim2= env->clip_lim2;
00082   if (env->get_bool (GR_AUTO_CROP)) {
00083     SI x1= MAX_SI, y1= MAX_SI, x2= -MAX_SI, y2= -MAX_SI;
00084     for (int i=1; i<N(bs); i++) {
00085       box b= bs[i];
00086       //cout << i << ", " << b << ", " << b->get_type () << "\n";
00087       x1= min (x1, b->x1); y1= min (y1, b->y1);
00088       x2= max (x2, b->x2); y2= max (y2, b->y2);
00089       x1= min (x1, b->x3); y1= min (y1, b->y3);
00090       x2= max (x2, b->x4); y2= max (y2, b->y4);
00091     }
00092     SI pad= env->get_length (GR_CROP_PADDING);
00093     lim1= env->fr [point (x1 - pad, y1 - pad)];
00094     lim2= env->fr [point (x2 + pad, y2 + pad)];
00095     //cout << lim1 << " -- " << lim2 << "\n";
00096   }
00097 
00098   gr= as_grid (env->read (GR_EDIT_GRID));
00099   gr->set_aspect (env->read (GR_EDIT_GRID_ASPECT));
00100   box b= graphics_box (ip, bs, env->fr, gr, lim1, lim2);
00101   print (b);
00102 
00103   notify_graphics_extents (t, lim1, lim2);
00104 END_MAGNIFY
00105 }
00106 
00107 void
00108 concater_rep::typeset_gr_group (tree t, path ip) {
00109 BEGIN_MAGNIFY
00110   array<box> bs;
00111   typeset_graphical (bs, t, ip);
00112   print (graphics_group_box (ip, bs));
00113 END_MAGNIFY
00114 }
00115 
00116 void
00117 concater_rep::typeset_superpose (tree t, path ip) {
00118   int i, n= N(t);
00119   array<box> bs (n);
00120   for (i=0; i<n; i++) {
00121     bs[i]= typeset_as_concat (env, t[i], descend (ip, i));
00122     if (is_func (t[i], FREEZE))
00123       // FIXME: this dirty hack is necessary, because typeset_as_concat
00124       // would put an accessible concat_box around bs[i] otherwise
00125       bs[i]= frozen_box (decorate_middle (descend (ip, i)), bs[i]);
00126   }
00127   print (superpose_box (ip, bs));
00128 }
00129 
00130 void
00131 concater_rep::typeset_gr_linear_transform (tree t, path ip) {
00132   if (N(t) != 2 || !is_tuple (t[1])) { typeset_error (t, ip); }
00133   frame f= affine_2D (as_matrix<double> (t[1]));
00134   box   b= typeset_as_concat (env, t[0], descend (ip, 0));
00135         /* The call should be performed with 'typeset_as_atomic()',
00136           but we should re-test transform() under these circumstances.
00137          */
00138   print (b->transform (env->fr * (f * invert (env->fr))));
00139 }
00140 
00141 void
00142 concater_rep::typeset_text_at (tree t, path ip) {
00143 BEGIN_MAGNIFY
00144   if (N(t) != 2) typeset_error (t, ip);
00145   else {
00146     box    b     = typeset_as_concat (env, t[0], descend (ip, 0));
00147     point  p     = env->fr (env->as_point (env->exec (t[1])));
00148     string halign= env->text_at_halign;
00149     string valign= env->text_at_valign;
00150 
00151     if (N(p) == 0)
00152       typeset_dynamic (tree (ERROR, "bad text-at"), ip);
00153     else {
00154       SI x= (SI) p[0], y= (SI) p[1], axis= (b->h() >> 1);
00155       if (halign == "left") x -= b->x1;
00156       else if (halign == "center") x -= ((b->x1 + b->x2) >> 1);
00157       else if (halign == "right") x -= b->x2;
00158       if (valign == "bottom") y -= b->y1;
00159       else if (valign == "axis") {
00160        axis= env->fn->yfrac - b->y1;
00161        y -= env->fn->yfrac;
00162       }
00163       else if (valign == "center") y -= ((b->y1 + b->y2) >> 1);
00164       else if (valign == "top") y -= b->y2;
00165       print (text_at_box (ip, b, x, y, axis, env->fn->spc->def));
00166     }
00167   }
00168 END_MAGNIFY
00169 }
00170 
00171 void
00172 concater_rep::typeset_math_at (tree t, path ip) {
00173 BEGIN_MAGNIFY
00174   if (N(t) != 2) typeset_error (t, ip);
00175   else {
00176     // FIXME: attaching ip to compound ("math", t[0]) is a bit hacky,
00177     // but it seems to work fine for the time being
00178     box    b     = typeset_as_concat (env, compound ("math", t[0]), ip);
00179     point  p     = env->fr (env->as_point (env->exec (t[1])));
00180     string halign= env->text_at_halign;
00181     string valign= env->text_at_valign;
00182 
00183     if (N(p) == 0)
00184       typeset_dynamic (tree (ERROR, "bad text-at"), ip);
00185     else {
00186       SI x= (SI) p[0], y= (SI) p[1], axis= (b->h() >> 1);
00187       if (halign == "left") x -= b->x1;
00188       else if (halign == "center") x -= ((b->x1 + b->x2) >> 1);
00189       else if (halign == "right") x -= b->x2;
00190       if (valign == "bottom") y -= b->y1;
00191       else if (valign == "axis") {
00192        axis= (env->fn->yx >> 1) - b->y1;
00193        y -= (env->fn->yx >> 1);
00194       }
00195       else if (valign == "center") y -= ((b->y1 + b->y2) >> 1);
00196       else if (valign == "top") y -= b->y2;
00197       print (text_at_box (ip, b, x, y, axis, env->fn->spc->def));
00198     }
00199   }
00200 END_MAGNIFY
00201 }
00202 
00203 void
00204 concater_rep::typeset_point (tree t, path ip) {
00205 BEGIN_MAGNIFY
00206   if (N(t) < 2) typeset_error (t, ip);
00207   else {
00208     int i, n= N(t);
00209     tree u (TUPLE, N(t));
00210     for (i=0; i<n; i++)
00211       u[i]= env->exec (t[i]);
00212     point p= env->fr (env->as_point (u));
00213     print (point_box (ip, p, 20*PIXEL, env->col,
00214                       env->fill_mode, env->fill_color,
00215                       env->point_style));
00216   }
00217 END_MAGNIFY
00218 }
00219 
00220 static tree
00221 protect_arrow (edit_env env, tree t) {
00222   return tree (WITH, "arrow-begin", "none", "arrow-end", "none",
00223                "dash-style", "none", t);
00224 }
00225 
00226 array<box>
00227 concater_rep::typeset_line_arrows (path ip) {
00228   array<box> bs (2);
00229   if (env->line_arrows[0] != "") {
00230     tree a= protect_arrow (env, env->line_arrows[0]);
00231     bs[0]= typeset_as_concat (env, a, decorate (ip));
00232   }
00233   if (env->line_arrows[1] != "") {
00234     tree a= protect_arrow (env, env->line_arrows[1]);
00235     bs[1]= typeset_as_concat (env, a, decorate (ip));
00236   }
00237   return bs;
00238 }
00239 
00240 void
00241 concater_rep::typeset_line (tree t, path ip, bool close) {
00242 BEGIN_MAGNIFY
00243   int i, n= N(t);
00244   array<point> a(n);
00245   for (i=0; i<n; i++)
00246     a[i]= env->as_point (env->exec (t[i]));
00247   array<path> cip(n);
00248   for (i=0; i<n; i++)
00249     cip[i]= descend (ip, i);
00250   if (close) {
00251     a << copy (a[0]);
00252     cip << cip[0];
00253   }
00254   if (N(a) == 0 || N(a[0]) == 0) typeset_error (t, ip);
00255   else {
00256     if (N(a) == 1) {
00257       a << copy (a[0]);
00258       cip << cip[0];
00259     }
00260     curve c= env->fr (poly_segment (a, cip));
00261     print (curve_box (ip, c, env->lw, env->col,
00262                       env->dash_style, env->dash_style_unit,
00263                       env->fill_mode, env->fill_color,
00264                       typeset_line_arrows (ip)));
00265   }
00266 END_MAGNIFY
00267 }
00268 
00269 void
00270 concater_rep::typeset_arc (tree t, path ip, bool close) {
00271 BEGIN_MAGNIFY
00272   int i, n= N(t);
00273   array<point> a(n);
00274   for (i=0; i<n; i++)
00275     a[i]= env->as_point (env->exec (t[i]));
00276   array<path> cip(n);
00277   for (i=0; i<n; i++)
00278     cip[i]= descend (ip, i);
00279   if (N(a) == 0 || N(a[0]) == 0) typeset_error (t, ip);
00280   else {
00281     if (n != 3 || linearly_dependent (a[0], a[1], a[2]) ||
00282         (N (intersection (midperp (a[0], a[1], a[2]),
00283                           midperp (a[1], a[2], a[0]))) == 0))
00284       typeset_line (t, ip, close);
00285     else {
00286       curve c= env->fr (arc (a, cip, close));
00287       print (curve_box (ip, c, env->lw, env->col,
00288                         env->dash_style, env->dash_style_unit,
00289                         env->fill_mode, env->fill_color,
00290                         typeset_line_arrows (ip)));
00291     }
00292   }
00293 END_MAGNIFY
00294 }
00295 
00296 void
00297 concater_rep::typeset_spline (tree t, path ip, bool close) {
00298 BEGIN_MAGNIFY
00299   int i, n= N(t);
00300   array<point> a(n);
00301   for (i=0; i<n; i++)
00302     a[i]= env->as_point (env->exec (t[i]));
00303   array<path> cip(n);
00304   for (i=0; i<n; i++)
00305     cip[i]= descend (ip, i);
00306   if (N(a) == 0 || N(a[0]) == 0) typeset_error (t, ip);
00307   else {
00308     if (N(a) == 1) {
00309       a << copy (a[0]);
00310       cip << cip[0];
00311     }
00312     curve c= env->fr (N(a)>=3 ? spline (a, cip, close): poly_segment (a, cip));
00313     print (curve_box (ip, c, env->lw, env->col,
00314                       env->dash_style, env->dash_style_unit,
00315                       env->fill_mode, env->fill_color,
00316                       typeset_line_arrows (ip)));
00317   }
00318 END_MAGNIFY
00319 }
00320 
00321 void
00322 concater_rep::typeset_var_spline (tree t, path ip) {
00323   (void) t; (void) ip;
00324   print (test_box (ip));
00325 }
00326 
00327 void
00328 concater_rep::typeset_cspline (tree t, path ip) {
00329   typeset_spline (t, ip, true);
00330 }
00331 
00332 void
00333 concater_rep::typeset_fill (tree t, path ip) {
00334   (void) t; (void) ip;
00335   print (test_box (ip));
00336 }
00337 
00338 /******************************************************************************
00339 * Constrainted graphics
00340 ******************************************************************************/
00341 
00342 hashmap<string,tree> graphical_values (UNINIT);
00343 hashset<string> graphical_modified;
00344 
00345 void
00346 set_graphical_value (tree var, tree val) {
00347   //cout << "Set " << var << " := " << val << "\n";
00348   if (is_atomic (var))
00349     graphical_values (var->label)= val;
00350 }
00351 
00352 bool
00353 has_graphical_value (tree var) {
00354   //cout << "Has " << var << "?\n";
00355   return is_atomic (var) && graphical_values->contains (var->label);
00356 }
00357 
00358 tree
00359 get_graphical_value (tree var) {
00360   ASSERT (has_graphical_value (var), "invalid graphical id");
00361   //cout << "Get " << var << " = " << graphical_values [var->label] << "\n";
00362   return graphical_values [var->label];
00363 }
00364 
00365 bool
00366 graphics_needs_update () {
00367   return N(graphical_modified) > 0;
00368 }
00369 
00370 void
00371 graphics_require_update (tree var) {
00372   if (is_atomic (var))
00373     graphical_modified->insert (var->label);
00374   //cout << "Set " << var << ", " << N(graphical_modified) << "\n";
00375 }
00376 
00377 void
00378 graphics_notify_update (tree var) {
00379   if (is_atomic (var))
00380     graphical_modified->remove (var->label);
00381   //cout << "Reset " << var << ", " << N(graphical_modified) << "\n";
00382 }
00383 
00384 static void
00385 set_graphical_values (tree t) {
00386   if (is_atomic (t));
00387   else if (is_func (t, WITH)) {
00388     for (int i=0; i<N(t)-1; i+=2)
00389       if (t[i] == GID && is_atomic (t[i+1]))
00390         set_graphical_value (t[i+1]->label, t[N(t)-1]);
00391     set_graphical_values (t[N(t)-1]);
00392   }
00393   else {
00394     for (int i=0; i<N(t); i++)
00395       set_graphical_values (t[i]);
00396   }
00397 }
00398 
00399 void
00400 concater_rep::typeset_graphical (array<box>& bs, tree t, path ip) {
00401   int i, n= N(t);
00402   set_graphical_values (t);
00403 
00404   for (i=0; i<n; i++)
00405     if (the_drd->get_type (t[i]) == TYPE_CONSTRAINT) {
00406       tree u= t[i];
00407       switch (L(u)) {
00408       case IS_EQUAL:
00409         if (has_graphical_value (u[1]))
00410           set_graphical_value (u[0], get_graphical_value (u[1]));
00411         break;
00412       case IS_INTERSECTION:
00413        cout << "Not yet implemented 'is-intersection'\n";
00414         break;
00415       case ON_CURVE:
00416        cout << "Not yet implemented 'on-curve'\n";
00417         break;
00418       case ON_TEXT_BORDER:
00419        cout << "Not yet implemented 'on-text-border'\n";
00420         break;
00421       case ON_GRID:
00422        cout << "Not yet implemented 'on-grid'\n";
00423         break;
00424       default:
00425         break;
00426       }
00427     }
00428 
00429   for (i=0; i<n; i++)
00430     if (the_drd->get_type (t[i]) != TYPE_CONSTRAINT && !is_atomic (t[i]))
00431       bs << typeset_as_atomic (env, t[i], descend (ip, i));
00432 }