* src/groff/preproc/pic/lex.cpp (table): Add box attributes
[s-roff.git] / src / preproc / pic / object.cpp
blobf16ab40c715c2748f23017cd52aba070aa43accb
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 #include "pic.h"
24 #include "ptable.h"
25 #include "object.h"
27 void print_object_list(object *);
29 line_type::line_type()
30 : type(solid), thickness(1.0)
34 output::output() : args(0), desired_height(0.0), desired_width(0.0)
38 output::~output()
40 a_delete args;
43 void output::set_desired_width_height(double wid, double ht)
45 desired_width = wid;
46 desired_height = ht;
49 void output::set_args(const char *s)
51 a_delete args;
52 if (s == 0 || *s == '\0')
53 args = 0;
54 else
55 args = strsave(s);
58 int output::supports_filled_polygons()
60 return 0;
63 void output::begin_block(const position &, const position &)
67 void output::end_block()
71 double output::compute_scale(double sc, const position &ll, const position &ur)
73 distance dim = ur - ll;
74 if (desired_width != 0.0 || desired_height != 0.0) {
75 sc = 0.0;
76 if (desired_width != 0.0) {
77 if (dim.x == 0.0)
78 error("width specified for picture with zero width");
79 else
80 sc = dim.x/desired_width;
82 if (desired_height != 0.0) {
83 if (dim.y == 0.0)
84 error("height specified for picture with zero height");
85 else {
86 double tem = dim.y/desired_height;
87 if (tem > sc)
88 sc = tem;
91 return sc == 0.0 ? 1.0 : sc;
93 else {
94 if (sc <= 0.0)
95 sc = 1.0;
96 distance sdim = dim/sc;
97 double max_width = 0.0;
98 lookup_variable("maxpswid", &max_width);
99 double max_height = 0.0;
100 lookup_variable("maxpsht", &max_height);
101 if ((max_width > 0.0 && sdim.x > max_width)
102 || (max_height > 0.0 && sdim.y > max_height)) {
103 double xscale = dim.x/max_width;
104 double yscale = dim.y/max_height;
105 return xscale > yscale ? xscale : yscale;
107 else
108 return sc;
112 position::position(const place &pl)
114 if (pl.obj != 0) {
115 // Use two statements to work around bug in SGI C++.
116 object *tem = pl.obj;
117 *this = tem->origin();
119 else {
120 x = pl.x;
121 y = pl.y;
125 position::position() : x(0.0), y(0.0)
129 position::position(double a, double b) : x(a), y(b)
134 int operator==(const position &a, const position &b)
136 return a.x == b.x && a.y == b.y;
139 int operator!=(const position &a, const position &b)
141 return a.x != b.x || a.y != b.y;
144 position &position::operator+=(const position &a)
146 x += a.x;
147 y += a.y;
148 return *this;
151 position &position::operator-=(const position &a)
153 x -= a.x;
154 y -= a.y;
155 return *this;
158 position &position::operator*=(double a)
160 x *= a;
161 y *= a;
162 return *this;
165 position &position::operator/=(double a)
167 x /= a;
168 y /= a;
169 return *this;
172 position operator-(const position &a)
174 return position(-a.x, -a.y);
177 position operator+(const position &a, const position &b)
179 return position(a.x + b.x, a.y + b.y);
182 position operator-(const position &a, const position &b)
184 return position(a.x - b.x, a.y - b.y);
187 position operator/(const position &a, double n)
189 return position(a.x/n, a.y/n);
192 position operator*(const position &a, double n)
194 return position(a.x*n, a.y*n);
197 // dot product
199 double operator*(const position &a, const position &b)
201 return a.x*b.x + a.y*b.y;
204 double hypot(const position &a)
206 return groff_hypot(a.x, a.y);
209 struct arrow_head_type {
210 double height;
211 double width;
212 int solid;
215 void draw_arrow(const position &pos, const distance &dir,
216 const arrow_head_type &aht, const line_type &lt,
217 char *outline_color_for_fill)
219 double hyp = hypot(dir);
220 if (hyp == 0.0) {
221 error("cannot draw arrow on object with zero length");
222 return;
224 position base = -dir;
225 base *= aht.height/hyp;
226 position n(dir.y, -dir.x);
227 n *= aht.width/(hyp*2.0);
228 line_type slt = lt;
229 slt.type = line_type::solid;
230 if (aht.solid && out->supports_filled_polygons()) {
231 position v[3];
232 v[0] = pos;
233 v[1] = pos + base + n;
234 v[2] = pos + base - n;
235 // fill with outline color
236 out->set_color(outline_color_for_fill, outline_color_for_fill);
237 // make stroke thin to avoid arrow sticking
238 slt.thickness = 0.1;
239 out->polygon(v, 3, slt, 1);
241 else {
242 // use two line segments to avoid arrow sticking
243 out->line(pos + base - n, &pos, 1, slt);
244 out->line(pos + base + n, &pos, 1, slt);
248 object::object() : prev(0), next(0)
252 object::~object()
256 void object::move_by(const position &)
260 void object::print()
264 void object::print_text()
268 int object::blank()
270 return 0;
273 struct bounding_box {
274 int blank;
275 position ll;
276 position ur;
278 bounding_box();
279 void encompass(const position &);
282 bounding_box::bounding_box()
283 : blank(1)
287 void bounding_box::encompass(const position &pos)
289 if (blank) {
290 ll = pos;
291 ur = pos;
292 blank = 0;
294 else {
295 if (pos.x < ll.x)
296 ll.x = pos.x;
297 if (pos.y < ll.y)
298 ll.y = pos.y;
299 if (pos.x > ur.x)
300 ur.x = pos.x;
301 if (pos.y > ur.y)
302 ur.y = pos.y;
306 void object::update_bounding_box(bounding_box *)
310 position object::origin()
312 return position(0.0,0.0);
315 position object::north()
317 return origin();
320 position object::south()
322 return origin();
325 position object::east()
327 return origin();
330 position object::west()
332 return origin();
335 position object::north_east()
337 return origin();
340 position object::north_west()
342 return origin();
345 position object::south_east()
347 return origin();
350 position object::south_west()
352 return origin();
355 position object::start()
357 return origin();
360 position object::end()
362 return origin();
365 position object::center()
367 return origin();
370 double object::width()
372 return 0.0;
375 double object::radius()
377 return 0.0;
380 double object::height()
382 return 0.0;
385 place *object::find_label(const char *)
387 return 0;
390 segment::segment(const position &a, int n, segment *p)
391 : is_absolute(n), pos(a), next(p)
395 text_item::text_item(char *t, const char *fn, int ln)
396 : next(0), text(t), filename(fn), lineno(ln)
398 adj.h = CENTER_ADJUST;
399 adj.v = NONE_ADJUST;
402 text_item::~text_item()
404 a_delete text;
407 object_spec::object_spec(object_type t) : type(t)
409 flags = 0;
410 tbl = 0;
411 segment_list = 0;
412 segment_width = segment_height = 0.0;
413 segment_is_absolute = 0;
414 text = 0;
415 shaded = 0;
416 xslanted = 0;
417 yslanted = 0;
418 outlined = 0;
419 with = 0;
420 dir = RIGHT_DIRECTION;
423 object_spec::~object_spec()
425 delete tbl;
426 while (segment_list != 0) {
427 segment *tem = segment_list;
428 segment_list = segment_list->next;
429 delete tem;
431 object *p = oblist.head;
432 while (p != 0) {
433 object *tem = p;
434 p = p->next;
435 delete tem;
437 while (text != 0) {
438 text_item *tem = text;
439 text = text->next;
440 delete tem;
442 delete with;
443 a_delete shaded;
444 a_delete outlined;
447 class command_object : public object {
448 char *s;
449 const char *filename;
450 int lineno;
451 public:
452 command_object(char *, const char *, int);
453 ~command_object();
454 object_type type() { return OTHER_OBJECT; }
455 void print();
458 command_object::command_object(char *p, const char *fn, int ln)
459 : s(p), filename(fn), lineno(ln)
463 command_object::~command_object()
465 a_delete s;
468 void command_object::print()
470 out->command(s, filename, lineno);
473 object *make_command_object(char *s, const char *fn, int ln)
475 return new command_object(s, fn, ln);
478 class mark_object : public object {
479 public:
480 mark_object();
481 object_type type();
484 object *make_mark_object()
486 return new mark_object();
489 mark_object::mark_object()
493 object_type mark_object::type()
495 return MARK_OBJECT;
498 object_list::object_list() : head(0), tail(0)
502 void object_list::append(object *obj)
504 if (tail == 0) {
505 obj->next = obj->prev = 0;
506 head = tail = obj;
508 else {
509 obj->prev = tail;
510 obj->next = 0;
511 tail->next = obj;
512 tail = obj;
516 void object_list::wrap_up_block(object_list *ol)
518 object *p;
519 for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
521 assert(p != 0);
522 ol->head = p->next;
523 if (ol->head) {
524 ol->tail = tail;
525 ol->head->prev = 0;
527 else
528 ol->tail = 0;
529 tail = p->prev;
530 if (tail)
531 tail->next = 0;
532 else
533 head = 0;
534 delete p;
537 text_piece::text_piece()
538 : text(0), filename(0), lineno(-1)
540 adj.h = CENTER_ADJUST;
541 adj.v = NONE_ADJUST;
544 text_piece::~text_piece()
546 a_delete text;
549 class graphic_object : public object {
550 int ntext;
551 text_piece *text;
552 int aligned;
553 protected:
554 line_type lt;
555 char *outline_color;
556 char *color_fill;
557 public:
558 graphic_object();
559 ~graphic_object();
560 object_type type() = 0;
561 void print_text();
562 void add_text(text_item *, int);
563 void set_dotted(double);
564 void set_dashed(double);
565 void set_thickness(double);
566 void set_invisible();
567 void set_outline_color(char *);
568 char *get_outline_color();
569 virtual void set_fill(double);
570 virtual void set_xslanted(double);
571 virtual void set_yslanted(double);
572 virtual void set_fill_color(char *);
575 graphic_object::graphic_object()
576 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
580 void graphic_object::set_dotted(double wid)
582 lt.type = line_type::dotted;
583 lt.dash_width = wid;
586 void graphic_object::set_dashed(double wid)
588 lt.type = line_type::dashed;
589 lt.dash_width = wid;
592 void graphic_object::set_thickness(double th)
594 lt.thickness = th;
597 void graphic_object::set_fill(double)
601 void graphic_object::set_xslanted(double)
605 void graphic_object::set_yslanted(double)
609 void graphic_object::set_fill_color(char *c)
611 color_fill = strsave(c);
614 void graphic_object::set_outline_color(char *c)
616 outline_color = strsave(c);
619 char *graphic_object::get_outline_color()
621 return outline_color;
624 void graphic_object::set_invisible()
626 lt.type = line_type::invisible;
629 void graphic_object::add_text(text_item *t, int a)
631 aligned = a;
632 int len = 0;
633 text_item *p;
634 for (p = t; p; p = p->next)
635 len++;
636 if (len == 0)
637 text = 0;
638 else {
639 text = new text_piece[len];
640 for (p = t, len = 0; p; p = p->next, len++) {
641 text[len].text = p->text;
642 p->text = 0;
643 text[len].adj = p->adj;
644 text[len].filename = p->filename;
645 text[len].lineno = p->lineno;
648 ntext = len;
651 void graphic_object::print_text()
653 double angle = 0.0;
654 if (aligned) {
655 position d(end() - start());
656 if (d.x != 0.0 || d.y != 0.0)
657 angle = atan2(d.y, d.x);
659 if (text != 0) {
660 out->set_color(color_fill, get_outline_color());
661 out->text(center(), text, ntext, angle);
662 out->reset_color();
666 graphic_object::~graphic_object()
668 if (text)
669 ad_delete(ntext) text;
672 class rectangle_object : public graphic_object {
673 protected:
674 position cent;
675 position dim;
676 public:
677 rectangle_object(const position &);
678 double width() { return dim.x; }
679 double height() { return dim.y; }
680 position origin() { return cent; }
681 position center() { return cent; }
682 position north() { return position(cent.x, cent.y + dim.y/2.0); }
683 position south() { return position(cent.x, cent.y - dim.y/2.0); }
684 position east() { return position(cent.x + dim.x/2.0, cent.y); }
685 position west() { return position(cent.x - dim.x/2.0, cent.y); }
686 position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
687 position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
688 position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
689 position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
690 object_type type() = 0;
691 void update_bounding_box(bounding_box *);
692 void move_by(const position &);
695 rectangle_object::rectangle_object(const position &d)
696 : dim(d)
700 void rectangle_object::update_bounding_box(bounding_box *p)
702 p->encompass(cent - dim/2.0);
703 p->encompass(cent + dim/2.0);
706 void rectangle_object::move_by(const position &a)
708 cent += a;
711 class closed_object : public rectangle_object {
712 public:
713 closed_object(const position &);
714 object_type type() = 0;
715 void set_fill(double);
716 void set_xslanted(double);
717 void set_yslanted(double);
718 void set_fill_color(char *fill);
719 protected:
720 double fill; // < 0 if not filled
721 double xslanted; // !=0 if x is slanted
722 double yslanted; // !=0 if y is slanted
723 char *color_fill; // = 0 if not colored
726 closed_object::closed_object(const position &pos)
727 : rectangle_object(pos), fill(-1.0), xslanted(0), yslanted(0), color_fill(0)
731 void closed_object::set_fill(double f)
733 assert(f >= 0.0);
734 fill = f;
737 /* accept positive and negative values */
738 void closed_object::set_xslanted(double s)
740 //assert(s >= 0.0);
741 xslanted = s;
743 /* accept positive and negative values */
744 void closed_object::set_yslanted(double s)
746 //assert(s >= 0.0);
747 yslanted = s;
750 void closed_object::set_fill_color(char *f)
752 color_fill = strsave(f);
755 class box_object : public closed_object {
756 double xrad;
757 double yrad;
758 public:
759 box_object(const position &, double);
760 object_type type() { return BOX_OBJECT; }
761 void print();
762 position north_east();
763 position north_west();
764 position south_east();
765 position south_west();
768 box_object::box_object(const position &pos, double r)
769 : closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
773 const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
775 position box_object::north_east()
777 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
778 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
781 position box_object::north_west()
783 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
784 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
787 position box_object::south_east()
789 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
790 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
793 position box_object::south_west()
795 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
796 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
799 void box_object::print()
801 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
802 return;
803 out->set_color(color_fill, graphic_object::get_outline_color());
804 if (xrad == 0.0) {
805 distance dim2 = dim/2.0;
806 position vec[4];
807 /* error("x slanted %1", xslanted); */
808 /* error("y slanted %1", yslanted); */
809 vec[0] = cent + position(dim2.x, -(dim2.y - yslanted)); /* lr */
810 vec[1] = cent + position(dim2.x + xslanted, dim2.y + yslanted); /* ur */
811 vec[2] = cent + position(-(dim2.x - xslanted), dim2.y); /* ul */
812 vec[3] = cent + position(-(dim2.x), -dim2.y); /* ll */
813 out->polygon(vec, 4, lt, fill);
815 else {
816 distance abs_dim(fabs(dim.x), fabs(dim.y));
817 out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill, color_fill);
819 out->reset_color();
822 graphic_object *object_spec::make_box(position *curpos, direction *dirp)
824 static double last_box_height;
825 static double last_box_width;
826 static double last_box_radius;
827 static int have_last_box = 0;
828 if (!(flags & HAS_HEIGHT)) {
829 if ((flags & IS_SAME) && have_last_box)
830 height = last_box_height;
831 else
832 lookup_variable("boxht", &height);
834 if (!(flags & HAS_WIDTH)) {
835 if ((flags & IS_SAME) && have_last_box)
836 width = last_box_width;
837 else
838 lookup_variable("boxwid", &width);
840 if (!(flags & HAS_RADIUS)) {
841 if ((flags & IS_SAME) && have_last_box)
842 radius = last_box_radius;
843 else
844 lookup_variable("boxrad", &radius);
846 last_box_width = width;
847 last_box_height = height;
848 last_box_radius = radius;
849 have_last_box = 1;
850 radius = fabs(radius);
851 if (radius*2.0 > fabs(width))
852 radius = fabs(width/2.0);
853 if (radius*2.0 > fabs(height))
854 radius = fabs(height/2.0);
855 box_object *p = new box_object(position(width, height), radius);
856 if (!position_rectangle(p, curpos, dirp)) {
857 delete p;
858 p = 0;
860 return p;
863 // return non-zero for success
865 int object_spec::position_rectangle(rectangle_object *p,
866 position *curpos, direction *dirp)
868 position pos;
869 dir = *dirp; // ignore any direction in attribute list
870 position motion;
871 switch (dir) {
872 case UP_DIRECTION:
873 motion.y = p->height()/2.0;
874 break;
875 case DOWN_DIRECTION:
876 motion.y = -p->height()/2.0;
877 break;
878 case LEFT_DIRECTION:
879 motion.x = -p->width()/2.0;
880 break;
881 case RIGHT_DIRECTION:
882 motion.x = p->width()/2.0;
883 break;
884 default:
885 assert(0);
887 if (flags & HAS_AT) {
888 pos = at;
889 if (flags & HAS_WITH) {
890 place offset;
891 place here;
892 here.obj = p;
893 if (!with->follow(here, &offset))
894 return 0;
895 pos -= offset;
898 else {
899 pos = *curpos;
900 pos += motion;
902 p->move_by(pos);
903 pos += motion;
904 *curpos = pos;
905 return 1;
908 class block_object : public rectangle_object {
909 object_list oblist;
910 PTABLE(place) *tbl;
911 public:
912 block_object(const position &, const object_list &ol, PTABLE(place) *t);
913 ~block_object();
914 place *find_label(const char *);
915 object_type type();
916 void move_by(const position &);
917 void print();
920 block_object::block_object(const position &d, const object_list &ol,
921 PTABLE(place) *t)
922 : rectangle_object(d), oblist(ol), tbl(t)
926 block_object::~block_object()
928 delete tbl;
929 object *p = oblist.head;
930 while (p != 0) {
931 object *tem = p;
932 p = p->next;
933 delete tem;
937 void block_object::print()
939 out->begin_block(south_west(), north_east());
940 print_object_list(oblist.head);
941 out->end_block();
944 static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
946 // Adjust all the labels that aren't attached to objects.
947 PTABLE_ITERATOR(place) iter(tbl);
948 const char *key;
949 place *pl;
950 while (iter.next(&key, &pl))
951 if (key && csupper(key[0]) && pl->obj == 0) {
952 pl->x += a.x;
953 pl->y += a.y;
957 void block_object::move_by(const position &a)
959 cent += a;
960 for (object *p = oblist.head; p; p = p->next)
961 p->move_by(a);
962 adjust_objectless_places(tbl, a);
966 place *block_object::find_label(const char *name)
968 return tbl->lookup(name);
971 object_type block_object::type()
973 return BLOCK_OBJECT;
976 graphic_object *object_spec::make_block(position *curpos, direction *dirp)
978 bounding_box bb;
979 for (object *p = oblist.head; p; p = p->next)
980 p->update_bounding_box(&bb);
981 position dim;
982 if (!bb.blank) {
983 position m = -(bb.ll + bb.ur)/2.0;
984 for (object *p = oblist.head; p; p = p->next)
985 p->move_by(m);
986 adjust_objectless_places(tbl, m);
987 dim = bb.ur - bb.ll;
989 if (flags & HAS_WIDTH)
990 dim.x = width;
991 if (flags & HAS_HEIGHT)
992 dim.y = height;
993 block_object *block = new block_object(dim, oblist, tbl);
994 if (!position_rectangle(block, curpos, dirp)) {
995 delete block;
996 block = 0;
998 tbl = 0;
999 oblist.head = oblist.tail = 0;
1000 return block;
1003 class text_object : public rectangle_object {
1004 public:
1005 text_object(const position &);
1006 object_type type() { return TEXT_OBJECT; }
1009 text_object::text_object(const position &d)
1010 : rectangle_object(d)
1014 graphic_object *object_spec::make_text(position *curpos, direction *dirp)
1016 if (!(flags & HAS_HEIGHT)) {
1017 lookup_variable("textht", &height);
1018 int nitems = 0;
1019 for (text_item *t = text; t; t = t->next)
1020 nitems++;
1021 height *= nitems;
1023 if (!(flags & HAS_WIDTH))
1024 lookup_variable("textwid", &width);
1025 text_object *p = new text_object(position(width, height));
1026 if (!position_rectangle(p, curpos, dirp)) {
1027 delete p;
1028 p = 0;
1030 return p;
1034 class ellipse_object : public closed_object {
1035 public:
1036 ellipse_object(const position &);
1037 position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1038 cent.y + dim.y/(M_SQRT2*2.0)); }
1039 position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1040 cent.y + dim.y/(M_SQRT2*2.0)); }
1041 position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1042 cent.y - dim.y/(M_SQRT2*2.0)); }
1043 position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1044 cent.y - dim.y/(M_SQRT2*2.0)); }
1045 double radius() { return dim.x/2.0; }
1046 object_type type() { return ELLIPSE_OBJECT; }
1047 void print();
1050 ellipse_object::ellipse_object(const position &d)
1051 : closed_object(d)
1055 void ellipse_object::print()
1057 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1058 return;
1059 out->set_color(color_fill, graphic_object::get_outline_color());
1060 out->ellipse(cent, dim, lt, fill);
1061 out->reset_color();
1064 graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1066 static double last_ellipse_height;
1067 static double last_ellipse_width;
1068 static int have_last_ellipse = 0;
1069 if (!(flags & HAS_HEIGHT)) {
1070 if ((flags & IS_SAME) && have_last_ellipse)
1071 height = last_ellipse_height;
1072 else
1073 lookup_variable("ellipseht", &height);
1075 if (!(flags & HAS_WIDTH)) {
1076 if ((flags & IS_SAME) && have_last_ellipse)
1077 width = last_ellipse_width;
1078 else
1079 lookup_variable("ellipsewid", &width);
1081 last_ellipse_width = width;
1082 last_ellipse_height = height;
1083 have_last_ellipse = 1;
1084 ellipse_object *p = new ellipse_object(position(width, height));
1085 if (!position_rectangle(p, curpos, dirp)) {
1086 delete p;
1087 return 0;
1089 return p;
1092 class circle_object : public ellipse_object {
1093 public:
1094 circle_object(double);
1095 object_type type() { return CIRCLE_OBJECT; }
1096 void print();
1099 circle_object::circle_object(double diam)
1100 : ellipse_object(position(diam, diam))
1104 void circle_object::print()
1106 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1107 return;
1108 out->set_color(color_fill, graphic_object::get_outline_color());
1109 out->circle(cent, dim.x/2.0, lt, fill);
1110 out->reset_color();
1113 graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1115 static double last_circle_radius;
1116 static int have_last_circle = 0;
1117 if (!(flags & HAS_RADIUS)) {
1118 if ((flags & IS_SAME) && have_last_circle)
1119 radius = last_circle_radius;
1120 else
1121 lookup_variable("circlerad", &radius);
1123 last_circle_radius = radius;
1124 have_last_circle = 1;
1125 circle_object *p = new circle_object(radius*2.0);
1126 if (!position_rectangle(p, curpos, dirp)) {
1127 delete p;
1128 return 0;
1130 return p;
1133 class move_object : public graphic_object {
1134 position strt;
1135 position en;
1136 public:
1137 move_object(const position &s, const position &e);
1138 position origin() { return en; }
1139 object_type type() { return MOVE_OBJECT; }
1140 void update_bounding_box(bounding_box *);
1141 void move_by(const position &);
1144 move_object::move_object(const position &s, const position &e)
1145 : strt(s), en(e)
1149 void move_object::update_bounding_box(bounding_box *p)
1151 p->encompass(strt);
1152 p->encompass(en);
1155 void move_object::move_by(const position &a)
1157 strt += a;
1158 en += a;
1161 graphic_object *object_spec::make_move(position *curpos, direction *dirp)
1163 static position last_move;
1164 static int have_last_move = 0;
1165 *dirp = dir;
1166 // No need to look at at since `at' attribute sets `from' attribute.
1167 position startpos = (flags & HAS_FROM) ? from : *curpos;
1168 if (!(flags & HAS_SEGMENT)) {
1169 if ((flags & IS_SAME) && have_last_move)
1170 segment_pos = last_move;
1171 else {
1172 switch (dir) {
1173 case UP_DIRECTION:
1174 segment_pos.y = segment_height;
1175 break;
1176 case DOWN_DIRECTION:
1177 segment_pos.y = -segment_height;
1178 break;
1179 case LEFT_DIRECTION:
1180 segment_pos.x = -segment_width;
1181 break;
1182 case RIGHT_DIRECTION:
1183 segment_pos.x = segment_width;
1184 break;
1185 default:
1186 assert(0);
1190 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1191 // Reverse the segment_list so that it's in forward order.
1192 segment *old = segment_list;
1193 segment_list = 0;
1194 while (old != 0) {
1195 segment *tem = old->next;
1196 old->next = segment_list;
1197 segment_list = old;
1198 old = tem;
1200 // Compute the end position.
1201 position endpos = startpos;
1202 for (segment *s = segment_list; s; s = s->next)
1203 if (s->is_absolute)
1204 endpos = s->pos;
1205 else
1206 endpos += s->pos;
1207 have_last_move = 1;
1208 last_move = endpos - startpos;
1209 move_object *p = new move_object(startpos, endpos);
1210 *curpos = endpos;
1211 return p;
1214 class linear_object : public graphic_object {
1215 protected:
1216 char arrow_at_start;
1217 char arrow_at_end;
1218 arrow_head_type aht;
1219 position strt;
1220 position en;
1221 public:
1222 linear_object(const position &s, const position &e);
1223 position start() { return strt; }
1224 position end() { return en; }
1225 void move_by(const position &);
1226 void update_bounding_box(bounding_box *) = 0;
1227 object_type type() = 0;
1228 void add_arrows(int at_start, int at_end, const arrow_head_type &);
1231 class line_object : public linear_object {
1232 protected:
1233 position *v;
1234 int n;
1235 public:
1236 line_object(const position &s, const position &e, position *, int);
1237 ~line_object();
1238 position origin() { return strt; }
1239 position center() { return (strt + en)/2.0; }
1240 position north() { return (en.y - strt.y) > 0 ? en : strt; }
1241 position south() { return (en.y - strt.y) < 0 ? en : strt; }
1242 position east() { return (en.x - strt.x) > 0 ? en : strt; }
1243 position west() { return (en.x - strt.x) < 0 ? en : strt; }
1244 object_type type() { return LINE_OBJECT; }
1245 void update_bounding_box(bounding_box *);
1246 void print();
1247 void move_by(const position &);
1250 class arrow_object : public line_object {
1251 public:
1252 arrow_object(const position &, const position &, position *, int);
1253 object_type type() { return ARROW_OBJECT; }
1256 class spline_object : public line_object {
1257 public:
1258 spline_object(const position &, const position &, position *, int);
1259 object_type type() { return SPLINE_OBJECT; }
1260 void print();
1261 void update_bounding_box(bounding_box *);
1264 linear_object::linear_object(const position &s, const position &e)
1265 : arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1269 void linear_object::move_by(const position &a)
1271 strt += a;
1272 en += a;
1275 void linear_object::add_arrows(int at_start, int at_end,
1276 const arrow_head_type &a)
1278 arrow_at_start = at_start;
1279 arrow_at_end = at_end;
1280 aht = a;
1283 line_object::line_object(const position &s, const position &e,
1284 position *p, int i)
1285 : linear_object(s, e), v(p), n(i)
1289 void line_object::print()
1291 if (lt.type == line_type::invisible)
1292 return;
1293 out->set_color(0, graphic_object::get_outline_color());
1294 // shorten line length to avoid arrow sticking.
1295 position sp = strt;
1296 if (arrow_at_start) {
1297 position base = v[0] - strt;
1298 double hyp = hypot(base);
1299 if (hyp == 0.0) {
1300 error("cannot draw arrow on object with zero length");
1301 return;
1303 if (aht.solid && out->supports_filled_polygons()) {
1304 base *= aht.height / hyp;
1305 draw_arrow(strt, strt - v[0], aht, lt,
1306 graphic_object::get_outline_color());
1307 sp = strt + base;
1308 } else {
1309 base *= fabs(lt.thickness) / hyp / 72 / 4;
1310 sp = strt + base;
1311 draw_arrow(sp, sp - v[0], aht, lt,
1312 graphic_object::get_outline_color());
1315 if (arrow_at_end) {
1316 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1317 double hyp = hypot(base);
1318 if (hyp == 0.0) {
1319 error("cannot draw arrow on object with zero length");
1320 return;
1322 if (aht.solid && out->supports_filled_polygons()) {
1323 base *= aht.height / hyp;
1324 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1325 graphic_object::get_outline_color());
1326 v[n-1] = en - base;
1327 } else {
1328 base *= fabs(lt.thickness) / hyp / 72 / 4;
1329 v[n-1] = en - base;
1330 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1331 graphic_object::get_outline_color());
1334 out->line(sp, v, n, lt);
1335 out->reset_color();
1338 void line_object::update_bounding_box(bounding_box *p)
1340 p->encompass(strt);
1341 for (int i = 0; i < n; i++)
1342 p->encompass(v[i]);
1345 void line_object::move_by(const position &pos)
1347 linear_object::move_by(pos);
1348 for (int i = 0; i < n; i++)
1349 v[i] += pos;
1352 void spline_object::update_bounding_box(bounding_box *p)
1354 p->encompass(strt);
1355 p->encompass(en);
1360 p1 = q1/2 + q2/2
1361 p2 = q1/6 + q2*5/6
1362 p3 = q2*5/6 + q3/6
1363 p4 = q2/2 + q3/2
1364 [ the points for the Bezier cubic ]
1368 t = .5
1370 then
1372 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1373 [ the equation for the Bezier cubic ]
1375 = .125*q1 + .75*q2 + .125*q3
1378 for (int i = 1; i < n; i++)
1379 p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1382 arrow_object::arrow_object(const position &s, const position &e,
1383 position *p, int i)
1384 : line_object(s, e, p, i)
1388 spline_object::spline_object(const position &s, const position &e,
1389 position *p, int i)
1390 : line_object(s, e, p, i)
1394 void spline_object::print()
1396 if (lt.type == line_type::invisible)
1397 return;
1398 out->set_color(0, graphic_object::get_outline_color());
1399 // shorten line length for spline to avoid arrow sticking
1400 position sp = strt;
1401 if (arrow_at_start) {
1402 position base = v[0] - strt;
1403 double hyp = hypot(base);
1404 if (hyp == 0.0) {
1405 error("cannot draw arrow on object with zero length");
1406 return;
1408 if (aht.solid && out->supports_filled_polygons()) {
1409 base *= aht.height / hyp;
1410 draw_arrow(strt, strt - v[0], aht, lt,
1411 graphic_object::get_outline_color());
1412 sp = strt + base*0.1; // to reserve spline shape
1413 } else {
1414 base *= fabs(lt.thickness) / hyp / 72 / 4;
1415 sp = strt + base;
1416 draw_arrow(sp, sp - v[0], aht, lt,
1417 graphic_object::get_outline_color());
1420 if (arrow_at_end) {
1421 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1422 double hyp = hypot(base);
1423 if (hyp == 0.0) {
1424 error("cannot draw arrow on object with zero length");
1425 return;
1427 if (aht.solid && out->supports_filled_polygons()) {
1428 base *= aht.height / hyp;
1429 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1430 graphic_object::get_outline_color());
1431 v[n-1] = en - base*0.1; // to reserve spline shape
1432 } else {
1433 base *= fabs(lt.thickness) / hyp / 72 / 4;
1434 v[n-1] = en - base;
1435 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1436 graphic_object::get_outline_color());
1439 out->spline(sp, v, n, lt);
1440 out->reset_color();
1443 line_object::~line_object()
1445 a_delete v;
1448 linear_object *object_spec::make_line(position *curpos, direction *dirp)
1450 static position last_line;
1451 static int have_last_line = 0;
1452 *dirp = dir;
1453 // We handle `at' only in conjunction with `with', otherwise it is
1454 // the same as the `from' attribute.
1455 position startpos;
1456 if ((flags & HAS_AT) && (flags & HAS_WITH))
1457 // handled later -- we need the end position
1458 startpos = *curpos;
1459 else if (flags & HAS_FROM)
1460 startpos = from;
1461 else
1462 startpos = *curpos;
1463 if (!(flags & HAS_SEGMENT)) {
1464 if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1465 && have_last_line)
1466 segment_pos = last_line;
1467 else
1468 switch (dir) {
1469 case UP_DIRECTION:
1470 segment_pos.y = segment_height;
1471 break;
1472 case DOWN_DIRECTION:
1473 segment_pos.y = -segment_height;
1474 break;
1475 case LEFT_DIRECTION:
1476 segment_pos.x = -segment_width;
1477 break;
1478 case RIGHT_DIRECTION:
1479 segment_pos.x = segment_width;
1480 break;
1481 default:
1482 assert(0);
1485 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1486 // reverse the segment_list so that it's in forward order
1487 segment *old = segment_list;
1488 segment_list = 0;
1489 while (old != 0) {
1490 segment *tem = old->next;
1491 old->next = segment_list;
1492 segment_list = old;
1493 old = tem;
1495 // Absolutise all movements
1496 position endpos = startpos;
1497 int nsegments = 0;
1498 segment *s;
1499 for (s = segment_list; s; s = s->next, nsegments++)
1500 if (s->is_absolute)
1501 endpos = s->pos;
1502 else {
1503 endpos += s->pos;
1504 s->pos = endpos;
1505 s->is_absolute = 1; // to avoid confusion
1507 if ((flags & HAS_AT) && (flags & HAS_WITH)) {
1508 // `tmpobj' works for arrows and splines too -- we only need positions
1509 line_object tmpobj(startpos, endpos, 0, 0);
1510 position pos = at;
1511 place offset;
1512 place here;
1513 here.obj = &tmpobj;
1514 if (!with->follow(here, &offset))
1515 return 0;
1516 pos -= offset;
1517 for (s = segment_list; s; s = s->next)
1518 s->pos += pos;
1519 startpos += pos;
1520 endpos += pos;
1522 // handle chop
1523 line_object *p = 0;
1524 position *v = new position[nsegments];
1525 int i = 0;
1526 for (s = segment_list; s; s = s->next, i++)
1527 v[i] = s->pos;
1528 if (flags & IS_DEFAULT_CHOPPED) {
1529 lookup_variable("circlerad", &start_chop);
1530 end_chop = start_chop;
1531 flags |= IS_CHOPPED;
1533 if (flags & IS_CHOPPED) {
1534 position start_chop_vec, end_chop_vec;
1535 if (start_chop != 0.0) {
1536 start_chop_vec = v[0] - startpos;
1537 start_chop_vec *= start_chop / hypot(start_chop_vec);
1539 if (end_chop != 0.0) {
1540 end_chop_vec = (v[nsegments - 1]
1541 - (nsegments > 1 ? v[nsegments - 2] : startpos));
1542 end_chop_vec *= end_chop / hypot(end_chop_vec);
1544 startpos += start_chop_vec;
1545 v[nsegments - 1] -= end_chop_vec;
1546 endpos -= end_chop_vec;
1548 switch (type) {
1549 case SPLINE_OBJECT:
1550 p = new spline_object(startpos, endpos, v, nsegments);
1551 break;
1552 case ARROW_OBJECT:
1553 p = new arrow_object(startpos, endpos, v, nsegments);
1554 break;
1555 case LINE_OBJECT:
1556 p = new line_object(startpos, endpos, v, nsegments);
1557 break;
1558 default:
1559 assert(0);
1561 have_last_line = 1;
1562 last_line = endpos - startpos;
1563 *curpos = endpos;
1564 return p;
1567 class arc_object : public linear_object {
1568 int clockwise;
1569 position cent;
1570 double rad;
1571 public:
1572 arc_object(int, const position &, const position &, const position &);
1573 position origin() { return cent; }
1574 position center() { return cent; }
1575 double radius() { return rad; }
1576 position north();
1577 position south();
1578 position east();
1579 position west();
1580 position north_east();
1581 position north_west();
1582 position south_east();
1583 position south_west();
1584 void update_bounding_box(bounding_box *);
1585 object_type type() { return ARC_OBJECT; }
1586 void print();
1587 void move_by(const position &pos);
1590 arc_object::arc_object(int cw, const position &s, const position &e,
1591 const position &c)
1592 : linear_object(s, e), clockwise(cw), cent(c)
1594 rad = hypot(c - s);
1597 void arc_object::move_by(const position &pos)
1599 linear_object::move_by(pos);
1600 cent += pos;
1603 // we get arc corners from the corresponding circle
1605 position arc_object::north()
1607 position result(cent);
1608 result.y += rad;
1609 return result;
1612 position arc_object::south()
1614 position result(cent);
1615 result.y -= rad;
1616 return result;
1619 position arc_object::east()
1621 position result(cent);
1622 result.x += rad;
1623 return result;
1626 position arc_object::west()
1628 position result(cent);
1629 result.x -= rad;
1630 return result;
1633 position arc_object::north_east()
1635 position result(cent);
1636 result.x += rad/M_SQRT2;
1637 result.y += rad/M_SQRT2;
1638 return result;
1641 position arc_object::north_west()
1643 position result(cent);
1644 result.x -= rad/M_SQRT2;
1645 result.y += rad/M_SQRT2;
1646 return result;
1649 position arc_object::south_east()
1651 position result(cent);
1652 result.x += rad/M_SQRT2;
1653 result.y -= rad/M_SQRT2;
1654 return result;
1657 position arc_object::south_west()
1659 position result(cent);
1660 result.x -= rad/M_SQRT2;
1661 result.y -= rad/M_SQRT2;
1662 return result;
1666 void arc_object::print()
1668 if (lt.type == line_type::invisible)
1669 return;
1670 out->set_color(0, graphic_object::get_outline_color());
1671 // handle arrow direction; make shorter line for arc
1672 position sp, ep, b;
1673 if (clockwise) {
1674 sp = en;
1675 ep = strt;
1676 } else {
1677 sp = strt;
1678 ep = en;
1680 if (arrow_at_start) {
1681 double theta = aht.height / rad;
1682 if (clockwise)
1683 theta = - theta;
1684 b = strt - cent;
1685 b = position(b.x*cos(theta) - b.y*sin(theta),
1686 b.x*sin(theta) + b.y*cos(theta)) + cent;
1687 if (clockwise)
1688 ep = b;
1689 else
1690 sp = b;
1691 if (aht.solid && out->supports_filled_polygons()) {
1692 draw_arrow(strt, strt - b, aht, lt,
1693 graphic_object::get_outline_color());
1694 } else {
1695 position v = b;
1696 theta = fabs(lt.thickness) / 72 / 4 / rad;
1697 if (clockwise)
1698 theta = - theta;
1699 b = strt - cent;
1700 b = position(b.x*cos(theta) - b.y*sin(theta),
1701 b.x*sin(theta) + b.y*cos(theta)) + cent;
1702 draw_arrow(b, b - v, aht, lt,
1703 graphic_object::get_outline_color());
1704 out->line(b, &v, 1, lt);
1707 if (arrow_at_end) {
1708 double theta = aht.height / rad;
1709 if (!clockwise)
1710 theta = - theta;
1711 b = en - cent;
1712 b = position(b.x*cos(theta) - b.y*sin(theta),
1713 b.x*sin(theta) + b.y*cos(theta)) + cent;
1714 if (clockwise)
1715 sp = b;
1716 else
1717 ep = b;
1718 if (aht.solid && out->supports_filled_polygons()) {
1719 draw_arrow(en, en - b, aht, lt,
1720 graphic_object::get_outline_color());
1721 } else {
1722 position v = b;
1723 theta = fabs(lt.thickness) / 72 / 4 / rad;
1724 if (!clockwise)
1725 theta = - theta;
1726 b = en - cent;
1727 b = position(b.x*cos(theta) - b.y*sin(theta),
1728 b.x*sin(theta) + b.y*cos(theta)) + cent;
1729 draw_arrow(b, b - v, aht, lt,
1730 graphic_object::get_outline_color());
1731 out->line(b, &v, 1, lt);
1734 out->arc(sp, cent, ep, lt);
1735 out->reset_color();
1738 inline double max(double a, double b)
1740 return a > b ? a : b;
1743 void arc_object::update_bounding_box(bounding_box *p)
1745 p->encompass(strt);
1746 p->encompass(en);
1747 position start_offset = strt - cent;
1748 if (start_offset.x == 0.0 && start_offset.y == 0.0)
1749 return;
1750 position end_offset = en - cent;
1751 if (end_offset.x == 0.0 && end_offset.y == 0.0)
1752 return;
1753 double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1754 double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1755 if (clockwise) {
1756 double temp = start_quad;
1757 start_quad = end_quad;
1758 end_quad = temp;
1760 if (start_quad < 0.0)
1761 start_quad += 4.0;
1762 while (end_quad <= start_quad)
1763 end_quad += 4.0;
1764 double r = max(hypot(start_offset), hypot(end_offset));
1765 for (int q = int(start_quad) + 1; q < end_quad; q++) {
1766 position offset;
1767 switch (q % 4) {
1768 case 0:
1769 offset.x = r;
1770 break;
1771 case 1:
1772 offset.y = r;
1773 break;
1774 case 2:
1775 offset.x = -r;
1776 break;
1777 case 3:
1778 offset.y = -r;
1779 break;
1781 p->encompass(cent + offset);
1785 // We ignore the with attribute. The at attribute always refers to the center.
1787 linear_object *object_spec::make_arc(position *curpos, direction *dirp)
1789 *dirp = dir;
1790 int cw = (flags & IS_CLOCKWISE) != 0;
1791 // compute the start
1792 position startpos;
1793 if (flags & HAS_FROM)
1794 startpos = from;
1795 else
1796 startpos = *curpos;
1797 if (!(flags & HAS_RADIUS))
1798 lookup_variable("arcrad", &radius);
1799 // compute the end
1800 position endpos;
1801 if (flags & HAS_TO)
1802 endpos = to;
1803 else {
1804 position m(radius, radius);
1805 // Adjust the signs.
1806 if (cw) {
1807 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1808 m.x = -m.x;
1809 if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1810 m.y = -m.y;
1811 *dirp = direction((dir + 3) % 4);
1813 else {
1814 if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1815 m.x = -m.x;
1816 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1817 m.y = -m.y;
1818 *dirp = direction((dir + 1) % 4);
1820 endpos = startpos + m;
1822 // compute the center
1823 position centerpos;
1824 if (flags & HAS_AT)
1825 centerpos = at;
1826 else if (startpos == endpos)
1827 centerpos = startpos;
1828 else {
1829 position h = (endpos - startpos)/2.0;
1830 double d = hypot(h);
1831 if (radius <= 0)
1832 radius = .25;
1833 // make the radius big enough
1834 while (radius < d)
1835 radius *= 2.0;
1836 double alpha = acos(d/radius);
1837 double theta = atan2(h.y, h.x);
1838 if (cw)
1839 theta -= alpha;
1840 else
1841 theta += alpha;
1842 centerpos = position(cos(theta), sin(theta))*radius + startpos;
1844 arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1845 *curpos = endpos;
1846 return p;
1849 graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1851 linear_object *obj;
1852 if (type == ARC_OBJECT)
1853 obj = make_arc(curpos, dirp);
1854 else
1855 obj = make_line(curpos, dirp);
1856 if (type == ARROW_OBJECT
1857 && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1858 flags |= HAS_RIGHT_ARROW_HEAD;
1859 if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1860 arrow_head_type a;
1861 int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1862 int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1863 if (flags & HAS_HEIGHT)
1864 a.height = height;
1865 else
1866 lookup_variable("arrowht", &a.height);
1867 if (flags & HAS_WIDTH)
1868 a.width = width;
1869 else
1870 lookup_variable("arrowwid", &a.width);
1871 double solid;
1872 lookup_variable("arrowhead", &solid);
1873 a.solid = solid != 0.0;
1874 obj->add_arrows(at_start, at_end, a);
1876 return obj;
1879 object *object_spec::make_object(position *curpos, direction *dirp)
1881 graphic_object *obj = 0;
1882 switch (type) {
1883 case BLOCK_OBJECT:
1884 obj = make_block(curpos, dirp);
1885 break;
1886 case BOX_OBJECT:
1887 obj = make_box(curpos, dirp);
1888 break;
1889 case TEXT_OBJECT:
1890 obj = make_text(curpos, dirp);
1891 break;
1892 case ELLIPSE_OBJECT:
1893 obj = make_ellipse(curpos, dirp);
1894 break;
1895 case CIRCLE_OBJECT:
1896 obj = make_circle(curpos, dirp);
1897 break;
1898 case MOVE_OBJECT:
1899 obj = make_move(curpos, dirp);
1900 break;
1901 case ARC_OBJECT:
1902 case LINE_OBJECT:
1903 case SPLINE_OBJECT:
1904 case ARROW_OBJECT:
1905 obj = make_linear(curpos, dirp);
1906 break;
1907 case MARK_OBJECT:
1908 case OTHER_OBJECT:
1909 default:
1910 assert(0);
1911 break;
1913 if (obj) {
1914 if (flags & IS_INVISIBLE)
1915 obj->set_invisible();
1916 if (text != 0)
1917 obj->add_text(text, (flags & IS_ALIGNED) != 0);
1918 if (flags & IS_DOTTED)
1919 obj->set_dotted(dash_width);
1920 else if (flags & IS_DASHED)
1921 obj->set_dashed(dash_width);
1922 double th;
1923 if (flags & HAS_THICKNESS)
1924 th = thickness;
1925 else
1926 lookup_variable("linethick", &th);
1927 obj->set_thickness(th);
1928 if (flags & IS_OUTLINED)
1929 obj->set_outline_color(outlined);
1930 if (flags & IS_XSLANTED)
1931 obj->set_xslanted(xslanted);
1932 if (flags & IS_YSLANTED)
1933 obj->set_yslanted(yslanted);
1934 if (flags & (IS_DEFAULT_FILLED | IS_FILLED)) {
1935 if (flags & IS_SHADED)
1936 obj->set_fill_color(shaded);
1937 else {
1938 if (flags & IS_DEFAULT_FILLED)
1939 lookup_variable("fillval", &fill);
1940 if (fill < 0.0)
1941 error("bad fill value %1", fill);
1942 else
1943 obj->set_fill(fill);
1947 return obj;
1950 struct string_list {
1951 string_list *next;
1952 char *str;
1953 string_list(char *);
1954 ~string_list();
1957 string_list::string_list(char *s)
1958 : next(0), str(s)
1962 string_list::~string_list()
1964 a_delete str;
1967 /* A path is used to hold the argument to the `with' attribute. For
1968 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1969 take a place and follow the path through the place to place within the
1970 place. Note that `.A.B.C.sw' will work.
1972 For compatibility with DWB pic, `with' accepts positions also (this
1973 is incorrectly documented in CSTR 116). */
1975 path::path(corner c)
1976 : crn(c), label_list(0), ypath(0), is_position(0)
1980 path::path(position p)
1981 : crn(0), label_list(0), ypath(0), is_position(1)
1983 pos.x = p.x;
1984 pos.y = p.y;
1987 path::path(char *l, corner c)
1988 : crn(c), ypath(0), is_position(0)
1990 label_list = new string_list(l);
1993 path::~path()
1995 while (label_list) {
1996 string_list *tem = label_list;
1997 label_list = label_list->next;
1998 delete tem;
2000 delete ypath;
2003 void path::append(corner c)
2005 assert(crn == 0);
2006 crn = c;
2009 void path::append(char *s)
2011 string_list **p;
2012 for (p = &label_list; *p; p = &(*p)->next)
2014 *p = new string_list(s);
2017 void path::set_ypath(path *p)
2019 ypath = p;
2022 // return non-zero for success
2024 int path::follow(const place &pl, place *result) const
2026 if (is_position) {
2027 result->x = pos.x;
2028 result->y = pos.y;
2029 result->obj = 0;
2030 return 1;
2032 const place *p = &pl;
2033 for (string_list *lb = label_list; lb; lb = lb->next)
2034 if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
2035 lex_error("object does not contain a place `%1'", lb->str);
2036 return 0;
2038 if (crn == 0 || p->obj == 0)
2039 *result = *p;
2040 else {
2041 position ps = ((p->obj)->*(crn))();
2042 result->x = ps.x;
2043 result->y = ps.y;
2044 result->obj = 0;
2046 if (ypath) {
2047 place tem;
2048 if (!ypath->follow(pl, &tem))
2049 return 0;
2050 result->y = tem.y;
2051 if (result->obj != tem.obj)
2052 result->obj = 0;
2054 return 1;
2057 void print_object_list(object *p)
2059 for (; p; p = p->next) {
2060 p->print();
2061 p->print_text();
2065 void print_picture(object *obj)
2067 bounding_box bb;
2068 for (object *p = obj; p; p = p->next)
2069 p->update_bounding_box(&bb);
2070 double scale;
2071 lookup_variable("scale", &scale);
2072 out->start_picture(scale, bb.ll, bb.ur);
2073 print_object_list(obj);
2074 out->finish_picture();