Sync-to-go: update copyright for 2015
[s-roff.git] / src / pre-pic / object.cpp
blob7f0d2085fc65313446aa55eced5e6eb139df1514
1 /*@
2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 1992, 2001 - 2007 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2, or (at your option) any later
10 * version.
12 * This is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with groff; see the file COPYING. If not, write to the Free Software
19 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "config.h"
23 #include "pic-config.h"
25 #include "object.h"
26 #include "pic.h"
27 #include "ptable.h"
29 void print_object_list(object *);
31 line_type::line_type()
32 : type(solid), thickness(1.0)
36 output::output() : args(0), desired_height(0.0), desired_width(0.0)
40 output::~output()
42 a_delete args;
45 void output::set_desired_width_height(double wid, double ht)
47 desired_width = wid;
48 desired_height = ht;
51 void output::set_args(const char *s)
53 a_delete args;
54 if (s == 0 || *s == '\0')
55 args = 0;
56 else
57 args = strsave(s);
60 int output::supports_filled_polygons()
62 return 0;
65 void output::begin_block(const position &, const position &)
69 void output::end_block()
73 double output::compute_scale(double sc, const position &ll, const position &ur)
75 distance dim = ur - ll;
76 if (desired_width != 0.0 || desired_height != 0.0) {
77 sc = 0.0;
78 if (desired_width != 0.0) {
79 if (dim.x == 0.0)
80 error("width specified for picture with zero width");
81 else
82 sc = dim.x/desired_width;
84 if (desired_height != 0.0) {
85 if (dim.y == 0.0)
86 error("height specified for picture with zero height");
87 else {
88 double tem = dim.y/desired_height;
89 if (tem > sc)
90 sc = tem;
93 return sc == 0.0 ? 1.0 : sc;
95 else {
96 if (sc <= 0.0)
97 sc = 1.0;
98 distance sdim = dim/sc;
99 double max_width = 0.0;
100 lookup_variable("maxpswid", &max_width);
101 double max_height = 0.0;
102 lookup_variable("maxpsht", &max_height);
103 if ((max_width > 0.0 && sdim.x > max_width)
104 || (max_height > 0.0 && sdim.y > max_height)) {
105 double xscale = dim.x/max_width;
106 double yscale = dim.y/max_height;
107 return xscale > yscale ? xscale : yscale;
109 else
110 return sc;
114 position::position(const place &pl)
116 if (pl.obj != 0) {
117 // Use two statements to work around bug in SGI C++.
118 object *tem = pl.obj;
119 *this = tem->origin();
121 else {
122 x = pl.x;
123 y = pl.y;
127 position::position() : x(0.0), y(0.0)
131 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 xslanted = 0;
418 yslanted = 0;
419 outlined = 0;
420 with = 0;
421 dir = RIGHT_DIRECTION;
424 object_spec::~object_spec()
426 delete tbl;
427 while (segment_list != 0) {
428 segment *tem = segment_list;
429 segment_list = segment_list->next;
430 delete tem;
432 object *p = oblist.head;
433 while (p != 0) {
434 object *tem = p;
435 p = p->next;
436 delete tem;
438 while (text != 0) {
439 text_item *tem = text;
440 text = text->next;
441 delete tem;
443 delete with;
444 a_delete shaded;
445 a_delete outlined;
448 class command_object
449 : public object
451 char *s;
452 const char *filename;
453 int lineno;
455 public:
456 command_object(char *, const char *, int);
457 ~command_object();
458 object_type type() { return OTHER_OBJECT; }
459 void print();
462 command_object::command_object(char *p, const char *fn, int ln)
463 : s(p), filename(fn), lineno(ln)
467 command_object::~command_object()
469 a_delete s;
472 void command_object::print()
474 out->command(s, filename, lineno);
477 object *make_command_object(char *s, const char *fn, int ln)
479 return new command_object(s, fn, ln);
482 class mark_object
483 : public object
485 public:
486 mark_object();
487 object_type type();
490 object *make_mark_object()
492 return new mark_object();
495 mark_object::mark_object()
499 object_type mark_object::type()
501 return MARK_OBJECT;
504 object_list::object_list() : head(0), tail(0)
508 void object_list::append(object *obj)
510 if (tail == 0) {
511 obj->next = obj->prev = 0;
512 head = tail = obj;
514 else {
515 obj->prev = tail;
516 obj->next = 0;
517 tail->next = obj;
518 tail = obj;
522 void object_list::wrap_up_block(object_list *ol)
524 object *p;
525 for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
527 assert(p != 0);
528 ol->head = p->next;
529 if (ol->head) {
530 ol->tail = tail;
531 ol->head->prev = 0;
533 else
534 ol->tail = 0;
535 tail = p->prev;
536 if (tail)
537 tail->next = 0;
538 else
539 head = 0;
540 delete p;
543 text_piece::text_piece()
544 : text(0), filename(0), lineno(-1)
546 adj.h = CENTER_ADJUST;
547 adj.v = NONE_ADJUST;
550 text_piece::~text_piece()
552 a_delete text;
555 class graphic_object
556 : public object
558 int ntext;
559 text_piece *text;
560 int aligned;
562 protected:
563 line_type lt;
564 char *outline_color;
565 char *color_fill;
567 public:
568 graphic_object();
569 ~graphic_object();
570 object_type type() = 0;
571 void print_text();
572 void add_text(text_item *, int);
573 void set_dotted(double);
574 void set_dashed(double);
575 void set_thickness(double);
576 void set_invisible();
577 void set_outline_color(char *);
578 char *get_outline_color();
579 virtual void set_fill(double);
580 virtual void set_xslanted(double);
581 virtual void set_yslanted(double);
582 virtual void set_fill_color(char *);
585 graphic_object::graphic_object()
586 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
590 void graphic_object::set_dotted(double wid)
592 lt.type = line_type::dotted;
593 lt.dash_width = wid;
596 void graphic_object::set_dashed(double wid)
598 lt.type = line_type::dashed;
599 lt.dash_width = wid;
602 void graphic_object::set_thickness(double th)
604 lt.thickness = th;
607 void graphic_object::set_fill(double)
611 void graphic_object::set_xslanted(double)
615 void graphic_object::set_yslanted(double)
619 void graphic_object::set_fill_color(char *c)
621 color_fill = strsave(c);
624 void graphic_object::set_outline_color(char *c)
626 outline_color = strsave(c);
629 char *graphic_object::get_outline_color()
631 return outline_color;
634 void graphic_object::set_invisible()
636 lt.type = line_type::invisible;
639 void graphic_object::add_text(text_item *t, int a)
641 aligned = a;
642 int len = 0;
643 text_item *p;
644 for (p = t; p; p = p->next)
645 len++;
646 if (len == 0)
647 text = 0;
648 else {
649 text = new text_piece[len];
650 for (p = t, len = 0; p; p = p->next, len++) {
651 text[len].text = p->text;
652 p->text = 0;
653 text[len].adj = p->adj;
654 text[len].filename = p->filename;
655 text[len].lineno = p->lineno;
658 ntext = len;
661 void graphic_object::print_text()
663 double angle = 0.0;
664 if (aligned) {
665 position d(end() - start());
666 if (d.x != 0.0 || d.y != 0.0)
667 angle = atan2(d.y, d.x);
669 if (text != 0) {
670 out->set_color(color_fill, get_outline_color());
671 out->text(center(), text, ntext, angle);
672 out->reset_color();
676 graphic_object::~graphic_object()
678 if (text)
679 ad_delete(ntext) text;
682 class rectangle_object
683 : public graphic_object
685 protected:
686 position cent;
687 position dim;
689 public:
690 rectangle_object(const position &);
691 double width() { return dim.x; }
692 double height() { return dim.y; }
693 position origin() { return cent; }
694 position center() { return cent; }
695 position north() { return position(cent.x, cent.y + dim.y/2.0); }
696 position south() { return position(cent.x, cent.y - dim.y/2.0); }
697 position east() { return position(cent.x + dim.x/2.0, cent.y); }
698 position west() { return position(cent.x - dim.x/2.0, cent.y); }
699 position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
700 position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
701 position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
702 position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
703 object_type type() = 0;
704 void update_bounding_box(bounding_box *);
705 void move_by(const position &);
708 rectangle_object::rectangle_object(const position &d)
709 : dim(d)
713 void rectangle_object::update_bounding_box(bounding_box *p)
715 p->encompass(cent - dim/2.0);
716 p->encompass(cent + dim/2.0);
719 void rectangle_object::move_by(const position &a)
721 cent += a;
724 class closed_object
725 : public rectangle_object
727 protected:
728 double fill; // < 0 if not filled
729 double xslanted; // !=0 if x is slanted
730 double yslanted; // !=0 if y is slanted
731 char *color_fill; // = 0 if not colored
733 public:
734 closed_object(const position &);
735 object_type type() = 0;
736 void set_fill(double);
737 void set_xslanted(double);
738 void set_yslanted(double);
739 void set_fill_color(char *fill);
742 closed_object::closed_object(const position &pos)
743 : rectangle_object(pos), fill(-1.0), xslanted(0), yslanted(0), color_fill(0)
747 void closed_object::set_fill(double f)
749 assert(f >= 0.0);
750 fill = f;
753 /* accept positive and negative values */
754 void closed_object::set_xslanted(double s)
756 //assert(s >= 0.0);
757 xslanted = s;
759 /* accept positive and negative values */
760 void closed_object::set_yslanted(double s)
762 //assert(s >= 0.0);
763 yslanted = s;
766 void closed_object::set_fill_color(char *f)
768 color_fill = strsave(f);
771 class box_object
772 : public closed_object
774 double xrad;
775 double yrad;
777 public:
778 box_object(const position &, double);
779 object_type type() { return BOX_OBJECT; }
780 void print();
781 position north_east();
782 position north_west();
783 position south_east();
784 position south_west();
787 box_object::box_object(const position &pos, double r)
788 : closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
792 const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
794 position box_object::north_east()
796 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
797 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
800 position box_object::north_west()
802 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
803 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
806 position box_object::south_east()
808 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
809 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
812 position box_object::south_west()
814 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
815 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
818 void box_object::print()
820 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
821 return;
822 out->set_color(color_fill, graphic_object::get_outline_color());
823 if (xrad == 0.0) {
824 distance dim2 = dim/2.0;
825 position vec[4];
826 /* error("x slanted %1", xslanted); */
827 /* error("y slanted %1", yslanted); */
828 vec[0] = cent + position(dim2.x, -(dim2.y - yslanted)); /* lr */
829 vec[1] = cent + position(dim2.x + xslanted, dim2.y + yslanted); /* ur */
830 vec[2] = cent + position(-(dim2.x - xslanted), dim2.y); /* ul */
831 vec[3] = cent + position(-(dim2.x), -dim2.y); /* ll */
832 out->polygon(vec, 4, lt, fill);
834 else {
835 distance abs_dim(fabs(dim.x), fabs(dim.y));
836 out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill, color_fill);
838 out->reset_color();
841 graphic_object *object_spec::make_box(position *curpos, direction *dirp)
843 static double last_box_height;
844 static double last_box_width;
845 static double last_box_radius;
846 static int have_last_box = 0;
847 if (!(flags & HAS_HEIGHT)) {
848 if ((flags & IS_SAME) && have_last_box)
849 height = last_box_height;
850 else
851 lookup_variable("boxht", &height);
853 if (!(flags & HAS_WIDTH)) {
854 if ((flags & IS_SAME) && have_last_box)
855 width = last_box_width;
856 else
857 lookup_variable("boxwid", &width);
859 if (!(flags & HAS_RADIUS)) {
860 if ((flags & IS_SAME) && have_last_box)
861 radius = last_box_radius;
862 else
863 lookup_variable("boxrad", &radius);
865 last_box_width = width;
866 last_box_height = height;
867 last_box_radius = radius;
868 have_last_box = 1;
869 radius = fabs(radius);
870 if (radius*2.0 > fabs(width))
871 radius = fabs(width/2.0);
872 if (radius*2.0 > fabs(height))
873 radius = fabs(height/2.0);
874 box_object *p = new box_object(position(width, height), radius);
875 if (!position_rectangle(p, curpos, dirp)) {
876 delete p;
877 p = 0;
879 return p;
882 // return non-zero for success
884 int object_spec::position_rectangle(rectangle_object *p,
885 position *curpos, direction *dirp)
887 position pos;
888 dir = *dirp; // ignore any direction in attribute list
889 position motion;
890 switch (dir) {
891 case UP_DIRECTION:
892 motion.y = p->height()/2.0;
893 break;
894 case DOWN_DIRECTION:
895 motion.y = -p->height()/2.0;
896 break;
897 case LEFT_DIRECTION:
898 motion.x = -p->width()/2.0;
899 break;
900 case RIGHT_DIRECTION:
901 motion.x = p->width()/2.0;
902 break;
903 default:
904 assert(0);
906 if (flags & HAS_AT) {
907 pos = at;
908 if (flags & HAS_WITH) {
909 place offset;
910 place here;
911 here.obj = p;
912 if (!with->follow(here, &offset))
913 return 0;
914 pos -= offset;
917 else {
918 pos = *curpos;
919 pos += motion;
921 p->move_by(pos);
922 pos += motion;
923 *curpos = pos;
924 return 1;
927 class block_object
928 : public rectangle_object
930 object_list oblist;
931 PTABLE(place) *tbl;
933 public:
934 block_object(const position &, const object_list &ol, PTABLE(place) *t);
935 ~block_object();
936 place *find_label(const char *);
937 object_type type();
938 void move_by(const position &);
939 void print();
942 block_object::block_object(const position &d, const object_list &ol,
943 PTABLE(place) *t)
944 : rectangle_object(d), oblist(ol), tbl(t)
948 block_object::~block_object()
950 delete tbl;
951 object *p = oblist.head;
952 while (p != 0) {
953 object *tem = p;
954 p = p->next;
955 delete tem;
959 void block_object::print()
961 out->begin_block(south_west(), north_east());
962 print_object_list(oblist.head);
963 out->end_block();
966 static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
968 // Adjust all the labels that aren't attached to objects.
969 PTABLE_ITERATOR(place) iter(tbl);
970 const char *key;
971 place *pl;
972 while (iter.next(&key, &pl))
973 if (key && csupper(key[0]) && pl->obj == 0) {
974 pl->x += a.x;
975 pl->y += a.y;
979 void block_object::move_by(const position &a)
981 cent += a;
982 for (object *p = oblist.head; p; p = p->next)
983 p->move_by(a);
984 adjust_objectless_places(tbl, a);
987 place *block_object::find_label(const char *name)
989 return tbl->lookup(name);
992 object_type block_object::type()
994 return BLOCK_OBJECT;
997 graphic_object *object_spec::make_block(position *curpos, direction *dirp)
999 bounding_box bb;
1000 for (object *p = oblist.head; p; p = p->next)
1001 p->update_bounding_box(&bb);
1002 position dim;
1003 if (!bb.blank) {
1004 position m = -(bb.ll + bb.ur)/2.0;
1005 for (object *p = oblist.head; p; p = p->next)
1006 p->move_by(m);
1007 adjust_objectless_places(tbl, m);
1008 dim = bb.ur - bb.ll;
1010 if (flags & HAS_WIDTH)
1011 dim.x = width;
1012 if (flags & HAS_HEIGHT)
1013 dim.y = height;
1014 block_object *block = new block_object(dim, oblist, tbl);
1015 if (!position_rectangle(block, curpos, dirp)) {
1016 delete block;
1017 block = 0;
1019 tbl = 0;
1020 oblist.head = oblist.tail = 0;
1021 return block;
1024 class text_object : public rectangle_object {
1025 public:
1026 text_object(const position &);
1027 object_type type() { return TEXT_OBJECT; }
1030 text_object::text_object(const position &d)
1031 : rectangle_object(d)
1035 graphic_object *object_spec::make_text(position *curpos, direction *dirp)
1037 if (!(flags & HAS_HEIGHT)) {
1038 lookup_variable("textht", &height);
1039 int nitems = 0;
1040 for (text_item *t = text; t; t = t->next)
1041 nitems++;
1042 height *= nitems;
1044 if (!(flags & HAS_WIDTH))
1045 lookup_variable("textwid", &width);
1046 text_object *p = new text_object(position(width, height));
1047 if (!position_rectangle(p, curpos, dirp)) {
1048 delete p;
1049 p = 0;
1051 return p;
1054 class ellipse_object
1055 : public closed_object
1057 public:
1058 ellipse_object(const position &);
1059 position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1060 cent.y + dim.y/(M_SQRT2*2.0)); }
1061 position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1062 cent.y + dim.y/(M_SQRT2*2.0)); }
1063 position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1064 cent.y - dim.y/(M_SQRT2*2.0)); }
1065 position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1066 cent.y - dim.y/(M_SQRT2*2.0)); }
1067 double radius() { return dim.x/2.0; }
1068 object_type type() { return ELLIPSE_OBJECT; }
1069 void print();
1072 ellipse_object::ellipse_object(const position &d)
1073 : closed_object(d)
1077 void ellipse_object::print()
1079 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1080 return;
1081 out->set_color(color_fill, graphic_object::get_outline_color());
1082 out->ellipse(cent, dim, lt, fill);
1083 out->reset_color();
1086 graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1088 static double last_ellipse_height;
1089 static double last_ellipse_width;
1090 static int have_last_ellipse = 0;
1091 if (!(flags & HAS_HEIGHT)) {
1092 if ((flags & IS_SAME) && have_last_ellipse)
1093 height = last_ellipse_height;
1094 else
1095 lookup_variable("ellipseht", &height);
1097 if (!(flags & HAS_WIDTH)) {
1098 if ((flags & IS_SAME) && have_last_ellipse)
1099 width = last_ellipse_width;
1100 else
1101 lookup_variable("ellipsewid", &width);
1103 last_ellipse_width = width;
1104 last_ellipse_height = height;
1105 have_last_ellipse = 1;
1106 ellipse_object *p = new ellipse_object(position(width, height));
1107 if (!position_rectangle(p, curpos, dirp)) {
1108 delete p;
1109 return 0;
1111 return p;
1114 class circle_object
1115 : public ellipse_object
1117 public:
1118 circle_object(double);
1119 object_type type() { return CIRCLE_OBJECT; }
1120 void print();
1123 circle_object::circle_object(double diam)
1124 : ellipse_object(position(diam, diam))
1128 void circle_object::print()
1130 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1131 return;
1132 out->set_color(color_fill, graphic_object::get_outline_color());
1133 out->circle(cent, dim.x/2.0, lt, fill);
1134 out->reset_color();
1137 graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1139 static double last_circle_radius;
1140 static int have_last_circle = 0;
1141 if (!(flags & HAS_RADIUS)) {
1142 if ((flags & IS_SAME) && have_last_circle)
1143 radius = last_circle_radius;
1144 else
1145 lookup_variable("circlerad", &radius);
1147 last_circle_radius = radius;
1148 have_last_circle = 1;
1149 circle_object *p = new circle_object(radius*2.0);
1150 if (!position_rectangle(p, curpos, dirp)) {
1151 delete p;
1152 return 0;
1154 return p;
1157 class move_object
1158 : public graphic_object
1160 position strt;
1161 position en;
1163 public:
1164 move_object(const position &s, const position &e);
1165 position origin() { return en; }
1166 object_type type() { return MOVE_OBJECT; }
1167 void update_bounding_box(bounding_box *);
1168 void move_by(const position &);
1171 move_object::move_object(const position &s, const position &e)
1172 : strt(s), en(e)
1176 void move_object::update_bounding_box(bounding_box *p)
1178 p->encompass(strt);
1179 p->encompass(en);
1182 void move_object::move_by(const position &a)
1184 strt += a;
1185 en += a;
1188 graphic_object *object_spec::make_move(position *curpos, direction *dirp)
1190 static position last_move;
1191 static int have_last_move = 0;
1192 *dirp = dir;
1193 // No need to look at at since `at' attribute sets `from' attribute.
1194 position startpos = (flags & HAS_FROM) ? from : *curpos;
1195 if (!(flags & HAS_SEGMENT)) {
1196 if ((flags & IS_SAME) && have_last_move)
1197 segment_pos = last_move;
1198 else {
1199 switch (dir) {
1200 case UP_DIRECTION:
1201 segment_pos.y = segment_height;
1202 break;
1203 case DOWN_DIRECTION:
1204 segment_pos.y = -segment_height;
1205 break;
1206 case LEFT_DIRECTION:
1207 segment_pos.x = -segment_width;
1208 break;
1209 case RIGHT_DIRECTION:
1210 segment_pos.x = segment_width;
1211 break;
1212 default:
1213 assert(0);
1217 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1218 // Reverse the segment_list so that it's in forward order.
1219 segment *old = segment_list;
1220 segment_list = 0;
1221 while (old != 0) {
1222 segment *tem = old->next;
1223 old->next = segment_list;
1224 segment_list = old;
1225 old = tem;
1227 // Compute the end position.
1228 position endpos = startpos;
1229 for (segment *s = segment_list; s; s = s->next)
1230 if (s->is_absolute)
1231 endpos = s->pos;
1232 else
1233 endpos += s->pos;
1234 have_last_move = 1;
1235 last_move = endpos - startpos;
1236 move_object *p = new move_object(startpos, endpos);
1237 *curpos = endpos;
1238 return p;
1241 class linear_object
1242 : public graphic_object
1244 protected:
1245 char arrow_at_start;
1246 char arrow_at_end;
1247 arrow_head_type aht;
1248 position strt;
1249 position en;
1251 public:
1252 linear_object(const position &s, const position &e);
1253 position start() { return strt; }
1254 position end() { return en; }
1255 void move_by(const position &);
1256 void update_bounding_box(bounding_box *) = 0;
1257 object_type type() = 0;
1258 void add_arrows(int at_start, int at_end, const arrow_head_type &);
1261 class line_object
1262 : public linear_object
1264 protected:
1265 position *v;
1266 int n;
1268 public:
1269 line_object(const position &s, const position &e, position *, int);
1270 ~line_object();
1271 position origin() { return strt; }
1272 position center() { return (strt + en)/2.0; }
1273 position north() { return (en.y - strt.y) > 0 ? en : strt; }
1274 position south() { return (en.y - strt.y) < 0 ? en : strt; }
1275 position east() { return (en.x - strt.x) > 0 ? en : strt; }
1276 position west() { return (en.x - strt.x) < 0 ? en : strt; }
1277 object_type type() { return LINE_OBJECT; }
1278 void update_bounding_box(bounding_box *);
1279 void print();
1280 void move_by(const position &);
1283 class arrow_object
1284 : public line_object
1286 public:
1287 arrow_object(const position &, const position &, position *, int);
1288 object_type type() { return ARROW_OBJECT; }
1291 class spline_object
1292 : public line_object
1294 public:
1295 spline_object(const position &, const position &, position *, int);
1296 object_type type() { return SPLINE_OBJECT; }
1297 void print();
1298 void update_bounding_box(bounding_box *);
1301 linear_object::linear_object(const position &s, const position &e)
1302 : arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1306 void linear_object::move_by(const position &a)
1308 strt += a;
1309 en += a;
1312 void linear_object::add_arrows(int at_start, int at_end,
1313 const arrow_head_type &a)
1315 arrow_at_start = at_start;
1316 arrow_at_end = at_end;
1317 aht = a;
1320 line_object::line_object(const position &s, const position &e,
1321 position *p, int i)
1322 : linear_object(s, e), v(p), n(i)
1326 void line_object::print()
1328 if (lt.type == line_type::invisible)
1329 return;
1330 out->set_color(0, graphic_object::get_outline_color());
1331 // shorten line length to avoid arrow sticking.
1332 position sp = strt;
1333 if (arrow_at_start) {
1334 position base = v[0] - strt;
1335 double hyp = hypot(base);
1336 if (hyp == 0.0) {
1337 error("cannot draw arrow on object with zero length");
1338 return;
1340 if (aht.solid && out->supports_filled_polygons()) {
1341 base *= aht.height / hyp;
1342 draw_arrow(strt, strt - v[0], aht, lt,
1343 graphic_object::get_outline_color());
1344 sp = strt + base;
1345 } else {
1346 base *= fabs(lt.thickness) / hyp / 72 / 4;
1347 sp = strt + base;
1348 draw_arrow(sp, sp - v[0], aht, lt,
1349 graphic_object::get_outline_color());
1352 if (arrow_at_end) {
1353 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1354 double hyp = hypot(base);
1355 if (hyp == 0.0) {
1356 error("cannot draw arrow on object with zero length");
1357 return;
1359 if (aht.solid && out->supports_filled_polygons()) {
1360 base *= aht.height / hyp;
1361 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1362 graphic_object::get_outline_color());
1363 v[n-1] = en - base;
1364 } else {
1365 base *= fabs(lt.thickness) / hyp / 72 / 4;
1366 v[n-1] = en - base;
1367 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1368 graphic_object::get_outline_color());
1371 out->line(sp, v, n, lt);
1372 out->reset_color();
1375 void line_object::update_bounding_box(bounding_box *p)
1377 p->encompass(strt);
1378 for (int i = 0; i < n; i++)
1379 p->encompass(v[i]);
1382 void line_object::move_by(const position &pos)
1384 linear_object::move_by(pos);
1385 for (int i = 0; i < n; i++)
1386 v[i] += pos;
1389 void spline_object::update_bounding_box(bounding_box *p)
1391 p->encompass(strt);
1392 p->encompass(en);
1397 p1 = q1/2 + q2/2
1398 p2 = q1/6 + q2*5/6
1399 p3 = q2*5/6 + q3/6
1400 p4 = q2/2 + q3/2
1401 [ the points for the Bezier cubic ]
1405 t = .5
1407 then
1409 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1410 [ the equation for the Bezier cubic ]
1412 = .125*q1 + .75*q2 + .125*q3
1415 for (int i = 1; i < n; i++)
1416 p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1419 arrow_object::arrow_object(const position &s, const position &e,
1420 position *p, int i)
1421 : line_object(s, e, p, i)
1425 spline_object::spline_object(const position &s, const position &e,
1426 position *p, int i)
1427 : line_object(s, e, p, i)
1431 void spline_object::print()
1433 if (lt.type == line_type::invisible)
1434 return;
1435 out->set_color(0, graphic_object::get_outline_color());
1436 // shorten line length for spline to avoid arrow sticking
1437 position sp = strt;
1438 if (arrow_at_start) {
1439 position base = v[0] - strt;
1440 double hyp = hypot(base);
1441 if (hyp == 0.0) {
1442 error("cannot draw arrow on object with zero length");
1443 return;
1445 if (aht.solid && out->supports_filled_polygons()) {
1446 base *= aht.height / hyp;
1447 draw_arrow(strt, strt - v[0], aht, lt,
1448 graphic_object::get_outline_color());
1449 sp = strt + base*0.1; // to reserve spline shape
1450 } else {
1451 base *= fabs(lt.thickness) / hyp / 72 / 4;
1452 sp = strt + base;
1453 draw_arrow(sp, sp - v[0], aht, lt,
1454 graphic_object::get_outline_color());
1457 if (arrow_at_end) {
1458 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1459 double hyp = hypot(base);
1460 if (hyp == 0.0) {
1461 error("cannot draw arrow on object with zero length");
1462 return;
1464 if (aht.solid && out->supports_filled_polygons()) {
1465 base *= aht.height / hyp;
1466 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1467 graphic_object::get_outline_color());
1468 v[n-1] = en - base*0.1; // to reserve spline shape
1469 } else {
1470 base *= fabs(lt.thickness) / hyp / 72 / 4;
1471 v[n-1] = en - base;
1472 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1473 graphic_object::get_outline_color());
1476 out->spline(sp, v, n, lt);
1477 out->reset_color();
1480 line_object::~line_object()
1482 a_delete v;
1485 linear_object *object_spec::make_line(position *curpos, direction *dirp)
1487 static position last_line;
1488 static int have_last_line = 0;
1489 *dirp = dir;
1490 // We handle `at' only in conjunction with `with', otherwise it is
1491 // the same as the `from' attribute.
1492 position startpos;
1493 if ((flags & HAS_AT) && (flags & HAS_WITH))
1494 // handled later -- we need the end position
1495 startpos = *curpos;
1496 else if (flags & HAS_FROM)
1497 startpos = from;
1498 else
1499 startpos = *curpos;
1500 if (!(flags & HAS_SEGMENT)) {
1501 if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1502 && have_last_line)
1503 segment_pos = last_line;
1504 else
1505 switch (dir) {
1506 case UP_DIRECTION:
1507 segment_pos.y = segment_height;
1508 break;
1509 case DOWN_DIRECTION:
1510 segment_pos.y = -segment_height;
1511 break;
1512 case LEFT_DIRECTION:
1513 segment_pos.x = -segment_width;
1514 break;
1515 case RIGHT_DIRECTION:
1516 segment_pos.x = segment_width;
1517 break;
1518 default:
1519 assert(0);
1522 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1523 // reverse the segment_list so that it's in forward order
1524 segment *old = segment_list;
1525 segment_list = 0;
1526 while (old != 0) {
1527 segment *tem = old->next;
1528 old->next = segment_list;
1529 segment_list = old;
1530 old = tem;
1532 // Absolutise all movements
1533 position endpos = startpos;
1534 int nsegments = 0;
1535 segment *s;
1536 for (s = segment_list; s; s = s->next, nsegments++)
1537 if (s->is_absolute)
1538 endpos = s->pos;
1539 else {
1540 endpos += s->pos;
1541 s->pos = endpos;
1542 s->is_absolute = 1; // to avoid confusion
1544 if ((flags & HAS_AT) && (flags & HAS_WITH)) {
1545 // `tmpobj' works for arrows and splines too -- we only need positions
1546 line_object tmpobj(startpos, endpos, 0, 0);
1547 position pos = at;
1548 place offset;
1549 place here;
1550 here.obj = &tmpobj;
1551 if (!with->follow(here, &offset))
1552 return 0;
1553 pos -= offset;
1554 for (s = segment_list; s; s = s->next)
1555 s->pos += pos;
1556 startpos += pos;
1557 endpos += pos;
1559 // handle chop
1560 line_object *p = 0;
1561 position *v = new position[nsegments];
1562 int i = 0;
1563 for (s = segment_list; s; s = s->next, i++)
1564 v[i] = s->pos;
1565 if (flags & IS_DEFAULT_CHOPPED) {
1566 lookup_variable("circlerad", &start_chop);
1567 end_chop = start_chop;
1568 flags |= IS_CHOPPED;
1570 if (flags & IS_CHOPPED) {
1571 position start_chop_vec, end_chop_vec;
1572 if (start_chop != 0.0) {
1573 start_chop_vec = v[0] - startpos;
1574 start_chop_vec *= start_chop / hypot(start_chop_vec);
1576 if (end_chop != 0.0) {
1577 end_chop_vec = (v[nsegments - 1]
1578 - (nsegments > 1 ? v[nsegments - 2] : startpos));
1579 end_chop_vec *= end_chop / hypot(end_chop_vec);
1581 startpos += start_chop_vec;
1582 v[nsegments - 1] -= end_chop_vec;
1583 endpos -= end_chop_vec;
1585 switch (type) {
1586 case SPLINE_OBJECT:
1587 p = new spline_object(startpos, endpos, v, nsegments);
1588 break;
1589 case ARROW_OBJECT:
1590 p = new arrow_object(startpos, endpos, v, nsegments);
1591 break;
1592 case LINE_OBJECT:
1593 p = new line_object(startpos, endpos, v, nsegments);
1594 break;
1595 default:
1596 assert(0);
1598 have_last_line = 1;
1599 last_line = endpos - startpos;
1600 *curpos = endpos;
1601 return p;
1604 class arc_object
1605 : public linear_object
1607 int clockwise;
1608 position cent;
1609 double rad;
1611 public:
1612 arc_object(int, const position &, const position &, const position &);
1613 position origin() { return cent; }
1614 position center() { return cent; }
1615 double radius() { return rad; }
1616 position north();
1617 position south();
1618 position east();
1619 position west();
1620 position north_east();
1621 position north_west();
1622 position south_east();
1623 position south_west();
1624 void update_bounding_box(bounding_box *);
1625 object_type type() { return ARC_OBJECT; }
1626 void print();
1627 void move_by(const position &pos);
1630 arc_object::arc_object(int cw, const position &s, const position &e,
1631 const position &c)
1632 : linear_object(s, e), clockwise(cw), cent(c)
1634 rad = hypot(c - s);
1637 void arc_object::move_by(const position &pos)
1639 linear_object::move_by(pos);
1640 cent += pos;
1643 // we get arc corners from the corresponding circle
1645 position arc_object::north()
1647 position result(cent);
1648 result.y += rad;
1649 return result;
1652 position arc_object::south()
1654 position result(cent);
1655 result.y -= rad;
1656 return result;
1659 position arc_object::east()
1661 position result(cent);
1662 result.x += rad;
1663 return result;
1666 position arc_object::west()
1668 position result(cent);
1669 result.x -= rad;
1670 return result;
1673 position arc_object::north_east()
1675 position result(cent);
1676 result.x += rad/M_SQRT2;
1677 result.y += rad/M_SQRT2;
1678 return result;
1681 position arc_object::north_west()
1683 position result(cent);
1684 result.x -= rad/M_SQRT2;
1685 result.y += rad/M_SQRT2;
1686 return result;
1689 position arc_object::south_east()
1691 position result(cent);
1692 result.x += rad/M_SQRT2;
1693 result.y -= rad/M_SQRT2;
1694 return result;
1697 position arc_object::south_west()
1699 position result(cent);
1700 result.x -= rad/M_SQRT2;
1701 result.y -= rad/M_SQRT2;
1702 return result;
1705 void arc_object::print()
1707 if (lt.type == line_type::invisible)
1708 return;
1709 out->set_color(0, graphic_object::get_outline_color());
1710 // handle arrow direction; make shorter line for arc
1711 position sp, ep, b;
1712 if (clockwise) {
1713 sp = en;
1714 ep = strt;
1715 } else {
1716 sp = strt;
1717 ep = en;
1719 if (arrow_at_start) {
1720 double theta = aht.height / rad;
1721 if (clockwise)
1722 theta = - theta;
1723 b = strt - cent;
1724 b = position(b.x*cos(theta) - b.y*sin(theta),
1725 b.x*sin(theta) + b.y*cos(theta)) + cent;
1726 if (clockwise)
1727 ep = b;
1728 else
1729 sp = b;
1730 if (aht.solid && out->supports_filled_polygons()) {
1731 draw_arrow(strt, strt - b, aht, lt,
1732 graphic_object::get_outline_color());
1733 } else {
1734 position v = b;
1735 theta = fabs(lt.thickness) / 72 / 4 / rad;
1736 if (clockwise)
1737 theta = - theta;
1738 b = strt - cent;
1739 b = position(b.x*cos(theta) - b.y*sin(theta),
1740 b.x*sin(theta) + b.y*cos(theta)) + cent;
1741 draw_arrow(b, b - v, aht, lt,
1742 graphic_object::get_outline_color());
1743 out->line(b, &v, 1, lt);
1746 if (arrow_at_end) {
1747 double theta = aht.height / rad;
1748 if (!clockwise)
1749 theta = - theta;
1750 b = en - cent;
1751 b = position(b.x*cos(theta) - b.y*sin(theta),
1752 b.x*sin(theta) + b.y*cos(theta)) + cent;
1753 if (clockwise)
1754 sp = b;
1755 else
1756 ep = b;
1757 if (aht.solid && out->supports_filled_polygons()) {
1758 draw_arrow(en, en - b, aht, lt,
1759 graphic_object::get_outline_color());
1760 } else {
1761 position v = b;
1762 theta = fabs(lt.thickness) / 72 / 4 / rad;
1763 if (!clockwise)
1764 theta = - theta;
1765 b = en - cent;
1766 b = position(b.x*cos(theta) - b.y*sin(theta),
1767 b.x*sin(theta) + b.y*cos(theta)) + cent;
1768 draw_arrow(b, b - v, aht, lt,
1769 graphic_object::get_outline_color());
1770 out->line(b, &v, 1, lt);
1773 out->arc(sp, cent, ep, lt);
1774 out->reset_color();
1777 inline double max(double a, double b)
1779 return a > b ? a : b;
1782 void arc_object::update_bounding_box(bounding_box *p)
1784 p->encompass(strt);
1785 p->encompass(en);
1786 position start_offset = strt - cent;
1787 if (start_offset.x == 0.0 && start_offset.y == 0.0)
1788 return;
1789 position end_offset = en - cent;
1790 if (end_offset.x == 0.0 && end_offset.y == 0.0)
1791 return;
1792 double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1793 double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1794 if (clockwise) {
1795 double temp = start_quad;
1796 start_quad = end_quad;
1797 end_quad = temp;
1799 if (start_quad < 0.0)
1800 start_quad += 4.0;
1801 while (end_quad <= start_quad)
1802 end_quad += 4.0;
1803 double r = max(hypot(start_offset), hypot(end_offset));
1804 for (int q = int(start_quad) + 1; q < end_quad; q++) {
1805 position offset;
1806 switch (q % 4) {
1807 case 0:
1808 offset.x = r;
1809 break;
1810 case 1:
1811 offset.y = r;
1812 break;
1813 case 2:
1814 offset.x = -r;
1815 break;
1816 case 3:
1817 offset.y = -r;
1818 break;
1820 p->encompass(cent + offset);
1824 // We ignore the width attribute. The at attribute always refers to the center.
1826 linear_object *object_spec::make_arc(position *curpos, direction *dirp)
1828 *dirp = dir;
1829 int cw = (flags & IS_CLOCKWISE) != 0;
1830 // compute the start
1831 position startpos;
1832 if (flags & HAS_FROM)
1833 startpos = from;
1834 else
1835 startpos = *curpos;
1836 if (!(flags & HAS_RADIUS))
1837 lookup_variable("arcrad", &radius);
1838 // compute the end
1839 position endpos;
1840 if (flags & HAS_TO)
1841 endpos = to;
1842 else {
1843 position m(radius, radius);
1844 // Adjust the signs.
1845 if (cw) {
1846 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1847 m.x = -m.x;
1848 if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1849 m.y = -m.y;
1850 *dirp = direction((dir + 3) % 4);
1852 else {
1853 if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1854 m.x = -m.x;
1855 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1856 m.y = -m.y;
1857 *dirp = direction((dir + 1) % 4);
1859 endpos = startpos + m;
1861 // compute the center
1862 position centerpos;
1863 if (flags & HAS_AT)
1864 centerpos = at;
1865 else if (startpos == endpos)
1866 centerpos = startpos;
1867 else {
1868 position h = (endpos - startpos)/2.0;
1869 double d = hypot(h);
1870 if (radius <= 0)
1871 radius = .25;
1872 // make the radius big enough
1873 while (radius < d)
1874 radius *= 2.0;
1875 double alpha = acos(d/radius);
1876 double theta = atan2(h.y, h.x);
1877 if (cw)
1878 theta -= alpha;
1879 else
1880 theta += alpha;
1881 centerpos = position(cos(theta), sin(theta))*radius + startpos;
1883 arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1884 *curpos = endpos;
1885 return p;
1888 graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1890 linear_object *obj;
1891 if (type == ARC_OBJECT)
1892 obj = make_arc(curpos, dirp);
1893 else
1894 obj = make_line(curpos, dirp);
1895 if (type == ARROW_OBJECT
1896 && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1897 flags |= HAS_RIGHT_ARROW_HEAD;
1898 if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1899 arrow_head_type a;
1900 int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1901 int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1902 if (flags & HAS_HEIGHT)
1903 a.height = height;
1904 else
1905 lookup_variable("arrowht", &a.height);
1906 if (flags & HAS_WIDTH)
1907 a.width = width;
1908 else
1909 lookup_variable("arrowwid", &a.width);
1910 double solid;
1911 lookup_variable("arrowhead", &solid);
1912 a.solid = solid != 0.0;
1913 obj->add_arrows(at_start, at_end, a);
1915 return obj;
1918 object *object_spec::make_object(position *curpos, direction *dirp)
1920 graphic_object *obj = 0;
1921 switch (type) {
1922 case BLOCK_OBJECT:
1923 obj = make_block(curpos, dirp);
1924 break;
1925 case BOX_OBJECT:
1926 obj = make_box(curpos, dirp);
1927 break;
1928 case TEXT_OBJECT:
1929 obj = make_text(curpos, dirp);
1930 break;
1931 case ELLIPSE_OBJECT:
1932 obj = make_ellipse(curpos, dirp);
1933 break;
1934 case CIRCLE_OBJECT:
1935 obj = make_circle(curpos, dirp);
1936 break;
1937 case MOVE_OBJECT:
1938 obj = make_move(curpos, dirp);
1939 break;
1940 case ARC_OBJECT:
1941 case LINE_OBJECT:
1942 case SPLINE_OBJECT:
1943 case ARROW_OBJECT:
1944 obj = make_linear(curpos, dirp);
1945 break;
1946 case MARK_OBJECT:
1947 case OTHER_OBJECT:
1948 default:
1949 assert(0);
1950 break;
1952 if (obj) {
1953 if (flags & IS_INVISIBLE)
1954 obj->set_invisible();
1955 if (text != 0)
1956 obj->add_text(text, (flags & IS_ALIGNED) != 0);
1957 if (flags & IS_DOTTED)
1958 obj->set_dotted(dash_width);
1959 else if (flags & IS_DASHED)
1960 obj->set_dashed(dash_width);
1961 double th;
1962 if (flags & HAS_THICKNESS)
1963 th = thickness;
1964 else
1965 lookup_variable("linethick", &th);
1966 obj->set_thickness(th);
1967 if (flags & IS_OUTLINED)
1968 obj->set_outline_color(outlined);
1969 if (flags & IS_XSLANTED)
1970 obj->set_xslanted(xslanted);
1971 if (flags & IS_YSLANTED)
1972 obj->set_yslanted(yslanted);
1973 if (flags & (IS_DEFAULT_FILLED | IS_FILLED)) {
1974 if (flags & IS_SHADED)
1975 obj->set_fill_color(shaded);
1976 else {
1977 if (flags & IS_DEFAULT_FILLED)
1978 lookup_variable("fillval", &fill);
1979 if (fill < 0.0)
1980 error("bad fill value %1", fill);
1981 else
1982 obj->set_fill(fill);
1986 return obj;
1989 class string_list
1991 public:
1992 string_list *next;
1993 char *str;
1994 string_list(char *);
1995 ~string_list();
1998 string_list::string_list(char *s)
1999 : next(0), str(s)
2003 string_list::~string_list()
2005 a_delete str;
2008 /* A path is used to hold the argument to the `with' attribute. For
2009 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
2010 take a place and follow the path through the place to place within the
2011 place. Note that `.A.B.C.sw' will work.
2013 For compatibility with DWB pic, `with' accepts positions also (this
2014 is incorrectly documented in CSTR 116). */
2016 path::path(corner c)
2017 : crn(c), label_list(0), ypath(0), is_position(0)
2021 path::path(position p)
2022 : crn(0), label_list(0), ypath(0), is_position(1)
2024 pos.x = p.x;
2025 pos.y = p.y;
2028 path::path(char *l, corner c)
2029 : crn(c), ypath(0), is_position(0)
2031 label_list = new string_list(l);
2034 path::~path()
2036 while (label_list) {
2037 string_list *tem = label_list;
2038 label_list = label_list->next;
2039 delete tem;
2041 delete ypath;
2044 void path::append(corner c)
2046 assert(crn == 0);
2047 crn = c;
2050 void path::append(char *s)
2052 string_list **p;
2053 for (p = &label_list; *p; p = &(*p)->next)
2055 *p = new string_list(s);
2058 void path::set_ypath(path *p)
2060 ypath = p;
2063 // return non-zero for success
2065 int path::follow(const place &pl, place *result) const
2067 if (is_position) {
2068 result->x = pos.x;
2069 result->y = pos.y;
2070 result->obj = 0;
2071 return 1;
2073 const place *p = &pl;
2074 for (string_list *lb = label_list; lb; lb = lb->next)
2075 if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
2076 lex_error("object does not contain a place `%1'", lb->str);
2077 return 0;
2079 if (crn == 0 || p->obj == 0)
2080 *result = *p;
2081 else {
2082 position ps = ((p->obj)->*(crn))();
2083 result->x = ps.x;
2084 result->y = ps.y;
2085 result->obj = 0;
2087 if (ypath) {
2088 place tem;
2089 if (!ypath->follow(pl, &tem))
2090 return 0;
2091 result->y = tem.y;
2092 if (result->obj != tem.obj)
2093 result->obj = 0;
2095 return 1;
2098 void print_object_list(object *p)
2100 for (; p; p = p->next) {
2101 p->print();
2102 p->print_text();
2106 void print_picture(object *obj)
2108 bounding_box bb;
2109 for (object *p = obj; p; p = p->next)
2110 p->update_bounding_box(&bb);
2111 double scale;
2112 lookup_variable("scale", &scale);
2113 out->start_picture(scale, bb.ll, bb.ur);
2114 print_object_list(obj);
2115 out->finish_picture();
2118 // s-it2-mode