184fe7d0bSbostic // -*- C++ -*-
284fe7d0bSbostic /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
384fe7d0bSbostic      Written by James Clark (jjc@jclark.com)
484fe7d0bSbostic 
584fe7d0bSbostic This file is part of groff.
684fe7d0bSbostic 
784fe7d0bSbostic groff is free software; you can redistribute it and/or modify it under
884fe7d0bSbostic the terms of the GNU General Public License as published by the Free
984fe7d0bSbostic Software Foundation; either version 2, or (at your option) any later
1084fe7d0bSbostic version.
1184fe7d0bSbostic 
1284fe7d0bSbostic groff is distributed in the hope that it will be useful, but WITHOUT ANY
1384fe7d0bSbostic WARRANTY; without even the implied warranty of MERCHANTABILITY or
1484fe7d0bSbostic FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1584fe7d0bSbostic for more details.
1684fe7d0bSbostic 
1784fe7d0bSbostic You should have received a copy of the GNU General Public License along
1884fe7d0bSbostic with groff; see the file COPYING.  If not, write to the Free Software
1984fe7d0bSbostic Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
2084fe7d0bSbostic 
2184fe7d0bSbostic #include "pic.h"
2284fe7d0bSbostic #include "ptable.h"
2384fe7d0bSbostic #include "object.h"
2484fe7d0bSbostic 
2584fe7d0bSbostic void print_object_list(object *);
2684fe7d0bSbostic 
line_type()2784fe7d0bSbostic line_type::line_type()
2884fe7d0bSbostic : type(solid), thickness(1.0)
2984fe7d0bSbostic {
3084fe7d0bSbostic }
3184fe7d0bSbostic 
output()3284fe7d0bSbostic output::output() : desired_height(0.0), desired_width(0.0), args(0)
3384fe7d0bSbostic {
3484fe7d0bSbostic }
3584fe7d0bSbostic 
~output()3684fe7d0bSbostic output::~output()
3784fe7d0bSbostic {
3884fe7d0bSbostic   a_delete args;
3984fe7d0bSbostic }
4084fe7d0bSbostic 
set_desired_width_height(double wid,double ht)4184fe7d0bSbostic void output::set_desired_width_height(double wid, double ht)
4284fe7d0bSbostic {
4384fe7d0bSbostic   desired_width = wid;
4484fe7d0bSbostic   desired_height = ht;
4584fe7d0bSbostic }
4684fe7d0bSbostic 
set_args(const char * s)4784fe7d0bSbostic void output::set_args(const char *s)
4884fe7d0bSbostic {
4984fe7d0bSbostic   a_delete args;
5084fe7d0bSbostic   if (s == 0 || *s == '\0')
5184fe7d0bSbostic     args = 0;
5284fe7d0bSbostic   else
5384fe7d0bSbostic     args = strsave(s);
5484fe7d0bSbostic }
5584fe7d0bSbostic 
command(const char *,const char *,int)5684fe7d0bSbostic void output::command(const char *, const char *, int)
5784fe7d0bSbostic {
5884fe7d0bSbostic }
5984fe7d0bSbostic 
set_location(const char *,int)6084fe7d0bSbostic void output::set_location(const char *, int)
6184fe7d0bSbostic {
6284fe7d0bSbostic }
6384fe7d0bSbostic 
supports_filled_polygons()6484fe7d0bSbostic int output::supports_filled_polygons()
6584fe7d0bSbostic {
6684fe7d0bSbostic   return 0;
6784fe7d0bSbostic }
6884fe7d0bSbostic 
begin_block(const position &,const position &)6984fe7d0bSbostic void output::begin_block(const position &, const position &)
7084fe7d0bSbostic {
7184fe7d0bSbostic }
7284fe7d0bSbostic 
end_block()7384fe7d0bSbostic void output::end_block()
7484fe7d0bSbostic {
7584fe7d0bSbostic }
7684fe7d0bSbostic 
compute_scale(double sc,const position & ll,const position & ur)7784fe7d0bSbostic double output::compute_scale(double sc, const position &ll, const position &ur)
7884fe7d0bSbostic {
7984fe7d0bSbostic   distance dim = ur - ll;
8084fe7d0bSbostic   if (desired_width != 0.0 || desired_height != 0.0) {
8184fe7d0bSbostic     sc = 0.0;
8284fe7d0bSbostic     if (desired_width != 0.0) {
8384fe7d0bSbostic       if (dim.x == 0.0)
8484fe7d0bSbostic 	error("width specified for picture with zero width");
8584fe7d0bSbostic       else
8684fe7d0bSbostic 	sc = dim.x/desired_width;
8784fe7d0bSbostic     }
8884fe7d0bSbostic     if (desired_height != 0.0) {
8984fe7d0bSbostic       if (dim.y == 0.0)
9084fe7d0bSbostic 	error("height specified for picture with zero height");
9184fe7d0bSbostic       else {
9284fe7d0bSbostic 	double tem = dim.y/desired_height;
9384fe7d0bSbostic 	if (tem > sc)
9484fe7d0bSbostic 	  sc = tem;
9584fe7d0bSbostic       }
9684fe7d0bSbostic     }
9784fe7d0bSbostic     return sc == 0.0 ? 1.0 : sc;
9884fe7d0bSbostic   }
9984fe7d0bSbostic   else {
10084fe7d0bSbostic     if (sc <= 0.0)
10184fe7d0bSbostic       sc = 1.0;
10284fe7d0bSbostic     distance sdim = dim/sc;
10384fe7d0bSbostic     double max_width = 0.0;
10484fe7d0bSbostic     lookup_variable("maxpswid", &max_width);
10584fe7d0bSbostic     double max_height = 0.0;
10684fe7d0bSbostic     lookup_variable("maxpsht", &max_height);
10784fe7d0bSbostic     if ((max_width > 0.0 && sdim.x > max_width)
10884fe7d0bSbostic 	|| (max_height > 0.0 && sdim.y > max_height)) {
10984fe7d0bSbostic       double xscale = dim.x/max_width;
11084fe7d0bSbostic       double yscale = dim.y/max_height;
11184fe7d0bSbostic       return xscale > yscale ? xscale : yscale;
11284fe7d0bSbostic     }
11384fe7d0bSbostic     else
11484fe7d0bSbostic       return sc;
11584fe7d0bSbostic   }
11684fe7d0bSbostic }
11784fe7d0bSbostic 
position(const place & pl)11884fe7d0bSbostic position::position(const place &pl)
11984fe7d0bSbostic {
12084fe7d0bSbostic   if (pl.obj != 0) {
12184fe7d0bSbostic     // Use two statements to work around bug in SGI C++.
12284fe7d0bSbostic     object *tem = pl.obj;
12384fe7d0bSbostic     *this = tem->origin();
12484fe7d0bSbostic   }
12584fe7d0bSbostic   else {
12684fe7d0bSbostic     x = pl.x;
12784fe7d0bSbostic     y = pl.y;
12884fe7d0bSbostic   }
12984fe7d0bSbostic }
13084fe7d0bSbostic 
position()13184fe7d0bSbostic position::position() : x(0.0), y(0.0)
13284fe7d0bSbostic {
13384fe7d0bSbostic }
13484fe7d0bSbostic 
position(double a,double b)13584fe7d0bSbostic position::position(double a, double b) : x(a), y(b)
13684fe7d0bSbostic {
13784fe7d0bSbostic }
13884fe7d0bSbostic 
139*faaca10aSbostic /*
140*faaca10aSbostic  * XXX workaround for gcc 2.3.3 initializer bug.
141*faaca10aSbostic  * From: Chris Torek <torek@BSDI.COM>
142*faaca10aSbostic  */
posref(position p)143*faaca10aSbostic position &posref(position p) { return p; }
144*faaca10aSbostic 
14584fe7d0bSbostic 
operator ==(const position & a,const position & b)14684fe7d0bSbostic int operator==(const position &a, const position &b)
14784fe7d0bSbostic {
14884fe7d0bSbostic   return a.x == b.x && a.y == b.y;
14984fe7d0bSbostic }
15084fe7d0bSbostic 
operator !=(const position & a,const position & b)15184fe7d0bSbostic int operator!=(const position &a, const position &b)
15284fe7d0bSbostic {
15384fe7d0bSbostic   return a.x != b.x || a.y != b.y;
15484fe7d0bSbostic }
15584fe7d0bSbostic 
operator +=(const position & a)15684fe7d0bSbostic position &position::operator+=(const position &a)
15784fe7d0bSbostic {
15884fe7d0bSbostic   x += a.x;
15984fe7d0bSbostic   y += a.y;
16084fe7d0bSbostic   return *this;
16184fe7d0bSbostic }
16284fe7d0bSbostic 
operator -=(const position & a)16384fe7d0bSbostic position &position::operator-=(const position &a)
16484fe7d0bSbostic {
16584fe7d0bSbostic   x -= a.x;
16684fe7d0bSbostic   y -= a.y;
16784fe7d0bSbostic   return *this;
16884fe7d0bSbostic }
16984fe7d0bSbostic 
operator *=(double a)17084fe7d0bSbostic position &position::operator*=(double a)
17184fe7d0bSbostic {
17284fe7d0bSbostic   x *= a;
17384fe7d0bSbostic   y *= a;
17484fe7d0bSbostic   return *this;
17584fe7d0bSbostic }
17684fe7d0bSbostic 
operator /=(double a)17784fe7d0bSbostic position &position::operator/=(double a)
17884fe7d0bSbostic {
17984fe7d0bSbostic   x /= a;
18084fe7d0bSbostic   y /= a;
18184fe7d0bSbostic   return *this;
18284fe7d0bSbostic }
18384fe7d0bSbostic 
operator -(const position & a)18484fe7d0bSbostic position operator-(const position &a)
18584fe7d0bSbostic {
18684fe7d0bSbostic   return position(-a.x, -a.y);
18784fe7d0bSbostic }
18884fe7d0bSbostic 
operator +(const position & a,const position & b)18984fe7d0bSbostic position operator+(const position &a, const position &b)
19084fe7d0bSbostic {
19184fe7d0bSbostic   return position(a.x + b.x, a.y + b.y);
19284fe7d0bSbostic }
19384fe7d0bSbostic 
operator -(const position & a,const position & b)19484fe7d0bSbostic position operator-(const position &a, const position &b)
19584fe7d0bSbostic {
19684fe7d0bSbostic   return position(a.x - b.x, a.y - b.y);
19784fe7d0bSbostic }
19884fe7d0bSbostic 
operator /(const position & a,double n)19984fe7d0bSbostic position operator/(const position &a, double n)
20084fe7d0bSbostic {
20184fe7d0bSbostic   return position(a.x/n, a.y/n);
20284fe7d0bSbostic }
20384fe7d0bSbostic 
operator *(const position & a,double n)20484fe7d0bSbostic position operator*(const position &a, double n)
20584fe7d0bSbostic {
20684fe7d0bSbostic   return position(a.x*n, a.y*n);
20784fe7d0bSbostic }
20884fe7d0bSbostic 
20984fe7d0bSbostic // dot product
21084fe7d0bSbostic 
operator *(const position & a,const position & b)21184fe7d0bSbostic double operator*(const position &a, const position &b)
21284fe7d0bSbostic {
21384fe7d0bSbostic   return a.x*b.x + a.y*b.y;
21484fe7d0bSbostic }
21584fe7d0bSbostic 
hypot(const position & a)21684fe7d0bSbostic double hypot(const position &a)
21784fe7d0bSbostic {
21884fe7d0bSbostic   return hypot(a.x, a.y);
21984fe7d0bSbostic }
22084fe7d0bSbostic 
22184fe7d0bSbostic struct arrow_head_type {
22284fe7d0bSbostic   double height;
22384fe7d0bSbostic   double width;
22484fe7d0bSbostic   int solid;
22584fe7d0bSbostic };
22684fe7d0bSbostic 
draw_arrow(const position & pos,const distance & dir,const arrow_head_type & aht,const line_type & lt)22784fe7d0bSbostic void draw_arrow(const position &pos, const distance &dir,
22884fe7d0bSbostic 		const arrow_head_type &aht, const line_type &lt)
22984fe7d0bSbostic {
23084fe7d0bSbostic   double hyp = hypot(dir);
23184fe7d0bSbostic   if (hyp == 0.0) {
23284fe7d0bSbostic     error("cannot draw arrow on object with zero length");
23384fe7d0bSbostic     return;
23484fe7d0bSbostic   }
23584fe7d0bSbostic   position base = -dir;
23684fe7d0bSbostic   base *= aht.height/hyp;
23784fe7d0bSbostic   position n(dir.y, -dir.x);
23884fe7d0bSbostic   n *= aht.width/(hyp*2.0);
23984fe7d0bSbostic   line_type slt = lt;
24084fe7d0bSbostic   slt.type = line_type::solid;
24184fe7d0bSbostic   if (aht.solid && out->supports_filled_polygons()) {
24284fe7d0bSbostic     position v[3];
24384fe7d0bSbostic     v[0] = pos;
24484fe7d0bSbostic     v[1] = pos + base + n;
24584fe7d0bSbostic     v[2] = pos + base - n;
24684fe7d0bSbostic     // A value > 1 means fill with the current color.
24784fe7d0bSbostic     out->polygon(v, 3, slt, 2.0);
24884fe7d0bSbostic   }
24984fe7d0bSbostic   else {
25084fe7d0bSbostic     position v[2];
25184fe7d0bSbostic     v[0] = pos;
25284fe7d0bSbostic     v[1] = pos + base + n;
25384fe7d0bSbostic     out->line(pos + base - n, v, 2, slt);
25484fe7d0bSbostic   }
25584fe7d0bSbostic }
25684fe7d0bSbostic 
object()25784fe7d0bSbostic object::object() : prev(0), next(0)
25884fe7d0bSbostic {
25984fe7d0bSbostic }
26084fe7d0bSbostic 
~object()26184fe7d0bSbostic object::~object()
26284fe7d0bSbostic {
26384fe7d0bSbostic }
26484fe7d0bSbostic 
move_by(const position &)26584fe7d0bSbostic void object::move_by(const position &)
26684fe7d0bSbostic {
26784fe7d0bSbostic }
26884fe7d0bSbostic 
print()26984fe7d0bSbostic void object::print()
27084fe7d0bSbostic {
27184fe7d0bSbostic }
27284fe7d0bSbostic 
print_text()27384fe7d0bSbostic void object::print_text()
27484fe7d0bSbostic {
27584fe7d0bSbostic }
27684fe7d0bSbostic 
blank()27784fe7d0bSbostic int object::blank()
27884fe7d0bSbostic {
27984fe7d0bSbostic   return 0;
28084fe7d0bSbostic }
28184fe7d0bSbostic 
28284fe7d0bSbostic struct bounding_box {
28384fe7d0bSbostic   int blank;
28484fe7d0bSbostic   position ll;
28584fe7d0bSbostic   position ur;
28684fe7d0bSbostic 
28784fe7d0bSbostic   bounding_box();
28884fe7d0bSbostic   void encompass(const position &);
28984fe7d0bSbostic };
29084fe7d0bSbostic 
bounding_box()29184fe7d0bSbostic bounding_box::bounding_box()
29284fe7d0bSbostic : blank(1)
29384fe7d0bSbostic {
29484fe7d0bSbostic }
29584fe7d0bSbostic 
encompass(const position & pos)29684fe7d0bSbostic void bounding_box::encompass(const position &pos)
29784fe7d0bSbostic {
29884fe7d0bSbostic   if (blank) {
29984fe7d0bSbostic     ll = pos;
30084fe7d0bSbostic     ur = pos;
30184fe7d0bSbostic     blank = 0;
30284fe7d0bSbostic   }
30384fe7d0bSbostic   else {
30484fe7d0bSbostic     if (pos.x < ll.x)
30584fe7d0bSbostic       ll.x = pos.x;
30684fe7d0bSbostic     if (pos.y < ll.y)
30784fe7d0bSbostic       ll.y = pos.y;
30884fe7d0bSbostic     if (pos.x > ur.x)
30984fe7d0bSbostic       ur.x = pos.x;
31084fe7d0bSbostic     if (pos.y > ur.y)
31184fe7d0bSbostic       ur.y = pos.y;
31284fe7d0bSbostic   }
31384fe7d0bSbostic }
31484fe7d0bSbostic 
update_bounding_box(bounding_box *)31584fe7d0bSbostic void object::update_bounding_box(bounding_box *)
31684fe7d0bSbostic {
31784fe7d0bSbostic }
31884fe7d0bSbostic 
origin()31984fe7d0bSbostic position object::origin()
32084fe7d0bSbostic {
32184fe7d0bSbostic   return position(0.0,0.0);
32284fe7d0bSbostic }
32384fe7d0bSbostic 
north()32484fe7d0bSbostic position object::north()
32584fe7d0bSbostic {
32684fe7d0bSbostic   return origin();
32784fe7d0bSbostic }
32884fe7d0bSbostic 
south()32984fe7d0bSbostic position object::south()
33084fe7d0bSbostic {
33184fe7d0bSbostic   return origin();
33284fe7d0bSbostic }
33384fe7d0bSbostic 
east()33484fe7d0bSbostic position object::east()
33584fe7d0bSbostic {
33684fe7d0bSbostic   return origin();
33784fe7d0bSbostic }
33884fe7d0bSbostic 
west()33984fe7d0bSbostic position object::west()
34084fe7d0bSbostic {
34184fe7d0bSbostic   return origin();
34284fe7d0bSbostic }
34384fe7d0bSbostic 
north_east()34484fe7d0bSbostic position object::north_east()
34584fe7d0bSbostic {
34684fe7d0bSbostic   return origin();
34784fe7d0bSbostic }
34884fe7d0bSbostic 
north_west()34984fe7d0bSbostic position object::north_west()
35084fe7d0bSbostic {
35184fe7d0bSbostic   return origin();
35284fe7d0bSbostic }
35384fe7d0bSbostic 
south_east()35484fe7d0bSbostic position object::south_east()
35584fe7d0bSbostic {
35684fe7d0bSbostic   return origin();
35784fe7d0bSbostic }
35884fe7d0bSbostic 
south_west()35984fe7d0bSbostic position object::south_west()
36084fe7d0bSbostic {
36184fe7d0bSbostic   return origin();
36284fe7d0bSbostic }
36384fe7d0bSbostic 
start()36484fe7d0bSbostic position object::start()
36584fe7d0bSbostic {
36684fe7d0bSbostic   return origin();
36784fe7d0bSbostic }
36884fe7d0bSbostic 
end()36984fe7d0bSbostic position object::end()
37084fe7d0bSbostic {
37184fe7d0bSbostic   return origin();
37284fe7d0bSbostic }
37384fe7d0bSbostic 
center()37484fe7d0bSbostic position object::center()
37584fe7d0bSbostic {
37684fe7d0bSbostic   return origin();
37784fe7d0bSbostic }
37884fe7d0bSbostic 
width()37984fe7d0bSbostic double object::width()
38084fe7d0bSbostic {
38184fe7d0bSbostic   return 0.0;
38284fe7d0bSbostic }
38384fe7d0bSbostic 
radius()38484fe7d0bSbostic double object::radius()
38584fe7d0bSbostic {
38684fe7d0bSbostic   return 0.0;
38784fe7d0bSbostic }
38884fe7d0bSbostic 
height()38984fe7d0bSbostic double object::height()
39084fe7d0bSbostic {
39184fe7d0bSbostic   return 0.0;
39284fe7d0bSbostic }
39384fe7d0bSbostic 
find_label(const char *)39484fe7d0bSbostic place *object::find_label(const char *)
39584fe7d0bSbostic {
39684fe7d0bSbostic   return 0;
39784fe7d0bSbostic }
39884fe7d0bSbostic 
segment(const position & a,int n,segment * p)39984fe7d0bSbostic segment::segment(const position &a, int n, segment *p)
40084fe7d0bSbostic : pos(a), is_absolute(n), next(p)
40184fe7d0bSbostic {
40284fe7d0bSbostic }
40384fe7d0bSbostic 
text_item(char * t,const char * fn,int ln)40484fe7d0bSbostic text_item::text_item(char *t, const char *fn, int ln)
40584fe7d0bSbostic : filename(fn), lineno(ln), text(t), next(0)
40684fe7d0bSbostic {
40784fe7d0bSbostic   adj.h = CENTER_ADJUST;
40884fe7d0bSbostic   adj.v = NONE_ADJUST;
40984fe7d0bSbostic }
41084fe7d0bSbostic 
~text_item()41184fe7d0bSbostic text_item::~text_item()
41284fe7d0bSbostic {
41384fe7d0bSbostic   a_delete text;
41484fe7d0bSbostic }
41584fe7d0bSbostic 
object_spec(object_type t)41684fe7d0bSbostic object_spec::object_spec(object_type t) : type(t)
41784fe7d0bSbostic {
41884fe7d0bSbostic   flags = 0;
41984fe7d0bSbostic   tbl = 0;
42084fe7d0bSbostic   segment_list = 0;
42184fe7d0bSbostic   segment_width = segment_height = 0.0;
42284fe7d0bSbostic   segment_is_absolute = 0;
42384fe7d0bSbostic   text = 0;
42484fe7d0bSbostic   with = 0;
42584fe7d0bSbostic   dir = RIGHT_DIRECTION;
42684fe7d0bSbostic }
42784fe7d0bSbostic 
~object_spec()42884fe7d0bSbostic object_spec::~object_spec()
42984fe7d0bSbostic {
43084fe7d0bSbostic   delete tbl;
43184fe7d0bSbostic   while (segment_list != 0) {
43284fe7d0bSbostic     segment *tem = segment_list;
43384fe7d0bSbostic     segment_list = segment_list->next;
43484fe7d0bSbostic     delete tem;
43584fe7d0bSbostic   }
43684fe7d0bSbostic   object *p = oblist.head;
43784fe7d0bSbostic   while (p != 0) {
43884fe7d0bSbostic     object *tem = p;
43984fe7d0bSbostic     p = p->next;
44084fe7d0bSbostic     delete tem;
44184fe7d0bSbostic   }
44284fe7d0bSbostic   while (text != 0) {
44384fe7d0bSbostic     text_item *tem = text;
44484fe7d0bSbostic     text = text->next;
44584fe7d0bSbostic     delete tem;
44684fe7d0bSbostic   }
44784fe7d0bSbostic   delete with;
44884fe7d0bSbostic }
44984fe7d0bSbostic 
45084fe7d0bSbostic class command_object : public object {
45184fe7d0bSbostic   char *s;
45284fe7d0bSbostic   const char *filename;
45384fe7d0bSbostic   int lineno;
45484fe7d0bSbostic public:
45584fe7d0bSbostic   command_object(char *, const char *, int);
45684fe7d0bSbostic   ~command_object();
type()45784fe7d0bSbostic   object_type type() { return OTHER_OBJECT; }
45884fe7d0bSbostic   void print();
45984fe7d0bSbostic };
46084fe7d0bSbostic 
command_object(char * p,const char * fn,int ln)46184fe7d0bSbostic command_object::command_object(char *p, const char *fn, int ln)
46284fe7d0bSbostic : s(p), filename(fn), lineno(ln)
46384fe7d0bSbostic {
46484fe7d0bSbostic }
46584fe7d0bSbostic 
~command_object()46684fe7d0bSbostic command_object::~command_object()
46784fe7d0bSbostic {
46884fe7d0bSbostic   a_delete s;
46984fe7d0bSbostic }
47084fe7d0bSbostic 
print()47184fe7d0bSbostic void command_object::print()
47284fe7d0bSbostic {
47384fe7d0bSbostic   out->command(s, filename, lineno);
47484fe7d0bSbostic }
47584fe7d0bSbostic 
make_command_object(char * s,const char * fn,int ln)47684fe7d0bSbostic object *make_command_object(char *s, const char *fn, int ln)
47784fe7d0bSbostic {
47884fe7d0bSbostic   return new command_object(s, fn, ln);
47984fe7d0bSbostic }
48084fe7d0bSbostic 
48184fe7d0bSbostic class mark_object : public object {
48284fe7d0bSbostic public:
48384fe7d0bSbostic   mark_object();
48484fe7d0bSbostic   object_type type();
48584fe7d0bSbostic };
48684fe7d0bSbostic 
make_mark_object()48784fe7d0bSbostic object *make_mark_object()
48884fe7d0bSbostic {
48984fe7d0bSbostic   return new mark_object();
49084fe7d0bSbostic }
49184fe7d0bSbostic 
mark_object()49284fe7d0bSbostic mark_object::mark_object()
49384fe7d0bSbostic {
49484fe7d0bSbostic }
49584fe7d0bSbostic 
type()49684fe7d0bSbostic object_type mark_object::type()
49784fe7d0bSbostic {
49884fe7d0bSbostic   return MARK_OBJECT;
49984fe7d0bSbostic }
50084fe7d0bSbostic 
object_list()50184fe7d0bSbostic object_list::object_list() : head(0), tail(0)
50284fe7d0bSbostic {
50384fe7d0bSbostic }
50484fe7d0bSbostic 
append(object * obj)50584fe7d0bSbostic void object_list::append(object *obj)
50684fe7d0bSbostic {
50784fe7d0bSbostic   if (tail == 0) {
50884fe7d0bSbostic     obj->next = obj->prev = 0;
50984fe7d0bSbostic     head = tail = obj;
51084fe7d0bSbostic   }
51184fe7d0bSbostic   else {
51284fe7d0bSbostic     obj->prev = tail;
51384fe7d0bSbostic     obj->next = 0;
51484fe7d0bSbostic     tail->next = obj;
51584fe7d0bSbostic     tail = obj;
51684fe7d0bSbostic   }
51784fe7d0bSbostic }
51884fe7d0bSbostic 
wrap_up_block(object_list * ol)51984fe7d0bSbostic void object_list::wrap_up_block(object_list *ol)
52084fe7d0bSbostic {
52184fe7d0bSbostic   for (object *p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
52284fe7d0bSbostic     ;
52384fe7d0bSbostic   assert(p != 0);
52484fe7d0bSbostic   ol->head = p->next;
52584fe7d0bSbostic   if (ol->head) {
52684fe7d0bSbostic     ol->tail = tail;
52784fe7d0bSbostic     ol->head->prev = 0;
52884fe7d0bSbostic   }
52984fe7d0bSbostic   else
53084fe7d0bSbostic     ol->tail = 0;
53184fe7d0bSbostic   tail = p->prev;
53284fe7d0bSbostic   if (tail)
53384fe7d0bSbostic     tail->next = 0;
53484fe7d0bSbostic   else
53584fe7d0bSbostic     head = 0;
53684fe7d0bSbostic   delete p;
53784fe7d0bSbostic }
53884fe7d0bSbostic 
text_piece()53984fe7d0bSbostic text_piece::text_piece()
54084fe7d0bSbostic : text(0), filename(0), lineno(-1)
54184fe7d0bSbostic {
54284fe7d0bSbostic   adj.h = CENTER_ADJUST;
54384fe7d0bSbostic   adj.v = NONE_ADJUST;
54484fe7d0bSbostic }
54584fe7d0bSbostic 
~text_piece()54684fe7d0bSbostic text_piece::~text_piece()
54784fe7d0bSbostic {
54884fe7d0bSbostic   a_delete text;
54984fe7d0bSbostic }
55084fe7d0bSbostic 
55184fe7d0bSbostic class graphic_object : public object {
55284fe7d0bSbostic   int ntext;
55384fe7d0bSbostic   text_piece *text;
55484fe7d0bSbostic   int aligned;
55584fe7d0bSbostic protected:
55684fe7d0bSbostic   line_type lt;
55784fe7d0bSbostic public:
55884fe7d0bSbostic   graphic_object();
55984fe7d0bSbostic   ~graphic_object();
56084fe7d0bSbostic   object_type type() = 0;
56184fe7d0bSbostic   void print_text();
56284fe7d0bSbostic   void add_text(text_item *, int);
56384fe7d0bSbostic   void set_dotted(double);
56484fe7d0bSbostic   void set_dashed(double);
56584fe7d0bSbostic   void set_thickness(double);
56684fe7d0bSbostic   void set_invisible();
56784fe7d0bSbostic   virtual void set_fill(double);
56884fe7d0bSbostic };
56984fe7d0bSbostic 
graphic_object()57084fe7d0bSbostic graphic_object::graphic_object() : ntext(0), text(0), aligned(0)
57184fe7d0bSbostic {
57284fe7d0bSbostic }
57384fe7d0bSbostic 
set_dotted(double wid)57484fe7d0bSbostic void graphic_object::set_dotted(double wid)
57584fe7d0bSbostic {
57684fe7d0bSbostic   lt.type = line_type::dotted;
57784fe7d0bSbostic   lt.dash_width = wid;
57884fe7d0bSbostic }
57984fe7d0bSbostic 
set_dashed(double wid)58084fe7d0bSbostic void graphic_object::set_dashed(double wid)
58184fe7d0bSbostic {
58284fe7d0bSbostic   lt.type = line_type::dashed;
58384fe7d0bSbostic   lt.dash_width = wid;
58484fe7d0bSbostic }
58584fe7d0bSbostic 
set_thickness(double th)58684fe7d0bSbostic void graphic_object::set_thickness(double th)
58784fe7d0bSbostic {
58884fe7d0bSbostic   lt.thickness = th;
58984fe7d0bSbostic }
59084fe7d0bSbostic 
set_fill(double)59184fe7d0bSbostic void graphic_object::set_fill(double)
59284fe7d0bSbostic {
59384fe7d0bSbostic }
59484fe7d0bSbostic 
set_invisible()59584fe7d0bSbostic void graphic_object::set_invisible()
59684fe7d0bSbostic {
59784fe7d0bSbostic   lt.type = line_type::invisible;
59884fe7d0bSbostic }
59984fe7d0bSbostic 
add_text(text_item * t,int a)60084fe7d0bSbostic void graphic_object::add_text(text_item *t, int a)
60184fe7d0bSbostic {
60284fe7d0bSbostic   aligned = a;
60384fe7d0bSbostic   int len = 0;
60484fe7d0bSbostic   for (text_item *p = t; p; p = p->next)
60584fe7d0bSbostic     len++;
60684fe7d0bSbostic   if (len == 0)
60784fe7d0bSbostic     text = 0;
60884fe7d0bSbostic   else {
60984fe7d0bSbostic     text = new text_piece[len];
61084fe7d0bSbostic     for (p = t, len = 0; p; p = p->next, len++) {
61184fe7d0bSbostic       text[len].text = p->text;
61284fe7d0bSbostic       p->text = 0;
61384fe7d0bSbostic       text[len].adj = p->adj;
61484fe7d0bSbostic       text[len].filename = p->filename;
61584fe7d0bSbostic       text[len].lineno = p->lineno;
61684fe7d0bSbostic     }
61784fe7d0bSbostic   }
61884fe7d0bSbostic   ntext = len;
61984fe7d0bSbostic }
62084fe7d0bSbostic 
print_text()62184fe7d0bSbostic void graphic_object::print_text()
62284fe7d0bSbostic {
62384fe7d0bSbostic   double angle = 0.0;
62484fe7d0bSbostic   if (aligned) {
62584fe7d0bSbostic     position d(end() - start());
62684fe7d0bSbostic     if (d.x != 0.0 || d.y != 0.0)
62784fe7d0bSbostic       angle = atan2(d.y, d.x);
62884fe7d0bSbostic   }
62984fe7d0bSbostic   if (text != 0)
63084fe7d0bSbostic     out->text(center(), text, ntext, angle);
63184fe7d0bSbostic }
63284fe7d0bSbostic 
~graphic_object()63384fe7d0bSbostic graphic_object::~graphic_object()
63484fe7d0bSbostic {
63584fe7d0bSbostic   if (text)
63684fe7d0bSbostic     ad_delete(ntext) text;
63784fe7d0bSbostic }
63884fe7d0bSbostic 
63984fe7d0bSbostic class rectangle_object : public graphic_object {
64084fe7d0bSbostic protected:
64184fe7d0bSbostic   position cent;
64284fe7d0bSbostic   position dim;
64384fe7d0bSbostic public:
64484fe7d0bSbostic   rectangle_object(const position &);
width()64584fe7d0bSbostic   double width() { return dim.x; }
height()64684fe7d0bSbostic   double height() { return dim.y; }
origin()64784fe7d0bSbostic   position origin() { return cent; }
center()64884fe7d0bSbostic   position center() { return cent; }
north()64984fe7d0bSbostic   position north() { return position(cent.x, cent.y + dim.y/2.0); }
south()65084fe7d0bSbostic   position south() { return position(cent.x, cent.y - dim.y/2.0); }
east()65184fe7d0bSbostic   position east() { return position(cent.x + dim.x/2.0, cent.y); }
west()65284fe7d0bSbostic   position west() { return position(cent.x - dim.x/2.0, cent.y); }
north_east()65384fe7d0bSbostic   position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
north_west()65484fe7d0bSbostic   position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
south_east()65584fe7d0bSbostic   position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
south_west()65684fe7d0bSbostic   position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
65784fe7d0bSbostic   object_type type() = 0;
65884fe7d0bSbostic   void update_bounding_box(bounding_box *);
65984fe7d0bSbostic   void move_by(const position &);
66084fe7d0bSbostic };
66184fe7d0bSbostic 
rectangle_object(const position & d)66284fe7d0bSbostic rectangle_object::rectangle_object(const position &d)
66384fe7d0bSbostic : dim(d)
66484fe7d0bSbostic {
66584fe7d0bSbostic }
66684fe7d0bSbostic 
update_bounding_box(bounding_box * p)66784fe7d0bSbostic void rectangle_object::update_bounding_box(bounding_box *p)
66884fe7d0bSbostic {
66984fe7d0bSbostic   p->encompass(cent - dim/2.0);
67084fe7d0bSbostic   p->encompass(cent + dim/2.0);
67184fe7d0bSbostic }
67284fe7d0bSbostic 
move_by(const position & a)67384fe7d0bSbostic void rectangle_object::move_by(const position &a)
67484fe7d0bSbostic {
67584fe7d0bSbostic   cent += a;
67684fe7d0bSbostic }
67784fe7d0bSbostic 
67884fe7d0bSbostic class closed_object : public rectangle_object {
67984fe7d0bSbostic public:
68084fe7d0bSbostic   closed_object(const position &);
68184fe7d0bSbostic   object_type type() = 0;
68284fe7d0bSbostic   void set_fill(double);
68384fe7d0bSbostic protected:
68484fe7d0bSbostic   double fill;			// < 0 if not filled
68584fe7d0bSbostic };
68684fe7d0bSbostic 
closed_object(const position & pos)68784fe7d0bSbostic closed_object::closed_object(const position &pos)
68884fe7d0bSbostic : rectangle_object(pos), fill(-1.0)
68984fe7d0bSbostic {
69084fe7d0bSbostic }
69184fe7d0bSbostic 
set_fill(double f)69284fe7d0bSbostic void closed_object::set_fill(double f)
69384fe7d0bSbostic {
69484fe7d0bSbostic   assert(f >= 0.0);
69584fe7d0bSbostic   fill = f;
69684fe7d0bSbostic }
69784fe7d0bSbostic 
69884fe7d0bSbostic 
69984fe7d0bSbostic class box_object : public closed_object {
70084fe7d0bSbostic   double xrad;
70184fe7d0bSbostic   double yrad;
70284fe7d0bSbostic public:
70384fe7d0bSbostic   box_object(const position &, double);
type()70484fe7d0bSbostic   object_type type() { return BOX_OBJECT; }
70584fe7d0bSbostic   void print();
70684fe7d0bSbostic   position north_east();
70784fe7d0bSbostic   position north_west();
70884fe7d0bSbostic   position south_east();
70984fe7d0bSbostic   position south_west();
71084fe7d0bSbostic };
71184fe7d0bSbostic 
box_object(const position & pos,double r)71284fe7d0bSbostic box_object::box_object(const position &pos, double r)
71384fe7d0bSbostic : closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
71484fe7d0bSbostic {
71584fe7d0bSbostic }
71684fe7d0bSbostic 
71784fe7d0bSbostic const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
71884fe7d0bSbostic 
north_east()71984fe7d0bSbostic position box_object::north_east()
72084fe7d0bSbostic {
72184fe7d0bSbostic   return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
72284fe7d0bSbostic 		  cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
72384fe7d0bSbostic }
72484fe7d0bSbostic 
north_west()72584fe7d0bSbostic position box_object::north_west()
72684fe7d0bSbostic {
72784fe7d0bSbostic   return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
72884fe7d0bSbostic 		  cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
72984fe7d0bSbostic }
73084fe7d0bSbostic 
south_east()73184fe7d0bSbostic position box_object::south_east()
73284fe7d0bSbostic {
73384fe7d0bSbostic   return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
73484fe7d0bSbostic 		  cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
73584fe7d0bSbostic }
73684fe7d0bSbostic 
south_west()73784fe7d0bSbostic position box_object::south_west()
73884fe7d0bSbostic {
73984fe7d0bSbostic   return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
74084fe7d0bSbostic 		  cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
74184fe7d0bSbostic }
74284fe7d0bSbostic 
print()74384fe7d0bSbostic void box_object::print()
74484fe7d0bSbostic {
74584fe7d0bSbostic   if (lt.type == line_type::invisible && fill < 0.0)
74684fe7d0bSbostic     return;
74784fe7d0bSbostic   if (xrad == 0.0) {
74884fe7d0bSbostic     distance dim2 = dim/2.0;
74984fe7d0bSbostic     position vec[4];
75084fe7d0bSbostic     vec[0] = cent + position(dim2.x, -dim2.y);
75184fe7d0bSbostic     vec[1] = cent + position(dim2.x, dim2.y);
75284fe7d0bSbostic     vec[2] = cent + position(-dim2.x, dim2.y);
75384fe7d0bSbostic     vec[3] = cent + position(-dim2.x, -dim2.y);
75484fe7d0bSbostic     out->polygon(vec, 4, lt, fill);
75584fe7d0bSbostic   }
75684fe7d0bSbostic   else {
75784fe7d0bSbostic     distance abs_dim(fabs(dim.x), fabs(dim.y));
75884fe7d0bSbostic     out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
75984fe7d0bSbostic   }
76084fe7d0bSbostic }
76184fe7d0bSbostic 
make_box(position * curpos,direction * dirp)76284fe7d0bSbostic graphic_object *object_spec::make_box(position *curpos, direction *dirp)
76384fe7d0bSbostic {
76484fe7d0bSbostic   static double last_box_height;
76584fe7d0bSbostic   static double last_box_width;
76684fe7d0bSbostic   static double last_box_radius;
76784fe7d0bSbostic   static int have_last_box = 0;
76884fe7d0bSbostic   if (!(flags & HAS_HEIGHT)) {
76984fe7d0bSbostic     if ((flags & IS_SAME) && have_last_box)
77084fe7d0bSbostic       height = last_box_height;
77184fe7d0bSbostic     else
77284fe7d0bSbostic       lookup_variable("boxht", &height);
77384fe7d0bSbostic   }
77484fe7d0bSbostic   if (!(flags & HAS_WIDTH)) {
77584fe7d0bSbostic     if ((flags & IS_SAME) && have_last_box)
77684fe7d0bSbostic       width = last_box_width;
77784fe7d0bSbostic     else
77884fe7d0bSbostic       lookup_variable("boxwid", &width);
77984fe7d0bSbostic   }
78084fe7d0bSbostic   if (!(flags & HAS_RADIUS)) {
78184fe7d0bSbostic     if ((flags & IS_SAME) && have_last_box)
78284fe7d0bSbostic       radius = last_box_radius;
78384fe7d0bSbostic     else
78484fe7d0bSbostic       lookup_variable("boxrad", &radius);
78584fe7d0bSbostic   }
78684fe7d0bSbostic   last_box_width = width;
78784fe7d0bSbostic   last_box_height = height;
78884fe7d0bSbostic   last_box_radius = radius;
78984fe7d0bSbostic   have_last_box = 1;
79084fe7d0bSbostic   radius = fabs(radius);
79184fe7d0bSbostic   if (radius*2.0 > fabs(width))
79284fe7d0bSbostic     radius = fabs(width/2.0);
79384fe7d0bSbostic   if (radius*2.0 > fabs(height))
79484fe7d0bSbostic     radius = fabs(height/2.0);
79584fe7d0bSbostic   box_object *p = new box_object(position(width, height), radius);
79684fe7d0bSbostic   if (!position_rectangle(p, curpos, dirp)) {
79784fe7d0bSbostic     delete p;
79884fe7d0bSbostic     p = 0;
79984fe7d0bSbostic   }
80084fe7d0bSbostic   return p;
80184fe7d0bSbostic }
80284fe7d0bSbostic 
80384fe7d0bSbostic // return non-zero for success
80484fe7d0bSbostic 
position_rectangle(rectangle_object * p,position * curpos,direction * dirp)80584fe7d0bSbostic int object_spec::position_rectangle(rectangle_object *p,
80684fe7d0bSbostic 				    position *curpos, direction *dirp)
80784fe7d0bSbostic {
80884fe7d0bSbostic   position pos;
80984fe7d0bSbostic   dir = *dirp;			// ignore any direction in attribute list
81084fe7d0bSbostic   position motion;
81184fe7d0bSbostic   switch (dir) {
81284fe7d0bSbostic   case UP_DIRECTION:
81384fe7d0bSbostic     motion.y = p->height()/2.0;
81484fe7d0bSbostic     break;
81584fe7d0bSbostic   case DOWN_DIRECTION:
81684fe7d0bSbostic     motion.y = -p->height()/2.0;
81784fe7d0bSbostic     break;
81884fe7d0bSbostic   case LEFT_DIRECTION:
81984fe7d0bSbostic     motion.x = -p->width()/2.0;
82084fe7d0bSbostic     break;
82184fe7d0bSbostic   case RIGHT_DIRECTION:
82284fe7d0bSbostic     motion.x = p->width()/2.0;
82384fe7d0bSbostic     break;
82484fe7d0bSbostic   default:
82584fe7d0bSbostic     assert(0);
82684fe7d0bSbostic   }
82784fe7d0bSbostic   if (flags & HAS_AT) {
82884fe7d0bSbostic     pos = at;
82984fe7d0bSbostic     if (flags & HAS_WITH) {
83084fe7d0bSbostic       place offset;
83184fe7d0bSbostic       place here;
83284fe7d0bSbostic       here.obj = p;
83384fe7d0bSbostic       if (!with->follow(here, &offset))
83484fe7d0bSbostic 	return 0;
83584fe7d0bSbostic       pos -= offset;
83684fe7d0bSbostic     }
83784fe7d0bSbostic   }
83884fe7d0bSbostic   else {
83984fe7d0bSbostic     pos = *curpos;
84084fe7d0bSbostic     pos += motion;
84184fe7d0bSbostic   }
84284fe7d0bSbostic   p->move_by(pos);
84384fe7d0bSbostic   pos += motion;
84484fe7d0bSbostic   *curpos = pos;
84584fe7d0bSbostic   return 1;
84684fe7d0bSbostic }
84784fe7d0bSbostic 
84884fe7d0bSbostic class block_object : public rectangle_object {
84984fe7d0bSbostic   object_list oblist;
85084fe7d0bSbostic   PTABLE(place) *tbl;
85184fe7d0bSbostic public:
85284fe7d0bSbostic   block_object(const position &, const object_list &ol, PTABLE(place) *t);
85384fe7d0bSbostic   ~block_object();
85484fe7d0bSbostic   place *find_label(const char *);
85584fe7d0bSbostic   object_type type();
85684fe7d0bSbostic   void move_by(const position &);
85784fe7d0bSbostic   void print();
85884fe7d0bSbostic };
85984fe7d0bSbostic 
block_object(const position & d,const object_list & ol,PTABLE (place)* t)86084fe7d0bSbostic block_object::block_object(const position &d, const object_list &ol,
86184fe7d0bSbostic 			   PTABLE(place) *t)
86284fe7d0bSbostic : oblist(ol), tbl(t), rectangle_object(d)
86384fe7d0bSbostic {
86484fe7d0bSbostic }
86584fe7d0bSbostic 
~block_object()86684fe7d0bSbostic block_object::~block_object()
86784fe7d0bSbostic {
86884fe7d0bSbostic   delete tbl;
86984fe7d0bSbostic   object *p = oblist.head;
87084fe7d0bSbostic   while (p != 0) {
87184fe7d0bSbostic     object *tem = p;
87284fe7d0bSbostic     p = p->next;
87384fe7d0bSbostic     delete tem;
87484fe7d0bSbostic   }
87584fe7d0bSbostic }
87684fe7d0bSbostic 
print()87784fe7d0bSbostic void block_object::print()
87884fe7d0bSbostic {
87984fe7d0bSbostic   out->begin_block(south_west(), north_east());
88084fe7d0bSbostic   print_object_list(oblist.head);
88184fe7d0bSbostic   out->end_block();
88284fe7d0bSbostic }
88384fe7d0bSbostic 
adjust_objectless_places(PTABLE (place)* tbl,const position & a)88484fe7d0bSbostic static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
88584fe7d0bSbostic {
88684fe7d0bSbostic   // Adjust all the labels that aren't attached to objects.
88784fe7d0bSbostic   PTABLE_ITERATOR(place) iter(tbl);
88884fe7d0bSbostic   const char *key;
88984fe7d0bSbostic   place *pl;
89084fe7d0bSbostic   while (iter.next(&key, &pl))
89184fe7d0bSbostic     if (key && csupper(key[0]) && pl->obj == 0) {
89284fe7d0bSbostic       pl->x += a.x;
89384fe7d0bSbostic       pl->y += a.y;
89484fe7d0bSbostic     }
89584fe7d0bSbostic }
89684fe7d0bSbostic 
move_by(const position & a)89784fe7d0bSbostic void block_object::move_by(const position &a)
89884fe7d0bSbostic {
89984fe7d0bSbostic   cent += a;
90084fe7d0bSbostic   for (object *p = oblist.head; p; p = p->next)
90184fe7d0bSbostic     p->move_by(a);
90284fe7d0bSbostic   adjust_objectless_places(tbl, a);
90384fe7d0bSbostic }
90484fe7d0bSbostic 
90584fe7d0bSbostic 
find_label(const char * name)90684fe7d0bSbostic place *block_object::find_label(const char *name)
90784fe7d0bSbostic {
90884fe7d0bSbostic   return tbl->lookup(name);
90984fe7d0bSbostic }
91084fe7d0bSbostic 
type()91184fe7d0bSbostic object_type block_object::type()
91284fe7d0bSbostic {
91384fe7d0bSbostic   return BLOCK_OBJECT;
91484fe7d0bSbostic }
91584fe7d0bSbostic 
make_block(position * curpos,direction * dirp)91684fe7d0bSbostic graphic_object *object_spec::make_block(position *curpos, direction *dirp)
91784fe7d0bSbostic {
91884fe7d0bSbostic   bounding_box bb;
91984fe7d0bSbostic   for (object *p = oblist.head; p; p = p->next)
92084fe7d0bSbostic     p->update_bounding_box(&bb);
92184fe7d0bSbostic   position dim;
92284fe7d0bSbostic   if (!bb.blank) {
92384fe7d0bSbostic     position m = -(bb.ll + bb.ur)/2.0;
92484fe7d0bSbostic     for (object *p = oblist.head; p; p = p->next)
92584fe7d0bSbostic       p->move_by(m);
92684fe7d0bSbostic     adjust_objectless_places(tbl, m);
92784fe7d0bSbostic     dim = bb.ur - bb.ll;
92884fe7d0bSbostic   }
92984fe7d0bSbostic   if (flags & HAS_WIDTH)
93084fe7d0bSbostic     dim.x = width;
93184fe7d0bSbostic   if (flags & HAS_HEIGHT)
93284fe7d0bSbostic     dim.y = height;
93384fe7d0bSbostic   block_object *block = new block_object(dim, oblist, tbl);
93484fe7d0bSbostic   if (!position_rectangle(block, curpos, dirp)) {
93584fe7d0bSbostic     delete block;
93684fe7d0bSbostic     block = 0;
93784fe7d0bSbostic   }
93884fe7d0bSbostic   tbl = 0;
93984fe7d0bSbostic   oblist.head = oblist.tail = 0;
94084fe7d0bSbostic   return block;
94184fe7d0bSbostic }
94284fe7d0bSbostic 
94384fe7d0bSbostic class text_object : public rectangle_object {
94484fe7d0bSbostic public:
94584fe7d0bSbostic   text_object(const position &);
type()94684fe7d0bSbostic   object_type type() { return TEXT_OBJECT; }
94784fe7d0bSbostic };
94884fe7d0bSbostic 
text_object(const position & d)94984fe7d0bSbostic text_object::text_object(const position &d)
95084fe7d0bSbostic : rectangle_object(d)
95184fe7d0bSbostic {
95284fe7d0bSbostic }
95384fe7d0bSbostic 
make_text(position * curpos,direction * dirp)95484fe7d0bSbostic graphic_object *object_spec::make_text(position *curpos, direction *dirp)
95584fe7d0bSbostic {
95684fe7d0bSbostic   if (!(flags & HAS_HEIGHT)) {
95784fe7d0bSbostic     lookup_variable("textht", &height);
95884fe7d0bSbostic     int nitems = 0;
95984fe7d0bSbostic     for (text_item *t = text; t; t = t->next)
96084fe7d0bSbostic       nitems++;
96184fe7d0bSbostic     height *= nitems;
96284fe7d0bSbostic   }
96384fe7d0bSbostic   if (!(flags & HAS_WIDTH))
96484fe7d0bSbostic     lookup_variable("textwid", &width);
96584fe7d0bSbostic   text_object *p = new text_object(position(width, height));
96684fe7d0bSbostic   if (!position_rectangle(p, curpos, dirp)) {
96784fe7d0bSbostic     delete p;
96884fe7d0bSbostic     p = 0;
96984fe7d0bSbostic   }
97084fe7d0bSbostic   return p;
97184fe7d0bSbostic }
97284fe7d0bSbostic 
97384fe7d0bSbostic 
97484fe7d0bSbostic class ellipse_object : public closed_object {
97584fe7d0bSbostic public:
97684fe7d0bSbostic   ellipse_object(const position &);
north_east()97784fe7d0bSbostic   position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
97884fe7d0bSbostic 					  cent.y + dim.y/(M_SQRT2*2.0)); }
north_west()97984fe7d0bSbostic   position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
98084fe7d0bSbostic 					  cent.y + dim.y/(M_SQRT2*2.0)); }
south_east()98184fe7d0bSbostic   position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
98284fe7d0bSbostic 					  cent.y - dim.y/(M_SQRT2*2.0)); }
south_west()98384fe7d0bSbostic   position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
98484fe7d0bSbostic 					  cent.y - dim.y/(M_SQRT2*2.0)); }
radius()98584fe7d0bSbostic   double radius() { return dim.x/2.0; }
type()98684fe7d0bSbostic   object_type type() { return ELLIPSE_OBJECT; }
98784fe7d0bSbostic   void print();
98884fe7d0bSbostic };
98984fe7d0bSbostic 
ellipse_object(const position & d)99084fe7d0bSbostic ellipse_object::ellipse_object(const position &d)
99184fe7d0bSbostic : closed_object(d)
99284fe7d0bSbostic {
99384fe7d0bSbostic }
99484fe7d0bSbostic 
print()99584fe7d0bSbostic void ellipse_object::print()
99684fe7d0bSbostic {
99784fe7d0bSbostic   if (lt.type == line_type::invisible && fill < 0.0)
99884fe7d0bSbostic     return;
99984fe7d0bSbostic   out->ellipse(cent, dim, lt, fill);
100084fe7d0bSbostic }
100184fe7d0bSbostic 
make_ellipse(position * curpos,direction * dirp)100284fe7d0bSbostic graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
100384fe7d0bSbostic {
100484fe7d0bSbostic   static double last_ellipse_height;
100584fe7d0bSbostic   static double last_ellipse_width;
100684fe7d0bSbostic   static int have_last_ellipse = 0;
100784fe7d0bSbostic   if (!(flags & HAS_HEIGHT)) {
100884fe7d0bSbostic     if ((flags & IS_SAME) && have_last_ellipse)
100984fe7d0bSbostic       height = last_ellipse_height;
101084fe7d0bSbostic     else
101184fe7d0bSbostic       lookup_variable("ellipseht", &height);
101284fe7d0bSbostic   }
101384fe7d0bSbostic   if (!(flags & HAS_WIDTH)) {
101484fe7d0bSbostic     if ((flags & IS_SAME) && have_last_ellipse)
101584fe7d0bSbostic       width = last_ellipse_width;
101684fe7d0bSbostic     else
101784fe7d0bSbostic       lookup_variable("ellipsewid", &width);
101884fe7d0bSbostic   }
101984fe7d0bSbostic   last_ellipse_width = width;
102084fe7d0bSbostic   last_ellipse_height = height;
102184fe7d0bSbostic   have_last_ellipse = 1;
102284fe7d0bSbostic   ellipse_object *p = new ellipse_object(position(width, height));
102384fe7d0bSbostic   if (!position_rectangle(p, curpos, dirp)) {
102484fe7d0bSbostic     delete p;
102584fe7d0bSbostic     return 0;
102684fe7d0bSbostic   }
102784fe7d0bSbostic   return p;
102884fe7d0bSbostic }
102984fe7d0bSbostic 
103084fe7d0bSbostic class circle_object : public ellipse_object {
103184fe7d0bSbostic public:
103284fe7d0bSbostic   circle_object(double);
type()103384fe7d0bSbostic   object_type type() { return CIRCLE_OBJECT; }
103484fe7d0bSbostic   void print();
103584fe7d0bSbostic };
103684fe7d0bSbostic 
1037*faaca10aSbostic /*
1038*faaca10aSbostic  * XXX call posref to gain reference to avoid g++ core dump.
1039*faaca10aSbostic  * From: Chris Torek <torek@BSDI.COM>
1040*faaca10aSbostic  */
circle_object(double diam)104184fe7d0bSbostic circle_object::circle_object(double diam)
1042*faaca10aSbostic : ellipse_object(posref(position(diam, diam)))
104384fe7d0bSbostic {
104484fe7d0bSbostic }
104584fe7d0bSbostic 
print()104684fe7d0bSbostic void circle_object::print()
104784fe7d0bSbostic {
104884fe7d0bSbostic   if (lt.type == line_type::invisible && fill < 0.0)
104984fe7d0bSbostic     return;
105084fe7d0bSbostic   out->circle(cent, dim.x/2.0, lt, fill);
105184fe7d0bSbostic }
105284fe7d0bSbostic 
make_circle(position * curpos,direction * dirp)105384fe7d0bSbostic graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
105484fe7d0bSbostic {
105584fe7d0bSbostic   static double last_circle_radius;
105684fe7d0bSbostic   static int have_last_circle = 0;
105784fe7d0bSbostic   if (!(flags & HAS_RADIUS)) {
105884fe7d0bSbostic     if ((flags & IS_SAME) && have_last_circle)
105984fe7d0bSbostic       radius = last_circle_radius;
106084fe7d0bSbostic     else
106184fe7d0bSbostic       lookup_variable("circlerad", &radius);
106284fe7d0bSbostic   }
106384fe7d0bSbostic   last_circle_radius = radius;
106484fe7d0bSbostic   have_last_circle = 1;
106584fe7d0bSbostic   circle_object *p = new circle_object(radius*2.0);
106684fe7d0bSbostic   if (!position_rectangle(p, curpos, dirp)) {
106784fe7d0bSbostic     delete p;
106884fe7d0bSbostic     return 0;
106984fe7d0bSbostic   }
107084fe7d0bSbostic   return p;
107184fe7d0bSbostic }
107284fe7d0bSbostic 
107384fe7d0bSbostic class move_object : public graphic_object {
107484fe7d0bSbostic   position strt;
107584fe7d0bSbostic   position en;
107684fe7d0bSbostic public:
107784fe7d0bSbostic   move_object(const position &s, const position &e);
origin()107884fe7d0bSbostic   position origin() { return en; }
type()107984fe7d0bSbostic   object_type type() { return MOVE_OBJECT; }
108084fe7d0bSbostic   void update_bounding_box(bounding_box *);
108184fe7d0bSbostic   void move_by(const position &);
108284fe7d0bSbostic };
108384fe7d0bSbostic 
move_object(const position & s,const position & e)108484fe7d0bSbostic move_object::move_object(const position &s, const position &e)
108584fe7d0bSbostic : strt(s), en(e)
108684fe7d0bSbostic {
108784fe7d0bSbostic }
108884fe7d0bSbostic 
update_bounding_box(bounding_box * p)108984fe7d0bSbostic void move_object::update_bounding_box(bounding_box *p)
109084fe7d0bSbostic {
109184fe7d0bSbostic   p->encompass(strt);
109284fe7d0bSbostic   p->encompass(en);
109384fe7d0bSbostic }
109484fe7d0bSbostic 
move_by(const position & a)109584fe7d0bSbostic void move_object::move_by(const position &a)
109684fe7d0bSbostic {
109784fe7d0bSbostic   strt += a;
109884fe7d0bSbostic   en += a;
109984fe7d0bSbostic }
110084fe7d0bSbostic 
make_move(position * curpos,direction * dirp)110184fe7d0bSbostic graphic_object *object_spec::make_move(position *curpos, direction *dirp)
110284fe7d0bSbostic {
110384fe7d0bSbostic   static position last_move;
110484fe7d0bSbostic   static int have_last_move = 0;
110584fe7d0bSbostic   *dirp = dir;
110684fe7d0bSbostic   // No need to look at at since `at' attribute sets `from' attribute.
110784fe7d0bSbostic   position startpos = (flags & HAS_FROM) ? from : *curpos;
110884fe7d0bSbostic   if (!(flags & HAS_SEGMENT)) {
110984fe7d0bSbostic     if ((flags && IS_SAME) && have_last_move)
111084fe7d0bSbostic       segment_pos = last_move;
111184fe7d0bSbostic     else {
111284fe7d0bSbostic       switch (dir) {
111384fe7d0bSbostic       case UP_DIRECTION:
111484fe7d0bSbostic 	segment_pos.y = segment_height;
111584fe7d0bSbostic 	break;
111684fe7d0bSbostic       case DOWN_DIRECTION:
111784fe7d0bSbostic 	segment_pos.y = -segment_height;
111884fe7d0bSbostic 	break;
111984fe7d0bSbostic       case LEFT_DIRECTION:
112084fe7d0bSbostic 	segment_pos.x = -segment_width;
112184fe7d0bSbostic 	break;
112284fe7d0bSbostic       case RIGHT_DIRECTION:
112384fe7d0bSbostic 	segment_pos.x = segment_width;
112484fe7d0bSbostic 	break;
112584fe7d0bSbostic       default:
112684fe7d0bSbostic 	assert(0);
112784fe7d0bSbostic       }
112884fe7d0bSbostic     }
112984fe7d0bSbostic   }
113084fe7d0bSbostic   segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
113184fe7d0bSbostic   // Reverse the segment_list so that it's in forward order.
113284fe7d0bSbostic   segment *old = segment_list;
113384fe7d0bSbostic   segment_list = 0;
113484fe7d0bSbostic   while (old != 0) {
113584fe7d0bSbostic     segment *tem = old->next;
113684fe7d0bSbostic     old->next = segment_list;
113784fe7d0bSbostic     segment_list = old;
113884fe7d0bSbostic     old = tem;
113984fe7d0bSbostic   }
114084fe7d0bSbostic   // Compute the end position.
114184fe7d0bSbostic   position endpos = startpos;
114284fe7d0bSbostic   for (segment *s = segment_list; s; s = s->next)
114384fe7d0bSbostic     if (s->is_absolute)
114484fe7d0bSbostic       endpos = s->pos;
114584fe7d0bSbostic     else
114684fe7d0bSbostic       endpos += s->pos;
114784fe7d0bSbostic   have_last_move = 1;
114884fe7d0bSbostic   last_move = endpos - startpos;
114984fe7d0bSbostic   move_object *p = new move_object(startpos, endpos);
115084fe7d0bSbostic   *curpos = endpos;
115184fe7d0bSbostic   return p;
115284fe7d0bSbostic }
115384fe7d0bSbostic 
115484fe7d0bSbostic class linear_object : public graphic_object {
115584fe7d0bSbostic protected:
115684fe7d0bSbostic   char arrow_at_start;
115784fe7d0bSbostic   char arrow_at_end;
115884fe7d0bSbostic   arrow_head_type aht;
115984fe7d0bSbostic   position strt;
116084fe7d0bSbostic   position en;
116184fe7d0bSbostic public:
116284fe7d0bSbostic   linear_object(const position &s, const position &e);
start()116384fe7d0bSbostic   position start() { return strt; }
end()116484fe7d0bSbostic   position end() { return en; }
116584fe7d0bSbostic   void move_by(const position &);
116684fe7d0bSbostic   void update_bounding_box(bounding_box *) = 0;
116784fe7d0bSbostic   object_type type() = 0;
116884fe7d0bSbostic   void add_arrows(int at_start, int at_end, const arrow_head_type &);
116984fe7d0bSbostic };
117084fe7d0bSbostic 
117184fe7d0bSbostic class line_object : public linear_object {
117284fe7d0bSbostic protected:
117384fe7d0bSbostic   position *v;
117484fe7d0bSbostic   int n;
117584fe7d0bSbostic public:
117684fe7d0bSbostic   line_object(const position &s, const position &e, position *, int);
117784fe7d0bSbostic   ~line_object();
origin()117884fe7d0bSbostic   position origin() { return strt; }
center()117984fe7d0bSbostic   position center() { return (strt + en)/2.0; }
north()118084fe7d0bSbostic   position north() { return (en.y - strt.y) > 0 ? en : strt; }
south()118184fe7d0bSbostic   position south() { return (en.y - strt.y) < 0 ? en : strt; }
east()118284fe7d0bSbostic   position east() { return (en.x - strt.x) > 0 ? en : strt; }
west()118384fe7d0bSbostic   position west() { return (en.x - strt.x) < 0 ? en : strt; }
type()118484fe7d0bSbostic   object_type type() { return LINE_OBJECT; }
118584fe7d0bSbostic   void update_bounding_box(bounding_box *);
118684fe7d0bSbostic   void print();
118784fe7d0bSbostic   void move_by(const position &);
118884fe7d0bSbostic };
118984fe7d0bSbostic 
119084fe7d0bSbostic class arrow_object : public line_object {
119184fe7d0bSbostic public:
119284fe7d0bSbostic   arrow_object(const position &, const position &, position *, int);
type()119384fe7d0bSbostic   object_type type() { return ARROW_OBJECT; }
119484fe7d0bSbostic };
119584fe7d0bSbostic 
119684fe7d0bSbostic class spline_object : public line_object {
119784fe7d0bSbostic public:
119884fe7d0bSbostic   spline_object(const position &, const position &, position *, int);
type()119984fe7d0bSbostic   object_type type() { return SPLINE_OBJECT; }
120084fe7d0bSbostic   void print();
120184fe7d0bSbostic   void update_bounding_box(bounding_box *);
120284fe7d0bSbostic };
120384fe7d0bSbostic 
linear_object(const position & s,const position & e)120484fe7d0bSbostic linear_object::linear_object(const position &s, const position &e)
120584fe7d0bSbostic : strt(s), en(e), arrow_at_start(0), arrow_at_end(0)
120684fe7d0bSbostic {
120784fe7d0bSbostic }
120884fe7d0bSbostic 
move_by(const position & a)120984fe7d0bSbostic void linear_object::move_by(const position &a)
121084fe7d0bSbostic {
121184fe7d0bSbostic   strt += a;
121284fe7d0bSbostic   en += a;
121384fe7d0bSbostic }
121484fe7d0bSbostic 
add_arrows(int at_start,int at_end,const arrow_head_type & a)121584fe7d0bSbostic void linear_object::add_arrows(int at_start, int at_end,
121684fe7d0bSbostic 			       const arrow_head_type &a)
121784fe7d0bSbostic {
121884fe7d0bSbostic   arrow_at_start = at_start;
121984fe7d0bSbostic   arrow_at_end = at_end;
122084fe7d0bSbostic   aht = a;
122184fe7d0bSbostic }
122284fe7d0bSbostic 
line_object(const position & s,const position & e,position * p,int i)122384fe7d0bSbostic line_object::line_object(const position &s, const position &e,
122484fe7d0bSbostic 			 position *p, int i)
122584fe7d0bSbostic : v(p), n(i), linear_object(s, e)
122684fe7d0bSbostic {
122784fe7d0bSbostic }
122884fe7d0bSbostic 
print()122984fe7d0bSbostic void line_object::print()
123084fe7d0bSbostic {
123184fe7d0bSbostic   if (lt.type == line_type::invisible)
123284fe7d0bSbostic     return;
123384fe7d0bSbostic   out->line(strt, v, n, lt);
123484fe7d0bSbostic   if (arrow_at_start)
123584fe7d0bSbostic     draw_arrow(strt, strt-v[0], aht, lt);
123684fe7d0bSbostic   if (arrow_at_end)
123784fe7d0bSbostic     draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
123884fe7d0bSbostic }
123984fe7d0bSbostic 
update_bounding_box(bounding_box * p)124084fe7d0bSbostic void line_object::update_bounding_box(bounding_box *p)
124184fe7d0bSbostic {
124284fe7d0bSbostic   p->encompass(strt);
124384fe7d0bSbostic   for (int i = 0; i < n; i++)
124484fe7d0bSbostic     p->encompass(v[i]);
124584fe7d0bSbostic }
124684fe7d0bSbostic 
move_by(const position & pos)124784fe7d0bSbostic void line_object::move_by(const position &pos)
124884fe7d0bSbostic {
124984fe7d0bSbostic   linear_object::move_by(pos);
125084fe7d0bSbostic   for (int i = 0; i < n; i++)
125184fe7d0bSbostic     v[i] += pos;
125284fe7d0bSbostic }
125384fe7d0bSbostic 
update_bounding_box(bounding_box * p)125484fe7d0bSbostic void spline_object::update_bounding_box(bounding_box *p)
125584fe7d0bSbostic {
125684fe7d0bSbostic   p->encompass(strt);
125784fe7d0bSbostic   p->encompass(en);
125884fe7d0bSbostic   /*
125984fe7d0bSbostic 
126084fe7d0bSbostic   If
126184fe7d0bSbostic 
126284fe7d0bSbostic   p1 = q1/2 + q2/2
126384fe7d0bSbostic   p2 = q1/6 + q2*5/6
126484fe7d0bSbostic   p3 = q2*5/6 + q3/6
126584fe7d0bSbostic   p4 = q2/2 + q3/2
126684fe7d0bSbostic   [ the points for the Bezier cubic ]
126784fe7d0bSbostic 
126884fe7d0bSbostic   and
126984fe7d0bSbostic 
127084fe7d0bSbostic   t = .5
127184fe7d0bSbostic 
127284fe7d0bSbostic   then
127384fe7d0bSbostic 
127484fe7d0bSbostic   (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
127584fe7d0bSbostic   [ the equation for the Bezier cubic ]
127684fe7d0bSbostic 
127784fe7d0bSbostic   = .125*q1 + .75*q2 + .125*q3
127884fe7d0bSbostic 
127984fe7d0bSbostic   */
128084fe7d0bSbostic   for (int i = 1; i < n; i++)
128184fe7d0bSbostic     p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
128284fe7d0bSbostic }
128384fe7d0bSbostic 
arrow_object(const position & s,const position & e,position * p,int i)128484fe7d0bSbostic arrow_object::arrow_object(const position &s, const position &e,
128584fe7d0bSbostic 			   position *p, int i)
128684fe7d0bSbostic : line_object(s, e, p, i)
128784fe7d0bSbostic {
128884fe7d0bSbostic }
128984fe7d0bSbostic 
spline_object(const position & s,const position & e,position * p,int i)129084fe7d0bSbostic spline_object::spline_object(const position &s, const position &e,
129184fe7d0bSbostic 			     position *p, int i)
129284fe7d0bSbostic : line_object(s, e, p, i)
129384fe7d0bSbostic {
129484fe7d0bSbostic }
129584fe7d0bSbostic 
print()129684fe7d0bSbostic void spline_object::print()
129784fe7d0bSbostic {
129884fe7d0bSbostic   if (lt.type == line_type::invisible)
129984fe7d0bSbostic     return;
130084fe7d0bSbostic   out->spline(strt, v, n, lt);
130184fe7d0bSbostic   if (arrow_at_start)
130284fe7d0bSbostic     draw_arrow(strt, strt-v[0], aht, lt);
130384fe7d0bSbostic   if (arrow_at_end)
130484fe7d0bSbostic     draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
130584fe7d0bSbostic }
130684fe7d0bSbostic 
~line_object()130784fe7d0bSbostic line_object::~line_object()
130884fe7d0bSbostic {
130984fe7d0bSbostic   a_delete v;
131084fe7d0bSbostic }
131184fe7d0bSbostic 
make_line(position * curpos,direction * dirp)131284fe7d0bSbostic linear_object *object_spec::make_line(position *curpos, direction *dirp)
131384fe7d0bSbostic {
131484fe7d0bSbostic   static position last_line;
131584fe7d0bSbostic   static int have_last_line = 0;
131684fe7d0bSbostic   *dirp = dir;
131784fe7d0bSbostic   // No need to look at at since `at' attribute sets `from' attribute.
131884fe7d0bSbostic   position startpos = (flags & HAS_FROM) ? from : *curpos;
131984fe7d0bSbostic   if (!(flags & HAS_SEGMENT)) {
132084fe7d0bSbostic     if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
132184fe7d0bSbostic 	&& have_last_line)
132284fe7d0bSbostic       segment_pos = last_line;
132384fe7d0bSbostic     else
132484fe7d0bSbostic       switch (dir) {
132584fe7d0bSbostic       case UP_DIRECTION:
132684fe7d0bSbostic 	segment_pos.y = segment_height;
132784fe7d0bSbostic 	break;
132884fe7d0bSbostic       case DOWN_DIRECTION:
132984fe7d0bSbostic 	segment_pos.y = -segment_height;
133084fe7d0bSbostic 	break;
133184fe7d0bSbostic       case LEFT_DIRECTION:
133284fe7d0bSbostic 	segment_pos.x = -segment_width;
133384fe7d0bSbostic 	break;
133484fe7d0bSbostic       case RIGHT_DIRECTION:
133584fe7d0bSbostic 	segment_pos.x = segment_width;
133684fe7d0bSbostic 	break;
133784fe7d0bSbostic       default:
133884fe7d0bSbostic 	assert(0);
133984fe7d0bSbostic       }
134084fe7d0bSbostic   }
134184fe7d0bSbostic   segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
134284fe7d0bSbostic   // reverse the segment_list so that it's in forward order
134384fe7d0bSbostic   segment *old = segment_list;
134484fe7d0bSbostic   segment_list = 0;
134584fe7d0bSbostic   while (old != 0) {
134684fe7d0bSbostic     segment *tem = old->next;
134784fe7d0bSbostic     old->next = segment_list;
134884fe7d0bSbostic     segment_list = old;
134984fe7d0bSbostic     old = tem;
135084fe7d0bSbostic   }
135184fe7d0bSbostic   // Absolutise all movements
135284fe7d0bSbostic   position endpos = startpos;
135384fe7d0bSbostic   int nsegments = 0;
135484fe7d0bSbostic   for (segment *s = segment_list; s; s = s->next, nsegments++)
135584fe7d0bSbostic     if (s->is_absolute)
135684fe7d0bSbostic       endpos = s->pos;
135784fe7d0bSbostic     else {
135884fe7d0bSbostic       endpos += s->pos;
135984fe7d0bSbostic       s->pos = endpos;
136084fe7d0bSbostic       s->is_absolute = 1;	// to avoid confusion
136184fe7d0bSbostic     }
136284fe7d0bSbostic   // handle chop
136384fe7d0bSbostic   line_object *p = 0;
136484fe7d0bSbostic   position *v = new position[nsegments];
136584fe7d0bSbostic   int i = 0;
136684fe7d0bSbostic   for (s = segment_list; s; s = s->next, i++)
136784fe7d0bSbostic     v[i] = s->pos;
136884fe7d0bSbostic   if (flags & IS_DEFAULT_CHOPPED) {
136984fe7d0bSbostic     lookup_variable("circlerad", &start_chop);
137084fe7d0bSbostic     end_chop = start_chop;
137184fe7d0bSbostic     flags |= IS_CHOPPED;
137284fe7d0bSbostic   }
137384fe7d0bSbostic   if (flags & IS_CHOPPED) {
137484fe7d0bSbostic     position start_chop_vec, end_chop_vec;
137584fe7d0bSbostic     if (start_chop != 0.0) {
137684fe7d0bSbostic       start_chop_vec = v[0] - startpos;
137784fe7d0bSbostic       start_chop_vec *= start_chop / hypot(start_chop_vec);
137884fe7d0bSbostic     }
137984fe7d0bSbostic     if (end_chop != 0.0) {
138084fe7d0bSbostic       end_chop_vec = (v[nsegments - 1]
138184fe7d0bSbostic 		      - (nsegments > 1 ? v[nsegments - 2] : startpos));
138284fe7d0bSbostic       end_chop_vec *= end_chop / hypot(end_chop_vec);
138384fe7d0bSbostic     }
138484fe7d0bSbostic     startpos += start_chop_vec;
138584fe7d0bSbostic     v[nsegments - 1] -= end_chop_vec;
138684fe7d0bSbostic     endpos -= end_chop_vec;
138784fe7d0bSbostic   }
138884fe7d0bSbostic   switch (type) {
138984fe7d0bSbostic   case SPLINE_OBJECT:
139084fe7d0bSbostic     p = new spline_object(startpos, endpos, v, nsegments);
139184fe7d0bSbostic     break;
139284fe7d0bSbostic   case ARROW_OBJECT:
139384fe7d0bSbostic     p = new arrow_object(startpos, endpos, v, nsegments);
139484fe7d0bSbostic     break;
139584fe7d0bSbostic   case LINE_OBJECT:
139684fe7d0bSbostic     p = new line_object(startpos, endpos, v, nsegments);
139784fe7d0bSbostic     break;
139884fe7d0bSbostic   default:
139984fe7d0bSbostic     assert(0);
140084fe7d0bSbostic   }
140184fe7d0bSbostic   have_last_line = 1;
140284fe7d0bSbostic   last_line = endpos - startpos;
140384fe7d0bSbostic   *curpos = endpos;
140484fe7d0bSbostic   return p;
140584fe7d0bSbostic }
140684fe7d0bSbostic 
140784fe7d0bSbostic class arc_object : public linear_object {
140884fe7d0bSbostic   int clockwise;
140984fe7d0bSbostic   position cent;
141084fe7d0bSbostic   double rad;
141184fe7d0bSbostic public:
141284fe7d0bSbostic   arc_object(int, const position &, const position &, const position &);
origin()141384fe7d0bSbostic   position origin() { return cent; }
center()141484fe7d0bSbostic   position center() { return cent; }
radius()141584fe7d0bSbostic   double radius() { return rad; }
141684fe7d0bSbostic   position north();
141784fe7d0bSbostic   position south();
141884fe7d0bSbostic   position east();
141984fe7d0bSbostic   position west();
142084fe7d0bSbostic   position north_east();
142184fe7d0bSbostic   position north_west();
142284fe7d0bSbostic   position south_east();
142384fe7d0bSbostic   position south_west();
142484fe7d0bSbostic   void update_bounding_box(bounding_box *);
type()142584fe7d0bSbostic   object_type type() { return ARC_OBJECT; }
142684fe7d0bSbostic   void print();
142784fe7d0bSbostic   void move_by(const position &pos);
142884fe7d0bSbostic };
142984fe7d0bSbostic 
arc_object(int cw,const position & s,const position & e,const position & c)143084fe7d0bSbostic arc_object::arc_object(int cw, const position &s, const position &e,
143184fe7d0bSbostic 		       const position &c)
143284fe7d0bSbostic : linear_object(s, e), clockwise(cw), cent(c)
143384fe7d0bSbostic {
143484fe7d0bSbostic   rad = hypot(c - s);
143584fe7d0bSbostic }
143684fe7d0bSbostic 
move_by(const position & pos)143784fe7d0bSbostic void arc_object::move_by(const position &pos)
143884fe7d0bSbostic {
143984fe7d0bSbostic   linear_object::move_by(pos);
144084fe7d0bSbostic   cent += pos;
144184fe7d0bSbostic }
144284fe7d0bSbostic 
144384fe7d0bSbostic // we get arc corners from the corresponding circle
144484fe7d0bSbostic 
north()144584fe7d0bSbostic position arc_object::north()
144684fe7d0bSbostic {
144784fe7d0bSbostic   position result(cent);
144884fe7d0bSbostic   result.y += rad;
144984fe7d0bSbostic   return result;
145084fe7d0bSbostic }
145184fe7d0bSbostic 
south()145284fe7d0bSbostic position arc_object::south()
145384fe7d0bSbostic {
145484fe7d0bSbostic   position result(cent);
145584fe7d0bSbostic   result.y -= rad;
145684fe7d0bSbostic   return result;
145784fe7d0bSbostic }
145884fe7d0bSbostic 
east()145984fe7d0bSbostic position arc_object::east()
146084fe7d0bSbostic {
146184fe7d0bSbostic   position result(cent);
146284fe7d0bSbostic   result.x += rad;
146384fe7d0bSbostic   return result;
146484fe7d0bSbostic }
146584fe7d0bSbostic 
west()146684fe7d0bSbostic position arc_object::west()
146784fe7d0bSbostic {
146884fe7d0bSbostic   position result(cent);
146984fe7d0bSbostic   result.x -= rad;
147084fe7d0bSbostic   return result;
147184fe7d0bSbostic }
147284fe7d0bSbostic 
north_east()147384fe7d0bSbostic position arc_object::north_east()
147484fe7d0bSbostic {
147584fe7d0bSbostic   position result(cent);
147684fe7d0bSbostic   result.x += rad/M_SQRT2;
147784fe7d0bSbostic   result.y += rad/M_SQRT2;
147884fe7d0bSbostic   return result;
147984fe7d0bSbostic }
148084fe7d0bSbostic 
north_west()148184fe7d0bSbostic position arc_object::north_west()
148284fe7d0bSbostic {
148384fe7d0bSbostic   position result(cent);
148484fe7d0bSbostic   result.x -= rad/M_SQRT2;
148584fe7d0bSbostic   result.y += rad/M_SQRT2;
148684fe7d0bSbostic   return result;
148784fe7d0bSbostic }
148884fe7d0bSbostic 
south_east()148984fe7d0bSbostic position arc_object::south_east()
149084fe7d0bSbostic {
149184fe7d0bSbostic   position result(cent);
149284fe7d0bSbostic   result.x += rad/M_SQRT2;
149384fe7d0bSbostic   result.y -= rad/M_SQRT2;
149484fe7d0bSbostic   return result;
149584fe7d0bSbostic }
149684fe7d0bSbostic 
south_west()149784fe7d0bSbostic position arc_object::south_west()
149884fe7d0bSbostic {
149984fe7d0bSbostic   position result(cent);
150084fe7d0bSbostic   result.x -= rad/M_SQRT2;
150184fe7d0bSbostic   result.y -= rad/M_SQRT2;
150284fe7d0bSbostic   return result;
150384fe7d0bSbostic }
150484fe7d0bSbostic 
150584fe7d0bSbostic 
print()150684fe7d0bSbostic void arc_object::print()
150784fe7d0bSbostic {
150884fe7d0bSbostic   if (lt.type == line_type::invisible)
150984fe7d0bSbostic     return;
151084fe7d0bSbostic   if (clockwise)
151184fe7d0bSbostic     out->arc(en, cent, strt, lt);
151284fe7d0bSbostic   else
151384fe7d0bSbostic     out->arc(strt, cent, en, lt);
151484fe7d0bSbostic   if (arrow_at_start) {
151584fe7d0bSbostic     position c = cent - strt;
151684fe7d0bSbostic     draw_arrow(strt,
151784fe7d0bSbostic 	       (clockwise ? position(c.y, -c.x) : position(-c.y, c.x)),
151884fe7d0bSbostic 	       aht, lt);
151984fe7d0bSbostic   }
152084fe7d0bSbostic   if (arrow_at_end) {
152184fe7d0bSbostic     position e = en - cent;
152284fe7d0bSbostic     draw_arrow(en,
152384fe7d0bSbostic 	       (clockwise ? position(e.y, -e.x) : position(-e.y, e.x)),
152484fe7d0bSbostic 	       aht, lt);
152584fe7d0bSbostic   }
152684fe7d0bSbostic }
152784fe7d0bSbostic 
max(double a,double b)152884fe7d0bSbostic inline double max(double a, double b)
152984fe7d0bSbostic {
153084fe7d0bSbostic   return a > b ? a : b;
153184fe7d0bSbostic }
153284fe7d0bSbostic 
update_bounding_box(bounding_box * p)153384fe7d0bSbostic void arc_object::update_bounding_box(bounding_box *p)
153484fe7d0bSbostic {
153584fe7d0bSbostic   p->encompass(strt);
153684fe7d0bSbostic   p->encompass(en);
153784fe7d0bSbostic   position start_offset = strt - cent;
153884fe7d0bSbostic   if (start_offset.x == 0.0 && start_offset.y == 0.0)
153984fe7d0bSbostic     return;
154084fe7d0bSbostic   position end_offset = en  - cent;
154184fe7d0bSbostic   if (end_offset.x == 0.0 && end_offset.y == 0.0)
154284fe7d0bSbostic     return;
154384fe7d0bSbostic   double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
154484fe7d0bSbostic   double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
154584fe7d0bSbostic   if (clockwise) {
154684fe7d0bSbostic     double temp = start_quad;
154784fe7d0bSbostic     start_quad = end_quad;
154884fe7d0bSbostic     end_quad = temp;
154984fe7d0bSbostic   }
155084fe7d0bSbostic   if (start_quad < 0.0)
155184fe7d0bSbostic     start_quad += 4.0;
155284fe7d0bSbostic   while (end_quad <= start_quad)
155384fe7d0bSbostic     end_quad += 4.0;
155484fe7d0bSbostic   double radius = max(hypot(start_offset), hypot(end_offset));
155584fe7d0bSbostic   for (int q = int(start_quad) + 1; q < end_quad; q++) {
155684fe7d0bSbostic     position offset;
155784fe7d0bSbostic     switch (q % 4) {
155884fe7d0bSbostic     case 0:
155984fe7d0bSbostic       offset.x = radius;
156084fe7d0bSbostic       break;
156184fe7d0bSbostic     case 1:
156284fe7d0bSbostic       offset.y = radius;
156384fe7d0bSbostic       break;
156484fe7d0bSbostic     case 2:
156584fe7d0bSbostic       offset.x = -radius;
156684fe7d0bSbostic       break;
156784fe7d0bSbostic     case 3:
156884fe7d0bSbostic       offset.y = -radius;
156984fe7d0bSbostic       break;
157084fe7d0bSbostic     }
157184fe7d0bSbostic     p->encompass(cent + offset);
157284fe7d0bSbostic   }
157384fe7d0bSbostic }
157484fe7d0bSbostic 
157584fe7d0bSbostic // We ignore the with attribute. The at attribute always refers to the center.
157684fe7d0bSbostic 
make_arc(position * curpos,direction * dirp)157784fe7d0bSbostic linear_object *object_spec::make_arc(position *curpos, direction *dirp)
157884fe7d0bSbostic {
157984fe7d0bSbostic   *dirp = dir;
158084fe7d0bSbostic   int cw = (flags & IS_CLOCKWISE) != 0;
158184fe7d0bSbostic   // compute the start
158284fe7d0bSbostic   position startpos;
158384fe7d0bSbostic   if (flags & HAS_FROM)
158484fe7d0bSbostic     startpos = from;
158584fe7d0bSbostic   else
158684fe7d0bSbostic     startpos = *curpos;
158784fe7d0bSbostic   if (!(flags & HAS_RADIUS))
158884fe7d0bSbostic     lookup_variable("arcrad", &radius);
158984fe7d0bSbostic   // compute the end
159084fe7d0bSbostic   position endpos;
159184fe7d0bSbostic   if (flags & HAS_TO)
159284fe7d0bSbostic     endpos = to;
159384fe7d0bSbostic   else {
159484fe7d0bSbostic     position m(radius, radius);
159584fe7d0bSbostic     // Adjust the signs.
159684fe7d0bSbostic     if (cw) {
159784fe7d0bSbostic       if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
159884fe7d0bSbostic 	m.x = -m.x;
159984fe7d0bSbostic       if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
160084fe7d0bSbostic 	m.y = -m.y;
160184fe7d0bSbostic       *dirp = direction((dir + 3) % 4);
160284fe7d0bSbostic     }
160384fe7d0bSbostic     else {
160484fe7d0bSbostic       if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
160584fe7d0bSbostic 	m.x = -m.x;
160684fe7d0bSbostic       if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
160784fe7d0bSbostic 	m.y = -m.y;
160884fe7d0bSbostic       *dirp = direction((dir + 1) % 4);
160984fe7d0bSbostic     }
161084fe7d0bSbostic     endpos = startpos + m;
161184fe7d0bSbostic   }
161284fe7d0bSbostic   // compute the center
161384fe7d0bSbostic   position centerpos;
161484fe7d0bSbostic   if (flags & HAS_AT)
161584fe7d0bSbostic     centerpos = at;
161684fe7d0bSbostic   else if (startpos == endpos)
161784fe7d0bSbostic     centerpos = startpos;
161884fe7d0bSbostic   else {
161984fe7d0bSbostic     position h = (endpos - startpos)/2.0;
162084fe7d0bSbostic     double d = hypot(h);
162184fe7d0bSbostic     if (radius <= 0)
162284fe7d0bSbostic       radius = .25;
162384fe7d0bSbostic     // make the radius big enough
162484fe7d0bSbostic     while (radius < d)
162584fe7d0bSbostic       radius *= 2.0;
162684fe7d0bSbostic     double alpha = acos(d/radius);
162784fe7d0bSbostic     double theta = atan2(h.y, h.x);
162884fe7d0bSbostic     if (cw)
162984fe7d0bSbostic       theta -= alpha;
163084fe7d0bSbostic     else
163184fe7d0bSbostic       theta += alpha;
163284fe7d0bSbostic     centerpos = position(cos(theta), sin(theta))*radius + startpos;
163384fe7d0bSbostic   }
163484fe7d0bSbostic   arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
163584fe7d0bSbostic   *curpos = endpos;
163684fe7d0bSbostic   return p;
163784fe7d0bSbostic }
163884fe7d0bSbostic 
make_linear(position * curpos,direction * dirp)163984fe7d0bSbostic graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
164084fe7d0bSbostic {
164184fe7d0bSbostic   linear_object *obj;
164284fe7d0bSbostic   if (type == ARC_OBJECT)
164384fe7d0bSbostic     obj = make_arc(curpos, dirp);
164484fe7d0bSbostic   else
164584fe7d0bSbostic     obj = make_line(curpos, dirp);
164684fe7d0bSbostic   if (type == ARROW_OBJECT
164784fe7d0bSbostic       && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
164884fe7d0bSbostic     flags |= HAS_RIGHT_ARROW_HEAD;
164984fe7d0bSbostic   if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
165084fe7d0bSbostic     arrow_head_type a;
165184fe7d0bSbostic     int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
165284fe7d0bSbostic     int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
165384fe7d0bSbostic     if (flags & HAS_HEIGHT)
165484fe7d0bSbostic       a.height = height;
165584fe7d0bSbostic     else
165684fe7d0bSbostic       lookup_variable("arrowht", &a.height);
165784fe7d0bSbostic     if (flags & HAS_WIDTH)
165884fe7d0bSbostic       a.width = width;
165984fe7d0bSbostic     else
166084fe7d0bSbostic       lookup_variable("arrowwid", &a.width);
166184fe7d0bSbostic     double solid;
166284fe7d0bSbostic     lookup_variable("arrowhead", &solid);
166384fe7d0bSbostic     a.solid = solid != 0.0;
166484fe7d0bSbostic     obj->add_arrows(at_start, at_end, a);
166584fe7d0bSbostic   }
166684fe7d0bSbostic   return obj;
166784fe7d0bSbostic }
166884fe7d0bSbostic 
make_object(position * curpos,direction * dirp)166984fe7d0bSbostic object *object_spec::make_object(position *curpos, direction *dirp)
167084fe7d0bSbostic {
167184fe7d0bSbostic   graphic_object *obj = 0;
167284fe7d0bSbostic   switch (type) {
167384fe7d0bSbostic   case BLOCK_OBJECT:
167484fe7d0bSbostic     obj = make_block(curpos, dirp);
167584fe7d0bSbostic     break;
167684fe7d0bSbostic   case BOX_OBJECT:
167784fe7d0bSbostic     obj = make_box(curpos, dirp);
167884fe7d0bSbostic     break;
167984fe7d0bSbostic   case TEXT_OBJECT:
168084fe7d0bSbostic     obj = make_text(curpos, dirp);
168184fe7d0bSbostic     break;
168284fe7d0bSbostic   case ELLIPSE_OBJECT:
168384fe7d0bSbostic     obj = make_ellipse(curpos, dirp);
168484fe7d0bSbostic     break;
168584fe7d0bSbostic   case CIRCLE_OBJECT:
168684fe7d0bSbostic     obj = make_circle(curpos, dirp);
168784fe7d0bSbostic     break;
168884fe7d0bSbostic   case MOVE_OBJECT:
168984fe7d0bSbostic     obj = make_move(curpos, dirp);
169084fe7d0bSbostic     break;
169184fe7d0bSbostic   case ARC_OBJECT:
169284fe7d0bSbostic   case LINE_OBJECT:
169384fe7d0bSbostic   case SPLINE_OBJECT:
169484fe7d0bSbostic   case ARROW_OBJECT:
169584fe7d0bSbostic     obj = make_linear(curpos, dirp);
169684fe7d0bSbostic     break;
169784fe7d0bSbostic   case MARK_OBJECT:
169884fe7d0bSbostic   case OTHER_OBJECT:
169984fe7d0bSbostic   default:
170084fe7d0bSbostic     assert(0);
170184fe7d0bSbostic     break;
170284fe7d0bSbostic   }
170384fe7d0bSbostic   if (obj) {
170484fe7d0bSbostic     if (flags & IS_INVISIBLE)
170584fe7d0bSbostic       obj->set_invisible();
170684fe7d0bSbostic     if (text != 0)
170784fe7d0bSbostic       obj->add_text(text, (flags & IS_ALIGNED) != 0);
170884fe7d0bSbostic     if (flags & IS_DOTTED)
170984fe7d0bSbostic       obj->set_dotted(dash_width);
171084fe7d0bSbostic     else if (flags & IS_DASHED)
171184fe7d0bSbostic       obj->set_dashed(dash_width);
171284fe7d0bSbostic     double th;
171384fe7d0bSbostic     if (flags & HAS_THICKNESS)
171484fe7d0bSbostic       th = thickness;
171584fe7d0bSbostic     else
171684fe7d0bSbostic       lookup_variable("linethick", &th);
171784fe7d0bSbostic     obj->set_thickness(th);
171884fe7d0bSbostic     if (flags & (IS_DEFAULT_FILLED|IS_FILLED)) {
171984fe7d0bSbostic       if (flags & IS_DEFAULT_FILLED)
172084fe7d0bSbostic 	lookup_variable("fillval", &fill);
172184fe7d0bSbostic       if (fill < 0.0)
172284fe7d0bSbostic 	error("bad fill value %1", fill);
172384fe7d0bSbostic       else
172484fe7d0bSbostic 	obj->set_fill(fill);
172584fe7d0bSbostic     }
172684fe7d0bSbostic   }
172784fe7d0bSbostic   return obj;
172884fe7d0bSbostic }
172984fe7d0bSbostic 
173084fe7d0bSbostic struct string_list {
173184fe7d0bSbostic   string_list *next;
173284fe7d0bSbostic   char *str;
173384fe7d0bSbostic   string_list(char *);
173484fe7d0bSbostic   ~string_list();
173584fe7d0bSbostic };
173684fe7d0bSbostic 
string_list(char * s)173784fe7d0bSbostic string_list::string_list(char *s)
173884fe7d0bSbostic : next(0), str(s)
173984fe7d0bSbostic {
174084fe7d0bSbostic }
174184fe7d0bSbostic 
~string_list()174284fe7d0bSbostic string_list::~string_list()
174384fe7d0bSbostic {
174484fe7d0bSbostic   a_delete str;
174584fe7d0bSbostic }
174684fe7d0bSbostic 
174784fe7d0bSbostic /* A path is used to hold the argument to the with attribute. For example,
174884fe7d0bSbostic `.nw' or `.A.s' or `.A'. The major operation on a path is to take a
174984fe7d0bSbostic place and follow the path through the place to place within the place.
175084fe7d0bSbostic Note that `.A.B.C.sw' will work. */
175184fe7d0bSbostic 
path(corner c)175284fe7d0bSbostic path::path(corner c)
175384fe7d0bSbostic : label_list(0), crn(c)
175484fe7d0bSbostic {
175584fe7d0bSbostic }
175684fe7d0bSbostic 
path(char * l,corner c)175784fe7d0bSbostic path::path(char *l, corner c)
175884fe7d0bSbostic : crn(c)
175984fe7d0bSbostic {
176084fe7d0bSbostic   label_list = new string_list(l);
176184fe7d0bSbostic }
176284fe7d0bSbostic 
~path()176384fe7d0bSbostic path::~path()
176484fe7d0bSbostic {
176584fe7d0bSbostic   while (label_list) {
176684fe7d0bSbostic     string_list *tem = label_list;
176784fe7d0bSbostic     label_list = label_list->next;
176884fe7d0bSbostic     delete tem;
176984fe7d0bSbostic   }
177084fe7d0bSbostic }
177184fe7d0bSbostic 
append(corner c)177284fe7d0bSbostic void path::append(corner c)
177384fe7d0bSbostic {
177484fe7d0bSbostic   assert(crn == 0);
177584fe7d0bSbostic   crn = c;
177684fe7d0bSbostic }
177784fe7d0bSbostic 
append(char * s)177884fe7d0bSbostic void path::append(char *s)
177984fe7d0bSbostic {
178084fe7d0bSbostic   for (string_list **p = &label_list; *p; p = &(*p)->next)
178184fe7d0bSbostic     ;
178284fe7d0bSbostic   *p = new string_list(s);
178384fe7d0bSbostic }
178484fe7d0bSbostic 
178584fe7d0bSbostic // return non-zero for success
178684fe7d0bSbostic 
follow(const place & pl,place * result) const178784fe7d0bSbostic int path::follow(const place &pl, place *result) const
178884fe7d0bSbostic {
178984fe7d0bSbostic   const place *p = &pl;
179084fe7d0bSbostic   for (string_list *lb = label_list; lb; lb = lb->next)
179184fe7d0bSbostic     if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
179284fe7d0bSbostic       lex_error("object does not contain a place `%1'", lb->str);
179384fe7d0bSbostic       return 0;
179484fe7d0bSbostic     }
179584fe7d0bSbostic   if (crn == 0 || p->obj == 0)
179684fe7d0bSbostic     *result = *p;
179784fe7d0bSbostic   else {
179884fe7d0bSbostic     position pos = ((p->obj)->*(crn))();
179984fe7d0bSbostic     result->x = pos.x;
180084fe7d0bSbostic     result->y = pos.y;
180184fe7d0bSbostic     result->obj = 0;
180284fe7d0bSbostic   }
180384fe7d0bSbostic   return 1;
180484fe7d0bSbostic }
180584fe7d0bSbostic 
print_object_list(object * p)180684fe7d0bSbostic void print_object_list(object *p)
180784fe7d0bSbostic {
180884fe7d0bSbostic   for (; p; p = p->next) {
180984fe7d0bSbostic     p->print();
181084fe7d0bSbostic     p->print_text();
181184fe7d0bSbostic   }
181284fe7d0bSbostic }
181384fe7d0bSbostic 
print_picture(object * obj)181484fe7d0bSbostic void print_picture(object *obj)
181584fe7d0bSbostic {
181684fe7d0bSbostic   bounding_box bb;
181784fe7d0bSbostic   for (object *p = obj; p; p = p->next)
181884fe7d0bSbostic     p->update_bounding_box(&bb);
181984fe7d0bSbostic   double scale;
182084fe7d0bSbostic   lookup_variable("scale", &scale);
182184fe7d0bSbostic   out->start_picture(scale, bb.ll, bb.ur);
182284fe7d0bSbostic   print_object_list(obj);
182384fe7d0bSbostic   out->finish_picture();
182484fe7d0bSbostic }
182584fe7d0bSbostic 
1826