LCOV - code coverage report
Current view: top level - preproc/pic - object.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 977 1273 76.7 %
Date: 2026-01-16 17:51:41 Functions: 165 216 76.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14