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