Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / preproc / pic / object.cpp
blobaefbd45e3940a64dfc22f81adce26d976ab19608
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
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
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 #include "pic.h"
23 #include "ptable.h"
24 #include "object.h"
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)
37 output::~output()
39 a_delete args;
42 void output::set_desired_width_height(double wid, double ht)
44 desired_width = wid;
45 desired_height = ht;
48 void output::set_args(const char *s)
50 a_delete args;
51 if (s == 0 || *s == '\0')
52 args = 0;
53 else
54 args = strsave(s);
57 int output::supports_filled_polygons()
59 return 0;
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) {
74 sc = 0.0;
75 if (desired_width != 0.0) {
76 if (dim.x == 0.0)
77 error("width specified for picture with zero width");
78 else
79 sc = dim.x/desired_width;
81 if (desired_height != 0.0) {
82 if (dim.y == 0.0)
83 error("height specified for picture with zero height");
84 else {
85 double tem = dim.y/desired_height;
86 if (tem > sc)
87 sc = tem;
90 return sc == 0.0 ? 1.0 : sc;
92 else {
93 if (sc <= 0.0)
94 sc = 1.0;
95 distance sdim = dim/sc;
96 double max_width = 0.0;
97 lookup_variable("maxpswid", &max_width);
98 double max_height = 0.0;
99 lookup_variable("maxpsht", &max_height);
100 if ((max_width > 0.0 && sdim.x > max_width)
101 || (max_height > 0.0 && sdim.y > max_height)) {
102 double xscale = dim.x/max_width;
103 double yscale = dim.y/max_height;
104 return xscale > yscale ? xscale : yscale;
106 else
107 return sc;
111 position::position(const place &pl)
113 if (pl.obj != 0) {
114 // Use two statements to work around bug in SGI C++.
115 object *tem = pl.obj;
116 *this = tem->origin();
118 else {
119 x = pl.x;
120 y = pl.y;
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)
145 x += a.x;
146 y += a.y;
147 return *this;
150 position &position::operator-=(const position &a)
152 x -= a.x;
153 y -= a.y;
154 return *this;
157 position &position::operator*=(double a)
159 x *= a;
160 y *= a;
161 return *this;
164 position &position::operator/=(double a)
166 x /= a;
167 y /= a;
168 return *this;
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);
196 // dot product
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 groff_hypot(a.x, a.y);
208 struct arrow_head_type {
209 double height;
210 double width;
211 int solid;
214 void draw_arrow(const position &pos, const distance &dir,
215 const arrow_head_type &aht, const line_type &lt,
216 char *outline_color_for_fill)
218 double hyp = hypot(dir);
219 if (hyp == 0.0) {
220 error("cannot draw arrow on object with zero length");
221 return;
223 position base = -dir;
224 base *= aht.height/hyp;
225 position n(dir.y, -dir.x);
226 n *= aht.width/(hyp*2.0);
227 line_type slt = lt;
228 slt.type = line_type::solid;
229 if (aht.solid && out->supports_filled_polygons()) {
230 position v[3];
231 v[0] = pos;
232 v[1] = pos + base + n;
233 v[2] = pos + base - n;
234 // fill with outline color
235 out->set_color(outline_color_for_fill, outline_color_for_fill);
236 // make stroke thin to avoid arrow sticking
237 slt.thickness = 0.1;
238 out->polygon(v, 3, slt, 1);
240 else {
241 // use two line segments to avoid arrow sticking
242 out->line(pos + base - n, &pos, 1, slt);
243 out->line(pos + base + n, &pos, 1, slt);
247 object::object() : prev(0), next(0)
251 object::~object()
255 void object::move_by(const position &)
259 void object::print()
263 void object::print_text()
267 int object::blank()
269 return 0;
272 struct bounding_box {
273 int blank;
274 position ll;
275 position ur;
277 bounding_box();
278 void encompass(const position &);
281 bounding_box::bounding_box()
282 : blank(1)
286 void bounding_box::encompass(const position &pos)
288 if (blank) {
289 ll = pos;
290 ur = pos;
291 blank = 0;
293 else {
294 if (pos.x < ll.x)
295 ll.x = pos.x;
296 if (pos.y < ll.y)
297 ll.y = pos.y;
298 if (pos.x > ur.x)
299 ur.x = pos.x;
300 if (pos.y > ur.y)
301 ur.y = pos.y;
305 void object::update_bounding_box(bounding_box *)
309 position object::origin()
311 return position(0.0,0.0);
314 position object::north()
316 return origin();
319 position object::south()
321 return origin();
324 position object::east()
326 return origin();
329 position object::west()
331 return origin();
334 position object::north_east()
336 return origin();
339 position object::north_west()
341 return origin();
344 position object::south_east()
346 return origin();
349 position object::south_west()
351 return origin();
354 position object::start()
356 return origin();
359 position object::end()
361 return origin();
364 position object::center()
366 return origin();
369 double object::width()
371 return 0.0;
374 double object::radius()
376 return 0.0;
379 double object::height()
381 return 0.0;
384 place *object::find_label(const char *)
386 return 0;
389 segment::segment(const position &a, int n, segment *p)
390 : is_absolute(n), pos(a), next(p)
394 text_item::text_item(char *t, const char *fn, int ln)
395 : next(0), text(t), filename(fn), lineno(ln)
397 adj.h = CENTER_ADJUST;
398 adj.v = NONE_ADJUST;
401 text_item::~text_item()
403 a_delete text;
406 object_spec::object_spec(object_type t) : type(t)
408 flags = 0;
409 tbl = 0;
410 segment_list = 0;
411 segment_width = segment_height = 0.0;
412 segment_is_absolute = 0;
413 text = 0;
414 shaded = 0;
415 outlined = 0;
416 with = 0;
417 dir = RIGHT_DIRECTION;
420 object_spec::~object_spec()
422 delete tbl;
423 while (segment_list != 0) {
424 segment *tem = segment_list;
425 segment_list = segment_list->next;
426 delete tem;
428 object *p = oblist.head;
429 while (p != 0) {
430 object *tem = p;
431 p = p->next;
432 delete tem;
434 while (text != 0) {
435 text_item *tem = text;
436 text = text->next;
437 delete tem;
439 delete with;
440 a_delete shaded;
441 a_delete outlined;
444 class command_object : public object {
445 char *s;
446 const char *filename;
447 int lineno;
448 public:
449 command_object(char *, const char *, int);
450 ~command_object();
451 object_type type() { return OTHER_OBJECT; }
452 void print();
455 command_object::command_object(char *p, const char *fn, int ln)
456 : s(p), filename(fn), lineno(ln)
460 command_object::~command_object()
462 a_delete s;
465 void command_object::print()
467 out->command(s, filename, lineno);
470 object *make_command_object(char *s, const char *fn, int ln)
472 return new command_object(s, fn, ln);
475 class mark_object : public object {
476 public:
477 mark_object();
478 object_type type();
481 object *make_mark_object()
483 return new mark_object();
486 mark_object::mark_object()
490 object_type mark_object::type()
492 return MARK_OBJECT;
495 object_list::object_list() : head(0), tail(0)
499 void object_list::append(object *obj)
501 if (tail == 0) {
502 obj->next = obj->prev = 0;
503 head = tail = obj;
505 else {
506 obj->prev = tail;
507 obj->next = 0;
508 tail->next = obj;
509 tail = obj;
513 void object_list::wrap_up_block(object_list *ol)
515 object *p;
516 for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
518 assert(p != 0);
519 ol->head = p->next;
520 if (ol->head) {
521 ol->tail = tail;
522 ol->head->prev = 0;
524 else
525 ol->tail = 0;
526 tail = p->prev;
527 if (tail)
528 tail->next = 0;
529 else
530 head = 0;
531 delete p;
534 text_piece::text_piece()
535 : text(0), filename(0), lineno(-1)
537 adj.h = CENTER_ADJUST;
538 adj.v = NONE_ADJUST;
541 text_piece::~text_piece()
543 a_delete text;
546 class graphic_object : public object {
547 int ntext;
548 text_piece *text;
549 int aligned;
550 protected:
551 line_type lt;
552 char *outline_color;
553 char *color_fill;
554 public:
555 graphic_object();
556 ~graphic_object();
557 object_type type() = 0;
558 void print_text();
559 void add_text(text_item *, int);
560 void set_dotted(double);
561 void set_dashed(double);
562 void set_thickness(double);
563 void set_invisible();
564 void set_outline_color(char *);
565 char *get_outline_color();
566 virtual void set_fill(double);
567 virtual void set_fill_color(char *);
570 graphic_object::graphic_object()
571 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
575 void graphic_object::set_dotted(double wid)
577 lt.type = line_type::dotted;
578 lt.dash_width = wid;
581 void graphic_object::set_dashed(double wid)
583 lt.type = line_type::dashed;
584 lt.dash_width = wid;
587 void graphic_object::set_thickness(double th)
589 lt.thickness = th;
592 void graphic_object::set_fill(double)
596 void graphic_object::set_fill_color(char *c)
598 color_fill = strsave(c);
601 void graphic_object::set_outline_color(char *c)
603 outline_color = strsave(c);
606 char *graphic_object::get_outline_color()
608 return outline_color;
611 void graphic_object::set_invisible()
613 lt.type = line_type::invisible;
616 void graphic_object::add_text(text_item *t, int a)
618 aligned = a;
619 int len = 0;
620 text_item *p;
621 for (p = t; p; p = p->next)
622 len++;
623 if (len == 0)
624 text = 0;
625 else {
626 text = new text_piece[len];
627 for (p = t, len = 0; p; p = p->next, len++) {
628 text[len].text = p->text;
629 p->text = 0;
630 text[len].adj = p->adj;
631 text[len].filename = p->filename;
632 text[len].lineno = p->lineno;
635 ntext = len;
638 void graphic_object::print_text()
640 double angle = 0.0;
641 if (aligned) {
642 position d(end() - start());
643 if (d.x != 0.0 || d.y != 0.0)
644 angle = atan2(d.y, d.x);
646 if (text != 0) {
647 out->set_color(color_fill, get_outline_color());
648 out->text(center(), text, ntext, angle);
649 out->reset_color();
653 graphic_object::~graphic_object()
655 if (text)
656 ad_delete(ntext) text;
659 class rectangle_object : public graphic_object {
660 protected:
661 position cent;
662 position dim;
663 public:
664 rectangle_object(const position &);
665 double width() { return dim.x; }
666 double height() { return dim.y; }
667 position origin() { return cent; }
668 position center() { return cent; }
669 position north() { return position(cent.x, cent.y + dim.y/2.0); }
670 position south() { return position(cent.x, cent.y - dim.y/2.0); }
671 position east() { return position(cent.x + dim.x/2.0, cent.y); }
672 position west() { return position(cent.x - dim.x/2.0, cent.y); }
673 position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
674 position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
675 position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
676 position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
677 object_type type() = 0;
678 void update_bounding_box(bounding_box *);
679 void move_by(const position &);
682 rectangle_object::rectangle_object(const position &d)
683 : dim(d)
687 void rectangle_object::update_bounding_box(bounding_box *p)
689 p->encompass(cent - dim/2.0);
690 p->encompass(cent + dim/2.0);
693 void rectangle_object::move_by(const position &a)
695 cent += a;
698 class closed_object : public rectangle_object {
699 public:
700 closed_object(const position &);
701 object_type type() = 0;
702 void set_fill(double);
703 void set_fill_color(char *fill);
704 protected:
705 double fill; // < 0 if not filled
706 char *color_fill; // = 0 if not colored
709 closed_object::closed_object(const position &pos)
710 : rectangle_object(pos), fill(-1.0), color_fill(0)
714 void closed_object::set_fill(double f)
716 assert(f >= 0.0);
717 fill = f;
720 void closed_object::set_fill_color(char *f)
722 color_fill = strsave(f);
725 class box_object : public closed_object {
726 double xrad;
727 double yrad;
728 public:
729 box_object(const position &, double);
730 object_type type() { return BOX_OBJECT; }
731 void print();
732 position north_east();
733 position north_west();
734 position south_east();
735 position south_west();
738 box_object::box_object(const position &pos, double r)
739 : closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
743 const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
745 position box_object::north_east()
747 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
748 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
751 position box_object::north_west()
753 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
754 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
757 position box_object::south_east()
759 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
760 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
763 position box_object::south_west()
765 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
766 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
769 void box_object::print()
771 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
772 return;
773 out->set_color(color_fill, graphic_object::get_outline_color());
774 if (xrad == 0.0) {
775 distance dim2 = dim/2.0;
776 position vec[4];
777 vec[0] = cent + position(dim2.x, -dim2.y);
778 vec[1] = cent + position(dim2.x, dim2.y);
779 vec[2] = cent + position(-dim2.x, dim2.y);
780 vec[3] = cent + position(-dim2.x, -dim2.y);
781 out->polygon(vec, 4, lt, fill);
783 else {
784 distance abs_dim(fabs(dim.x), fabs(dim.y));
785 out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
787 out->reset_color();
790 graphic_object *object_spec::make_box(position *curpos, direction *dirp)
792 static double last_box_height;
793 static double last_box_width;
794 static double last_box_radius;
795 static int have_last_box = 0;
796 if (!(flags & HAS_HEIGHT)) {
797 if ((flags & IS_SAME) && have_last_box)
798 height = last_box_height;
799 else
800 lookup_variable("boxht", &height);
802 if (!(flags & HAS_WIDTH)) {
803 if ((flags & IS_SAME) && have_last_box)
804 width = last_box_width;
805 else
806 lookup_variable("boxwid", &width);
808 if (!(flags & HAS_RADIUS)) {
809 if ((flags & IS_SAME) && have_last_box)
810 radius = last_box_radius;
811 else
812 lookup_variable("boxrad", &radius);
814 last_box_width = width;
815 last_box_height = height;
816 last_box_radius = radius;
817 have_last_box = 1;
818 radius = fabs(radius);
819 if (radius*2.0 > fabs(width))
820 radius = fabs(width/2.0);
821 if (radius*2.0 > fabs(height))
822 radius = fabs(height/2.0);
823 box_object *p = new box_object(position(width, height), radius);
824 if (!position_rectangle(p, curpos, dirp)) {
825 delete p;
826 p = 0;
828 return p;
831 // return non-zero for success
833 int object_spec::position_rectangle(rectangle_object *p,
834 position *curpos, direction *dirp)
836 position pos;
837 dir = *dirp; // ignore any direction in attribute list
838 position motion;
839 switch (dir) {
840 case UP_DIRECTION:
841 motion.y = p->height()/2.0;
842 break;
843 case DOWN_DIRECTION:
844 motion.y = -p->height()/2.0;
845 break;
846 case LEFT_DIRECTION:
847 motion.x = -p->width()/2.0;
848 break;
849 case RIGHT_DIRECTION:
850 motion.x = p->width()/2.0;
851 break;
852 default:
853 assert(0);
855 if (flags & HAS_AT) {
856 pos = at;
857 if (flags & HAS_WITH) {
858 place offset;
859 place here;
860 here.obj = p;
861 if (!with->follow(here, &offset))
862 return 0;
863 pos -= offset;
866 else {
867 pos = *curpos;
868 pos += motion;
870 p->move_by(pos);
871 pos += motion;
872 *curpos = pos;
873 return 1;
876 class block_object : public rectangle_object {
877 object_list oblist;
878 PTABLE(place) *tbl;
879 public:
880 block_object(const position &, const object_list &ol, PTABLE(place) *t);
881 ~block_object();
882 place *find_label(const char *);
883 object_type type();
884 void move_by(const position &);
885 void print();
888 block_object::block_object(const position &d, const object_list &ol,
889 PTABLE(place) *t)
890 : rectangle_object(d), oblist(ol), tbl(t)
894 block_object::~block_object()
896 delete tbl;
897 object *p = oblist.head;
898 while (p != 0) {
899 object *tem = p;
900 p = p->next;
901 delete tem;
905 void block_object::print()
907 out->begin_block(south_west(), north_east());
908 print_object_list(oblist.head);
909 out->end_block();
912 static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
914 // Adjust all the labels that aren't attached to objects.
915 PTABLE_ITERATOR(place) iter(tbl);
916 const char *key;
917 place *pl;
918 while (iter.next(&key, &pl))
919 if (key && csupper(key[0]) && pl->obj == 0) {
920 pl->x += a.x;
921 pl->y += a.y;
925 void block_object::move_by(const position &a)
927 cent += a;
928 for (object *p = oblist.head; p; p = p->next)
929 p->move_by(a);
930 adjust_objectless_places(tbl, a);
934 place *block_object::find_label(const char *name)
936 return tbl->lookup(name);
939 object_type block_object::type()
941 return BLOCK_OBJECT;
944 graphic_object *object_spec::make_block(position *curpos, direction *dirp)
946 bounding_box bb;
947 for (object *p = oblist.head; p; p = p->next)
948 p->update_bounding_box(&bb);
949 position dim;
950 if (!bb.blank) {
951 position m = -(bb.ll + bb.ur)/2.0;
952 for (object *p = oblist.head; p; p = p->next)
953 p->move_by(m);
954 adjust_objectless_places(tbl, m);
955 dim = bb.ur - bb.ll;
957 if (flags & HAS_WIDTH)
958 dim.x = width;
959 if (flags & HAS_HEIGHT)
960 dim.y = height;
961 block_object *block = new block_object(dim, oblist, tbl);
962 if (!position_rectangle(block, curpos, dirp)) {
963 delete block;
964 block = 0;
966 tbl = 0;
967 oblist.head = oblist.tail = 0;
968 return block;
971 class text_object : public rectangle_object {
972 public:
973 text_object(const position &);
974 object_type type() { return TEXT_OBJECT; }
977 text_object::text_object(const position &d)
978 : rectangle_object(d)
982 graphic_object *object_spec::make_text(position *curpos, direction *dirp)
984 if (!(flags & HAS_HEIGHT)) {
985 lookup_variable("textht", &height);
986 int nitems = 0;
987 for (text_item *t = text; t; t = t->next)
988 nitems++;
989 height *= nitems;
991 if (!(flags & HAS_WIDTH))
992 lookup_variable("textwid", &width);
993 text_object *p = new text_object(position(width, height));
994 if (!position_rectangle(p, curpos, dirp)) {
995 delete p;
996 p = 0;
998 return p;
1002 class ellipse_object : public closed_object {
1003 public:
1004 ellipse_object(const position &);
1005 position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1006 cent.y + dim.y/(M_SQRT2*2.0)); }
1007 position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1008 cent.y + dim.y/(M_SQRT2*2.0)); }
1009 position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1010 cent.y - dim.y/(M_SQRT2*2.0)); }
1011 position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1012 cent.y - dim.y/(M_SQRT2*2.0)); }
1013 double radius() { return dim.x/2.0; }
1014 object_type type() { return ELLIPSE_OBJECT; }
1015 void print();
1018 ellipse_object::ellipse_object(const position &d)
1019 : closed_object(d)
1023 void ellipse_object::print()
1025 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1026 return;
1027 out->set_color(color_fill, graphic_object::get_outline_color());
1028 out->ellipse(cent, dim, lt, fill);
1029 out->reset_color();
1032 graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1034 static double last_ellipse_height;
1035 static double last_ellipse_width;
1036 static int have_last_ellipse = 0;
1037 if (!(flags & HAS_HEIGHT)) {
1038 if ((flags & IS_SAME) && have_last_ellipse)
1039 height = last_ellipse_height;
1040 else
1041 lookup_variable("ellipseht", &height);
1043 if (!(flags & HAS_WIDTH)) {
1044 if ((flags & IS_SAME) && have_last_ellipse)
1045 width = last_ellipse_width;
1046 else
1047 lookup_variable("ellipsewid", &width);
1049 last_ellipse_width = width;
1050 last_ellipse_height = height;
1051 have_last_ellipse = 1;
1052 ellipse_object *p = new ellipse_object(position(width, height));
1053 if (!position_rectangle(p, curpos, dirp)) {
1054 delete p;
1055 return 0;
1057 return p;
1060 class circle_object : public ellipse_object {
1061 public:
1062 circle_object(double);
1063 object_type type() { return CIRCLE_OBJECT; }
1064 void print();
1067 circle_object::circle_object(double diam)
1068 : ellipse_object(position(diam, diam))
1072 void circle_object::print()
1074 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1075 return;
1076 out->set_color(color_fill, graphic_object::get_outline_color());
1077 out->circle(cent, dim.x/2.0, lt, fill);
1078 out->reset_color();
1081 graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1083 static double last_circle_radius;
1084 static int have_last_circle = 0;
1085 if (!(flags & HAS_RADIUS)) {
1086 if ((flags & IS_SAME) && have_last_circle)
1087 radius = last_circle_radius;
1088 else
1089 lookup_variable("circlerad", &radius);
1091 last_circle_radius = radius;
1092 have_last_circle = 1;
1093 circle_object *p = new circle_object(radius*2.0);
1094 if (!position_rectangle(p, curpos, dirp)) {
1095 delete p;
1096 return 0;
1098 return p;
1101 class move_object : public graphic_object {
1102 position strt;
1103 position en;
1104 public:
1105 move_object(const position &s, const position &e);
1106 position origin() { return en; }
1107 object_type type() { return MOVE_OBJECT; }
1108 void update_bounding_box(bounding_box *);
1109 void move_by(const position &);
1112 move_object::move_object(const position &s, const position &e)
1113 : strt(s), en(e)
1117 void move_object::update_bounding_box(bounding_box *p)
1119 p->encompass(strt);
1120 p->encompass(en);
1123 void move_object::move_by(const position &a)
1125 strt += a;
1126 en += a;
1129 graphic_object *object_spec::make_move(position *curpos, direction *dirp)
1131 static position last_move;
1132 static int have_last_move = 0;
1133 *dirp = dir;
1134 // No need to look at at since `at' attribute sets `from' attribute.
1135 position startpos = (flags & HAS_FROM) ? from : *curpos;
1136 if (!(flags & HAS_SEGMENT)) {
1137 if ((flags & IS_SAME) && have_last_move)
1138 segment_pos = last_move;
1139 else {
1140 switch (dir) {
1141 case UP_DIRECTION:
1142 segment_pos.y = segment_height;
1143 break;
1144 case DOWN_DIRECTION:
1145 segment_pos.y = -segment_height;
1146 break;
1147 case LEFT_DIRECTION:
1148 segment_pos.x = -segment_width;
1149 break;
1150 case RIGHT_DIRECTION:
1151 segment_pos.x = segment_width;
1152 break;
1153 default:
1154 assert(0);
1158 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1159 // Reverse the segment_list so that it's in forward order.
1160 segment *old = segment_list;
1161 segment_list = 0;
1162 while (old != 0) {
1163 segment *tem = old->next;
1164 old->next = segment_list;
1165 segment_list = old;
1166 old = tem;
1168 // Compute the end position.
1169 position endpos = startpos;
1170 for (segment *s = segment_list; s; s = s->next)
1171 if (s->is_absolute)
1172 endpos = s->pos;
1173 else
1174 endpos += s->pos;
1175 have_last_move = 1;
1176 last_move = endpos - startpos;
1177 move_object *p = new move_object(startpos, endpos);
1178 *curpos = endpos;
1179 return p;
1182 class linear_object : public graphic_object {
1183 protected:
1184 char arrow_at_start;
1185 char arrow_at_end;
1186 arrow_head_type aht;
1187 position strt;
1188 position en;
1189 public:
1190 linear_object(const position &s, const position &e);
1191 position start() { return strt; }
1192 position end() { return en; }
1193 void move_by(const position &);
1194 void update_bounding_box(bounding_box *) = 0;
1195 object_type type() = 0;
1196 void add_arrows(int at_start, int at_end, const arrow_head_type &);
1199 class line_object : public linear_object {
1200 protected:
1201 position *v;
1202 int n;
1203 public:
1204 line_object(const position &s, const position &e, position *, int);
1205 ~line_object();
1206 position origin() { return strt; }
1207 position center() { return (strt + en)/2.0; }
1208 position north() { return (en.y - strt.y) > 0 ? en : strt; }
1209 position south() { return (en.y - strt.y) < 0 ? en : strt; }
1210 position east() { return (en.x - strt.x) > 0 ? en : strt; }
1211 position west() { return (en.x - strt.x) < 0 ? en : strt; }
1212 object_type type() { return LINE_OBJECT; }
1213 void update_bounding_box(bounding_box *);
1214 void print();
1215 void move_by(const position &);
1218 class arrow_object : public line_object {
1219 public:
1220 arrow_object(const position &, const position &, position *, int);
1221 object_type type() { return ARROW_OBJECT; }
1224 class spline_object : public line_object {
1225 public:
1226 spline_object(const position &, const position &, position *, int);
1227 object_type type() { return SPLINE_OBJECT; }
1228 void print();
1229 void update_bounding_box(bounding_box *);
1232 linear_object::linear_object(const position &s, const position &e)
1233 : arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1237 void linear_object::move_by(const position &a)
1239 strt += a;
1240 en += a;
1243 void linear_object::add_arrows(int at_start, int at_end,
1244 const arrow_head_type &a)
1246 arrow_at_start = at_start;
1247 arrow_at_end = at_end;
1248 aht = a;
1251 line_object::line_object(const position &s, const position &e,
1252 position *p, int i)
1253 : linear_object(s, e), v(p), n(i)
1257 void line_object::print()
1259 if (lt.type == line_type::invisible)
1260 return;
1261 out->set_color(0, graphic_object::get_outline_color());
1262 // shorten line length to avoid arrow sticking.
1263 position sp = strt;
1264 if (arrow_at_start) {
1265 position base = v[0] - strt;
1266 double hyp = hypot(base);
1267 if (hyp == 0.0) {
1268 error("cannot draw arrow on object with zero length");
1269 return;
1271 if (aht.solid && out->supports_filled_polygons()) {
1272 base *= aht.height / hyp;
1273 draw_arrow(strt, strt - v[0], aht, lt,
1274 graphic_object::get_outline_color());
1275 sp = strt + base;
1276 } else {
1277 base *= fabs(lt.thickness) / hyp / 72 / 4;
1278 sp = strt + base;
1279 draw_arrow(sp, sp - v[0], aht, lt,
1280 graphic_object::get_outline_color());
1283 if (arrow_at_end) {
1284 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1285 double hyp = hypot(base);
1286 if (hyp == 0.0) {
1287 error("cannot draw arrow on object with zero length");
1288 return;
1290 if (aht.solid && out->supports_filled_polygons()) {
1291 base *= aht.height / hyp;
1292 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1293 graphic_object::get_outline_color());
1294 v[n-1] = en - base;
1295 } else {
1296 base *= fabs(lt.thickness) / hyp / 72 / 4;
1297 v[n-1] = en - base;
1298 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1299 graphic_object::get_outline_color());
1302 out->line(sp, v, n, lt);
1303 out->reset_color();
1306 void line_object::update_bounding_box(bounding_box *p)
1308 p->encompass(strt);
1309 for (int i = 0; i < n; i++)
1310 p->encompass(v[i]);
1313 void line_object::move_by(const position &pos)
1315 linear_object::move_by(pos);
1316 for (int i = 0; i < n; i++)
1317 v[i] += pos;
1320 void spline_object::update_bounding_box(bounding_box *p)
1322 p->encompass(strt);
1323 p->encompass(en);
1328 p1 = q1/2 + q2/2
1329 p2 = q1/6 + q2*5/6
1330 p3 = q2*5/6 + q3/6
1331 p4 = q2/2 + q3/2
1332 [ the points for the Bezier cubic ]
1336 t = .5
1338 then
1340 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1341 [ the equation for the Bezier cubic ]
1343 = .125*q1 + .75*q2 + .125*q3
1346 for (int i = 1; i < n; i++)
1347 p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1350 arrow_object::arrow_object(const position &s, const position &e,
1351 position *p, int i)
1352 : line_object(s, e, p, i)
1356 spline_object::spline_object(const position &s, const position &e,
1357 position *p, int i)
1358 : line_object(s, e, p, i)
1362 void spline_object::print()
1364 if (lt.type == line_type::invisible)
1365 return;
1366 out->set_color(0, graphic_object::get_outline_color());
1367 // shorten line length for spline to avoid arrow sticking
1368 position sp = strt;
1369 if (arrow_at_start) {
1370 position base = v[0] - strt;
1371 double hyp = hypot(base);
1372 if (hyp == 0.0) {
1373 error("cannot draw arrow on object with zero length");
1374 return;
1376 if (aht.solid && out->supports_filled_polygons()) {
1377 base *= aht.height / hyp;
1378 draw_arrow(strt, strt - v[0], aht, lt,
1379 graphic_object::get_outline_color());
1380 sp = strt + base*0.1; // to reserve spline shape
1381 } else {
1382 base *= fabs(lt.thickness) / hyp / 72 / 4;
1383 sp = strt + base;
1384 draw_arrow(sp, sp - v[0], aht, lt,
1385 graphic_object::get_outline_color());
1388 if (arrow_at_end) {
1389 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1390 double hyp = hypot(base);
1391 if (hyp == 0.0) {
1392 error("cannot draw arrow on object with zero length");
1393 return;
1395 if (aht.solid && out->supports_filled_polygons()) {
1396 base *= aht.height / hyp;
1397 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1398 graphic_object::get_outline_color());
1399 v[n-1] = en - base*0.1; // to reserve spline shape
1400 } else {
1401 base *= fabs(lt.thickness) / hyp / 72 / 4;
1402 v[n-1] = en - base;
1403 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1404 graphic_object::get_outline_color());
1407 out->spline(sp, v, n, lt);
1408 out->reset_color();
1411 line_object::~line_object()
1413 a_delete v;
1416 linear_object *object_spec::make_line(position *curpos, direction *dirp)
1418 static position last_line;
1419 static int have_last_line = 0;
1420 *dirp = dir;
1421 // No need to look at at since `at' attribute sets `from' attribute.
1422 position startpos = (flags & HAS_FROM) ? from : *curpos;
1423 if (!(flags & HAS_SEGMENT)) {
1424 if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1425 && have_last_line)
1426 segment_pos = last_line;
1427 else
1428 switch (dir) {
1429 case UP_DIRECTION:
1430 segment_pos.y = segment_height;
1431 break;
1432 case DOWN_DIRECTION:
1433 segment_pos.y = -segment_height;
1434 break;
1435 case LEFT_DIRECTION:
1436 segment_pos.x = -segment_width;
1437 break;
1438 case RIGHT_DIRECTION:
1439 segment_pos.x = segment_width;
1440 break;
1441 default:
1442 assert(0);
1445 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1446 // reverse the segment_list so that it's in forward order
1447 segment *old = segment_list;
1448 segment_list = 0;
1449 while (old != 0) {
1450 segment *tem = old->next;
1451 old->next = segment_list;
1452 segment_list = old;
1453 old = tem;
1455 // Absolutise all movements
1456 position endpos = startpos;
1457 int nsegments = 0;
1458 segment *s;
1459 for (s = segment_list; s; s = s->next, nsegments++)
1460 if (s->is_absolute)
1461 endpos = s->pos;
1462 else {
1463 endpos += s->pos;
1464 s->pos = endpos;
1465 s->is_absolute = 1; // to avoid confusion
1467 // handle chop
1468 line_object *p = 0;
1469 position *v = new position[nsegments];
1470 int i = 0;
1471 for (s = segment_list; s; s = s->next, i++)
1472 v[i] = s->pos;
1473 if (flags & IS_DEFAULT_CHOPPED) {
1474 lookup_variable("circlerad", &start_chop);
1475 end_chop = start_chop;
1476 flags |= IS_CHOPPED;
1478 if (flags & IS_CHOPPED) {
1479 position start_chop_vec, end_chop_vec;
1480 if (start_chop != 0.0) {
1481 start_chop_vec = v[0] - startpos;
1482 start_chop_vec *= start_chop / hypot(start_chop_vec);
1484 if (end_chop != 0.0) {
1485 end_chop_vec = (v[nsegments - 1]
1486 - (nsegments > 1 ? v[nsegments - 2] : startpos));
1487 end_chop_vec *= end_chop / hypot(end_chop_vec);
1489 startpos += start_chop_vec;
1490 v[nsegments - 1] -= end_chop_vec;
1491 endpos -= end_chop_vec;
1493 switch (type) {
1494 case SPLINE_OBJECT:
1495 p = new spline_object(startpos, endpos, v, nsegments);
1496 break;
1497 case ARROW_OBJECT:
1498 p = new arrow_object(startpos, endpos, v, nsegments);
1499 break;
1500 case LINE_OBJECT:
1501 p = new line_object(startpos, endpos, v, nsegments);
1502 break;
1503 default:
1504 assert(0);
1506 have_last_line = 1;
1507 last_line = endpos - startpos;
1508 *curpos = endpos;
1509 return p;
1512 class arc_object : public linear_object {
1513 int clockwise;
1514 position cent;
1515 double rad;
1516 public:
1517 arc_object(int, const position &, const position &, const position &);
1518 position origin() { return cent; }
1519 position center() { return cent; }
1520 double radius() { return rad; }
1521 position north();
1522 position south();
1523 position east();
1524 position west();
1525 position north_east();
1526 position north_west();
1527 position south_east();
1528 position south_west();
1529 void update_bounding_box(bounding_box *);
1530 object_type type() { return ARC_OBJECT; }
1531 void print();
1532 void move_by(const position &pos);
1535 arc_object::arc_object(int cw, const position &s, const position &e,
1536 const position &c)
1537 : linear_object(s, e), clockwise(cw), cent(c)
1539 rad = hypot(c - s);
1542 void arc_object::move_by(const position &pos)
1544 linear_object::move_by(pos);
1545 cent += pos;
1548 // we get arc corners from the corresponding circle
1550 position arc_object::north()
1552 position result(cent);
1553 result.y += rad;
1554 return result;
1557 position arc_object::south()
1559 position result(cent);
1560 result.y -= rad;
1561 return result;
1564 position arc_object::east()
1566 position result(cent);
1567 result.x += rad;
1568 return result;
1571 position arc_object::west()
1573 position result(cent);
1574 result.x -= rad;
1575 return result;
1578 position arc_object::north_east()
1580 position result(cent);
1581 result.x += rad/M_SQRT2;
1582 result.y += rad/M_SQRT2;
1583 return result;
1586 position arc_object::north_west()
1588 position result(cent);
1589 result.x -= rad/M_SQRT2;
1590 result.y += rad/M_SQRT2;
1591 return result;
1594 position arc_object::south_east()
1596 position result(cent);
1597 result.x += rad/M_SQRT2;
1598 result.y -= rad/M_SQRT2;
1599 return result;
1602 position arc_object::south_west()
1604 position result(cent);
1605 result.x -= rad/M_SQRT2;
1606 result.y -= rad/M_SQRT2;
1607 return result;
1611 void arc_object::print()
1613 if (lt.type == line_type::invisible)
1614 return;
1615 out->set_color(0, graphic_object::get_outline_color());
1616 // handle arrow direction; make shorter line for arc
1617 position sp, ep, b;
1618 if (clockwise) {
1619 sp = en;
1620 ep = strt;
1621 } else {
1622 sp = strt;
1623 ep = en;
1625 if (arrow_at_start) {
1626 double theta = aht.height / rad;
1627 if (clockwise)
1628 theta = - theta;
1629 b = strt - cent;
1630 b = position(b.x*cos(theta) - b.y*sin(theta),
1631 b.x*sin(theta) + b.y*cos(theta)) + cent;
1632 if (clockwise)
1633 ep = b;
1634 else
1635 sp = b;
1636 if (aht.solid && out->supports_filled_polygons()) {
1637 draw_arrow(strt, strt - b, aht, lt,
1638 graphic_object::get_outline_color());
1639 } else {
1640 position v = b;
1641 theta = fabs(lt.thickness) / 72 / 4 / rad;
1642 if (clockwise)
1643 theta = - theta;
1644 b = strt - cent;
1645 b = position(b.x*cos(theta) - b.y*sin(theta),
1646 b.x*sin(theta) + b.y*cos(theta)) + cent;
1647 draw_arrow(b, b - v, aht, lt,
1648 graphic_object::get_outline_color());
1649 out->line(b, &v, 1, lt);
1652 if (arrow_at_end) {
1653 double theta = aht.height / rad;
1654 if (!clockwise)
1655 theta = - theta;
1656 b = en - cent;
1657 b = position(b.x*cos(theta) - b.y*sin(theta),
1658 b.x*sin(theta) + b.y*cos(theta)) + cent;
1659 if (clockwise)
1660 sp = b;
1661 else
1662 ep = b;
1663 if (aht.solid && out->supports_filled_polygons()) {
1664 draw_arrow(en, en - b, aht, lt,
1665 graphic_object::get_outline_color());
1666 } else {
1667 position v = b;
1668 theta = fabs(lt.thickness) / 72 / 4 / rad;
1669 if (!clockwise)
1670 theta = - theta;
1671 b = en - cent;
1672 b = position(b.x*cos(theta) - b.y*sin(theta),
1673 b.x*sin(theta) + b.y*cos(theta)) + cent;
1674 draw_arrow(b, b - v, aht, lt,
1675 graphic_object::get_outline_color());
1676 out->line(b, &v, 1, lt);
1679 out->arc(sp, cent, ep, lt);
1680 out->reset_color();
1683 inline double max(double a, double b)
1685 return a > b ? a : b;
1688 void arc_object::update_bounding_box(bounding_box *p)
1690 p->encompass(strt);
1691 p->encompass(en);
1692 position start_offset = strt - cent;
1693 if (start_offset.x == 0.0 && start_offset.y == 0.0)
1694 return;
1695 position end_offset = en - cent;
1696 if (end_offset.x == 0.0 && end_offset.y == 0.0)
1697 return;
1698 double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1699 double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1700 if (clockwise) {
1701 double temp = start_quad;
1702 start_quad = end_quad;
1703 end_quad = temp;
1705 if (start_quad < 0.0)
1706 start_quad += 4.0;
1707 while (end_quad <= start_quad)
1708 end_quad += 4.0;
1709 double r = max(hypot(start_offset), hypot(end_offset));
1710 for (int q = int(start_quad) + 1; q < end_quad; q++) {
1711 position offset;
1712 switch (q % 4) {
1713 case 0:
1714 offset.x = r;
1715 break;
1716 case 1:
1717 offset.y = r;
1718 break;
1719 case 2:
1720 offset.x = -r;
1721 break;
1722 case 3:
1723 offset.y = -r;
1724 break;
1726 p->encompass(cent + offset);
1730 // We ignore the with attribute. The at attribute always refers to the center.
1732 linear_object *object_spec::make_arc(position *curpos, direction *dirp)
1734 *dirp = dir;
1735 int cw = (flags & IS_CLOCKWISE) != 0;
1736 // compute the start
1737 position startpos;
1738 if (flags & HAS_FROM)
1739 startpos = from;
1740 else
1741 startpos = *curpos;
1742 if (!(flags & HAS_RADIUS))
1743 lookup_variable("arcrad", &radius);
1744 // compute the end
1745 position endpos;
1746 if (flags & HAS_TO)
1747 endpos = to;
1748 else {
1749 position m(radius, radius);
1750 // Adjust the signs.
1751 if (cw) {
1752 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1753 m.x = -m.x;
1754 if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1755 m.y = -m.y;
1756 *dirp = direction((dir + 3) % 4);
1758 else {
1759 if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1760 m.x = -m.x;
1761 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1762 m.y = -m.y;
1763 *dirp = direction((dir + 1) % 4);
1765 endpos = startpos + m;
1767 // compute the center
1768 position centerpos;
1769 if (flags & HAS_AT)
1770 centerpos = at;
1771 else if (startpos == endpos)
1772 centerpos = startpos;
1773 else {
1774 position h = (endpos - startpos)/2.0;
1775 double d = hypot(h);
1776 if (radius <= 0)
1777 radius = .25;
1778 // make the radius big enough
1779 while (radius < d)
1780 radius *= 2.0;
1781 double alpha = acos(d/radius);
1782 double theta = atan2(h.y, h.x);
1783 if (cw)
1784 theta -= alpha;
1785 else
1786 theta += alpha;
1787 centerpos = position(cos(theta), sin(theta))*radius + startpos;
1789 arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1790 *curpos = endpos;
1791 return p;
1794 graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1796 linear_object *obj;
1797 if (type == ARC_OBJECT)
1798 obj = make_arc(curpos, dirp);
1799 else
1800 obj = make_line(curpos, dirp);
1801 if (type == ARROW_OBJECT
1802 && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1803 flags |= HAS_RIGHT_ARROW_HEAD;
1804 if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1805 arrow_head_type a;
1806 int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1807 int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1808 if (flags & HAS_HEIGHT)
1809 a.height = height;
1810 else
1811 lookup_variable("arrowht", &a.height);
1812 if (flags & HAS_WIDTH)
1813 a.width = width;
1814 else
1815 lookup_variable("arrowwid", &a.width);
1816 double solid;
1817 lookup_variable("arrowhead", &solid);
1818 a.solid = solid != 0.0;
1819 obj->add_arrows(at_start, at_end, a);
1821 return obj;
1824 object *object_spec::make_object(position *curpos, direction *dirp)
1826 graphic_object *obj = 0;
1827 switch (type) {
1828 case BLOCK_OBJECT:
1829 obj = make_block(curpos, dirp);
1830 break;
1831 case BOX_OBJECT:
1832 obj = make_box(curpos, dirp);
1833 break;
1834 case TEXT_OBJECT:
1835 obj = make_text(curpos, dirp);
1836 break;
1837 case ELLIPSE_OBJECT:
1838 obj = make_ellipse(curpos, dirp);
1839 break;
1840 case CIRCLE_OBJECT:
1841 obj = make_circle(curpos, dirp);
1842 break;
1843 case MOVE_OBJECT:
1844 obj = make_move(curpos, dirp);
1845 break;
1846 case ARC_OBJECT:
1847 case LINE_OBJECT:
1848 case SPLINE_OBJECT:
1849 case ARROW_OBJECT:
1850 obj = make_linear(curpos, dirp);
1851 break;
1852 case MARK_OBJECT:
1853 case OTHER_OBJECT:
1854 default:
1855 assert(0);
1856 break;
1858 if (obj) {
1859 if (flags & IS_INVISIBLE)
1860 obj->set_invisible();
1861 if (text != 0)
1862 obj->add_text(text, (flags & IS_ALIGNED) != 0);
1863 if (flags & IS_DOTTED)
1864 obj->set_dotted(dash_width);
1865 else if (flags & IS_DASHED)
1866 obj->set_dashed(dash_width);
1867 double th;
1868 if (flags & HAS_THICKNESS)
1869 th = thickness;
1870 else
1871 lookup_variable("linethick", &th);
1872 obj->set_thickness(th);
1873 if (flags & IS_OUTLINED)
1874 obj->set_outline_color(outlined);
1875 if (flags & (IS_DEFAULT_FILLED | IS_FILLED)) {
1876 if (flags & IS_SHADED)
1877 obj->set_fill_color(shaded);
1878 else {
1879 if (flags & IS_DEFAULT_FILLED)
1880 lookup_variable("fillval", &fill);
1881 if (fill < 0.0)
1882 error("bad fill value %1", fill);
1883 else
1884 obj->set_fill(fill);
1888 return obj;
1891 struct string_list {
1892 string_list *next;
1893 char *str;
1894 string_list(char *);
1895 ~string_list();
1898 string_list::string_list(char *s)
1899 : next(0), str(s)
1903 string_list::~string_list()
1905 a_delete str;
1908 /* A path is used to hold the argument to the `with' attribute. For
1909 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1910 take a place and follow the path through the place to place within the
1911 place. Note that `.A.B.C.sw' will work.
1913 For compatibility with DWB pic, `with' accepts positions also (this
1914 is incorrectly documented in CSTR 116). */
1916 path::path(corner c)
1917 : crn(c), label_list(0), ypath(0), is_position(0)
1921 path::path(position p)
1922 : crn(0), label_list(0), ypath(0), is_position(1)
1924 pos.x = p.x;
1925 pos.y = p.y;
1928 path::path(char *l, corner c)
1929 : crn(c), ypath(0), is_position(0)
1931 label_list = new string_list(l);
1934 path::~path()
1936 while (label_list) {
1937 string_list *tem = label_list;
1938 label_list = label_list->next;
1939 delete tem;
1941 delete ypath;
1944 void path::append(corner c)
1946 assert(crn == 0);
1947 crn = c;
1950 void path::append(char *s)
1952 string_list **p;
1953 for (p = &label_list; *p; p = &(*p)->next)
1955 *p = new string_list(s);
1958 void path::set_ypath(path *p)
1960 ypath = p;
1963 // return non-zero for success
1965 int path::follow(const place &pl, place *result) const
1967 if (is_position) {
1968 result->x = pos.x;
1969 result->y = pos.y;
1970 result->obj = 0;
1971 return 1;
1973 const place *p = &pl;
1974 for (string_list *lb = label_list; lb; lb = lb->next)
1975 if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
1976 lex_error("object does not contain a place `%1'", lb->str);
1977 return 0;
1979 if (crn == 0 || p->obj == 0)
1980 *result = *p;
1981 else {
1982 position ps = ((p->obj)->*(crn))();
1983 result->x = ps.x;
1984 result->y = ps.y;
1985 result->obj = 0;
1987 if (ypath) {
1988 place tem;
1989 if (!ypath->follow(pl, &tem))
1990 return 0;
1991 result->y = tem.y;
1992 if (result->obj != tem.obj)
1993 result->obj = 0;
1995 return 1;
1998 void print_object_list(object *p)
2000 for (; p; p = p->next) {
2001 p->print();
2002 p->print_text();
2006 void print_picture(object *obj)
2008 bounding_box bb;
2009 for (object *p = obj; p; p = p->next)
2010 p->update_bounding_box(&bb);
2011 double scale;
2012 lookup_variable("scale", &scale);
2013 out->start_picture(scale, bb.ll, bb.ur);
2014 print_object_list(obj);
2015 out->finish_picture();