home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / pic / object.cc < prev    next >
C/C++ Source or Header  |  1995-06-22  |  39KB  |  1,834 lines

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