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