No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / groff / src / preproc / pic / object.cpp
blobf9b494f4b049beffa01f8fddf4d5a07a398bc6c9
1 /* $NetBSD$ */
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
24 #include "pic.h"
25 #include "ptable.h"
26 #include "object.h"
28 void print_object_list(object *);
30 line_type::line_type()
31 : type(solid), thickness(1.0)
35 output::output() : args(0), desired_height(0.0), desired_width(0.0)
39 output::~output()
41 a_delete args;
44 void output::set_desired_width_height(double wid, double ht)
46 desired_width = wid;
47 desired_height = ht;
50 void output::set_args(const char *s)
52 a_delete args;
53 if (s == 0 || *s == '\0')
54 args = 0;
55 else
56 args = strsave(s);
59 int output::supports_filled_polygons()
61 return 0;
64 void output::begin_block(const position &, const position &)
68 void output::end_block()
72 double output::compute_scale(double sc, const position &ll, const position &ur)
74 distance dim = ur - ll;
75 if (desired_width != 0.0 || desired_height != 0.0) {
76 sc = 0.0;
77 if (desired_width != 0.0) {
78 if (dim.x == 0.0)
79 error("width specified for picture with zero width");
80 else
81 sc = dim.x/desired_width;
83 if (desired_height != 0.0) {
84 if (dim.y == 0.0)
85 error("height specified for picture with zero height");
86 else {
87 double tem = dim.y/desired_height;
88 if (tem > sc)
89 sc = tem;
92 return sc == 0.0 ? 1.0 : sc;
94 else {
95 if (sc <= 0.0)
96 sc = 1.0;
97 distance sdim = dim/sc;
98 double max_width = 0.0;
99 lookup_variable("maxpswid", &max_width);
100 double max_height = 0.0;
101 lookup_variable("maxpsht", &max_height);
102 if ((max_width > 0.0 && sdim.x > max_width)
103 || (max_height > 0.0 && sdim.y > max_height)) {
104 double xscale = dim.x/max_width;
105 double yscale = dim.y/max_height;
106 return xscale > yscale ? xscale : yscale;
108 else
109 return sc;
113 position::position(const place &pl)
115 if (pl.obj != 0) {
116 // Use two statements to work around bug in SGI C++.
117 object *tem = pl.obj;
118 *this = tem->origin();
120 else {
121 x = pl.x;
122 y = pl.y;
126 position::position() : x(0.0), y(0.0)
130 position::position(double a, double b) : x(a), y(b)
135 int operator==(const position &a, const position &b)
137 return a.x == b.x && a.y == b.y;
140 int operator!=(const position &a, const position &b)
142 return a.x != b.x || a.y != b.y;
145 position &position::operator+=(const position &a)
147 x += a.x;
148 y += a.y;
149 return *this;
152 position &position::operator-=(const position &a)
154 x -= a.x;
155 y -= a.y;
156 return *this;
159 position &position::operator*=(double a)
161 x *= a;
162 y *= a;
163 return *this;
166 position &position::operator/=(double a)
168 x /= a;
169 y /= a;
170 return *this;
173 position operator-(const position &a)
175 return position(-a.x, -a.y);
178 position operator+(const position &a, const position &b)
180 return position(a.x + b.x, a.y + b.y);
183 position operator-(const position &a, const position &b)
185 return position(a.x - b.x, a.y - b.y);
188 position operator/(const position &a, double n)
190 return position(a.x/n, a.y/n);
193 position operator*(const position &a, double n)
195 return position(a.x*n, a.y*n);
198 // dot product
200 double operator*(const position &a, const position &b)
202 return a.x*b.x + a.y*b.y;
205 double hypot(const position &a)
207 return groff_hypot(a.x, a.y);
210 struct arrow_head_type {
211 double height;
212 double width;
213 int solid;
216 void draw_arrow(const position &pos, const distance &dir,
217 const arrow_head_type &aht, const line_type &lt,
218 char *outline_color_for_fill)
220 double hyp = hypot(dir);
221 if (hyp == 0.0) {
222 error("cannot draw arrow on object with zero length");
223 return;
225 position base = -dir;
226 base *= aht.height/hyp;
227 position n(dir.y, -dir.x);
228 n *= aht.width/(hyp*2.0);
229 line_type slt = lt;
230 slt.type = line_type::solid;
231 if (aht.solid && out->supports_filled_polygons()) {
232 position v[3];
233 v[0] = pos;
234 v[1] = pos + base + n;
235 v[2] = pos + base - n;
236 // fill with outline color
237 out->set_color(outline_color_for_fill, outline_color_for_fill);
238 // make stroke thin to avoid arrow sticking
239 slt.thickness = 0.1;
240 out->polygon(v, 3, slt, 1);
242 else {
243 // use two line segments to avoid arrow sticking
244 out->line(pos + base - n, &pos, 1, slt);
245 out->line(pos + base + n, &pos, 1, slt);
249 object::object() : prev(0), next(0)
253 object::~object()
257 void object::move_by(const position &)
261 void object::print()
265 void object::print_text()
269 int object::blank()
271 return 0;
274 struct bounding_box {
275 int blank;
276 position ll;
277 position ur;
279 bounding_box();
280 void encompass(const position &);
283 bounding_box::bounding_box()
284 : blank(1)
288 void bounding_box::encompass(const position &pos)
290 if (blank) {
291 ll = pos;
292 ur = pos;
293 blank = 0;
295 else {
296 if (pos.x < ll.x)
297 ll.x = pos.x;
298 if (pos.y < ll.y)
299 ll.y = pos.y;
300 if (pos.x > ur.x)
301 ur.x = pos.x;
302 if (pos.y > ur.y)
303 ur.y = pos.y;
307 void object::update_bounding_box(bounding_box *)
311 position object::origin()
313 return position(0.0,0.0);
316 position object::north()
318 return origin();
321 position object::south()
323 return origin();
326 position object::east()
328 return origin();
331 position object::west()
333 return origin();
336 position object::north_east()
338 return origin();
341 position object::north_west()
343 return origin();
346 position object::south_east()
348 return origin();
351 position object::south_west()
353 return origin();
356 position object::start()
358 return origin();
361 position object::end()
363 return origin();
366 position object::center()
368 return origin();
371 double object::width()
373 return 0.0;
376 double object::radius()
378 return 0.0;
381 double object::height()
383 return 0.0;
386 place *object::find_label(const char *)
388 return 0;
391 segment::segment(const position &a, int n, segment *p)
392 : is_absolute(n), pos(a), next(p)
396 text_item::text_item(char *t, const char *fn, int ln)
397 : next(0), text(t), filename(fn), lineno(ln)
399 adj.h = CENTER_ADJUST;
400 adj.v = NONE_ADJUST;
403 text_item::~text_item()
405 a_delete text;
408 object_spec::object_spec(object_type t) : type(t)
410 flags = 0;
411 tbl = 0;
412 segment_list = 0;
413 segment_width = segment_height = 0.0;
414 segment_is_absolute = 0;
415 text = 0;
416 shaded = 0;
417 outlined = 0;
418 with = 0;
419 dir = RIGHT_DIRECTION;
422 object_spec::~object_spec()
424 delete tbl;
425 while (segment_list != 0) {
426 segment *tem = segment_list;
427 segment_list = segment_list->next;
428 delete tem;
430 object *p = oblist.head;
431 while (p != 0) {
432 object *tem = p;
433 p = p->next;
434 delete tem;
436 while (text != 0) {
437 text_item *tem = text;
438 text = text->next;
439 delete tem;
441 delete with;
442 a_delete shaded;
443 a_delete outlined;
446 class command_object : public object {
447 char *s;
448 const char *filename;
449 int lineno;
450 public:
451 command_object(char *, const char *, int);
452 ~command_object();
453 object_type type() { return OTHER_OBJECT; }
454 void print();
457 command_object::command_object(char *p, const char *fn, int ln)
458 : s(p), filename(fn), lineno(ln)
462 command_object::~command_object()
464 a_delete s;
467 void command_object::print()
469 out->command(s, filename, lineno);
472 object *make_command_object(char *s, const char *fn, int ln)
474 return new command_object(s, fn, ln);
477 class mark_object : public object {
478 public:
479 mark_object();
480 object_type type();
483 object *make_mark_object()
485 return new mark_object();
488 mark_object::mark_object()
492 object_type mark_object::type()
494 return MARK_OBJECT;
497 object_list::object_list() : head(0), tail(0)
501 void object_list::append(object *obj)
503 if (tail == 0) {
504 obj->next = obj->prev = 0;
505 head = tail = obj;
507 else {
508 obj->prev = tail;
509 obj->next = 0;
510 tail->next = obj;
511 tail = obj;
515 void object_list::wrap_up_block(object_list *ol)
517 object *p;
518 for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
520 assert(p != 0);
521 ol->head = p->next;
522 if (ol->head) {
523 ol->tail = tail;
524 ol->head->prev = 0;
526 else
527 ol->tail = 0;
528 tail = p->prev;
529 if (tail)
530 tail->next = 0;
531 else
532 head = 0;
533 delete p;
536 text_piece::text_piece()
537 : text(0), filename(0), lineno(-1)
539 adj.h = CENTER_ADJUST;
540 adj.v = NONE_ADJUST;
543 text_piece::~text_piece()
545 a_delete text;
548 class graphic_object : public object {
549 int ntext;
550 text_piece *text;
551 int aligned;
552 protected:
553 line_type lt;
554 char *outline_color;
555 char *color_fill;
556 public:
557 graphic_object();
558 ~graphic_object();
559 object_type type() = 0;
560 void print_text();
561 void add_text(text_item *, int);
562 void set_dotted(double);
563 void set_dashed(double);
564 void set_thickness(double);
565 void set_invisible();
566 void set_outline_color(char *);
567 char *get_outline_color();
568 virtual void set_fill(double);
569 virtual void set_fill_color(char *);
572 graphic_object::graphic_object()
573 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
577 void graphic_object::set_dotted(double wid)
579 lt.type = line_type::dotted;
580 lt.dash_width = wid;
583 void graphic_object::set_dashed(double wid)
585 lt.type = line_type::dashed;
586 lt.dash_width = wid;
589 void graphic_object::set_thickness(double th)
591 lt.thickness = th;
594 void graphic_object::set_fill(double)
598 void graphic_object::set_fill_color(char *c)
600 color_fill = strsave(c);
603 void graphic_object::set_outline_color(char *c)
605 outline_color = strsave(c);
608 char *graphic_object::get_outline_color()
610 return outline_color;
613 void graphic_object::set_invisible()
615 lt.type = line_type::invisible;
618 void graphic_object::add_text(text_item *t, int a)
620 aligned = a;
621 int len = 0;
622 text_item *p;
623 for (p = t; p; p = p->next)
624 len++;
625 if (len == 0)
626 text = 0;
627 else {
628 text = new text_piece[len];
629 for (p = t, len = 0; p; p = p->next, len++) {
630 text[len].text = p->text;
631 p->text = 0;
632 text[len].adj = p->adj;
633 text[len].filename = p->filename;
634 text[len].lineno = p->lineno;
637 ntext = len;
640 void graphic_object::print_text()
642 double angle = 0.0;
643 if (aligned) {
644 position d(end() - start());
645 if (d.x != 0.0 || d.y != 0.0)
646 angle = atan2(d.y, d.x);
648 if (text != 0) {
649 out->set_color(color_fill, get_outline_color());
650 out->text(center(), text, ntext, angle);
651 out->reset_color();
655 graphic_object::~graphic_object()
657 if (text)
658 ad_delete(ntext) text;
661 class rectangle_object : public graphic_object {
662 protected:
663 position cent;
664 position dim;
665 public:
666 rectangle_object(const position &);
667 double width() { return dim.x; }
668 double height() { return dim.y; }
669 position origin() { return cent; }
670 position center() { return cent; }
671 position north() { return position(cent.x, cent.y + dim.y/2.0); }
672 position south() { return position(cent.x, cent.y - dim.y/2.0); }
673 position east() { return position(cent.x + dim.x/2.0, cent.y); }
674 position west() { return position(cent.x - dim.x/2.0, cent.y); }
675 position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
676 position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
677 position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
678 position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
679 object_type type() = 0;
680 void update_bounding_box(bounding_box *);
681 void move_by(const position &);
684 rectangle_object::rectangle_object(const position &d)
685 : dim(d)
689 void rectangle_object::update_bounding_box(bounding_box *p)
691 p->encompass(cent - dim/2.0);
692 p->encompass(cent + dim/2.0);
695 void rectangle_object::move_by(const position &a)
697 cent += a;
700 class closed_object : public rectangle_object {
701 public:
702 closed_object(const position &);
703 object_type type() = 0;
704 void set_fill(double);
705 void set_fill_color(char *fill);
706 protected:
707 double fill; // < 0 if not filled
708 char *color_fill; // = 0 if not colored
711 closed_object::closed_object(const position &pos)
712 : rectangle_object(pos), fill(-1.0), color_fill(0)
716 void closed_object::set_fill(double f)
718 assert(f >= 0.0);
719 fill = f;
722 void closed_object::set_fill_color(char *f)
724 color_fill = strsave(f);
727 class box_object : public closed_object {
728 double xrad;
729 double yrad;
730 public:
731 box_object(const position &, double);
732 object_type type() { return BOX_OBJECT; }
733 void print();
734 position north_east();
735 position north_west();
736 position south_east();
737 position south_west();
740 box_object::box_object(const position &pos, double r)
741 : closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
745 const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
747 position box_object::north_east()
749 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
750 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
753 position box_object::north_west()
755 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
756 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
759 position box_object::south_east()
761 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
762 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
765 position box_object::south_west()
767 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
768 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
771 void box_object::print()
773 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
774 return;
775 out->set_color(color_fill, graphic_object::get_outline_color());
776 if (xrad == 0.0) {
777 distance dim2 = dim/2.0;
778 position vec[4];
779 vec[0] = cent + position(dim2.x, -dim2.y);
780 vec[1] = cent + position(dim2.x, dim2.y);
781 vec[2] = cent + position(-dim2.x, dim2.y);
782 vec[3] = cent + position(-dim2.x, -dim2.y);
783 out->polygon(vec, 4, lt, fill);
785 else {
786 distance abs_dim(fabs(dim.x), fabs(dim.y));
787 out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
789 out->reset_color();
792 graphic_object *object_spec::make_box(position *curpos, direction *dirp)
794 static double last_box_height;
795 static double last_box_width;
796 static double last_box_radius;
797 static int have_last_box = 0;
798 if (!(flags & HAS_HEIGHT)) {
799 if ((flags & IS_SAME) && have_last_box)
800 height = last_box_height;
801 else
802 lookup_variable("boxht", &height);
804 if (!(flags & HAS_WIDTH)) {
805 if ((flags & IS_SAME) && have_last_box)
806 width = last_box_width;
807 else
808 lookup_variable("boxwid", &width);
810 if (!(flags & HAS_RADIUS)) {
811 if ((flags & IS_SAME) && have_last_box)
812 radius = last_box_radius;
813 else
814 lookup_variable("boxrad", &radius);
816 last_box_width = width;
817 last_box_height = height;
818 last_box_radius = radius;
819 have_last_box = 1;
820 radius = fabs(radius);
821 if (radius*2.0 > fabs(width))
822 radius = fabs(width/2.0);
823 if (radius*2.0 > fabs(height))
824 radius = fabs(height/2.0);
825 box_object *p = new box_object(position(width, height), radius);
826 if (!position_rectangle(p, curpos, dirp)) {
827 delete p;
828 p = 0;
830 return p;
833 // return non-zero for success
835 int object_spec::position_rectangle(rectangle_object *p,
836 position *curpos, direction *dirp)
838 position pos;
839 dir = *dirp; // ignore any direction in attribute list
840 position motion;
841 switch (dir) {
842 case UP_DIRECTION:
843 motion.y = p->height()/2.0;
844 break;
845 case DOWN_DIRECTION:
846 motion.y = -p->height()/2.0;
847 break;
848 case LEFT_DIRECTION:
849 motion.x = -p->width()/2.0;
850 break;
851 case RIGHT_DIRECTION:
852 motion.x = p->width()/2.0;
853 break;
854 default:
855 assert(0);
857 if (flags & HAS_AT) {
858 pos = at;
859 if (flags & HAS_WITH) {
860 place offset;
861 place here;
862 here.obj = p;
863 if (!with->follow(here, &offset))
864 return 0;
865 pos -= offset;
868 else {
869 pos = *curpos;
870 pos += motion;
872 p->move_by(pos);
873 pos += motion;
874 *curpos = pos;
875 return 1;
878 class block_object : public rectangle_object {
879 object_list oblist;
880 PTABLE(place) *tbl;
881 public:
882 block_object(const position &, const object_list &ol, PTABLE(place) *t);
883 ~block_object();
884 place *find_label(const char *);
885 object_type type();
886 void move_by(const position &);
887 void print();
890 block_object::block_object(const position &d, const object_list &ol,
891 PTABLE(place) *t)
892 : rectangle_object(d), oblist(ol), tbl(t)
896 block_object::~block_object()
898 delete tbl;
899 object *p = oblist.head;
900 while (p != 0) {
901 object *tem = p;
902 p = p->next;
903 delete tem;
907 void block_object::print()
909 out->begin_block(south_west(), north_east());
910 print_object_list(oblist.head);
911 out->end_block();
914 static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
916 // Adjust all the labels that aren't attached to objects.
917 PTABLE_ITERATOR(place) iter(tbl);
918 const char *key;
919 place *pl;
920 while (iter.next(&key, &pl))
921 if (key && csupper(key[0]) && pl->obj == 0) {
922 pl->x += a.x;
923 pl->y += a.y;
927 void block_object::move_by(const position &a)
929 cent += a;
930 for (object *p = oblist.head; p; p = p->next)
931 p->move_by(a);
932 adjust_objectless_places(tbl, a);
936 place *block_object::find_label(const char *name)
938 return tbl->lookup(name);
941 object_type block_object::type()
943 return BLOCK_OBJECT;
946 graphic_object *object_spec::make_block(position *curpos, direction *dirp)
948 bounding_box bb;
949 for (object *p = oblist.head; p; p = p->next)
950 p->update_bounding_box(&bb);
951 position dim;
952 if (!bb.blank) {
953 position m = -(bb.ll + bb.ur)/2.0;
954 for (object *p = oblist.head; p; p = p->next)
955 p->move_by(m);
956 adjust_objectless_places(tbl, m);
957 dim = bb.ur - bb.ll;
959 if (flags & HAS_WIDTH)
960 dim.x = width;
961 if (flags & HAS_HEIGHT)
962 dim.y = height;
963 block_object *block = new block_object(dim, oblist, tbl);
964 if (!position_rectangle(block, curpos, dirp)) {
965 delete block;
966 block = 0;
968 tbl = 0;
969 oblist.head = oblist.tail = 0;
970 return block;
973 class text_object : public rectangle_object {
974 public:
975 text_object(const position &);
976 object_type type() { return TEXT_OBJECT; }
979 text_object::text_object(const position &d)
980 : rectangle_object(d)
984 graphic_object *object_spec::make_text(position *curpos, direction *dirp)
986 if (!(flags & HAS_HEIGHT)) {
987 lookup_variable("textht", &height);
988 int nitems = 0;
989 for (text_item *t = text; t; t = t->next)
990 nitems++;
991 height *= nitems;
993 if (!(flags & HAS_WIDTH))
994 lookup_variable("textwid", &width);
995 text_object *p = new text_object(position(width, height));
996 if (!position_rectangle(p, curpos, dirp)) {
997 delete p;
998 p = 0;
1000 return p;
1004 class ellipse_object : public closed_object {
1005 public:
1006 ellipse_object(const position &);
1007 position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1008 cent.y + dim.y/(M_SQRT2*2.0)); }
1009 position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1010 cent.y + dim.y/(M_SQRT2*2.0)); }
1011 position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1012 cent.y - dim.y/(M_SQRT2*2.0)); }
1013 position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1014 cent.y - dim.y/(M_SQRT2*2.0)); }
1015 double radius() { return dim.x/2.0; }
1016 object_type type() { return ELLIPSE_OBJECT; }
1017 void print();
1020 ellipse_object::ellipse_object(const position &d)
1021 : closed_object(d)
1025 void ellipse_object::print()
1027 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1028 return;
1029 out->set_color(color_fill, graphic_object::get_outline_color());
1030 out->ellipse(cent, dim, lt, fill);
1031 out->reset_color();
1034 graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1036 static double last_ellipse_height;
1037 static double last_ellipse_width;
1038 static int have_last_ellipse = 0;
1039 if (!(flags & HAS_HEIGHT)) {
1040 if ((flags & IS_SAME) && have_last_ellipse)
1041 height = last_ellipse_height;
1042 else
1043 lookup_variable("ellipseht", &height);
1045 if (!(flags & HAS_WIDTH)) {
1046 if ((flags & IS_SAME) && have_last_ellipse)
1047 width = last_ellipse_width;
1048 else
1049 lookup_variable("ellipsewid", &width);
1051 last_ellipse_width = width;
1052 last_ellipse_height = height;
1053 have_last_ellipse = 1;
1054 ellipse_object *p = new ellipse_object(position(width, height));
1055 if (!position_rectangle(p, curpos, dirp)) {
1056 delete p;
1057 return 0;
1059 return p;
1062 class circle_object : public ellipse_object {
1063 public:
1064 circle_object(double);
1065 object_type type() { return CIRCLE_OBJECT; }
1066 void print();
1069 circle_object::circle_object(double diam)
1070 : ellipse_object(position(diam, diam))
1074 void circle_object::print()
1076 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1077 return;
1078 out->set_color(color_fill, graphic_object::get_outline_color());
1079 out->circle(cent, dim.x/2.0, lt, fill);
1080 out->reset_color();
1083 graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1085 static double last_circle_radius;
1086 static int have_last_circle = 0;
1087 if (!(flags & HAS_RADIUS)) {
1088 if ((flags & IS_SAME) && have_last_circle)
1089 radius = last_circle_radius;
1090 else
1091 lookup_variable("circlerad", &radius);
1093 last_circle_radius = radius;
1094 have_last_circle = 1;
1095 circle_object *p = new circle_object(radius*2.0);
1096 if (!position_rectangle(p, curpos, dirp)) {
1097 delete p;
1098 return 0;
1100 return p;
1103 class move_object : public graphic_object {
1104 position strt;
1105 position en;
1106 public:
1107 move_object(const position &s, const position &e);
1108 position origin() { return en; }
1109 object_type type() { return MOVE_OBJECT; }
1110 void update_bounding_box(bounding_box *);
1111 void move_by(const position &);
1114 move_object::move_object(const position &s, const position &e)
1115 : strt(s), en(e)
1119 void move_object::update_bounding_box(bounding_box *p)
1121 p->encompass(strt);
1122 p->encompass(en);
1125 void move_object::move_by(const position &a)
1127 strt += a;
1128 en += a;
1131 graphic_object *object_spec::make_move(position *curpos, direction *dirp)
1133 static position last_move;
1134 static int have_last_move = 0;
1135 *dirp = dir;
1136 // No need to look at at since `at' attribute sets `from' attribute.
1137 position startpos = (flags & HAS_FROM) ? from : *curpos;
1138 if (!(flags & HAS_SEGMENT)) {
1139 if ((flags & IS_SAME) && have_last_move)
1140 segment_pos = last_move;
1141 else {
1142 switch (dir) {
1143 case UP_DIRECTION:
1144 segment_pos.y = segment_height;
1145 break;
1146 case DOWN_DIRECTION:
1147 segment_pos.y = -segment_height;
1148 break;
1149 case LEFT_DIRECTION:
1150 segment_pos.x = -segment_width;
1151 break;
1152 case RIGHT_DIRECTION:
1153 segment_pos.x = segment_width;
1154 break;
1155 default:
1156 assert(0);
1160 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1161 // Reverse the segment_list so that it's in forward order.
1162 segment *old = segment_list;
1163 segment_list = 0;
1164 while (old != 0) {
1165 segment *tem = old->next;
1166 old->next = segment_list;
1167 segment_list = old;
1168 old = tem;
1170 // Compute the end position.
1171 position endpos = startpos;
1172 for (segment *s = segment_list; s; s = s->next)
1173 if (s->is_absolute)
1174 endpos = s->pos;
1175 else
1176 endpos += s->pos;
1177 have_last_move = 1;
1178 last_move = endpos - startpos;
1179 move_object *p = new move_object(startpos, endpos);
1180 *curpos = endpos;
1181 return p;
1184 class linear_object : public graphic_object {
1185 protected:
1186 char arrow_at_start;
1187 char arrow_at_end;
1188 arrow_head_type aht;
1189 position strt;
1190 position en;
1191 public:
1192 linear_object(const position &s, const position &e);
1193 position start() { return strt; }
1194 position end() { return en; }
1195 void move_by(const position &);
1196 void update_bounding_box(bounding_box *) = 0;
1197 object_type type() = 0;
1198 void add_arrows(int at_start, int at_end, const arrow_head_type &);
1201 class line_object : public linear_object {
1202 protected:
1203 position *v;
1204 int n;
1205 public:
1206 line_object(const position &s, const position &e, position *, int);
1207 ~line_object();
1208 position origin() { return strt; }
1209 position center() { return (strt + en)/2.0; }
1210 position north() { return (en.y - strt.y) > 0 ? en : strt; }
1211 position south() { return (en.y - strt.y) < 0 ? en : strt; }
1212 position east() { return (en.x - strt.x) > 0 ? en : strt; }
1213 position west() { return (en.x - strt.x) < 0 ? en : strt; }
1214 object_type type() { return LINE_OBJECT; }
1215 void update_bounding_box(bounding_box *);
1216 void print();
1217 void move_by(const position &);
1220 class arrow_object : public line_object {
1221 public:
1222 arrow_object(const position &, const position &, position *, int);
1223 object_type type() { return ARROW_OBJECT; }
1226 class spline_object : public line_object {
1227 public:
1228 spline_object(const position &, const position &, position *, int);
1229 object_type type() { return SPLINE_OBJECT; }
1230 void print();
1231 void update_bounding_box(bounding_box *);
1234 linear_object::linear_object(const position &s, const position &e)
1235 : arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1239 void linear_object::move_by(const position &a)
1241 strt += a;
1242 en += a;
1245 void linear_object::add_arrows(int at_start, int at_end,
1246 const arrow_head_type &a)
1248 arrow_at_start = at_start;
1249 arrow_at_end = at_end;
1250 aht = a;
1253 line_object::line_object(const position &s, const position &e,
1254 position *p, int i)
1255 : linear_object(s, e), v(p), n(i)
1259 void line_object::print()
1261 if (lt.type == line_type::invisible)
1262 return;
1263 out->set_color(0, graphic_object::get_outline_color());
1264 // shorten line length to avoid arrow sticking.
1265 position sp = strt;
1266 if (arrow_at_start) {
1267 position base = v[0] - strt;
1268 double hyp = hypot(base);
1269 if (hyp == 0.0) {
1270 error("cannot draw arrow on object with zero length");
1271 return;
1273 if (aht.solid && out->supports_filled_polygons()) {
1274 base *= aht.height / hyp;
1275 draw_arrow(strt, strt - v[0], aht, lt,
1276 graphic_object::get_outline_color());
1277 sp = strt + base;
1278 } else {
1279 base *= fabs(lt.thickness) / hyp / 72 / 4;
1280 sp = strt + base;
1281 draw_arrow(sp, sp - v[0], aht, lt,
1282 graphic_object::get_outline_color());
1285 if (arrow_at_end) {
1286 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1287 double hyp = hypot(base);
1288 if (hyp == 0.0) {
1289 error("cannot draw arrow on object with zero length");
1290 return;
1292 if (aht.solid && out->supports_filled_polygons()) {
1293 base *= aht.height / hyp;
1294 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1295 graphic_object::get_outline_color());
1296 v[n-1] = en - base;
1297 } else {
1298 base *= fabs(lt.thickness) / hyp / 72 / 4;
1299 v[n-1] = en - base;
1300 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1301 graphic_object::get_outline_color());
1304 out->line(sp, v, n, lt);
1305 out->reset_color();
1308 void line_object::update_bounding_box(bounding_box *p)
1310 p->encompass(strt);
1311 for (int i = 0; i < n; i++)
1312 p->encompass(v[i]);
1315 void line_object::move_by(const position &pos)
1317 linear_object::move_by(pos);
1318 for (int i = 0; i < n; i++)
1319 v[i] += pos;
1322 void spline_object::update_bounding_box(bounding_box *p)
1324 p->encompass(strt);
1325 p->encompass(en);
1330 p1 = q1/2 + q2/2
1331 p2 = q1/6 + q2*5/6
1332 p3 = q2*5/6 + q3/6
1333 p4 = q2/2 + q3/2
1334 [ the points for the Bezier cubic ]
1338 t = .5
1340 then
1342 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1343 [ the equation for the Bezier cubic ]
1345 = .125*q1 + .75*q2 + .125*q3
1348 for (int i = 1; i < n; i++)
1349 p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1352 arrow_object::arrow_object(const position &s, const position &e,
1353 position *p, int i)
1354 : line_object(s, e, p, i)
1358 spline_object::spline_object(const position &s, const position &e,
1359 position *p, int i)
1360 : line_object(s, e, p, i)
1364 void spline_object::print()
1366 if (lt.type == line_type::invisible)
1367 return;
1368 out->set_color(0, graphic_object::get_outline_color());
1369 // shorten line length for spline to avoid arrow sticking
1370 position sp = strt;
1371 if (arrow_at_start) {
1372 position base = v[0] - strt;
1373 double hyp = hypot(base);
1374 if (hyp == 0.0) {
1375 error("cannot draw arrow on object with zero length");
1376 return;
1378 if (aht.solid && out->supports_filled_polygons()) {
1379 base *= aht.height / hyp;
1380 draw_arrow(strt, strt - v[0], aht, lt,
1381 graphic_object::get_outline_color());
1382 sp = strt + base*0.1; // to reserve spline shape
1383 } else {
1384 base *= fabs(lt.thickness) / hyp / 72 / 4;
1385 sp = strt + base;
1386 draw_arrow(sp, sp - v[0], aht, lt,
1387 graphic_object::get_outline_color());
1390 if (arrow_at_end) {
1391 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1392 double hyp = hypot(base);
1393 if (hyp == 0.0) {
1394 error("cannot draw arrow on object with zero length");
1395 return;
1397 if (aht.solid && out->supports_filled_polygons()) {
1398 base *= aht.height / hyp;
1399 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1400 graphic_object::get_outline_color());
1401 v[n-1] = en - base*0.1; // to reserve spline shape
1402 } else {
1403 base *= fabs(lt.thickness) / hyp / 72 / 4;
1404 v[n-1] = en - base;
1405 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1406 graphic_object::get_outline_color());
1409 out->spline(sp, v, n, lt);
1410 out->reset_color();
1413 line_object::~line_object()
1415 a_delete v;
1418 linear_object *object_spec::make_line(position *curpos, direction *dirp)
1420 static position last_line;
1421 static int have_last_line = 0;
1422 *dirp = dir;
1423 // No need to look at at since `at' attribute sets `from' attribute.
1424 position startpos = (flags & HAS_FROM) ? from : *curpos;
1425 if (!(flags & HAS_SEGMENT)) {
1426 if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1427 && have_last_line)
1428 segment_pos = last_line;
1429 else
1430 switch (dir) {
1431 case UP_DIRECTION:
1432 segment_pos.y = segment_height;
1433 break;
1434 case DOWN_DIRECTION:
1435 segment_pos.y = -segment_height;
1436 break;
1437 case LEFT_DIRECTION:
1438 segment_pos.x = -segment_width;
1439 break;
1440 case RIGHT_DIRECTION:
1441 segment_pos.x = segment_width;
1442 break;
1443 default:
1444 assert(0);
1447 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1448 // reverse the segment_list so that it's in forward order
1449 segment *old = segment_list;
1450 segment_list = 0;
1451 while (old != 0) {
1452 segment *tem = old->next;
1453 old->next = segment_list;
1454 segment_list = old;
1455 old = tem;
1457 // Absolutise all movements
1458 position endpos = startpos;
1459 int nsegments = 0;
1460 segment *s;
1461 for (s = segment_list; s; s = s->next, nsegments++)
1462 if (s->is_absolute)
1463 endpos = s->pos;
1464 else {
1465 endpos += s->pos;
1466 s->pos = endpos;
1467 s->is_absolute = 1; // to avoid confusion
1469 // handle chop
1470 line_object *p = 0;
1471 position *v = new position[nsegments];
1472 int i = 0;
1473 for (s = segment_list; s; s = s->next, i++)
1474 v[i] = s->pos;
1475 if (flags & IS_DEFAULT_CHOPPED) {
1476 lookup_variable("circlerad", &start_chop);
1477 end_chop = start_chop;
1478 flags |= IS_CHOPPED;
1480 if (flags & IS_CHOPPED) {
1481 position start_chop_vec, end_chop_vec;
1482 if (start_chop != 0.0) {
1483 start_chop_vec = v[0] - startpos;
1484 start_chop_vec *= start_chop / hypot(start_chop_vec);
1486 if (end_chop != 0.0) {
1487 end_chop_vec = (v[nsegments - 1]
1488 - (nsegments > 1 ? v[nsegments - 2] : startpos));
1489 end_chop_vec *= end_chop / hypot(end_chop_vec);
1491 startpos += start_chop_vec;
1492 v[nsegments - 1] -= end_chop_vec;
1493 endpos -= end_chop_vec;
1495 switch (type) {
1496 case SPLINE_OBJECT:
1497 p = new spline_object(startpos, endpos, v, nsegments);
1498 break;
1499 case ARROW_OBJECT:
1500 p = new arrow_object(startpos, endpos, v, nsegments);
1501 break;
1502 case LINE_OBJECT:
1503 p = new line_object(startpos, endpos, v, nsegments);
1504 break;
1505 default:
1506 assert(0);
1508 have_last_line = 1;
1509 last_line = endpos - startpos;
1510 *curpos = endpos;
1511 return p;
1514 class arc_object : public linear_object {
1515 int clockwise;
1516 position cent;
1517 double rad;
1518 public:
1519 arc_object(int, const position &, const position &, const position &);
1520 position origin() { return cent; }
1521 position center() { return cent; }
1522 double radius() { return rad; }
1523 position north();
1524 position south();
1525 position east();
1526 position west();
1527 position north_east();
1528 position north_west();
1529 position south_east();
1530 position south_west();
1531 void update_bounding_box(bounding_box *);
1532 object_type type() { return ARC_OBJECT; }
1533 void print();
1534 void move_by(const position &pos);
1537 arc_object::arc_object(int cw, const position &s, const position &e,
1538 const position &c)
1539 : linear_object(s, e), clockwise(cw), cent(c)
1541 rad = hypot(c - s);
1544 void arc_object::move_by(const position &pos)
1546 linear_object::move_by(pos);
1547 cent += pos;
1550 // we get arc corners from the corresponding circle
1552 position arc_object::north()
1554 position result(cent);
1555 result.y += rad;
1556 return result;
1559 position arc_object::south()
1561 position result(cent);
1562 result.y -= rad;
1563 return result;
1566 position arc_object::east()
1568 position result(cent);
1569 result.x += rad;
1570 return result;
1573 position arc_object::west()
1575 position result(cent);
1576 result.x -= rad;
1577 return result;
1580 position arc_object::north_east()
1582 position result(cent);
1583 result.x += rad/M_SQRT2;
1584 result.y += rad/M_SQRT2;
1585 return result;
1588 position arc_object::north_west()
1590 position result(cent);
1591 result.x -= rad/M_SQRT2;
1592 result.y += rad/M_SQRT2;
1593 return result;
1596 position arc_object::south_east()
1598 position result(cent);
1599 result.x += rad/M_SQRT2;
1600 result.y -= rad/M_SQRT2;
1601 return result;
1604 position arc_object::south_west()
1606 position result(cent);
1607 result.x -= rad/M_SQRT2;
1608 result.y -= rad/M_SQRT2;
1609 return result;
1613 void arc_object::print()
1615 if (lt.type == line_type::invisible)
1616 return;
1617 out->set_color(0, graphic_object::get_outline_color());
1618 // handle arrow direction; make shorter line for arc
1619 position sp, ep, b;
1620 if (clockwise) {
1621 sp = en;
1622 ep = strt;
1623 } else {
1624 sp = strt;
1625 ep = en;
1627 if (arrow_at_start) {
1628 double theta = aht.height / rad;
1629 if (clockwise)
1630 theta = - theta;
1631 b = strt - cent;
1632 b = position(b.x*cos(theta) - b.y*sin(theta),
1633 b.x*sin(theta) + b.y*cos(theta)) + cent;
1634 if (clockwise)
1635 ep = b;
1636 else
1637 sp = b;
1638 if (aht.solid && out->supports_filled_polygons()) {
1639 draw_arrow(strt, strt - b, aht, lt,
1640 graphic_object::get_outline_color());
1641 } else {
1642 position v = b;
1643 theta = fabs(lt.thickness) / 72 / 4 / rad;
1644 if (clockwise)
1645 theta = - theta;
1646 b = strt - cent;
1647 b = position(b.x*cos(theta) - b.y*sin(theta),
1648 b.x*sin(theta) + b.y*cos(theta)) + cent;
1649 draw_arrow(b, b - v, aht, lt,
1650 graphic_object::get_outline_color());
1651 out->line(b, &v, 1, lt);
1654 if (arrow_at_end) {
1655 double theta = aht.height / rad;
1656 if (!clockwise)
1657 theta = - theta;
1658 b = en - cent;
1659 b = position(b.x*cos(theta) - b.y*sin(theta),
1660 b.x*sin(theta) + b.y*cos(theta)) + cent;
1661 if (clockwise)
1662 sp = b;
1663 else
1664 ep = b;
1665 if (aht.solid && out->supports_filled_polygons()) {
1666 draw_arrow(en, en - b, aht, lt,
1667 graphic_object::get_outline_color());
1668 } else {
1669 position v = b;
1670 theta = fabs(lt.thickness) / 72 / 4 / rad;
1671 if (!clockwise)
1672 theta = - theta;
1673 b = en - cent;
1674 b = position(b.x*cos(theta) - b.y*sin(theta),
1675 b.x*sin(theta) + b.y*cos(theta)) + cent;
1676 draw_arrow(b, b - v, aht, lt,
1677 graphic_object::get_outline_color());
1678 out->line(b, &v, 1, lt);
1681 out->arc(sp, cent, ep, lt);
1682 out->reset_color();
1685 inline double max(double a, double b)
1687 return a > b ? a : b;
1690 void arc_object::update_bounding_box(bounding_box *p)
1692 p->encompass(strt);
1693 p->encompass(en);
1694 position start_offset = strt - cent;
1695 if (start_offset.x == 0.0 && start_offset.y == 0.0)
1696 return;
1697 position end_offset = en - cent;
1698 if (end_offset.x == 0.0 && end_offset.y == 0.0)
1699 return;
1700 double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1701 double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1702 if (clockwise) {
1703 double temp = start_quad;
1704 start_quad = end_quad;
1705 end_quad = temp;
1707 if (start_quad < 0.0)
1708 start_quad += 4.0;
1709 while (end_quad <= start_quad)
1710 end_quad += 4.0;
1711 double r = max(hypot(start_offset), hypot(end_offset));
1712 for (int q = int(start_quad) + 1; q < end_quad; q++) {
1713 position offset;
1714 switch (q % 4) {
1715 case 0:
1716 offset.x = r;
1717 break;
1718 case 1:
1719 offset.y = r;
1720 break;
1721 case 2:
1722 offset.x = -r;
1723 break;
1724 case 3:
1725 offset.y = -r;
1726 break;
1728 p->encompass(cent + offset);
1732 // We ignore the with attribute. The at attribute always refers to the center.
1734 linear_object *object_spec::make_arc(position *curpos, direction *dirp)
1736 *dirp = dir;
1737 int cw = (flags & IS_CLOCKWISE) != 0;
1738 // compute the start
1739 position startpos;
1740 if (flags & HAS_FROM)
1741 startpos = from;
1742 else
1743 startpos = *curpos;
1744 if (!(flags & HAS_RADIUS))
1745 lookup_variable("arcrad", &radius);
1746 // compute the end
1747 position endpos;
1748 if (flags & HAS_TO)
1749 endpos = to;
1750 else {
1751 position m(radius, radius);
1752 // Adjust the signs.
1753 if (cw) {
1754 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1755 m.x = -m.x;
1756 if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1757 m.y = -m.y;
1758 *dirp = direction((dir + 3) % 4);
1760 else {
1761 if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1762 m.x = -m.x;
1763 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1764 m.y = -m.y;
1765 *dirp = direction((dir + 1) % 4);
1767 endpos = startpos + m;
1769 // compute the center
1770 position centerpos;
1771 if (flags & HAS_AT)
1772 centerpos = at;
1773 else if (startpos == endpos)
1774 centerpos = startpos;
1775 else {
1776 position h = (endpos - startpos)/2.0;
1777 double d = hypot(h);
1778 if (radius <= 0)
1779 radius = .25;
1780 // make the radius big enough
1781 while (radius < d)
1782 radius *= 2.0;
1783 double alpha = acos(d/radius);
1784 double theta = atan2(h.y, h.x);
1785 if (cw)
1786 theta -= alpha;
1787 else
1788 theta += alpha;
1789 centerpos = position(cos(theta), sin(theta))*radius + startpos;
1791 arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1792 *curpos = endpos;
1793 return p;
1796 graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1798 linear_object *obj;
1799 if (type == ARC_OBJECT)
1800 obj = make_arc(curpos, dirp);
1801 else
1802 obj = make_line(curpos, dirp);
1803 if (type == ARROW_OBJECT
1804 && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1805 flags |= HAS_RIGHT_ARROW_HEAD;
1806 if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1807 arrow_head_type a;
1808 int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1809 int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1810 if (flags & HAS_HEIGHT)
1811 a.height = height;
1812 else
1813 lookup_variable("arrowht", &a.height);
1814 if (flags & HAS_WIDTH)
1815 a.width = width;
1816 else
1817 lookup_variable("arrowwid", &a.width);
1818 double solid;
1819 lookup_variable("arrowhead", &solid);
1820 a.solid = solid != 0.0;
1821 obj->add_arrows(at_start, at_end, a);
1823 return obj;
1826 object *object_spec::make_object(position *curpos, direction *dirp)
1828 graphic_object *obj = 0;
1829 switch (type) {
1830 case BLOCK_OBJECT:
1831 obj = make_block(curpos, dirp);
1832 break;
1833 case BOX_OBJECT:
1834 obj = make_box(curpos, dirp);
1835 break;
1836 case TEXT_OBJECT:
1837 obj = make_text(curpos, dirp);
1838 break;
1839 case ELLIPSE_OBJECT:
1840 obj = make_ellipse(curpos, dirp);
1841 break;
1842 case CIRCLE_OBJECT:
1843 obj = make_circle(curpos, dirp);
1844 break;
1845 case MOVE_OBJECT:
1846 obj = make_move(curpos, dirp);
1847 break;
1848 case ARC_OBJECT:
1849 case LINE_OBJECT:
1850 case SPLINE_OBJECT:
1851 case ARROW_OBJECT:
1852 obj = make_linear(curpos, dirp);
1853 break;
1854 case MARK_OBJECT:
1855 case OTHER_OBJECT:
1856 default:
1857 assert(0);
1858 break;
1860 if (obj) {
1861 if (flags & IS_INVISIBLE)
1862 obj->set_invisible();
1863 if (text != 0)
1864 obj->add_text(text, (flags & IS_ALIGNED) != 0);
1865 if (flags & IS_DOTTED)
1866 obj->set_dotted(dash_width);
1867 else if (flags & IS_DASHED)
1868 obj->set_dashed(dash_width);
1869 double th;
1870 if (flags & HAS_THICKNESS)
1871 th = thickness;
1872 else
1873 lookup_variable("linethick", &th);
1874 obj->set_thickness(th);
1875 if (flags & IS_OUTLINED)
1876 obj->set_outline_color(outlined);
1877 if (flags & (IS_DEFAULT_FILLED | IS_FILLED)) {
1878 if (flags & IS_SHADED)
1879 obj->set_fill_color(shaded);
1880 else {
1881 if (flags & IS_DEFAULT_FILLED)
1882 lookup_variable("fillval", &fill);
1883 if (fill < 0.0)
1884 error("bad fill value %1", fill);
1885 else
1886 obj->set_fill(fill);
1890 return obj;
1893 struct string_list {
1894 string_list *next;
1895 char *str;
1896 string_list(char *);
1897 ~string_list();
1900 string_list::string_list(char *s)
1901 : next(0), str(s)
1905 string_list::~string_list()
1907 a_delete str;
1910 /* A path is used to hold the argument to the `with' attribute. For
1911 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1912 take a place and follow the path through the place to place within the
1913 place. Note that `.A.B.C.sw' will work.
1915 For compatibility with DWB pic, `with' accepts positions also (this
1916 is incorrectly documented in CSTR 116). */
1918 path::path(corner c)
1919 : crn(c), label_list(0), ypath(0), is_position(0)
1923 path::path(position p)
1924 : crn(0), label_list(0), ypath(0), is_position(1)
1926 pos.x = p.x;
1927 pos.y = p.y;
1930 path::path(char *l, corner c)
1931 : crn(c), ypath(0), is_position(0)
1933 label_list = new string_list(l);
1936 path::~path()
1938 while (label_list) {
1939 string_list *tem = label_list;
1940 label_list = label_list->next;
1941 delete tem;
1943 delete ypath;
1946 void path::append(corner c)
1948 assert(crn == 0);
1949 crn = c;
1952 void path::append(char *s)
1954 string_list **p;
1955 for (p = &label_list; *p; p = &(*p)->next)
1957 *p = new string_list(s);
1960 void path::set_ypath(path *p)
1962 ypath = p;
1965 // return non-zero for success
1967 int path::follow(const place &pl, place *result) const
1969 if (is_position) {
1970 result->x = pos.x;
1971 result->y = pos.y;
1972 result->obj = 0;
1973 return 1;
1975 const place *p = &pl;
1976 for (string_list *lb = label_list; lb; lb = lb->next)
1977 if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
1978 lex_error("object does not contain a place `%1'", lb->str);
1979 return 0;
1981 if (crn == 0 || p->obj == 0)
1982 *result = *p;
1983 else {
1984 position ps = ((p->obj)->*(crn))();
1985 result->x = ps.x;
1986 result->y = ps.y;
1987 result->obj = 0;
1989 if (ypath) {
1990 place tem;
1991 if (!ypath->follow(pl, &tem))
1992 return 0;
1993 result->y = tem.y;
1994 if (result->obj != tem.obj)
1995 result->obj = 0;
1997 return 1;
2000 void print_object_list(object *p)
2002 for (; p; p = p->next) {
2003 p->print();
2004 p->print_text();
2008 void print_picture(object *obj)
2010 bounding_box bb;
2011 for (object *p = obj; p; p = p->next)
2012 p->update_bounding_box(&bb);
2013 double scale;
2014 lookup_variable("scale", &scale);
2015 out->start_picture(scale, bb.ll, bb.ur);
2016 print_object_list(obj);
2017 out->finish_picture();