2 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
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
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
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 void print_object_list(object *);
28 line_type::line_type()
29 : type(solid), thickness(1.0)
33 output::output() : args(0), desired_height(0.0), desired_width(0.0)
42 void output::set_desired_width_height(double wid, double ht)
48 void output::set_args(const char *s)
51 if (s == 0 || *s == '\0')
57 int output::supports_filled_polygons()
62 void output::begin_block(const position &, const position &)
66 void output::end_block()
70 double output::compute_scale(double sc, const position &ll, const position &ur)
72 distance dim = ur - ll;
73 if (desired_width != 0.0 || desired_height != 0.0) {
75 if (desired_width != 0.0) {
77 error("width specified for picture with zero width");
79 sc = dim.x/desired_width;
81 if (desired_height != 0.0) {
83 error("height specified for picture with zero height");
85 double tem = dim.y/desired_height;
90 return sc == 0.0 ? 1.0 : sc;
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;
111 position::position(const place &pl)
114 // Use two statements to work around bug in SGI C++.
115 object *tem = pl.obj;
116 *this = tem->origin();
124 position::position() : x(0.0), y(0.0)
128 position::position(double a, double b) : x(a), y(b)
133 int operator==(const position &a, const position &b)
135 return a.x == b.x && a.y == b.y;
138 int operator!=(const position &a, const position &b)
140 return a.x != b.x || a.y != b.y;
143 position &position::operator+=(const position &a)
150 position &position::operator-=(const position &a)
157 position &position::operator*=(double a)
164 position &position::operator/=(double a)
171 position operator-(const position &a)
173 return position(-a.x, -a.y);
176 position operator+(const position &a, const position &b)
178 return position(a.x + b.x, a.y + b.y);
181 position operator-(const position &a, const position &b)
183 return position(a.x - b.x, a.y - b.y);
186 position operator/(const position &a, double n)
188 return position(a.x/n, a.y/n);
191 position operator*(const position &a, double n)
193 return position(a.x*n, a.y*n);
198 double operator*(const position &a, const position &b)
200 return a.x*b.x + a.y*b.y;
203 double hypot(const position &a)
205 return hypot(a.x, a.y);
208 struct arrow_head_type {
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)
218 double hyp = hypot(dir);
220 error("cannot draw arrow on object with zero length");
223 position base = -dir;
224 base *= aht.height/hyp;
225 position n(dir.y, -dir.x);
226 n *= aht.width/(hyp*2.0);
228 slt.type = line_type::solid;
229 if (aht.solid && out->supports_filled_polygons()) {
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 out->polygon(v, 3, slt, 1);
241 v[1] = pos + base + n;
242 out->line(pos + base - n, v, 2, slt);
246 object::object() : prev(0), next(0)
254 void object::move_by(const position &)
262 void object::print_text()
271 struct bounding_box {
277 void encompass(const position &);
280 bounding_box::bounding_box()
285 void bounding_box::encompass(const position &pos)
304 void object::update_bounding_box(bounding_box *)
308 position object::origin()
310 return position(0.0,0.0);
313 position object::north()
318 position object::south()
323 position object::east()
328 position object::west()
333 position object::north_east()
338 position object::north_west()
343 position object::south_east()
348 position object::south_west()
353 position object::start()
358 position object::end()
363 position object::center()
368 double object::width()
373 double object::radius()
378 double object::height()
383 place *object::find_label(const char *)
388 segment::segment(const position &a, int n, segment *p)
389 : is_absolute(n), pos(a), next(p)
393 text_item::text_item(char *t, const char *fn, int ln)
394 : next(0), text(t), filename(fn), lineno(ln)
396 adj.h = CENTER_ADJUST;
400 text_item::~text_item()
405 object_spec::object_spec(object_type t) : type(t)
410 segment_width = segment_height = 0.0;
411 segment_is_absolute = 0;
416 dir = RIGHT_DIRECTION;
419 object_spec::~object_spec()
422 while (segment_list != 0) {
423 segment *tem = segment_list;
424 segment_list = segment_list->next;
427 object *p = oblist.head;
434 text_item *tem = text;
443 class command_object : public object {
445 const char *filename;
448 command_object(char *, const char *, int);
450 object_type type() { return OTHER_OBJECT; }
454 command_object::command_object(char *p, const char *fn, int ln)
455 : s(p), filename(fn), lineno(ln)
459 command_object::~command_object()
464 void command_object::print()
466 out->command(s, filename, lineno);
469 object *make_command_object(char *s, const char *fn, int ln)
471 return new command_object(s, fn, ln);
474 class mark_object : public object {
480 object *make_mark_object()
482 return new mark_object();
485 mark_object::mark_object()
489 object_type mark_object::type()
494 object_list::object_list() : head(0), tail(0)
498 void object_list::append(object *obj)
501 obj->next = obj->prev = 0;
512 void object_list::wrap_up_block(object_list *ol)
515 for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
533 text_piece::text_piece()
534 : text(0), filename(0), lineno(-1)
536 adj.h = CENTER_ADJUST;
540 text_piece::~text_piece()
545 class graphic_object : public object {
556 object_type type() = 0;
558 void add_text(text_item *, int);
559 void set_dotted(double);
560 void set_dashed(double);
561 void set_thickness(double);
562 void set_invisible();
563 void set_outline_color(char *);
564 char *get_outline_color();
565 virtual void set_fill(double);
566 virtual void set_fill_color(char *);
569 graphic_object::graphic_object()
570 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
574 void graphic_object::set_dotted(double wid)
576 lt.type = line_type::dotted;
580 void graphic_object::set_dashed(double wid)
582 lt.type = line_type::dashed;
586 void graphic_object::set_thickness(double th)
591 void graphic_object::set_fill(double)
595 void graphic_object::set_fill_color(char *c)
597 color_fill = strsave(c);
600 void graphic_object::set_outline_color(char *c)
602 outline_color = strsave(c);
605 char *graphic_object::get_outline_color()
607 return outline_color;
610 void graphic_object::set_invisible()
612 lt.type = line_type::invisible;
615 void graphic_object::add_text(text_item *t, int a)
620 for (p = t; p; p = p->next)
625 text = new text_piece[len];
626 for (p = t, len = 0; p; p = p->next, len++) {
627 text[len].text = p->text;
629 text[len].adj = p->adj;
630 text[len].filename = p->filename;
631 text[len].lineno = p->lineno;
637 void graphic_object::print_text()
641 position d(end() - start());
642 if (d.x != 0.0 || d.y != 0.0)
643 angle = atan2(d.y, d.x);
646 out->set_color(color_fill, get_outline_color());
647 out->text(center(), text, ntext, angle);
652 graphic_object::~graphic_object()
655 ad_delete(ntext) text;
658 class rectangle_object : public graphic_object {
663 rectangle_object(const position &);
664 double width() { return dim.x; }
665 double height() { return dim.y; }
666 position origin() { return cent; }
667 position center() { return cent; }
668 position north() { return position(cent.x, cent.y + dim.y/2.0); }
669 position south() { return position(cent.x, cent.y - dim.y/2.0); }
670 position east() { return position(cent.x + dim.x/2.0, cent.y); }
671 position west() { return position(cent.x - dim.x/2.0, cent.y); }
672 position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
673 position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
674 position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
675 position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
676 object_type type() = 0;
677 void update_bounding_box(bounding_box *);
678 void move_by(const position &);
681 rectangle_object::rectangle_object(const position &d)
686 void rectangle_object::update_bounding_box(bounding_box *p)
688 p->encompass(cent - dim/2.0);
689 p->encompass(cent + dim/2.0);
692 void rectangle_object::move_by(const position &a)
697 class closed_object : public rectangle_object {
699 closed_object(const position &);
700 object_type type() = 0;
701 void set_fill(double);
702 void set_fill_color(char *fill);
704 double fill; // < 0 if not filled
705 char *color_fill; // = 0 if not colored
708 closed_object::closed_object(const position &pos)
709 : rectangle_object(pos), fill(-1.0), color_fill(0)
713 void closed_object::set_fill(double f)
719 void closed_object::set_fill_color(char *f)
721 color_fill = strsave(f);
724 class box_object : public closed_object {
728 box_object(const position &, double);
729 object_type type() { return BOX_OBJECT; }
731 position north_east();
732 position north_west();
733 position south_east();
734 position south_west();
737 box_object::box_object(const position &pos, double r)
738 : closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
742 const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
744 position box_object::north_east()
746 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
747 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
750 position box_object::north_west()
752 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
753 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
756 position box_object::south_east()
758 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
759 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
762 position box_object::south_west()
764 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
765 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
768 void box_object::print()
770 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
772 out->set_color(color_fill, graphic_object::get_outline_color());
774 distance dim2 = dim/2.0;
776 vec[0] = cent + position(dim2.x, -dim2.y);
777 vec[1] = cent + position(dim2.x, dim2.y);
778 vec[2] = cent + position(-dim2.x, dim2.y);
779 vec[3] = cent + position(-dim2.x, -dim2.y);
780 out->polygon(vec, 4, lt, fill);
783 distance abs_dim(fabs(dim.x), fabs(dim.y));
784 out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
789 graphic_object *object_spec::make_box(position *curpos, direction *dirp)
791 static double last_box_height;
792 static double last_box_width;
793 static double last_box_radius;
794 static int have_last_box = 0;
795 if (!(flags & HAS_HEIGHT)) {
796 if ((flags & IS_SAME) && have_last_box)
797 height = last_box_height;
799 lookup_variable("boxht", &height);
801 if (!(flags & HAS_WIDTH)) {
802 if ((flags & IS_SAME) && have_last_box)
803 width = last_box_width;
805 lookup_variable("boxwid", &width);
807 if (!(flags & HAS_RADIUS)) {
808 if ((flags & IS_SAME) && have_last_box)
809 radius = last_box_radius;
811 lookup_variable("boxrad", &radius);
813 last_box_width = width;
814 last_box_height = height;
815 last_box_radius = radius;
817 radius = fabs(radius);
818 if (radius*2.0 > fabs(width))
819 radius = fabs(width/2.0);
820 if (radius*2.0 > fabs(height))
821 radius = fabs(height/2.0);
822 box_object *p = new box_object(position(width, height), radius);
823 if (!position_rectangle(p, curpos, dirp)) {
830 // return non-zero for success
832 int object_spec::position_rectangle(rectangle_object *p,
833 position *curpos, direction *dirp)
836 dir = *dirp; // ignore any direction in attribute list
840 motion.y = p->height()/2.0;
843 motion.y = -p->height()/2.0;
846 motion.x = -p->width()/2.0;
848 case RIGHT_DIRECTION:
849 motion.x = p->width()/2.0;
854 if (flags & HAS_AT) {
856 if (flags & HAS_WITH) {
860 if (!with->follow(here, &offset))
875 class block_object : public rectangle_object {
879 block_object(const position &, const object_list &ol, PTABLE(place) *t);
881 place *find_label(const char *);
883 void move_by(const position &);
887 block_object::block_object(const position &d, const object_list &ol,
889 : rectangle_object(d), oblist(ol), tbl(t)
893 block_object::~block_object()
896 object *p = oblist.head;
904 void block_object::print()
906 out->begin_block(south_west(), north_east());
907 print_object_list(oblist.head);
911 static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
913 // Adjust all the labels that aren't attached to objects.
914 PTABLE_ITERATOR(place) iter(tbl);
917 while (iter.next(&key, &pl))
918 if (key && csupper(key[0]) && pl->obj == 0) {
924 void block_object::move_by(const position &a)
927 for (object *p = oblist.head; p; p = p->next)
929 adjust_objectless_places(tbl, a);
933 place *block_object::find_label(const char *name)
935 return tbl->lookup(name);
938 object_type block_object::type()
943 graphic_object *object_spec::make_block(position *curpos, direction *dirp)
946 for (object *p = oblist.head; p; p = p->next)
947 p->update_bounding_box(&bb);
950 position m = -(bb.ll + bb.ur)/2.0;
951 for (object *p = oblist.head; p; p = p->next)
953 adjust_objectless_places(tbl, m);
956 if (flags & HAS_WIDTH)
958 if (flags & HAS_HEIGHT)
960 block_object *block = new block_object(dim, oblist, tbl);
961 if (!position_rectangle(block, curpos, dirp)) {
966 oblist.head = oblist.tail = 0;
970 class text_object : public rectangle_object {
972 text_object(const position &);
973 object_type type() { return TEXT_OBJECT; }
976 text_object::text_object(const position &d)
977 : rectangle_object(d)
981 graphic_object *object_spec::make_text(position *curpos, direction *dirp)
983 if (!(flags & HAS_HEIGHT)) {
984 lookup_variable("textht", &height);
986 for (text_item *t = text; t; t = t->next)
990 if (!(flags & HAS_WIDTH))
991 lookup_variable("textwid", &width);
992 text_object *p = new text_object(position(width, height));
993 if (!position_rectangle(p, curpos, dirp)) {
1001 class ellipse_object : public closed_object {
1003 ellipse_object(const position &);
1004 position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1005 cent.y + dim.y/(M_SQRT2*2.0)); }
1006 position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1007 cent.y + dim.y/(M_SQRT2*2.0)); }
1008 position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1009 cent.y - dim.y/(M_SQRT2*2.0)); }
1010 position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1011 cent.y - dim.y/(M_SQRT2*2.0)); }
1012 double radius() { return dim.x/2.0; }
1013 object_type type() { return ELLIPSE_OBJECT; }
1017 ellipse_object::ellipse_object(const position &d)
1022 void ellipse_object::print()
1024 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1026 out->set_color(color_fill, graphic_object::get_outline_color());
1027 out->ellipse(cent, dim, lt, fill);
1031 graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1033 static double last_ellipse_height;
1034 static double last_ellipse_width;
1035 static int have_last_ellipse = 0;
1036 if (!(flags & HAS_HEIGHT)) {
1037 if ((flags & IS_SAME) && have_last_ellipse)
1038 height = last_ellipse_height;
1040 lookup_variable("ellipseht", &height);
1042 if (!(flags & HAS_WIDTH)) {
1043 if ((flags & IS_SAME) && have_last_ellipse)
1044 width = last_ellipse_width;
1046 lookup_variable("ellipsewid", &width);
1048 last_ellipse_width = width;
1049 last_ellipse_height = height;
1050 have_last_ellipse = 1;
1051 ellipse_object *p = new ellipse_object(position(width, height));
1052 if (!position_rectangle(p, curpos, dirp)) {
1059 class circle_object : public ellipse_object {
1061 circle_object(double);
1062 object_type type() { return CIRCLE_OBJECT; }
1066 circle_object::circle_object(double diam)
1067 : ellipse_object(position(diam, diam))
1071 void circle_object::print()
1073 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1075 out->set_color(color_fill, graphic_object::get_outline_color());
1076 out->circle(cent, dim.x/2.0, lt, fill);
1080 graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1082 static double last_circle_radius;
1083 static int have_last_circle = 0;
1084 if (!(flags & HAS_RADIUS)) {
1085 if ((flags & IS_SAME) && have_last_circle)
1086 radius = last_circle_radius;
1088 lookup_variable("circlerad", &radius);
1090 last_circle_radius = radius;
1091 have_last_circle = 1;
1092 circle_object *p = new circle_object(radius*2.0);
1093 if (!position_rectangle(p, curpos, dirp)) {
1100 class move_object : public graphic_object {
1104 move_object(const position &s, const position &e);
1105 position origin() { return en; }
1106 object_type type() { return MOVE_OBJECT; }
1107 void update_bounding_box(bounding_box *);
1108 void move_by(const position &);
1111 move_object::move_object(const position &s, const position &e)
1116 void move_object::update_bounding_box(bounding_box *p)
1122 void move_object::move_by(const position &a)
1128 graphic_object *object_spec::make_move(position *curpos, direction *dirp)
1130 static position last_move;
1131 static int have_last_move = 0;
1133 // No need to look at at since `at' attribute sets `from' attribute.
1134 position startpos = (flags & HAS_FROM) ? from : *curpos;
1135 if (!(flags & HAS_SEGMENT)) {
1136 if ((flags & IS_SAME) && have_last_move)
1137 segment_pos = last_move;
1141 segment_pos.y = segment_height;
1143 case DOWN_DIRECTION:
1144 segment_pos.y = -segment_height;
1146 case LEFT_DIRECTION:
1147 segment_pos.x = -segment_width;
1149 case RIGHT_DIRECTION:
1150 segment_pos.x = segment_width;
1157 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1158 // Reverse the segment_list so that it's in forward order.
1159 segment *old = segment_list;
1162 segment *tem = old->next;
1163 old->next = segment_list;
1167 // Compute the end position.
1168 position endpos = startpos;
1169 for (segment *s = segment_list; s; s = s->next)
1175 last_move = endpos - startpos;
1176 move_object *p = new move_object(startpos, endpos);
1181 class linear_object : public graphic_object {
1183 char arrow_at_start;
1185 arrow_head_type aht;
1189 linear_object(const position &s, const position &e);
1190 position start() { return strt; }
1191 position end() { return en; }
1192 void move_by(const position &);
1193 void update_bounding_box(bounding_box *) = 0;
1194 object_type type() = 0;
1195 void add_arrows(int at_start, int at_end, const arrow_head_type &);
1198 class line_object : public linear_object {
1203 line_object(const position &s, const position &e, position *, int);
1205 position origin() { return strt; }
1206 position center() { return (strt + en)/2.0; }
1207 position north() { return (en.y - strt.y) > 0 ? en : strt; }
1208 position south() { return (en.y - strt.y) < 0 ? en : strt; }
1209 position east() { return (en.x - strt.x) > 0 ? en : strt; }
1210 position west() { return (en.x - strt.x) < 0 ? en : strt; }
1211 object_type type() { return LINE_OBJECT; }
1212 void update_bounding_box(bounding_box *);
1214 void move_by(const position &);
1217 class arrow_object : public line_object {
1219 arrow_object(const position &, const position &, position *, int);
1220 object_type type() { return ARROW_OBJECT; }
1223 class spline_object : public line_object {
1225 spline_object(const position &, const position &, position *, int);
1226 object_type type() { return SPLINE_OBJECT; }
1228 void update_bounding_box(bounding_box *);
1231 linear_object::linear_object(const position &s, const position &e)
1232 : arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1236 void linear_object::move_by(const position &a)
1242 void linear_object::add_arrows(int at_start, int at_end,
1243 const arrow_head_type &a)
1245 arrow_at_start = at_start;
1246 arrow_at_end = at_end;
1250 line_object::line_object(const position &s, const position &e,
1252 : linear_object(s, e), v(p), n(i)
1256 void line_object::print()
1258 if (lt.type == line_type::invisible)
1260 out->set_color(0, graphic_object::get_outline_color());
1261 out->line(strt, v, n, lt);
1263 draw_arrow(strt, strt-v[0], aht, lt, graphic_object::get_outline_color());
1265 draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt,
1266 graphic_object::get_outline_color());
1270 void line_object::update_bounding_box(bounding_box *p)
1273 for (int i = 0; i < n; i++)
1277 void line_object::move_by(const position &pos)
1279 linear_object::move_by(pos);
1280 for (int i = 0; i < n; i++)
1284 void spline_object::update_bounding_box(bounding_box *p)
1296 [ the points for the Bezier cubic ]
1304 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1305 [ the equation for the Bezier cubic ]
1307 = .125*q1 + .75*q2 + .125*q3
1310 for (int i = 1; i < n; i++)
1311 p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1314 arrow_object::arrow_object(const position &s, const position &e,
1316 : line_object(s, e, p, i)
1320 spline_object::spline_object(const position &s, const position &e,
1322 : line_object(s, e, p, i)
1326 void spline_object::print()
1328 if (lt.type == line_type::invisible)
1330 out->set_color(0, graphic_object::get_outline_color());
1331 out->spline(strt, v, n, lt);
1333 draw_arrow(strt, strt-v[0], aht, lt, graphic_object::get_outline_color());
1335 draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt,
1336 graphic_object::get_outline_color());
1340 line_object::~line_object()
1345 linear_object *object_spec::make_line(position *curpos, direction *dirp)
1347 static position last_line;
1348 static int have_last_line = 0;
1350 // No need to look at at since `at' attribute sets `from' attribute.
1351 position startpos = (flags & HAS_FROM) ? from : *curpos;
1352 if (!(flags & HAS_SEGMENT)) {
1353 if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1355 segment_pos = last_line;
1359 segment_pos.y = segment_height;
1361 case DOWN_DIRECTION:
1362 segment_pos.y = -segment_height;
1364 case LEFT_DIRECTION:
1365 segment_pos.x = -segment_width;
1367 case RIGHT_DIRECTION:
1368 segment_pos.x = segment_width;
1374 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1375 // reverse the segment_list so that it's in forward order
1376 segment *old = segment_list;
1379 segment *tem = old->next;
1380 old->next = segment_list;
1384 // Absolutise all movements
1385 position endpos = startpos;
1388 for (s = segment_list; s; s = s->next, nsegments++)
1394 s->is_absolute = 1; // to avoid confusion
1398 position *v = new position[nsegments];
1400 for (s = segment_list; s; s = s->next, i++)
1402 if (flags & IS_DEFAULT_CHOPPED) {
1403 lookup_variable("circlerad", &start_chop);
1404 end_chop = start_chop;
1405 flags |= IS_CHOPPED;
1407 if (flags & IS_CHOPPED) {
1408 position start_chop_vec, end_chop_vec;
1409 if (start_chop != 0.0) {
1410 start_chop_vec = v[0] - startpos;
1411 start_chop_vec *= start_chop / hypot(start_chop_vec);
1413 if (end_chop != 0.0) {
1414 end_chop_vec = (v[nsegments - 1]
1415 - (nsegments > 1 ? v[nsegments - 2] : startpos));
1416 end_chop_vec *= end_chop / hypot(end_chop_vec);
1418 startpos += start_chop_vec;
1419 v[nsegments - 1] -= end_chop_vec;
1420 endpos -= end_chop_vec;
1424 p = new spline_object(startpos, endpos, v, nsegments);
1427 p = new arrow_object(startpos, endpos, v, nsegments);
1430 p = new line_object(startpos, endpos, v, nsegments);
1436 last_line = endpos - startpos;
1441 class arc_object : public linear_object {
1446 arc_object(int, const position &, const position &, const position &);
1447 position origin() { return cent; }
1448 position center() { return cent; }
1449 double radius() { return rad; }
1454 position north_east();
1455 position north_west();
1456 position south_east();
1457 position south_west();
1458 void update_bounding_box(bounding_box *);
1459 object_type type() { return ARC_OBJECT; }
1461 void move_by(const position &pos);
1464 arc_object::arc_object(int cw, const position &s, const position &e,
1466 : linear_object(s, e), clockwise(cw), cent(c)
1471 void arc_object::move_by(const position &pos)
1473 linear_object::move_by(pos);
1477 // we get arc corners from the corresponding circle
1479 position arc_object::north()
1481 position result(cent);
1486 position arc_object::south()
1488 position result(cent);
1493 position arc_object::east()
1495 position result(cent);
1500 position arc_object::west()
1502 position result(cent);
1507 position arc_object::north_east()
1509 position result(cent);
1510 result.x += rad/M_SQRT2;
1511 result.y += rad/M_SQRT2;
1515 position arc_object::north_west()
1517 position result(cent);
1518 result.x -= rad/M_SQRT2;
1519 result.y += rad/M_SQRT2;
1523 position arc_object::south_east()
1525 position result(cent);
1526 result.x += rad/M_SQRT2;
1527 result.y -= rad/M_SQRT2;
1531 position arc_object::south_west()
1533 position result(cent);
1534 result.x -= rad/M_SQRT2;
1535 result.y -= rad/M_SQRT2;
1540 void arc_object::print()
1542 if (lt.type == line_type::invisible)
1544 out->set_color(0, graphic_object::get_outline_color());
1546 out->arc(en, cent, strt, lt);
1548 out->arc(strt, cent, en, lt);
1549 if (arrow_at_start) {
1550 position c = cent - strt;
1552 (clockwise ? position(c.y, -c.x) : position(-c.y, c.x)),
1553 aht, lt, graphic_object::get_outline_color());
1556 position e = en - cent;
1558 (clockwise ? position(e.y, -e.x) : position(-e.y, e.x)),
1559 aht, lt, graphic_object::get_outline_color());
1564 inline double max(double a, double b)
1566 return a > b ? a : b;
1569 void arc_object::update_bounding_box(bounding_box *p)
1573 position start_offset = strt - cent;
1574 if (start_offset.x == 0.0 && start_offset.y == 0.0)
1576 position end_offset = en - cent;
1577 if (end_offset.x == 0.0 && end_offset.y == 0.0)
1579 double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1580 double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1582 double temp = start_quad;
1583 start_quad = end_quad;
1586 if (start_quad < 0.0)
1588 while (end_quad <= start_quad)
1590 double r = max(hypot(start_offset), hypot(end_offset));
1591 for (int q = int(start_quad) + 1; q < end_quad; q++) {
1607 p->encompass(cent + offset);
1611 // We ignore the with attribute. The at attribute always refers to the center.
1613 linear_object *object_spec::make_arc(position *curpos, direction *dirp)
1616 int cw = (flags & IS_CLOCKWISE) != 0;
1617 // compute the start
1619 if (flags & HAS_FROM)
1623 if (!(flags & HAS_RADIUS))
1624 lookup_variable("arcrad", &radius);
1630 position m(radius, radius);
1631 // Adjust the signs.
1633 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1635 if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1637 *dirp = direction((dir + 3) % 4);
1640 if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1642 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1644 *dirp = direction((dir + 1) % 4);
1646 endpos = startpos + m;
1648 // compute the center
1652 else if (startpos == endpos)
1653 centerpos = startpos;
1655 position h = (endpos - startpos)/2.0;
1656 double d = hypot(h);
1659 // make the radius big enough
1662 double alpha = acos(d/radius);
1663 double theta = atan2(h.y, h.x);
1668 centerpos = position(cos(theta), sin(theta))*radius + startpos;
1670 arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1675 graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1678 if (type == ARC_OBJECT)
1679 obj = make_arc(curpos, dirp);
1681 obj = make_line(curpos, dirp);
1682 if (type == ARROW_OBJECT
1683 && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1684 flags |= HAS_RIGHT_ARROW_HEAD;
1685 if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1687 int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1688 int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1689 if (flags & HAS_HEIGHT)
1692 lookup_variable("arrowht", &a.height);
1693 if (flags & HAS_WIDTH)
1696 lookup_variable("arrowwid", &a.width);
1698 lookup_variable("arrowhead", &solid);
1699 a.solid = solid != 0.0;
1700 obj->add_arrows(at_start, at_end, a);
1705 object *object_spec::make_object(position *curpos, direction *dirp)
1707 graphic_object *obj = 0;
1710 obj = make_block(curpos, dirp);
1713 obj = make_box(curpos, dirp);
1716 obj = make_text(curpos, dirp);
1718 case ELLIPSE_OBJECT:
1719 obj = make_ellipse(curpos, dirp);
1722 obj = make_circle(curpos, dirp);
1725 obj = make_move(curpos, dirp);
1731 obj = make_linear(curpos, dirp);
1740 if (flags & IS_INVISIBLE)
1741 obj->set_invisible();
1743 obj->add_text(text, (flags & IS_ALIGNED) != 0);
1744 if (flags & IS_DOTTED)
1745 obj->set_dotted(dash_width);
1746 else if (flags & IS_DASHED)
1747 obj->set_dashed(dash_width);
1749 if (flags & HAS_THICKNESS)
1752 lookup_variable("linethick", &th);
1753 obj->set_thickness(th);
1754 if (flags & IS_OUTLINED)
1755 obj->set_outline_color(outlined);
1756 if (flags & (IS_DEFAULT_FILLED|IS_FILLED)) {
1757 if (flags & IS_SHADED)
1758 obj->set_fill_color(shaded);
1760 if (flags & IS_DEFAULT_FILLED)
1761 lookup_variable("fillval", &fill);
1763 error("bad fill value %1", fill);
1765 obj->set_fill(fill);
1772 struct string_list {
1775 string_list(char *);
1779 string_list::string_list(char *s)
1784 string_list::~string_list()
1789 /* A path is used to hold the argument to the `with' attribute. For
1790 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1791 take a place and follow the path through the place to place within the
1792 place. Note that `.A.B.C.sw' will work.
1794 For compatibility with DWB pic, `with' accepts positions also (this
1795 is incorrectly documented in CSTR 116). */
1797 path::path(corner c)
1798 : crn(c), label_list(0), ypath(0), is_position(0)
1802 path::path(position p)
1803 : crn(0), label_list(0), ypath(0), is_position(1)
1809 path::path(char *l, corner c)
1810 : crn(c), ypath(0), is_position(0)
1812 label_list = new string_list(l);
1817 while (label_list) {
1818 string_list *tem = label_list;
1819 label_list = label_list->next;
1825 void path::append(corner c)
1831 void path::append(char *s)
1834 for (p = &label_list; *p; p = &(*p)->next)
1836 *p = new string_list(s);
1839 void path::set_ypath(path *p)
1844 // return non-zero for success
1846 int path::follow(const place &pl, place *result) const
1854 const place *p = &pl;
1855 for (string_list *lb = label_list; lb; lb = lb->next)
1856 if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
1857 lex_error("object does not contain a place `%1'", lb->str);
1860 if (crn == 0 || p->obj == 0)
1863 position ps = ((p->obj)->*(crn))();
1870 if (!ypath->follow(pl, &tem))
1873 if (result->obj != tem.obj)
1879 void print_object_list(object *p)
1881 for (; p; p = p->next) {
1887 void print_picture(object *obj)
1890 for (object *p = obj; p; p = p->next)
1891 p->update_bounding_box(&bb);
1893 lookup_variable("scale", &scale);
1894 out->start_picture(scale, bb.ll, bb.ur);
1895 print_object_list(obj);
1896 out->finish_picture();