home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / groff / pic / object.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-30  |  36.9 KB  |  1,804 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.uucp)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 1, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file LICENSE.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "pic.h"
  22. #include "ptable.h"
  23. #include "object.h"
  24.  
  25. void print_object_list(object *);
  26.  
  27. line_type::line_type()
  28. : type(solid), thickness(1.0)
  29. {
  30. }
  31.  
  32. output::output() : desired_height(0.0), desired_width(0.0), args(0)
  33. {
  34. }
  35.  
  36. output::~output()
  37. {
  38.   delete args;
  39. }
  40.  
  41. void output::set_desired_width_height(double wid, double ht)
  42. {
  43.   desired_width = wid;
  44.   desired_height = ht;
  45. }
  46.  
  47. void output::set_args(const char *s)
  48. {
  49.   delete args;
  50.   if (s == 0 || *s == '\0')
  51.     args = 0;
  52.   else
  53.     args = strsave(s);
  54. }
  55.  
  56. void output::command(const char *, const char *, int)
  57. {
  58. }
  59.  
  60. void output::set_location(const char *, int)
  61. {
  62. }
  63.  
  64. int output::supports_filled_polygons()
  65. {
  66.   return 0;
  67. }
  68.  
  69. void output::begin_block(const position &, const position &)
  70. {
  71. }
  72.  
  73. void output::end_block()
  74. {
  75. }
  76.  
  77. double output::compute_scale(double sc, const position &ll, const position &ur)
  78. {
  79.   distance dim = ur - ll;
  80.   if (desired_width != 0.0 || desired_height != 0.0) {
  81.     sc = 0.0;
  82.     if (desired_width != 0.0) {
  83.       if (dim.x == 0.0)
  84.     error("width specified for picture with zero width");
  85.       else
  86.     sc = dim.x/desired_width;
  87.     }
  88.     if (desired_height != 0.0) {
  89.       if (dim.y == 0.0)
  90.     error("height specified for picture with zero height");
  91.       else {
  92.     double tem = dim.y/desired_height;
  93.     if (tem > sc)
  94.       sc = tem;
  95.       }
  96.     }
  97.     return sc == 0.0 ? 1.0 : sc;
  98.   }
  99.   else {
  100.     if (sc <= 0.0)
  101.       sc = 1.0;
  102.     distance sdim = dim/sc;
  103.     double max_width;
  104.     lookup_variable("maxpswid", &max_width);
  105.     double max_height;
  106.     lookup_variable("maxpsht", &max_height);
  107.     if ((max_width > 0.0 && sdim.x > max_width)
  108.     || (max_height > 0.0 && sdim.y > max_height)) {
  109.       double xscale = dim.x/max_width;
  110.       double yscale = dim.y/max_height;
  111.       return xscale > yscale ? xscale : yscale;
  112.     }
  113.     else
  114.       return sc;
  115.   }
  116. }
  117.  
  118. position::position(const place &pl)
  119. {
  120.   if (pl.obj != 0)
  121.     *this = pl.obj->origin();
  122.   else {
  123.     x = pl.x;
  124.     y = pl.y;
  125.   }
  126. }
  127.  
  128. position::position() : x(0.0), y(0.0)
  129. {
  130. }
  131.  
  132. position::position(double a, double b) : x(a), y(b)
  133. {
  134. }
  135.  
  136.  
  137. int operator==(const position &a, const position &b)
  138. {
  139.   return a.x == b.x && a.y == b.y;
  140. }
  141.  
  142. int operator!=(const position &a, const position &b)
  143. {
  144.   return a.x != b.x || a.y != b.y;
  145. }
  146.  
  147. position &position::operator+=(const position &a)
  148. {
  149.   x += a.x;
  150.   y += a.y;
  151.   return *this;
  152. }
  153.  
  154. position &position::operator-=(const position &a)
  155. {
  156.   x -= a.x;
  157.   y -= a.y;
  158.   return *this;
  159. }
  160.  
  161. position &position::operator*=(double a)
  162. {
  163.   x *= a;
  164.   y *= a;
  165.   return *this;
  166. }
  167.  
  168. position &position::operator/=(double a)
  169. {
  170.   x /= a;
  171.   y /= a;
  172.   return *this;
  173. }
  174.  
  175. position operator-(const position &a)
  176. {
  177.   return position(-a.x, -a.y);
  178. }
  179.  
  180. position operator+(const position &a, const position &b)
  181. {
  182.   return position(a.x + b.x, a.y + b.y);
  183. }
  184.  
  185. position operator-(const position &a, const position &b)
  186. {
  187.   return position(a.x - b.x, a.y - b.y);
  188. }
  189.  
  190. position operator/(const position &a, double n)
  191. {
  192.   return position(a.x/n, a.y/n);
  193. }
  194.  
  195. position operator*(const position &a, double n)
  196. {
  197.   return position(a.x*n, a.y*n);
  198. }
  199.  
  200. // dot product
  201.  
  202. double operator*(const position &a, const position &b)
  203. {
  204.   return a.x*b.x + a.y*b.y;
  205. }
  206.  
  207. double hypot(const position &a)
  208. {
  209.   return hypot(a.x, a.y);
  210. }
  211.  
  212. struct arrow_head_type {
  213.   double height;
  214.   double width;
  215.   int solid;
  216. };
  217.  
  218. void draw_arrow(const position &pos, const distance &dir,
  219.         const arrow_head_type &aht, const line_type <)
  220. {
  221.   double hyp = hypot(dir);
  222.   position base = -dir;
  223.   base *= aht.height/hyp;
  224.   position n(dir.y, -dir.x);
  225.   n *= aht.width/(hyp*2.0);
  226.   line_type slt = lt;
  227.   slt.type = line_type::solid;
  228.   if (aht.solid && out->supports_filled_polygons()) {
  229.     position v[3];
  230.     v[0] = pos;
  231.     v[1] = pos + base + n;
  232.     v[2] = pos + base - n;
  233.     // A value > 1 means fill with the current color.
  234.     out->polygon(v, 3, slt, 2.0);
  235.   }
  236.   else {
  237.     position v[2];
  238.     v[0] = pos;
  239.     v[1] = pos + base + n;
  240.     out->line(pos + base - n, v, 2, slt);
  241.   }
  242. }
  243.  
  244. object::object() : prev(0), next(0)
  245. {
  246. }
  247.  
  248. object::~object()
  249. {
  250. }
  251.  
  252. void object::move_by(const position &)
  253. {
  254. }
  255.  
  256. void object::print()
  257. {
  258. }
  259.  
  260. void object::print_text()
  261. {
  262. }
  263.  
  264. int object::blank()
  265. {
  266.   return 0;
  267. }
  268.  
  269. struct bounding_box {
  270.   int blank;
  271.   position ll;
  272.   position ur;
  273.  
  274.   bounding_box();
  275.   void encompass(const position &);
  276. };
  277.  
  278. bounding_box::bounding_box()
  279. : blank(1)
  280. {
  281. }
  282.  
  283. void bounding_box::encompass(const position &pos)
  284. {
  285.   if (blank) {
  286.     ll = pos;
  287.     ur = pos;
  288.     blank = 0;
  289.   }
  290.   else {
  291.     if (pos.x < ll.x)
  292.       ll.x = pos.x;
  293.     if (pos.y < ll.y)
  294.       ll.y = pos.y;
  295.     if (pos.x > ur.x)
  296.       ur.x = pos.x;
  297.     if (pos.y > ur.y)
  298.       ur.y = pos.y;
  299.   }
  300. }
  301.  
  302. void object::update_bounding_box(bounding_box *)
  303. {
  304. }
  305.  
  306. position object::origin()
  307. {
  308.   return position(0.0,0.0);
  309. }
  310.  
  311. position object::north()
  312. {
  313.   return origin();
  314. }
  315.  
  316. position object::south()
  317. {
  318.   return origin();
  319. }
  320.  
  321. position object::east()
  322. {
  323.   return origin();
  324. }
  325.  
  326. position object::west()
  327. {
  328.   return origin();
  329. }
  330.  
  331. position object::north_east()
  332. {
  333.   return origin();
  334. }
  335.  
  336. position object::north_west()
  337. {
  338.   return origin();
  339. }
  340.  
  341. position object::south_east()
  342. {
  343.   return origin();
  344. }
  345.  
  346. position object::south_west()
  347. {
  348.   return origin();
  349. }
  350.  
  351. position object::start()
  352. {
  353.   return origin();
  354. }
  355.  
  356. position object::end()
  357. {
  358.   return origin();
  359. }
  360.  
  361. position object::center()
  362. {
  363.   return origin();
  364. }
  365.  
  366. double object::width()
  367. {
  368.   return 0.0;
  369. }
  370.  
  371. double object::radius()
  372. {
  373.   return 0.0;
  374. }
  375.  
  376. double object::height()
  377. {
  378.   return 0.0;
  379. }
  380.  
  381. place *object::find_label(const char *)
  382. {
  383.   return 0;
  384. }
  385.  
  386. segment::segment(const position &a, int n, segment *p)
  387. : pos(a), is_absolute(n), next(p)
  388. {
  389. }
  390.  
  391. text_item::text_item(char *t, const char *fn, int ln)
  392. : filename(fn), lineno(ln), text(t), next(0)
  393. {
  394.   adj.h = CENTER_ADJUST;
  395.   adj.v = NONE_ADJUST;
  396. }
  397.  
  398. text_item::~text_item()
  399. {
  400.   delete text;
  401. }
  402.  
  403. object_spec::object_spec(object_type t) : type(t)
  404. {
  405.   flags = 0;
  406.   tbl = 0;
  407.   segment_list = 0;
  408.   segment_width = segment_height = 0.0;
  409.   segment_is_absolute = 0;
  410.   text = 0;
  411.   with = 0;
  412.   dir = RIGHT_DIRECTION;
  413. }
  414.  
  415. object_spec::~object_spec()
  416. {
  417.   delete tbl;
  418.   while (segment_list != 0) {
  419.     segment *tem = segment_list;
  420.     segment_list = segment_list->next;
  421.     delete tem;
  422.   }
  423.   object *p = oblist.head;
  424.   while (p != 0) {
  425.     object *tem = p;
  426.     p = p->next;
  427.     delete tem;
  428.   }
  429.   while (text != 0) {
  430.     text_item *tem = text;
  431.     text = text->next;
  432.     delete tem;
  433.   }
  434.   delete with;
  435. }
  436.  
  437. class command_object : public object {
  438.   char *s;
  439.   const char *filename;
  440.   int lineno;
  441. public:
  442.   command_object(char *, const char *, int);
  443.   ~command_object();
  444.   object_type type() { return OTHER_OBJECT; }
  445.   void print();
  446. };
  447.  
  448. command_object::command_object(char *p, const char *fn, int ln)
  449. : s(p), filename(fn), lineno(ln)
  450. {
  451. }
  452.  
  453. command_object::~command_object()
  454. {
  455.   delete s;
  456. }
  457.  
  458. void command_object::print()
  459. {
  460.   out->command(s, filename, lineno);
  461. }
  462.  
  463. object *make_command_object(char *s, const char *fn, int ln)
  464. {
  465.   return new command_object(s, fn, ln);
  466. }
  467.  
  468. class mark_object : public object {
  469. public:
  470.   mark_object();
  471.   object_type type();
  472. };
  473.  
  474. object *make_mark_object()
  475. {
  476.   return new mark_object();
  477. }
  478.  
  479. mark_object::mark_object()
  480. {
  481. }
  482.  
  483. object_type mark_object::type()
  484. {
  485.   return MARK_OBJECT;
  486. }
  487.  
  488. object_list::object_list() : head(0), tail(0)
  489. {
  490. }
  491.  
  492. void object_list::append(object *obj)
  493. {
  494.   if (tail == 0) {
  495.     obj->next = obj->prev = 0;
  496.     head = tail = obj;
  497.   }
  498.   else {
  499.     obj->prev = tail;
  500.     obj->next = 0;
  501.     tail->next = obj;
  502.     tail = obj;
  503.   }
  504. }
  505.  
  506. void object_list::wrap_up_block(object_list *ol)
  507. {
  508.   for (object *p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
  509.     ;
  510.   assert(p != 0);
  511.   ol->head = p->next;
  512.   if (ol->head) {
  513.     ol->tail = tail;
  514.     ol->head->prev = 0;
  515.   }
  516.   else
  517.     ol->tail = 0;
  518.   tail = p->prev;
  519.   if (tail)
  520.     tail->next = 0;
  521.   else
  522.     head = 0;
  523.   delete p;
  524. }
  525.  
  526. text_piece::text_piece()
  527. : text(0), filename(0), lineno(-1)
  528. {
  529.   adj.h = CENTER_ADJUST;
  530.   adj.v = NONE_ADJUST;
  531. }
  532.  
  533. text_piece::~text_piece()
  534. {
  535.   delete text;
  536. }
  537.  
  538. class graphic_object : public object {
  539.   int ntext;
  540.   text_piece *text;
  541.   int aligned;
  542. protected:
  543.   line_type lt;
  544. public:
  545.   graphic_object();
  546.   ~graphic_object();
  547.   object_type type() = 0;
  548.   void print_text();
  549.   void add_text(text_item *, int);
  550.   void set_dotted(double);
  551.   void set_dashed(double);
  552.   void set_thickness(double);
  553.   void set_invisible();
  554.   virtual void set_fill(double);
  555. };
  556.  
  557. graphic_object::graphic_object() : ntext(0), text(0), aligned(0)
  558. {
  559. }
  560.  
  561. void graphic_object::set_dotted(double wid)
  562. {
  563.   lt.type = line_type::dotted;
  564.   lt.dash_width = wid;
  565. }
  566.  
  567. void graphic_object::set_dashed(double wid)
  568. {
  569.   lt.type = line_type::dashed;
  570.   lt.dash_width = wid;
  571. }
  572.  
  573. void graphic_object::set_thickness(double th)
  574. {
  575.   lt.thickness = th;
  576. }
  577.  
  578. void graphic_object::set_fill(double)
  579. {
  580. }
  581.  
  582. void graphic_object::set_invisible()
  583. {
  584.   lt.type = line_type::invisible;
  585. }
  586.  
  587. void graphic_object::add_text(text_item *t, int a)
  588. {
  589.   aligned = a;
  590.   int len = 0;
  591.   for (text_item *p = t; p; p = p->next)
  592.     len++;
  593.   if (len == 0)
  594.     text = 0;
  595.   else {
  596.     text = new text_piece[len];
  597.     for (p = t, len = 0; p; p = p->next, len++) {
  598.       text[len].text = p->text;
  599.       p->text = 0;
  600.       text[len].adj = p->adj;
  601.       text[len].filename = p->filename;
  602.       text[len].lineno = p->lineno;
  603.     }
  604.   }
  605.   ntext = len;
  606. }
  607.  
  608. void graphic_object::print_text()
  609. {
  610.   double angle = 0.0;
  611.   if (aligned) {
  612.     position d(end() - start());
  613.     if (d.x != 0.0 || d.y != 0.0)
  614.       angle = atan2(d.y, d.x);
  615.   }
  616.   if (text != 0)
  617.     out->text(center(), text, ntext, angle);
  618. }
  619.  
  620. graphic_object::~graphic_object()
  621. {
  622.   delete [ntext] text;
  623. }
  624.  
  625. class rectangle_object : public graphic_object {
  626. protected:
  627.   position cent;
  628.   position dim;
  629. public:
  630.   rectangle_object(const position &);
  631.   double width() { return dim.x; }
  632.   double height() { return dim.y; }
  633.   position origin() { return cent; }
  634.   position center() { return cent; }
  635.   position north() { return position(cent.x, cent.y + dim.y/2.0); }
  636.   position south() { return position(cent.x, cent.y - dim.y/2.0); }
  637.   position east() { return position(cent.x + dim.x/2.0, cent.y); }
  638.   position west() { return position(cent.x - dim.x/2.0, cent.y); }
  639.   position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
  640.   position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
  641.   position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
  642.   position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
  643.   object_type type() = 0;
  644.   void update_bounding_box(bounding_box *);
  645.   void move_by(const position &);
  646. };
  647.  
  648. rectangle_object::rectangle_object(const position &d)
  649. : dim(d)
  650. {
  651. }
  652.  
  653. void rectangle_object::update_bounding_box(bounding_box *p)
  654. {
  655.   p->encompass(cent - dim/2.0);
  656.   p->encompass(cent + dim/2.0);
  657. }
  658.  
  659. void rectangle_object::move_by(const position &a)
  660. {
  661.   cent += a;
  662. }
  663.  
  664. class closed_object : public rectangle_object {
  665. public:
  666.   closed_object(const position &);
  667.   object_type type() = 0;
  668.   void set_fill(double);
  669. protected:
  670.   double fill;            // < 0 if not filled
  671. };
  672.  
  673. closed_object::closed_object(const position &pos)
  674. : rectangle_object(pos), fill(-1.0)
  675. {
  676. }
  677.  
  678. void closed_object::set_fill(double f)
  679. {
  680.   assert(f >= 0.0);
  681.   fill = f;
  682. }
  683.  
  684.  
  685. class box_object : public closed_object {
  686.   double rad;
  687. public:
  688.   box_object(const position &, double);
  689.   object_type type() { return BOX_OBJECT; }
  690.   void print();
  691.   position north_east();
  692.   position north_west();
  693.   position south_east();
  694.   position south_west();
  695. };
  696.  
  697. box_object::box_object(const position &pos, double r)
  698. : closed_object(pos), rad(r)
  699. {
  700. }
  701.  
  702. const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
  703.  
  704. position box_object::north_east()
  705. {
  706.   return position(cent.x + dim.x/2.0 - CHOP_FACTOR*rad,
  707.           cent.y + dim.y/2.0 - CHOP_FACTOR*rad);
  708. }
  709.  
  710. position box_object::north_west()
  711. {
  712.   return position(cent.x - dim.x/2.0 + CHOP_FACTOR*rad,
  713.           cent.y + dim.y/2.0 - CHOP_FACTOR*rad);
  714. }
  715.  
  716. position box_object::south_east()
  717. {
  718.   return position(cent.x + dim.x/2.0 - CHOP_FACTOR*rad,
  719.           cent.y - dim.y/2.0 + CHOP_FACTOR*rad);
  720. }
  721.  
  722. position box_object::south_west()
  723. {
  724.   return position(cent.x - dim.x/2.0 + CHOP_FACTOR*rad,
  725.           cent.y - dim.y/2.0 + CHOP_FACTOR*rad);
  726. }
  727.  
  728. void box_object::print()
  729. {
  730.   if (lt.type == line_type::invisible && fill < 0.0)
  731.     return;
  732.   if (rad == 0.0) {
  733.     distance dim2 = dim/2.0;
  734.     position vec[4];
  735.     vec[0] = cent + position(dim2.x, -dim2.y);
  736.     vec[1] = cent + position(dim2.x, dim2.y);
  737.     vec[2] = cent + position(-dim2.x, dim2.y);
  738.     vec[3] = cent + position(-dim2.x, -dim2.y);
  739.     out->polygon(vec, 4, lt, fill);
  740.   }
  741.   else
  742.     out->rounded_box(cent, dim, rad, lt, fill);
  743. }
  744.  
  745. graphic_object *object_spec::make_box(position *curpos, direction *dirp)
  746. {
  747.   static double last_box_height;
  748.   static double last_box_width;
  749.   static double last_box_radius;
  750.   static int have_last_box = 0;
  751.   if (!(flags & HAS_HEIGHT)) {
  752.     if ((flags & IS_SAME) && have_last_box)
  753.       height = last_box_height;
  754.     else
  755.       lookup_variable("boxht", &height);
  756.   }
  757.   if (!(flags & HAS_WIDTH)) {
  758.     if ((flags & IS_SAME) && have_last_box)
  759.       width = last_box_width;
  760.     else
  761.       lookup_variable("boxwid", &width);
  762.   }
  763.   if (!(flags & HAS_RADIUS)) {
  764.     if ((flags & IS_SAME) && have_last_box)
  765.       radius = last_box_radius;
  766.     else
  767.       lookup_variable("boxrad", &radius);
  768.   }
  769.   last_box_width = width;
  770.   last_box_height = height;
  771.   last_box_radius = radius;
  772.   have_last_box = 1;
  773.   if (radius*2.0 > width)
  774.     radius = width/2.0;
  775.   if (radius*2.0 > height)
  776.     radius = height/2.0;
  777.   box_object *p = new box_object(position(width, height), radius);
  778.   if (!position_rectangle(p, curpos, dirp)) {
  779.     delete p;
  780.     p = 0;
  781.   }
  782.   return p;
  783. }
  784.  
  785. // return non-zero for success
  786.  
  787. int object_spec::position_rectangle(rectangle_object *p,
  788.                     position *curpos, direction *dirp)
  789. {
  790.   position pos;
  791.   dir = *dirp;            // ignore any direction in attribute list
  792.   position motion;
  793.   switch (dir) {
  794.   case UP_DIRECTION:
  795.     motion.y = p->height()/2.0;
  796.     break;
  797.   case DOWN_DIRECTION:
  798.     motion.y = -p->height()/2.0;
  799.     break;
  800.   case LEFT_DIRECTION:
  801.     motion.x = -p->width()/2.0;
  802.     break;
  803.   case RIGHT_DIRECTION:
  804.     motion.x = p->width()/2.0;
  805.     break;
  806.   default:
  807.     assert(0);
  808.   }
  809.   if (flags & HAS_AT) {
  810.     pos = at;
  811.     if (flags & HAS_WITH) {
  812.       place offset;
  813.       place here;
  814.       here.obj = p;
  815.       if (!with->follow(here, &offset))
  816.     return 0;
  817.       pos -= offset;
  818.     }
  819.   }
  820.   else {
  821.     pos = *curpos;
  822.     pos += motion;
  823.   }
  824.   p->move_by(pos);
  825.   pos += motion;
  826.   *curpos = pos;
  827.   return 1;
  828. }
  829.  
  830. class block_object : public rectangle_object {
  831.   object_list oblist;
  832.   PTABLE(place) *tbl;
  833. public:
  834.   block_object(const position &, const object_list &ol, PTABLE(place) *t);
  835.   ~block_object();
  836.   place *find_label(const char *);
  837.   object_type type();
  838.   void move_by(const position &);
  839.   void print();
  840. };
  841.  
  842. block_object::block_object(const position &d, const object_list &ol,
  843.                PTABLE(place) *t)
  844. : oblist(ol), tbl(t), rectangle_object(d)
  845. {
  846. }
  847.  
  848. block_object::~block_object()
  849. {
  850.   delete tbl;
  851.   object *p = oblist.head;
  852.   while (p != 0) {
  853.     object *tem = p;
  854.     p = p->next;
  855.     delete tem;
  856.   }
  857. }
  858.  
  859. void block_object::print()
  860. {
  861.   out->begin_block(south_west(), north_east());
  862.   print_object_list(oblist.head);
  863.   out->end_block();
  864. }
  865.  
  866. static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
  867. {
  868.   // Adjust all the labels that aren't attached to objects.
  869.   PTABLE_ITERATOR(place) iter(tbl);
  870.   const char *key;
  871.   place *pl;
  872.   while (iter.next(&key, &pl))
  873.     if (key && csupper(key[0]) && pl->obj == 0) {
  874.       pl->x += a.x;
  875.       pl->y += a.y;
  876.     }
  877. }
  878.  
  879. void block_object::move_by(const position &a)
  880. {
  881.   cent += a;
  882.   for (object *p = oblist.head; p; p = p->next)
  883.     p->move_by(a);
  884.   adjust_objectless_places(tbl, a);
  885. }
  886.  
  887.  
  888. place *block_object::find_label(const char *name)
  889. {
  890.   return tbl->lookup(name);
  891. }
  892.  
  893. object_type block_object::type()
  894. {
  895.   return BLOCK_OBJECT;
  896. }
  897.  
  898. graphic_object *object_spec::make_block(position *curpos, direction *dirp)
  899. {
  900.   bounding_box bb;
  901.   for (object *p = oblist.head; p; p = p->next)
  902.     p->update_bounding_box(&bb);
  903.   position dim;
  904.   if (!bb.blank) {
  905.     position m = -(bb.ll + bb.ur)/2.0;
  906.     for (object *p = oblist.head; p; p = p->next)
  907.       p->move_by(m);
  908.     adjust_objectless_places(tbl, m);
  909.     dim = bb.ur - bb.ll;
  910.   }
  911.   if (flags & HAS_WIDTH)
  912.     dim.x = width;
  913.   if (flags & HAS_HEIGHT)
  914.     dim.y = height;
  915.   block_object *block = new block_object(dim, oblist, tbl);
  916.   if (!position_rectangle(block, curpos, dirp)) {
  917.     delete block;
  918.     block = 0;
  919.   }
  920.   tbl = 0;
  921.   oblist.head = oblist.tail = 0;
  922.   return block;
  923. }
  924.  
  925. class text_object : public rectangle_object {
  926. public:
  927.   text_object(const position &);
  928.   object_type type() { return TEXT_OBJECT; }
  929. };
  930.  
  931. text_object::text_object(const position &d)
  932. : rectangle_object(d)
  933. {
  934. }
  935.  
  936. graphic_object *object_spec::make_text(position *curpos, direction *dirp)
  937. {
  938.   if (!(flags & HAS_HEIGHT)) {
  939.     lookup_variable("textht", &height);
  940.     int nitems = 0;
  941.     for (text_item *t = text; t; t = t->next)
  942.       nitems++;
  943.     height *= nitems;
  944.   }
  945.   if (!(flags & HAS_WIDTH))
  946.     lookup_variable("textwid", &width);
  947.   text_object *p = new text_object(position(width, height));
  948.   if (!position_rectangle(p, curpos, dirp)) {
  949.     delete p;
  950.     p = 0;
  951.   }
  952.   return p;
  953. }
  954.  
  955.  
  956. class ellipse_object : public closed_object {
  957. public:
  958.   ellipse_object(const position &);
  959.   position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
  960.                       cent.y + dim.y/(M_SQRT2*2.0)); }
  961.   position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
  962.                       cent.y + dim.y/(M_SQRT2*2.0)); }
  963.   position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
  964.                       cent.y - dim.y/(M_SQRT2*2.0)); }
  965.   position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
  966.                       cent.y - dim.y/(M_SQRT2*2.0)); }
  967.   double radius() { return dim.x/2.0; }
  968.   object_type type() { return ELLIPSE_OBJECT; }
  969.   void print();
  970. };
  971.  
  972. ellipse_object::ellipse_object(const position &d)
  973. : closed_object(d)
  974. {
  975. }
  976.  
  977. void ellipse_object::print()
  978. {
  979.   if (lt.type == line_type::invisible && fill < 0.0)
  980.     return;
  981.   out->ellipse(cent, dim, lt, fill);
  982. }
  983.  
  984. graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
  985. {
  986.   static double last_ellipse_height;
  987.   static double last_ellipse_width;
  988.   static int have_last_ellipse = 0;
  989.   if (!(flags & HAS_HEIGHT)) {
  990.     if ((flags & IS_SAME) && have_last_ellipse)
  991.       height = last_ellipse_height;
  992.     else
  993.       lookup_variable("ellipseht", &height);
  994.   }
  995.   if (!(flags & HAS_WIDTH)) {
  996.     if ((flags & IS_SAME) && have_last_ellipse)
  997.       width = last_ellipse_width;
  998.     else
  999.       lookup_variable("ellipsewid", &width);
  1000.   }
  1001.   last_ellipse_width = width;
  1002.   last_ellipse_height = height;
  1003.   have_last_ellipse = 1;
  1004.   ellipse_object *p = new ellipse_object(position(width, height));
  1005.   if (!position_rectangle(p, curpos, dirp)) {
  1006.     delete p;
  1007.     return 0;
  1008.   }
  1009.   return p;
  1010. }
  1011.  
  1012. class circle_object : public ellipse_object {
  1013. public:
  1014.   circle_object(double);
  1015.   object_type type() { return CIRCLE_OBJECT; }
  1016.   void print();
  1017. };
  1018.  
  1019. circle_object::circle_object(double diam)
  1020. : ellipse_object(position(diam, diam))
  1021. {
  1022. }
  1023.  
  1024. void circle_object::print()
  1025. {
  1026.   if (lt.type == line_type::invisible && fill < 0.0)
  1027.     return;
  1028.   out->circle(cent, dim.x/2.0, lt, fill);
  1029. }
  1030.  
  1031. graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
  1032. {
  1033.   static double last_circle_radius;
  1034.   static int have_last_circle = 0;
  1035.   if (!(flags & HAS_RADIUS)) {
  1036.     if ((flags & IS_SAME) && have_last_circle)
  1037.       radius = last_circle_radius;
  1038.     else
  1039.       lookup_variable("circlerad", &radius);
  1040.   }
  1041.   last_circle_radius = radius;
  1042.   have_last_circle = 1;
  1043.   circle_object *p = new circle_object(radius*2.0);
  1044.   if (!position_rectangle(p, curpos, dirp)) {
  1045.     delete p;
  1046.     return 0;
  1047.   }
  1048.   return p;
  1049. }
  1050.  
  1051. class move_object : public graphic_object {
  1052.   position strt;
  1053.   position en;
  1054. public:
  1055.   move_object(const position &s, const position &e);
  1056.   position origin() { return en; }
  1057.   object_type type() { return MOVE_OBJECT; }
  1058.   void update_bounding_box(bounding_box *);
  1059.   void move_by(const position &);
  1060. };
  1061.  
  1062. move_object::move_object(const position &s, const position &e)
  1063. : strt(s), en(e)
  1064. {
  1065. }
  1066.  
  1067. void move_object::update_bounding_box(bounding_box *p)
  1068. {
  1069.   p->encompass(strt);
  1070.   p->encompass(en);
  1071. }
  1072.  
  1073. void move_object::move_by(const position &a)
  1074. {
  1075.   strt += a;
  1076.   en += a;
  1077. }
  1078.  
  1079. graphic_object *object_spec::make_move(position *curpos, direction *dirp)
  1080. {
  1081.   static position last_move;
  1082.   static int have_last_move = 0;
  1083.   *dirp = dir;
  1084.   // No need to look at at since `at' attribute sets `from' attribute.
  1085.   position startpos = (flags & HAS_FROM) ? from : *curpos;
  1086.   if (!(flags & HAS_SEGMENT)) {
  1087.     if ((flags && IS_SAME) && have_last_move)
  1088.       segment_pos = last_move;
  1089.     else {
  1090.       switch (dir) {
  1091.       case UP_DIRECTION:
  1092.     segment_pos.y = segment_height;
  1093.     break;
  1094.       case DOWN_DIRECTION:
  1095.     segment_pos.y = -segment_height;
  1096.     break;
  1097.       case LEFT_DIRECTION:
  1098.     segment_pos.x = -segment_width;
  1099.     break;
  1100.       case RIGHT_DIRECTION:
  1101.     segment_pos.x = segment_width;
  1102.     break;
  1103.       default:
  1104.     assert(0);
  1105.       }
  1106.     }
  1107.   }
  1108.   segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
  1109.   // Reverse the segment_list so that it's in forward order.
  1110.   segment *old = segment_list;
  1111.   segment_list = 0;
  1112.   while (old != 0) {
  1113.     segment *tem = old->next;
  1114.     old->next = segment_list;
  1115.     segment_list = old;
  1116.     old = tem;
  1117.   }
  1118.   // Compute the end position.
  1119.   position endpos = startpos;
  1120.   for (segment *s = segment_list; s; s = s->next)
  1121.     if (s->is_absolute)
  1122.       endpos = s->pos;
  1123.     else 
  1124.       endpos += s->pos;
  1125.   have_last_move = 1;
  1126.   last_move = endpos - startpos;
  1127.   move_object *p = new move_object(startpos, endpos);
  1128.   *curpos = endpos;
  1129.   return p;
  1130. }
  1131.  
  1132. class linear_object : public graphic_object {
  1133. protected:
  1134.   char arrow_at_start;
  1135.   char arrow_at_end;
  1136.   arrow_head_type aht;
  1137.   position strt;
  1138.   position en;
  1139. public:
  1140.   linear_object(const position &s, const position &e);
  1141.   position start() { return strt; }
  1142.   position end() { return en; }
  1143.   void move_by(const position &);
  1144.   void update_bounding_box(bounding_box *) = 0;
  1145.   object_type type() = 0;
  1146.   void add_arrows(int at_start, int at_end, const arrow_head_type &);
  1147. };
  1148.  
  1149. class line_object : public linear_object {
  1150. protected:
  1151.   position *v;
  1152.   int n;
  1153. public:
  1154.   line_object(const position &s, const position &e, position *, int);
  1155.   ~line_object();
  1156.   position origin() { return strt; }
  1157.   position center() { return (strt + en)/2.0; }
  1158.   position north() { return (en.y - strt.y) > 0 ? en : strt; }
  1159.   position south() { return (en.y - strt.y) < 0 ? en : strt; }
  1160.   position east() { return (en.x - strt.x) > 0 ? en : strt; }
  1161.   position west() { return (en.x - strt.x) < 0 ? en : strt; }
  1162.   object_type type() { return LINE_OBJECT; }
  1163.   void update_bounding_box(bounding_box *);
  1164.   void print();
  1165.   void move_by(const position &);
  1166. };
  1167.  
  1168. class arrow_object : public line_object {
  1169. public:
  1170.   arrow_object(const position &, const position &, position *, int);
  1171.   object_type type() { return ARROW_OBJECT; }
  1172. };
  1173.  
  1174. class spline_object : public line_object {
  1175. public:
  1176.   spline_object(const position &, const position &, position *, int);
  1177.   object_type type() { return SPLINE_OBJECT; }
  1178.   void print();
  1179.   void update_bounding_box(bounding_box *);
  1180. };
  1181.  
  1182. linear_object::linear_object(const position &s, const position &e)
  1183. : strt(s), en(e), arrow_at_start(0), arrow_at_end(0)
  1184. {
  1185. }
  1186.  
  1187. void linear_object::move_by(const position &a)
  1188. {
  1189.   strt += a;
  1190.   en += a;
  1191. }
  1192.  
  1193. void linear_object::add_arrows(int at_start, int at_end,
  1194.                    const arrow_head_type &a)
  1195. {
  1196.   arrow_at_start = at_start;
  1197.   arrow_at_end = at_end;
  1198.   aht = a;
  1199. }
  1200.  
  1201. line_object::line_object(const position &s, const position &e,
  1202.              position *p, int i)
  1203. : v(p), n(i), linear_object(s, e)
  1204. {
  1205. }
  1206.  
  1207. void line_object::print()
  1208. {
  1209.   if (lt.type == line_type::invisible)
  1210.     return;
  1211.   out->line(strt, v, n, lt);
  1212.   if (arrow_at_start)
  1213.     draw_arrow(strt, strt-v[0], aht, lt);
  1214.   if (arrow_at_end)
  1215.     draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
  1216. }
  1217.  
  1218. void line_object::update_bounding_box(bounding_box *p)
  1219. {
  1220.   p->encompass(strt);
  1221.   for (int i = 0; i < n; i++)
  1222.     p->encompass(v[i]);
  1223. }
  1224.  
  1225. void line_object::move_by(const position &pos)
  1226. {
  1227.   linear_object::move_by(pos);
  1228.   for (int i = 0; i < n; i++)
  1229.     v[i] += pos;
  1230. }
  1231.   
  1232. void spline_object::update_bounding_box(bounding_box *p)
  1233. {
  1234.   p->encompass(strt);
  1235.   p->encompass(en);
  1236.   /*
  1237.  
  1238.   If
  1239.  
  1240.   p1 = q1/2 + q2/2
  1241.   p2 = q1/6 + q2*5/6
  1242.   p3 = q2*5/6 + q3/6
  1243.   p4 = q2/2 + q3/2
  1244.   [ the points for the Bezier cubic ]
  1245.  
  1246.   and
  1247.  
  1248.   t = .5
  1249.  
  1250.   then
  1251.  
  1252.   (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
  1253.   [ the equation for the Bezier cubic ]
  1254.  
  1255.   = .125*q1 + .75*q2 + .125*q3
  1256.  
  1257.   */
  1258.   for (int i = 1; i < n; i++)
  1259.     p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
  1260. }
  1261.  
  1262. arrow_object::arrow_object(const position &s, const position &e,
  1263.                position *p, int i)
  1264. : line_object(s, e, p, i)
  1265. {
  1266. }
  1267.  
  1268. spline_object::spline_object(const position &s, const position &e,
  1269.                  position *p, int i)
  1270. : line_object(s, e, p, i)
  1271. {
  1272. }
  1273.  
  1274. void spline_object::print()
  1275. {
  1276.   if (lt.type == line_type::invisible)
  1277.     return;
  1278.   out->spline(strt, v, n, lt);
  1279.   if (arrow_at_start)
  1280.     draw_arrow(strt, strt-v[0], aht, lt);
  1281.   if (arrow_at_end)
  1282.     draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
  1283. }
  1284.  
  1285. line_object::~line_object()
  1286. {
  1287.   delete v;
  1288. }
  1289.  
  1290. linear_object *object_spec::make_line(position *curpos, direction *dirp)
  1291. {
  1292.   static position last_line;
  1293.   static int have_last_line = 0;
  1294.   *dirp = dir;
  1295.   // No need to look at at since `at' attribute sets `from' attribute.
  1296.   position startpos = (flags & HAS_FROM) ? from : *curpos;
  1297.   if (!(flags & HAS_SEGMENT)) {
  1298.     if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
  1299.     && have_last_line)
  1300.       segment_pos = last_line;
  1301.     else 
  1302.       switch (dir) {
  1303.       case UP_DIRECTION:
  1304.     segment_pos.y = segment_height;
  1305.     break;
  1306.       case DOWN_DIRECTION:
  1307.     segment_pos.y = -segment_height;
  1308.     break;
  1309.       case LEFT_DIRECTION:
  1310.     segment_pos.x = -segment_width;
  1311.     break;
  1312.       case RIGHT_DIRECTION:
  1313.     segment_pos.x = segment_width;
  1314.     break;
  1315.       default:
  1316.     assert(0);
  1317.       }
  1318.   }
  1319.   segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
  1320.   // reverse the segment_list so that it's in forward order
  1321.   segment *old = segment_list;
  1322.   segment_list = 0;
  1323.   while (old != 0) {
  1324.     segment *tem = old->next;
  1325.     old->next = segment_list;
  1326.     segment_list = old;
  1327.     old = tem;
  1328.   }
  1329.   // Absolutise all movements
  1330.   position endpos = startpos;
  1331.   int nsegments = 0;
  1332.   for (segment *s = segment_list; s; s = s->next, nsegments++)
  1333.     if (s->is_absolute)
  1334.       endpos = s->pos;
  1335.     else {
  1336.       endpos += s->pos;
  1337.       s->pos = endpos;
  1338.       s->is_absolute = 1;    // to avoid confusion
  1339.     }
  1340.   // handle chop
  1341.   line_object *p = 0;
  1342.   position *v = new position[nsegments];
  1343.   int i = 0;
  1344.   for (s = segment_list; s; s = s->next, i++)
  1345.     v[i] = s->pos;
  1346.   if (flags & IS_DEFAULT_CHOPPED) {
  1347.     lookup_variable("circlerad", &start_chop);
  1348.     end_chop = start_chop;
  1349.     flags |= IS_CHOPPED;
  1350.   }
  1351.   if (flags & IS_CHOPPED) {
  1352.     position start_chop_vec, end_chop_vec;
  1353.     if (start_chop != 0.0) {
  1354.       start_chop_vec = v[0] - startpos;
  1355.       start_chop_vec *= start_chop / hypot(start_chop_vec);
  1356.     }
  1357.     if (end_chop != 0.0) {
  1358.       end_chop_vec = (v[nsegments - 1]
  1359.               - (nsegments > 1 ? v[nsegments - 2] : startpos));
  1360.       end_chop_vec *= end_chop / hypot(end_chop_vec);
  1361.     }
  1362.     startpos += start_chop_vec;
  1363.     v[nsegments - 1] -= end_chop_vec;
  1364.     endpos -= end_chop_vec;
  1365.   }
  1366.   switch (type) {
  1367.   case SPLINE_OBJECT:
  1368.     p = new spline_object(startpos, endpos, v, nsegments);
  1369.     break;
  1370.   case ARROW_OBJECT:
  1371.     p = new arrow_object(startpos, endpos, v, nsegments);
  1372.     break;
  1373.   case LINE_OBJECT:
  1374.     p = new line_object(startpos, endpos, v, nsegments);
  1375.     break;
  1376.   default:
  1377.     assert(0);
  1378.   }
  1379.   have_last_line = 1;
  1380.   last_line = endpos - startpos;
  1381.   *curpos = endpos;
  1382.   return p;
  1383. }
  1384.  
  1385. class arc_object : public linear_object {
  1386.   int clockwise;
  1387.   position cent;
  1388.   double rad;
  1389. public:
  1390.   arc_object(int, const position &, const position &, const position &);
  1391.   position origin() { return cent; }
  1392.   position center() { return cent; }
  1393.   double radius() { return rad; }
  1394.   position north();
  1395.   position south();
  1396.   position east();
  1397.   position west();
  1398.   position north_east();
  1399.   position north_west();
  1400.   position south_east();
  1401.   position south_west();
  1402.   void update_bounding_box(bounding_box *);
  1403.   object_type type() { return ARC_OBJECT; }
  1404.   void print();
  1405.   void move_by(const position &pos);
  1406. };
  1407.  
  1408. arc_object::arc_object(int cw, const position &s, const position &e,
  1409.                const position &c)
  1410. : linear_object(s, e), clockwise(cw), cent(c)
  1411. {
  1412.   rad = hypot(c - s);
  1413. }
  1414.  
  1415. void arc_object::move_by(const position &pos)
  1416. {
  1417.   linear_object::move_by(pos);
  1418.   cent += pos;
  1419. }
  1420.  
  1421. // we get arc corners from the corresponding circle
  1422.  
  1423. position arc_object::north()
  1424. {
  1425.   position result(cent);
  1426.   result.y += rad;
  1427.   return result;
  1428. }
  1429.  
  1430. position arc_object::south()
  1431. {
  1432.   position result(cent);
  1433.   result.y -= rad;
  1434.   return result;
  1435. }
  1436.  
  1437. position arc_object::east()
  1438. {
  1439.   position result(cent);
  1440.   result.x += rad;
  1441.   return result;
  1442. }
  1443.  
  1444. position arc_object::west()
  1445. {
  1446.   position result(cent);
  1447.   result.x -= rad;
  1448.   return result;
  1449. }
  1450.  
  1451. position arc_object::north_east()
  1452. {
  1453.   position result(cent);
  1454.   result.x += rad/M_SQRT2;
  1455.   result.y += rad/M_SQRT2;
  1456.   return result;
  1457. }
  1458.  
  1459. position arc_object::north_west()
  1460. {
  1461.   position result(cent);
  1462.   result.x -= rad/M_SQRT2;
  1463.   result.y += rad/M_SQRT2;
  1464.   return result;
  1465. }
  1466.  
  1467. position arc_object::south_east()
  1468. {
  1469.   position result(cent);
  1470.   result.x += rad/M_SQRT2;
  1471.   result.y -= rad/M_SQRT2;
  1472.   return result;
  1473. }
  1474.  
  1475. position arc_object::south_west()
  1476. {
  1477.   position result(cent);
  1478.   result.x -= rad/M_SQRT2;
  1479.   result.y -= rad/M_SQRT2;
  1480.   return result;
  1481. }
  1482.  
  1483.  
  1484. void arc_object::print()
  1485. {
  1486.   if (lt.type == line_type::invisible)
  1487.     return;
  1488.   if (clockwise)
  1489.     out->arc(en, cent, strt, lt);
  1490.   else
  1491.     out->arc(strt, cent, en, lt);
  1492.   if (arrow_at_start) {
  1493.     position c = cent - strt;
  1494.     draw_arrow(strt,
  1495.            (clockwise ? position(c.y, -c.x) : position(-c.y, c.x)),
  1496.            aht, lt);
  1497.   }
  1498.   if (arrow_at_end) {
  1499.     position e = en - cent;
  1500.     draw_arrow(en,
  1501.            (clockwise ? position(e.y, -e.x) : position(-e.y, e.x)),
  1502.            aht, lt);
  1503.   }
  1504. }
  1505.  
  1506. inline double max(double a, double b)
  1507. {
  1508.   return a > b ? a : b;
  1509. }
  1510.  
  1511. void arc_object::update_bounding_box(bounding_box *p)
  1512. {
  1513.   p->encompass(strt);
  1514.   p->encompass(en);
  1515.   position start_offset = strt - cent;
  1516.   if (start_offset.x == 0.0 && start_offset.y == 0.0)
  1517.     return;
  1518.   position end_offset = en  - cent;
  1519.   if (end_offset.x == 0.0 && end_offset.y == 0.0)
  1520.     return;
  1521.   double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
  1522.   double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
  1523.   if (clockwise) {
  1524.     double temp = start_quad;
  1525.     start_quad = end_quad;
  1526.     end_quad = temp;
  1527.   }
  1528.   if (start_quad < 0.0)
  1529.     start_quad += 4.0;
  1530.   while (end_quad <= start_quad)
  1531.     end_quad += 4.0;
  1532.   double radius = max(hypot(start_offset), hypot(end_offset));
  1533.   for (int q = int(start_quad) + 1; q < end_quad; q++) {
  1534.     position offset;
  1535.     switch (q % 4) {
  1536.     case 0:
  1537.       offset.x = radius;
  1538.       break;
  1539.     case 1:
  1540.       offset.y = radius;
  1541.       break;
  1542.     case 2:
  1543.       offset.x = -radius;
  1544.       break;
  1545.     case 3:
  1546.       offset.y = -radius;
  1547.       break;
  1548.     }
  1549.     p->encompass(cent + offset);
  1550.   }
  1551. }
  1552.  
  1553. // We ignore the with attribute. The at attribute always refers to the center.
  1554.  
  1555. linear_object *object_spec::make_arc(position *curpos, direction *dirp)
  1556. {
  1557.   *dirp = dir;
  1558.   int cw = (flags & IS_CLOCKWISE) != 0;
  1559.   // compute the start
  1560.   position startpos;
  1561.   if (flags & HAS_FROM)
  1562.     startpos = from;
  1563.   else
  1564.     startpos = *curpos;
  1565.   if (!(flags & HAS_RADIUS))
  1566.     lookup_variable("arcrad", &radius);
  1567.   // compute the end
  1568.   position endpos;
  1569.   if (flags & HAS_TO)
  1570.     endpos = to;
  1571.   else {
  1572.     position m(radius, radius);
  1573.     // Adjust the signs.
  1574.     if (cw) {
  1575.       if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
  1576.     m.x = -m.x;
  1577.       if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
  1578.     m.y = -m.y;
  1579.       *dirp = direction((dir + 3) % 4);
  1580.     }
  1581.     else {
  1582.       if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
  1583.     m.x = -m.x;
  1584.       if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
  1585.     m.y = -m.y;
  1586.       *dirp = direction((dir + 1) % 4);
  1587.     }
  1588.     endpos = startpos + m;
  1589.   }
  1590.   // compute the center
  1591.   position centerpos;
  1592.   if (flags & HAS_AT)
  1593.     centerpos = at;
  1594.   else if (startpos == endpos)
  1595.     centerpos = startpos;
  1596.   else {
  1597.     position h = (endpos - startpos)/2.0;
  1598.     double d = hypot(h);
  1599.     if (radius <= 0)
  1600.       radius = .25;
  1601.     // make the radius big enough
  1602.     while (radius < d)
  1603.       radius *= 2.0;
  1604.     double alpha = acos(d/radius);
  1605.     double theta = atan2(h.y, h.x);
  1606.     if (cw)
  1607.       theta -= alpha;
  1608.     else
  1609.       theta += alpha;
  1610.     centerpos = position(cos(theta), sin(theta))*radius + startpos;
  1611.   }
  1612.   arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
  1613.   *curpos = endpos;
  1614.   return p;
  1615. }
  1616.  
  1617. graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
  1618. {
  1619.   linear_object *obj;
  1620.   if (type == ARC_OBJECT)
  1621.     obj = make_arc(curpos, dirp);
  1622.   else
  1623.     obj = make_line(curpos, dirp);
  1624.   if (type == ARROW_OBJECT
  1625.       && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
  1626.     flags |= HAS_RIGHT_ARROW_HEAD;
  1627.   if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
  1628.     arrow_head_type a;
  1629.     int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
  1630.     int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
  1631.     if (flags & HAS_HEIGHT)
  1632.       a.height = height;
  1633.     else
  1634.       lookup_variable("arrowht", &a.height);
  1635.     if (flags & HAS_WIDTH)
  1636.       a.width = width;
  1637.     else
  1638.       lookup_variable("arrowwid", &a.width);
  1639.     double solid;
  1640.     lookup_variable("arrowhead", &solid);
  1641.     a.solid = solid != 0.0;
  1642.     obj->add_arrows(at_start, at_end, a);
  1643.   }
  1644.   return obj;
  1645. }
  1646.  
  1647. object *object_spec::make_object(position *curpos, direction *dirp)
  1648. {
  1649.   graphic_object *obj = 0;
  1650.   switch (type) {
  1651.   case BLOCK_OBJECT:
  1652.     obj = make_block(curpos, dirp);
  1653.     break;
  1654.   case BOX_OBJECT:
  1655.     obj = make_box(curpos, dirp);
  1656.     break;
  1657.   case TEXT_OBJECT:
  1658.     obj = make_text(curpos, dirp);
  1659.     break;
  1660.   case ELLIPSE_OBJECT:
  1661.     obj = make_ellipse(curpos, dirp);
  1662.     break;
  1663.   case CIRCLE_OBJECT:
  1664.     obj = make_circle(curpos, dirp);
  1665.     break;
  1666.   case MOVE_OBJECT:
  1667.     obj = make_move(curpos, dirp);
  1668.     break;
  1669.   case ARC_OBJECT:
  1670.   case LINE_OBJECT:
  1671.   case SPLINE_OBJECT:
  1672.   case ARROW_OBJECT:
  1673.     obj = make_linear(curpos, dirp);
  1674.     break;
  1675.   case MARK_OBJECT:
  1676.   case OTHER_OBJECT:
  1677.   default:
  1678.     assert(0);
  1679.     break;
  1680.   }
  1681.   if (obj) {
  1682.     if (flags & IS_INVISIBLE)
  1683.       obj->set_invisible();
  1684.     if (text != 0)
  1685.       obj->add_text(text, (flags & IS_ALIGNED) != 0);
  1686.     if (flags & IS_DOTTED)
  1687.       obj->set_dotted(dash_width);
  1688.     else if (flags & IS_DASHED)
  1689.       obj->set_dashed(dash_width);
  1690.     double th;
  1691.     if (flags & HAS_THICKNESS)
  1692.       th = thickness;
  1693.     else
  1694.       lookup_variable("linethick", &th);
  1695.     obj->set_thickness(th);
  1696.     if (flags & (IS_DEFAULT_FILLED|IS_FILLED)) {
  1697.       if (flags & IS_DEFAULT_FILLED)
  1698.     lookup_variable("fillval", &fill);
  1699.       if (fill < 0.0)
  1700.     error("bad fill value %1", fill);
  1701.       else
  1702.     obj->set_fill(fill);
  1703.     }
  1704.   }
  1705.   return obj;
  1706. }
  1707.  
  1708. struct string_list {
  1709.   string_list *next;
  1710.   char *str;
  1711.   string_list(char *);
  1712.   ~string_list();
  1713. };
  1714.  
  1715. string_list::string_list(char *s)
  1716. : next(0), str(s)
  1717. {
  1718. }
  1719.  
  1720. string_list::~string_list()
  1721. {
  1722.   delete str;
  1723. }
  1724.   
  1725. /* A path is used to hold the argument to the with attribute. For example,
  1726. `.nw' or `.A.s' or `.A'. The major operation on a path is to take a 
  1727. place and follow the path through the place to place within the place.
  1728. Note that `.A.B.C.sw' will work. */
  1729.  
  1730. path::path(corner c)
  1731. : label_list(0), crn(c)
  1732. {
  1733. }
  1734.  
  1735. path::path(char *l, corner c)
  1736. : crn(c)
  1737. {
  1738.   label_list = new string_list(l);
  1739. }
  1740.  
  1741. path::~path()
  1742. {
  1743.   while (label_list) {
  1744.     string_list *tem = label_list;
  1745.     label_list = label_list->next;
  1746.     delete tem;
  1747.   }
  1748. }
  1749.  
  1750. void path::append(corner c)
  1751. {
  1752.   assert(crn == 0);
  1753.   crn = c;
  1754. }
  1755.  
  1756. void path::append(char *s)
  1757. {
  1758.   for (string_list **p = &label_list; *p; p = &(*p)->next)
  1759.     ;
  1760.   *p = new string_list(s);
  1761. }
  1762.  
  1763. // return non-zero for success
  1764.  
  1765. int path::follow(const place &pl, place *result) const
  1766. {
  1767.   const place *p = &pl;
  1768.   for (string_list *lb = label_list; lb; lb = lb->next)
  1769.     if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
  1770.       lex_error("object does not contain a place `%1'", lb->str);
  1771.       return 0;
  1772.     }
  1773.   if (crn == 0 || p->obj == 0)
  1774.     *result = *p;
  1775.   else {
  1776.     position pos = ((p->obj)->*(crn))();
  1777.     result->x = pos.x;
  1778.     result->y = pos.y;
  1779.     result->obj = 0;
  1780.   }
  1781.   return 1;
  1782. }
  1783.  
  1784. void print_object_list(object *p)
  1785. {
  1786.   for (; p; p = p->next) {
  1787.     p->print();
  1788.     p->print_text();
  1789.   }
  1790. }
  1791.  
  1792. void print_picture(object *obj)
  1793. {
  1794.   bounding_box bb;
  1795.   for (object *p = obj; p; p = p->next)
  1796.     p->update_bounding_box(&bb);
  1797.   double scale;
  1798.   lookup_variable("scale", &scale);
  1799.   out->start_picture(scale, bb.ll, bb.ur);
  1800.   print_object_list(obj);
  1801.   out->finish_picture();
  1802. }
  1803.  
  1804.