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