groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / preproc / pic / object.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548
JL
2/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2009
92d0a6a6
JR
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
6
7This file is part of groff.
8
9groff is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
4d3e9548
JL
11Software Foundation, either version 3 of the License, or
12(at your option) any later version.
92d0a6a6
JR
13
14groff is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17for more details.
18
4d3e9548
JL
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
21
22#include "pic.h"
23#include "ptable.h"
24#include "object.h"
25
26void print_object_list(object *);
27
28line_type::line_type()
29: type(solid), thickness(1.0)
30{
31}
32
33output::output() : args(0), desired_height(0.0), desired_width(0.0)
34{
35}
36
37output::~output()
38{
39 a_delete args;
40}
41
42void output::set_desired_width_height(double wid, double ht)
43{
44 desired_width = wid;
45 desired_height = ht;
46}
47
48void output::set_args(const char *s)
49{
50 a_delete args;
51 if (s == 0 || *s == '\0')
52 args = 0;
53 else
54 args = strsave(s);
55}
56
57int output::supports_filled_polygons()
58{
59 return 0;
60}
61
62void output::begin_block(const position &, const position &)
63{
64}
65
66void output::end_block()
67{
68}
69
70double output::compute_scale(double sc, const position &ll, const position &ur)
71{
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;
80 }
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;
88 }
89 }
90 return sc == 0.0 ? 1.0 : sc;
91 }
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;
105 }
106 else
107 return sc;
108 }
109}
110
111position::position(const place &pl)
112{
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();
117 }
118 else {
119 x = pl.x;
120 y = pl.y;
121 }
122}
123
124position::position() : x(0.0), y(0.0)
125{
126}
127
128position::position(double a, double b) : x(a), y(b)
129{
130}
131
132
133int operator==(const position &a, const position &b)
134{
135 return a.x == b.x && a.y == b.y;
136}
137
138int operator!=(const position &a, const position &b)
139{
140 return a.x != b.x || a.y != b.y;
141}
142
143position &position::operator+=(const position &a)
144{
145 x += a.x;
146 y += a.y;
147 return *this;
148}
149
150position &position::operator-=(const position &a)
151{
152 x -= a.x;
153 y -= a.y;
154 return *this;
155}
156
157position &position::operator*=(double a)
158{
159 x *= a;
160 y *= a;
161 return *this;
162}
163
164position &position::operator/=(double a)
165{
166 x /= a;
167 y /= a;
168 return *this;
169}
170
171position operator-(const position &a)
172{
173 return position(-a.x, -a.y);
174}
175
176position operator+(const position &a, const position &b)
177{
178 return position(a.x + b.x, a.y + b.y);
179}
180
181position operator-(const position &a, const position &b)
182{
183 return position(a.x - b.x, a.y - b.y);
184}
185
186position operator/(const position &a, double n)
187{
188 return position(a.x/n, a.y/n);
189}
190
191position operator*(const position &a, double n)
192{
193 return position(a.x*n, a.y*n);
194}
195
196// dot product
197
198double operator*(const position &a, const position &b)
199{
200 return a.x*b.x + a.y*b.y;
201}
202
203double hypot(const position &a)
204{
465b256c 205 return groff_hypot(a.x, a.y);
92d0a6a6
JR
206}
207
208struct arrow_head_type {
209 double height;
210 double width;
211 int solid;
212};
213
214void draw_arrow(const position &pos, const distance &dir,
215 const arrow_head_type &aht, const line_type &lt,
216 char *outline_color_for_fill)
217{
218 double hyp = hypot(dir);
219 if (hyp == 0.0) {
220 error("cannot draw arrow on object with zero length");
221 return;
222 }
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);
465b256c
JR
236 // make stroke thin to avoid arrow sticking
237 slt.thickness = 0.1;
92d0a6a6
JR
238 out->polygon(v, 3, slt, 1);
239 }
240 else {
465b256c
JR
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);
92d0a6a6
JR
244 }
245}
246
247object::object() : prev(0), next(0)
248{
249}
250
251object::~object()
252{
253}
254
255void object::move_by(const position &)
256{
257}
258
259void object::print()
260{
261}
262
263void object::print_text()
264{
265}
266
267int object::blank()
268{
269 return 0;
270}
271
272struct bounding_box {
273 int blank;
274 position ll;
275 position ur;
276
277 bounding_box();
278 void encompass(const position &);
279};
280
281bounding_box::bounding_box()
282: blank(1)
283{
284}
285
286void bounding_box::encompass(const position &pos)
287{
288 if (blank) {
289 ll = pos;
290 ur = pos;
291 blank = 0;
292 }
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;
302 }
303}
304
305void object::update_bounding_box(bounding_box *)
306{
307}
308
309position object::origin()
310{
311 return position(0.0,0.0);
312}
313
314position object::north()
315{
316 return origin();
317}
318
319position object::south()
320{
321 return origin();
322}
323
324position object::east()
325{
326 return origin();
327}
328
329position object::west()
330{
331 return origin();
332}
333
334position object::north_east()
335{
336 return origin();
337}
338
339position object::north_west()
340{
341 return origin();
342}
343
344position object::south_east()
345{
346 return origin();
347}
348
349position object::south_west()
350{
351 return origin();
352}
353
354position object::start()
355{
356 return origin();
357}
358
359position object::end()
360{
361 return origin();
362}
363
364position object::center()
365{
366 return origin();
367}
368
369double object::width()
370{
371 return 0.0;
372}
373
374double object::radius()
375{
376 return 0.0;
377}
378
379double object::height()
380{
381 return 0.0;
382}
383
384place *object::find_label(const char *)
385{
386 return 0;
387}
388
389segment::segment(const position &a, int n, segment *p)
390: is_absolute(n), pos(a), next(p)
391{
392}
393
394text_item::text_item(char *t, const char *fn, int ln)
395: next(0), text(t), filename(fn), lineno(ln)
396{
397 adj.h = CENTER_ADJUST;
398 adj.v = NONE_ADJUST;
399}
400
401text_item::~text_item()
402{
403 a_delete text;
404}
405
406object_spec::object_spec(object_type t) : type(t)
407{
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;
4d3e9548
JL
415 xslanted = 0;
416 yslanted = 0;
92d0a6a6
JR
417 outlined = 0;
418 with = 0;
419 dir = RIGHT_DIRECTION;
420}
421
422object_spec::~object_spec()
423{
424 delete tbl;
425 while (segment_list != 0) {
426 segment *tem = segment_list;
427 segment_list = segment_list->next;
428 delete tem;
429 }
430 object *p = oblist.head;
431 while (p != 0) {
432 object *tem = p;
433 p = p->next;
434 delete tem;
435 }
436 while (text != 0) {
437 text_item *tem = text;
438 text = text->next;
439 delete tem;
440 }
441 delete with;
442 a_delete shaded;
443 a_delete outlined;
444}
445
446class command_object : public object {
447 char *s;
448 const char *filename;
449 int lineno;
450public:
451 command_object(char *, const char *, int);
452 ~command_object();
453 object_type type() { return OTHER_OBJECT; }
454 void print();
455};
456
457command_object::command_object(char *p, const char *fn, int ln)
458: s(p), filename(fn), lineno(ln)
459{
460}
461
462command_object::~command_object()
463{
464 a_delete s;
465}
466
467void command_object::print()
468{
469 out->command(s, filename, lineno);
470}
471
472object *make_command_object(char *s, const char *fn, int ln)
473{
474 return new command_object(s, fn, ln);
475}
476
477class mark_object : public object {
478public:
479 mark_object();
480 object_type type();
481};
482
483object *make_mark_object()
484{
485 return new mark_object();
486}
487
488mark_object::mark_object()
489{
490}
491
492object_type mark_object::type()
493{
494 return MARK_OBJECT;
495}
496
497object_list::object_list() : head(0), tail(0)
498{
499}
500
501void object_list::append(object *obj)
502{
503 if (tail == 0) {
504 obj->next = obj->prev = 0;
505 head = tail = obj;
506 }
507 else {
508 obj->prev = tail;
509 obj->next = 0;
510 tail->next = obj;
511 tail = obj;
512 }
513}
514
515void object_list::wrap_up_block(object_list *ol)
516{
517 object *p;
518 for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
519 ;
520 assert(p != 0);
521 ol->head = p->next;
522 if (ol->head) {
523 ol->tail = tail;
524 ol->head->prev = 0;
525 }
526 else
527 ol->tail = 0;
528 tail = p->prev;
529 if (tail)
530 tail->next = 0;
531 else
532 head = 0;
533 delete p;
534}
535
536text_piece::text_piece()
537: text(0), filename(0), lineno(-1)
538{
539 adj.h = CENTER_ADJUST;
540 adj.v = NONE_ADJUST;
541}
542
543text_piece::~text_piece()
544{
545 a_delete text;
546}
547
548class graphic_object : public object {
549 int ntext;
550 text_piece *text;
551 int aligned;
552protected:
553 line_type lt;
554 char *outline_color;
555 char *color_fill;
556public:
557 graphic_object();
558 ~graphic_object();
559 object_type type() = 0;
560 void print_text();
561 void add_text(text_item *, int);
562 void set_dotted(double);
563 void set_dashed(double);
564 void set_thickness(double);
565 void set_invisible();
566 void set_outline_color(char *);
567 char *get_outline_color();
568 virtual void set_fill(double);
4d3e9548
JL
569 virtual void set_xslanted(double);
570 virtual void set_yslanted(double);
92d0a6a6
JR
571 virtual void set_fill_color(char *);
572};
573
574graphic_object::graphic_object()
575: ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
576{
577}
578
579void graphic_object::set_dotted(double wid)
580{
581 lt.type = line_type::dotted;
582 lt.dash_width = wid;
583}
584
585void graphic_object::set_dashed(double wid)
586{
587 lt.type = line_type::dashed;
588 lt.dash_width = wid;
589}
590
591void graphic_object::set_thickness(double th)
592{
593 lt.thickness = th;
594}
595
596void graphic_object::set_fill(double)
597{
598}
599
4d3e9548
JL
600void graphic_object::set_xslanted(double)
601{
602}
603
604void graphic_object::set_yslanted(double)
605{
606}
607
92d0a6a6
JR
608void graphic_object::set_fill_color(char *c)
609{
610 color_fill = strsave(c);
611}
612
613void graphic_object::set_outline_color(char *c)
614{
615 outline_color = strsave(c);
616}
617
618char *graphic_object::get_outline_color()
619{
620 return outline_color;
621}
622
623void graphic_object::set_invisible()
624{
625 lt.type = line_type::invisible;
626}
627
628void graphic_object::add_text(text_item *t, int a)
629{
630 aligned = a;
631 int len = 0;
632 text_item *p;
633 for (p = t; p; p = p->next)
634 len++;
635 if (len == 0)
636 text = 0;
637 else {
638 text = new text_piece[len];
639 for (p = t, len = 0; p; p = p->next, len++) {
640 text[len].text = p->text;
641 p->text = 0;
642 text[len].adj = p->adj;
643 text[len].filename = p->filename;
644 text[len].lineno = p->lineno;
645 }
646 }
647 ntext = len;
648}
649
650void graphic_object::print_text()
651{
652 double angle = 0.0;
653 if (aligned) {
654 position d(end() - start());
655 if (d.x != 0.0 || d.y != 0.0)
656 angle = atan2(d.y, d.x);
657 }
658 if (text != 0) {
659 out->set_color(color_fill, get_outline_color());
660 out->text(center(), text, ntext, angle);
661 out->reset_color();
662 }
663}
664
665graphic_object::~graphic_object()
666{
667 if (text)
668 ad_delete(ntext) text;
669}
670
671class rectangle_object : public graphic_object {
672protected:
673 position cent;
674 position dim;
675public:
676 rectangle_object(const position &);
677 double width() { return dim.x; }
678 double height() { return dim.y; }
679 position origin() { return cent; }
680 position center() { return cent; }
681 position north() { return position(cent.x, cent.y + dim.y/2.0); }
682 position south() { return position(cent.x, cent.y - dim.y/2.0); }
683 position east() { return position(cent.x + dim.x/2.0, cent.y); }
684 position west() { return position(cent.x - dim.x/2.0, cent.y); }
685 position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
686 position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
687 position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
688 position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
689 object_type type() = 0;
690 void update_bounding_box(bounding_box *);
691 void move_by(const position &);
692};
693
694rectangle_object::rectangle_object(const position &d)
695: dim(d)
696{
697}
698
699void rectangle_object::update_bounding_box(bounding_box *p)
700{
701 p->encompass(cent - dim/2.0);
702 p->encompass(cent + dim/2.0);
703}
704
705void rectangle_object::move_by(const position &a)
706{
707 cent += a;
708}
709
710class closed_object : public rectangle_object {
711public:
712 closed_object(const position &);
713 object_type type() = 0;
714 void set_fill(double);
4d3e9548
JL
715 void set_xslanted(double);
716 void set_yslanted(double);
92d0a6a6
JR
717 void set_fill_color(char *fill);
718protected:
719 double fill; // < 0 if not filled
4d3e9548
JL
720 double xslanted; // !=0 if x is slanted
721 double yslanted; // !=0 if y is slanted
92d0a6a6
JR
722 char *color_fill; // = 0 if not colored
723};
724
725closed_object::closed_object(const position &pos)
4d3e9548 726: rectangle_object(pos), fill(-1.0), xslanted(0), yslanted(0), color_fill(0)
92d0a6a6
JR
727{
728}
729
730void closed_object::set_fill(double f)
731{
732 assert(f >= 0.0);
733 fill = f;
734}
735
4d3e9548
JL
736/* accept positive and negative values */
737void closed_object::set_xslanted(double s)
738{
739 //assert(s >= 0.0);
740 xslanted = s;
741}
742/* accept positive and negative values */
743void closed_object::set_yslanted(double s)
744{
745 //assert(s >= 0.0);
746 yslanted = s;
747}
748
92d0a6a6
JR
749void closed_object::set_fill_color(char *f)
750{
751 color_fill = strsave(f);
752}
753
754class box_object : public closed_object {
755 double xrad;
756 double yrad;
757public:
758 box_object(const position &, double);
759 object_type type() { return BOX_OBJECT; }
760 void print();
761 position north_east();
762 position north_west();
763 position south_east();
764 position south_west();
765};
766
767box_object::box_object(const position &pos, double r)
768: closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
769{
770}
771
772const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
773
774position box_object::north_east()
775{
776 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
777 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
778}
779
780position box_object::north_west()
781{
782 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
783 cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
784}
785
786position box_object::south_east()
787{
788 return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
789 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
790}
791
792position box_object::south_west()
793{
794 return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
795 cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
796}
797
798void box_object::print()
799{
800 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
801 return;
802 out->set_color(color_fill, graphic_object::get_outline_color());
803 if (xrad == 0.0) {
804 distance dim2 = dim/2.0;
805 position vec[4];
4d3e9548
JL
806 /* error("x slanted %1", xslanted); */
807 /* error("y slanted %1", yslanted); */
808 vec[0] = cent + position(dim2.x, -(dim2.y - yslanted)); /* lr */
809 vec[1] = cent + position(dim2.x + xslanted, dim2.y + yslanted); /* ur */
810 vec[2] = cent + position(-(dim2.x - xslanted), dim2.y); /* ul */
811 vec[3] = cent + position(-(dim2.x), -dim2.y); /* ll */
92d0a6a6
JR
812 out->polygon(vec, 4, lt, fill);
813 }
814 else {
815 distance abs_dim(fabs(dim.x), fabs(dim.y));
4d3e9548 816 out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill, color_fill);
92d0a6a6
JR
817 }
818 out->reset_color();
819}
820
821graphic_object *object_spec::make_box(position *curpos, direction *dirp)
822{
823 static double last_box_height;
824 static double last_box_width;
825 static double last_box_radius;
826 static int have_last_box = 0;
827 if (!(flags & HAS_HEIGHT)) {
828 if ((flags & IS_SAME) && have_last_box)
829 height = last_box_height;
830 else
831 lookup_variable("boxht", &height);
832 }
833 if (!(flags & HAS_WIDTH)) {
834 if ((flags & IS_SAME) && have_last_box)
835 width = last_box_width;
836 else
837 lookup_variable("boxwid", &width);
838 }
839 if (!(flags & HAS_RADIUS)) {
840 if ((flags & IS_SAME) && have_last_box)
841 radius = last_box_radius;
842 else
843 lookup_variable("boxrad", &radius);
844 }
845 last_box_width = width;
846 last_box_height = height;
847 last_box_radius = radius;
848 have_last_box = 1;
849 radius = fabs(radius);
850 if (radius*2.0 > fabs(width))
851 radius = fabs(width/2.0);
852 if (radius*2.0 > fabs(height))
853 radius = fabs(height/2.0);
854 box_object *p = new box_object(position(width, height), radius);
855 if (!position_rectangle(p, curpos, dirp)) {
856 delete p;
857 p = 0;
858 }
859 return p;
860}
861
862// return non-zero for success
863
864int object_spec::position_rectangle(rectangle_object *p,
865 position *curpos, direction *dirp)
866{
867 position pos;
868 dir = *dirp; // ignore any direction in attribute list
869 position motion;
870 switch (dir) {
871 case UP_DIRECTION:
872 motion.y = p->height()/2.0;
873 break;
874 case DOWN_DIRECTION:
875 motion.y = -p->height()/2.0;
876 break;
877 case LEFT_DIRECTION:
878 motion.x = -p->width()/2.0;
879 break;
880 case RIGHT_DIRECTION:
881 motion.x = p->width()/2.0;
882 break;
883 default:
884 assert(0);
885 }
886 if (flags & HAS_AT) {
887 pos = at;
888 if (flags & HAS_WITH) {
889 place offset;
890 place here;
891 here.obj = p;
892 if (!with->follow(here, &offset))
893 return 0;
894 pos -= offset;
895 }
896 }
897 else {
898 pos = *curpos;
899 pos += motion;
900 }
901 p->move_by(pos);
902 pos += motion;
903 *curpos = pos;
904 return 1;
905}
906
907class block_object : public rectangle_object {
908 object_list oblist;
909 PTABLE(place) *tbl;
910public:
911 block_object(const position &, const object_list &ol, PTABLE(place) *t);
912 ~block_object();
913 place *find_label(const char *);
914 object_type type();
915 void move_by(const position &);
916 void print();
917};
918
919block_object::block_object(const position &d, const object_list &ol,
920 PTABLE(place) *t)
921: rectangle_object(d), oblist(ol), tbl(t)
922{
923}
924
925block_object::~block_object()
926{
927 delete tbl;
928 object *p = oblist.head;
929 while (p != 0) {
930 object *tem = p;
931 p = p->next;
932 delete tem;
933 }
934}
935
936void block_object::print()
937{
938 out->begin_block(south_west(), north_east());
939 print_object_list(oblist.head);
940 out->end_block();
941}
942
943static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
944{
945 // Adjust all the labels that aren't attached to objects.
946 PTABLE_ITERATOR(place) iter(tbl);
947 const char *key;
948 place *pl;
949 while (iter.next(&key, &pl))
950 if (key && csupper(key[0]) && pl->obj == 0) {
951 pl->x += a.x;
952 pl->y += a.y;
953 }
954}
955
956void block_object::move_by(const position &a)
957{
958 cent += a;
959 for (object *p = oblist.head; p; p = p->next)
960 p->move_by(a);
961 adjust_objectless_places(tbl, a);
962}
963
964
965place *block_object::find_label(const char *name)
966{
967 return tbl->lookup(name);
968}
969
970object_type block_object::type()
971{
972 return BLOCK_OBJECT;
973}
974
975graphic_object *object_spec::make_block(position *curpos, direction *dirp)
976{
977 bounding_box bb;
978 for (object *p = oblist.head; p; p = p->next)
979 p->update_bounding_box(&bb);
980 position dim;
981 if (!bb.blank) {
982 position m = -(bb.ll + bb.ur)/2.0;
983 for (object *p = oblist.head; p; p = p->next)
984 p->move_by(m);
985 adjust_objectless_places(tbl, m);
986 dim = bb.ur - bb.ll;
987 }
988 if (flags & HAS_WIDTH)
989 dim.x = width;
990 if (flags & HAS_HEIGHT)
991 dim.y = height;
992 block_object *block = new block_object(dim, oblist, tbl);
993 if (!position_rectangle(block, curpos, dirp)) {
994 delete block;
995 block = 0;
996 }
997 tbl = 0;
998 oblist.head = oblist.tail = 0;
999 return block;
1000}
1001
1002class text_object : public rectangle_object {
1003public:
1004 text_object(const position &);
1005 object_type type() { return TEXT_OBJECT; }
1006};
1007
1008text_object::text_object(const position &d)
1009: rectangle_object(d)
1010{
1011}
1012
1013graphic_object *object_spec::make_text(position *curpos, direction *dirp)
1014{
1015 if (!(flags & HAS_HEIGHT)) {
1016 lookup_variable("textht", &height);
1017 int nitems = 0;
1018 for (text_item *t = text; t; t = t->next)
1019 nitems++;
1020 height *= nitems;
1021 }
1022 if (!(flags & HAS_WIDTH))
1023 lookup_variable("textwid", &width);
1024 text_object *p = new text_object(position(width, height));
1025 if (!position_rectangle(p, curpos, dirp)) {
1026 delete p;
1027 p = 0;
1028 }
1029 return p;
1030}
1031
1032
1033class ellipse_object : public closed_object {
1034public:
1035 ellipse_object(const position &);
1036 position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1037 cent.y + dim.y/(M_SQRT2*2.0)); }
1038 position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1039 cent.y + dim.y/(M_SQRT2*2.0)); }
1040 position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1041 cent.y - dim.y/(M_SQRT2*2.0)); }
1042 position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1043 cent.y - dim.y/(M_SQRT2*2.0)); }
1044 double radius() { return dim.x/2.0; }
1045 object_type type() { return ELLIPSE_OBJECT; }
1046 void print();
1047};
1048
1049ellipse_object::ellipse_object(const position &d)
1050: closed_object(d)
1051{
1052}
1053
1054void ellipse_object::print()
1055{
1056 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1057 return;
1058 out->set_color(color_fill, graphic_object::get_outline_color());
1059 out->ellipse(cent, dim, lt, fill);
1060 out->reset_color();
1061}
1062
1063graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1064{
1065 static double last_ellipse_height;
1066 static double last_ellipse_width;
1067 static int have_last_ellipse = 0;
1068 if (!(flags & HAS_HEIGHT)) {
1069 if ((flags & IS_SAME) && have_last_ellipse)
1070 height = last_ellipse_height;
1071 else
1072 lookup_variable("ellipseht", &height);
1073 }
1074 if (!(flags & HAS_WIDTH)) {
1075 if ((flags & IS_SAME) && have_last_ellipse)
1076 width = last_ellipse_width;
1077 else
1078 lookup_variable("ellipsewid", &width);
1079 }
1080 last_ellipse_width = width;
1081 last_ellipse_height = height;
1082 have_last_ellipse = 1;
1083 ellipse_object *p = new ellipse_object(position(width, height));
1084 if (!position_rectangle(p, curpos, dirp)) {
1085 delete p;
1086 return 0;
1087 }
1088 return p;
1089}
1090
1091class circle_object : public ellipse_object {
1092public:
1093 circle_object(double);
1094 object_type type() { return CIRCLE_OBJECT; }
1095 void print();
1096};
1097
1098circle_object::circle_object(double diam)
1099: ellipse_object(position(diam, diam))
1100{
1101}
1102
1103void circle_object::print()
1104{
1105 if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1106 return;
1107 out->set_color(color_fill, graphic_object::get_outline_color());
1108 out->circle(cent, dim.x/2.0, lt, fill);
1109 out->reset_color();
1110}
1111
1112graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1113{
1114 static double last_circle_radius;
1115 static int have_last_circle = 0;
1116 if (!(flags & HAS_RADIUS)) {
1117 if ((flags & IS_SAME) && have_last_circle)
1118 radius = last_circle_radius;
1119 else
1120 lookup_variable("circlerad", &radius);
1121 }
1122 last_circle_radius = radius;
1123 have_last_circle = 1;
1124 circle_object *p = new circle_object(radius*2.0);
1125 if (!position_rectangle(p, curpos, dirp)) {
1126 delete p;
1127 return 0;
1128 }
1129 return p;
1130}
1131
1132class move_object : public graphic_object {
1133 position strt;
1134 position en;
1135public:
1136 move_object(const position &s, const position &e);
1137 position origin() { return en; }
1138 object_type type() { return MOVE_OBJECT; }
1139 void update_bounding_box(bounding_box *);
1140 void move_by(const position &);
1141};
1142
1143move_object::move_object(const position &s, const position &e)
1144: strt(s), en(e)
1145{
1146}
1147
1148void move_object::update_bounding_box(bounding_box *p)
1149{
1150 p->encompass(strt);
1151 p->encompass(en);
1152}
1153
1154void move_object::move_by(const position &a)
1155{
1156 strt += a;
1157 en += a;
1158}
1159
1160graphic_object *object_spec::make_move(position *curpos, direction *dirp)
1161{
1162 static position last_move;
1163 static int have_last_move = 0;
1164 *dirp = dir;
1165 // No need to look at at since `at' attribute sets `from' attribute.
1166 position startpos = (flags & HAS_FROM) ? from : *curpos;
1167 if (!(flags & HAS_SEGMENT)) {
1168 if ((flags & IS_SAME) && have_last_move)
1169 segment_pos = last_move;
1170 else {
1171 switch (dir) {
1172 case UP_DIRECTION:
1173 segment_pos.y = segment_height;
1174 break;
1175 case DOWN_DIRECTION:
1176 segment_pos.y = -segment_height;
1177 break;
1178 case LEFT_DIRECTION:
1179 segment_pos.x = -segment_width;
1180 break;
1181 case RIGHT_DIRECTION:
1182 segment_pos.x = segment_width;
1183 break;
1184 default:
1185 assert(0);
1186 }
1187 }
1188 }
1189 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1190 // Reverse the segment_list so that it's in forward order.
1191 segment *old = segment_list;
1192 segment_list = 0;
1193 while (old != 0) {
1194 segment *tem = old->next;
1195 old->next = segment_list;
1196 segment_list = old;
1197 old = tem;
1198 }
1199 // Compute the end position.
1200 position endpos = startpos;
1201 for (segment *s = segment_list; s; s = s->next)
1202 if (s->is_absolute)
1203 endpos = s->pos;
1204 else
1205 endpos += s->pos;
1206 have_last_move = 1;
1207 last_move = endpos - startpos;
1208 move_object *p = new move_object(startpos, endpos);
1209 *curpos = endpos;
1210 return p;
1211}
1212
1213class linear_object : public graphic_object {
1214protected:
1215 char arrow_at_start;
1216 char arrow_at_end;
1217 arrow_head_type aht;
1218 position strt;
1219 position en;
1220public:
1221 linear_object(const position &s, const position &e);
1222 position start() { return strt; }
1223 position end() { return en; }
1224 void move_by(const position &);
1225 void update_bounding_box(bounding_box *) = 0;
1226 object_type type() = 0;
1227 void add_arrows(int at_start, int at_end, const arrow_head_type &);
1228};
1229
1230class line_object : public linear_object {
1231protected:
1232 position *v;
1233 int n;
1234public:
1235 line_object(const position &s, const position &e, position *, int);
1236 ~line_object();
1237 position origin() { return strt; }
1238 position center() { return (strt + en)/2.0; }
1239 position north() { return (en.y - strt.y) > 0 ? en : strt; }
1240 position south() { return (en.y - strt.y) < 0 ? en : strt; }
1241 position east() { return (en.x - strt.x) > 0 ? en : strt; }
1242 position west() { return (en.x - strt.x) < 0 ? en : strt; }
1243 object_type type() { return LINE_OBJECT; }
1244 void update_bounding_box(bounding_box *);
1245 void print();
1246 void move_by(const position &);
1247};
1248
1249class arrow_object : public line_object {
1250public:
1251 arrow_object(const position &, const position &, position *, int);
1252 object_type type() { return ARROW_OBJECT; }
1253};
1254
1255class spline_object : public line_object {
1256public:
1257 spline_object(const position &, const position &, position *, int);
1258 object_type type() { return SPLINE_OBJECT; }
1259 void print();
1260 void update_bounding_box(bounding_box *);
1261};
1262
1263linear_object::linear_object(const position &s, const position &e)
1264: arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1265{
1266}
1267
1268void linear_object::move_by(const position &a)
1269{
1270 strt += a;
1271 en += a;
1272}
1273
1274void linear_object::add_arrows(int at_start, int at_end,
1275 const arrow_head_type &a)
1276{
1277 arrow_at_start = at_start;
1278 arrow_at_end = at_end;
1279 aht = a;
1280}
1281
1282line_object::line_object(const position &s, const position &e,
1283 position *p, int i)
1284: linear_object(s, e), v(p), n(i)
1285{
1286}
1287
1288void line_object::print()
1289{
1290 if (lt.type == line_type::invisible)
1291 return;
1292 out->set_color(0, graphic_object::get_outline_color());
465b256c
JR
1293 // shorten line length to avoid arrow sticking.
1294 position sp = strt;
1295 if (arrow_at_start) {
1296 position base = v[0] - strt;
1297 double hyp = hypot(base);
1298 if (hyp == 0.0) {
1299 error("cannot draw arrow on object with zero length");
1300 return;
1301 }
1302 if (aht.solid && out->supports_filled_polygons()) {
1303 base *= aht.height / hyp;
1304 draw_arrow(strt, strt - v[0], aht, lt,
1305 graphic_object::get_outline_color());
1306 sp = strt + base;
1307 } else {
1308 base *= fabs(lt.thickness) / hyp / 72 / 4;
1309 sp = strt + base;
1310 draw_arrow(sp, sp - v[0], aht, lt,
1311 graphic_object::get_outline_color());
1312 }
1313 }
1314 if (arrow_at_end) {
1315 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1316 double hyp = hypot(base);
1317 if (hyp == 0.0) {
1318 error("cannot draw arrow on object with zero length");
1319 return;
1320 }
1321 if (aht.solid && out->supports_filled_polygons()) {
1322 base *= aht.height / hyp;
1323 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1324 graphic_object::get_outline_color());
1325 v[n-1] = en - base;
1326 } else {
1327 base *= fabs(lt.thickness) / hyp / 72 / 4;
1328 v[n-1] = en - base;
1329 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1330 graphic_object::get_outline_color());
1331 }
1332 }
1333 out->line(sp, v, n, lt);
92d0a6a6
JR
1334 out->reset_color();
1335}
1336
1337void line_object::update_bounding_box(bounding_box *p)
1338{
1339 p->encompass(strt);
1340 for (int i = 0; i < n; i++)
1341 p->encompass(v[i]);
1342}
1343
1344void line_object::move_by(const position &pos)
1345{
1346 linear_object::move_by(pos);
1347 for (int i = 0; i < n; i++)
1348 v[i] += pos;
1349}
1350
1351void spline_object::update_bounding_box(bounding_box *p)
1352{
1353 p->encompass(strt);
1354 p->encompass(en);
1355 /*
1356
1357 If
1358
1359 p1 = q1/2 + q2/2
1360 p2 = q1/6 + q2*5/6
1361 p3 = q2*5/6 + q3/6
1362 p4 = q2/2 + q3/2
1363 [ the points for the Bezier cubic ]
1364
1365 and
1366
1367 t = .5
1368
1369 then
1370
1371 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1372 [ the equation for the Bezier cubic ]
1373
1374 = .125*q1 + .75*q2 + .125*q3
1375
1376 */
1377 for (int i = 1; i < n; i++)
1378 p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1379}
1380
1381arrow_object::arrow_object(const position &s, const position &e,
1382 position *p, int i)
1383: line_object(s, e, p, i)
1384{
1385}
1386
1387spline_object::spline_object(const position &s, const position &e,
1388 position *p, int i)
1389: line_object(s, e, p, i)
1390{
1391}
1392
1393void spline_object::print()
1394{
1395 if (lt.type == line_type::invisible)
1396 return;
1397 out->set_color(0, graphic_object::get_outline_color());
465b256c
JR
1398 // shorten line length for spline to avoid arrow sticking
1399 position sp = strt;
1400 if (arrow_at_start) {
1401 position base = v[0] - strt;
1402 double hyp = hypot(base);
1403 if (hyp == 0.0) {
1404 error("cannot draw arrow on object with zero length");
1405 return;
1406 }
1407 if (aht.solid && out->supports_filled_polygons()) {
1408 base *= aht.height / hyp;
1409 draw_arrow(strt, strt - v[0], aht, lt,
1410 graphic_object::get_outline_color());
1411 sp = strt + base*0.1; // to reserve spline shape
1412 } else {
1413 base *= fabs(lt.thickness) / hyp / 72 / 4;
1414 sp = strt + base;
1415 draw_arrow(sp, sp - v[0], aht, lt,
1416 graphic_object::get_outline_color());
1417 }
1418 }
1419 if (arrow_at_end) {
1420 position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1421 double hyp = hypot(base);
1422 if (hyp == 0.0) {
1423 error("cannot draw arrow on object with zero length");
1424 return;
1425 }
1426 if (aht.solid && out->supports_filled_polygons()) {
1427 base *= aht.height / hyp;
1428 draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1429 graphic_object::get_outline_color());
1430 v[n-1] = en - base*0.1; // to reserve spline shape
1431 } else {
1432 base *= fabs(lt.thickness) / hyp / 72 / 4;
1433 v[n-1] = en - base;
1434 draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1435 graphic_object::get_outline_color());
1436 }
1437 }
1438 out->spline(sp, v, n, lt);
92d0a6a6
JR
1439 out->reset_color();
1440}
1441
1442line_object::~line_object()
1443{
1444 a_delete v;
1445}
1446
1447linear_object *object_spec::make_line(position *curpos, direction *dirp)
1448{
1449 static position last_line;
1450 static int have_last_line = 0;
1451 *dirp = dir;
4d3e9548
JL
1452 // We handle `at' only in conjunction with `with', otherwise it is
1453 // the same as the `from' attribute.
1454 position startpos;
1455 if ((flags & HAS_AT) && (flags & HAS_WITH))
1456 // handled later -- we need the end position
1457 startpos = *curpos;
1458 else if (flags & HAS_FROM)
1459 startpos = from;
1460 else
1461 startpos = *curpos;
92d0a6a6
JR
1462 if (!(flags & HAS_SEGMENT)) {
1463 if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1464 && have_last_line)
1465 segment_pos = last_line;
1466 else
1467 switch (dir) {
1468 case UP_DIRECTION:
1469 segment_pos.y = segment_height;
1470 break;
1471 case DOWN_DIRECTION:
1472 segment_pos.y = -segment_height;
1473 break;
1474 case LEFT_DIRECTION:
1475 segment_pos.x = -segment_width;
1476 break;
1477 case RIGHT_DIRECTION:
1478 segment_pos.x = segment_width;
1479 break;
1480 default:
1481 assert(0);
1482 }
1483 }
1484 segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1485 // reverse the segment_list so that it's in forward order
1486 segment *old = segment_list;
1487 segment_list = 0;
1488 while (old != 0) {
1489 segment *tem = old->next;
1490 old->next = segment_list;
1491 segment_list = old;
1492 old = tem;
1493 }
1494 // Absolutise all movements
1495 position endpos = startpos;
1496 int nsegments = 0;
1497 segment *s;
1498 for (s = segment_list; s; s = s->next, nsegments++)
1499 if (s->is_absolute)
1500 endpos = s->pos;
1501 else {
1502 endpos += s->pos;
1503 s->pos = endpos;
1504 s->is_absolute = 1; // to avoid confusion
1505 }
4d3e9548
JL
1506 if ((flags & HAS_AT) && (flags & HAS_WITH)) {
1507 // `tmpobj' works for arrows and splines too -- we only need positions
1508 line_object tmpobj(startpos, endpos, 0, 0);
1509 position pos = at;
1510 place offset;
1511 place here;
1512 here.obj = &tmpobj;
1513 if (!with->follow(here, &offset))
1514 return 0;
1515 pos -= offset;
1516 for (s = segment_list; s; s = s->next)
1517 s->pos += pos;
1518 startpos += pos;
1519 endpos += pos;
1520 }
92d0a6a6
JR
1521 // handle chop
1522 line_object *p = 0;
1523 position *v = new position[nsegments];
1524 int i = 0;
1525 for (s = segment_list; s; s = s->next, i++)
1526 v[i] = s->pos;
1527 if (flags & IS_DEFAULT_CHOPPED) {
1528 lookup_variable("circlerad", &start_chop);
1529 end_chop = start_chop;
1530 flags |= IS_CHOPPED;
1531 }
1532 if (flags & IS_CHOPPED) {
1533 position start_chop_vec, end_chop_vec;
1534 if (start_chop != 0.0) {
1535 start_chop_vec = v[0] - startpos;
1536 start_chop_vec *= start_chop / hypot(start_chop_vec);
1537 }
1538 if (end_chop != 0.0) {
1539 end_chop_vec = (v[nsegments - 1]
1540 - (nsegments > 1 ? v[nsegments - 2] : startpos));
1541 end_chop_vec *= end_chop / hypot(end_chop_vec);
1542 }
1543 startpos += start_chop_vec;
1544 v[nsegments - 1] -= end_chop_vec;
1545 endpos -= end_chop_vec;
1546 }
1547 switch (type) {
1548 case SPLINE_OBJECT:
1549 p = new spline_object(startpos, endpos, v, nsegments);
1550 break;
1551 case ARROW_OBJECT:
1552 p = new arrow_object(startpos, endpos, v, nsegments);
1553 break;
1554 case LINE_OBJECT:
1555 p = new line_object(startpos, endpos, v, nsegments);
1556 break;
1557 default:
1558 assert(0);
1559 }
1560 have_last_line = 1;
1561 last_line = endpos - startpos;
1562 *curpos = endpos;
1563 return p;
1564}
1565
1566class arc_object : public linear_object {
1567 int clockwise;
1568 position cent;
1569 double rad;
1570public:
1571 arc_object(int, const position &, const position &, const position &);
1572 position origin() { return cent; }
1573 position center() { return cent; }
1574 double radius() { return rad; }
1575 position north();
1576 position south();
1577 position east();
1578 position west();
1579 position north_east();
1580 position north_west();
1581 position south_east();
1582 position south_west();
1583 void update_bounding_box(bounding_box *);
1584 object_type type() { return ARC_OBJECT; }
1585 void print();
1586 void move_by(const position &pos);
1587};
1588
1589arc_object::arc_object(int cw, const position &s, const position &e,
1590 const position &c)
1591: linear_object(s, e), clockwise(cw), cent(c)
1592{
1593 rad = hypot(c - s);
1594}
1595
1596void arc_object::move_by(const position &pos)
1597{
1598 linear_object::move_by(pos);
1599 cent += pos;
1600}
1601
1602// we get arc corners from the corresponding circle
1603
1604position arc_object::north()
1605{
1606 position result(cent);
1607 result.y += rad;
1608 return result;
1609}
1610
1611position arc_object::south()
1612{
1613 position result(cent);
1614 result.y -= rad;
1615 return result;
1616}
1617
1618position arc_object::east()
1619{
1620 position result(cent);
1621 result.x += rad;
1622 return result;
1623}
1624
1625position arc_object::west()
1626{
1627 position result(cent);
1628 result.x -= rad;
1629 return result;
1630}
1631
1632position arc_object::north_east()
1633{
1634 position result(cent);
1635 result.x += rad/M_SQRT2;
1636 result.y += rad/M_SQRT2;
1637 return result;
1638}
1639
1640position arc_object::north_west()
1641{
1642 position result(cent);
1643 result.x -= rad/M_SQRT2;
1644 result.y += rad/M_SQRT2;
1645 return result;
1646}
1647
1648position arc_object::south_east()
1649{
1650 position result(cent);
1651 result.x += rad/M_SQRT2;
1652 result.y -= rad/M_SQRT2;
1653 return result;
1654}
1655
1656position arc_object::south_west()
1657{
1658 position result(cent);
1659 result.x -= rad/M_SQRT2;
1660 result.y -= rad/M_SQRT2;
1661 return result;
1662}
1663
1664
1665void arc_object::print()
1666{
1667 if (lt.type == line_type::invisible)
1668 return;
1669 out->set_color(0, graphic_object::get_outline_color());
465b256c
JR
1670 // handle arrow direction; make shorter line for arc
1671 position sp, ep, b;
1672 if (clockwise) {
1673 sp = en;
1674 ep = strt;
1675 } else {
1676 sp = strt;
1677 ep = en;
1678 }
92d0a6a6 1679 if (arrow_at_start) {
465b256c
JR
1680 double theta = aht.height / rad;
1681 if (clockwise)
1682 theta = - theta;
1683 b = strt - cent;
1684 b = position(b.x*cos(theta) - b.y*sin(theta),
1685 b.x*sin(theta) + b.y*cos(theta)) + cent;
1686 if (clockwise)
1687 ep = b;
1688 else
1689 sp = b;
1690 if (aht.solid && out->supports_filled_polygons()) {
1691 draw_arrow(strt, strt - b, aht, lt,
1692 graphic_object::get_outline_color());
1693 } else {
1694 position v = b;
1695 theta = fabs(lt.thickness) / 72 / 4 / rad;
1696 if (clockwise)
1697 theta = - theta;
1698 b = strt - cent;
1699 b = position(b.x*cos(theta) - b.y*sin(theta),
1700 b.x*sin(theta) + b.y*cos(theta)) + cent;
1701 draw_arrow(b, b - v, aht, lt,
1702 graphic_object::get_outline_color());
1703 out->line(b, &v, 1, lt);
1704 }
92d0a6a6
JR
1705 }
1706 if (arrow_at_end) {
465b256c
JR
1707 double theta = aht.height / rad;
1708 if (!clockwise)
1709 theta = - theta;
1710 b = en - cent;
1711 b = position(b.x*cos(theta) - b.y*sin(theta),
1712 b.x*sin(theta) + b.y*cos(theta)) + cent;
1713 if (clockwise)
1714 sp = b;
1715 else
1716 ep = b;
1717 if (aht.solid && out->supports_filled_polygons()) {
1718 draw_arrow(en, en - b, aht, lt,
1719 graphic_object::get_outline_color());
1720 } else {
1721 position v = b;
1722 theta = fabs(lt.thickness) / 72 / 4 / rad;
1723 if (!clockwise)
1724 theta = - theta;
1725 b = en - cent;
1726 b = position(b.x*cos(theta) - b.y*sin(theta),
1727 b.x*sin(theta) + b.y*cos(theta)) + cent;
1728 draw_arrow(b, b - v, aht, lt,
1729 graphic_object::get_outline_color());
1730 out->line(b, &v, 1, lt);
1731 }
92d0a6a6 1732 }
465b256c 1733 out->arc(sp, cent, ep, lt);
92d0a6a6
JR
1734 out->reset_color();
1735}
1736
1737inline double max(double a, double b)
1738{
1739 return a > b ? a : b;
1740}
1741
1742void arc_object::update_bounding_box(bounding_box *p)
1743{
1744 p->encompass(strt);
1745 p->encompass(en);
1746 position start_offset = strt - cent;
1747 if (start_offset.x == 0.0 && start_offset.y == 0.0)
1748 return;
1749 position end_offset = en - cent;
1750 if (end_offset.x == 0.0 && end_offset.y == 0.0)
1751 return;
1752 double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1753 double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1754 if (clockwise) {
1755 double temp = start_quad;
1756 start_quad = end_quad;
1757 end_quad = temp;
1758 }
1759 if (start_quad < 0.0)
1760 start_quad += 4.0;
1761 while (end_quad <= start_quad)
1762 end_quad += 4.0;
1763 double r = max(hypot(start_offset), hypot(end_offset));
1764 for (int q = int(start_quad) + 1; q < end_quad; q++) {
1765 position offset;
1766 switch (q % 4) {
1767 case 0:
1768 offset.x = r;
1769 break;
1770 case 1:
1771 offset.y = r;
1772 break;
1773 case 2:
1774 offset.x = -r;
1775 break;
1776 case 3:
1777 offset.y = -r;
1778 break;
1779 }
1780 p->encompass(cent + offset);
1781 }
1782}
1783
1784// We ignore the with attribute. The at attribute always refers to the center.
1785
1786linear_object *object_spec::make_arc(position *curpos, direction *dirp)
1787{
1788 *dirp = dir;
1789 int cw = (flags & IS_CLOCKWISE) != 0;
1790 // compute the start
1791 position startpos;
1792 if (flags & HAS_FROM)
1793 startpos = from;
1794 else
1795 startpos = *curpos;
1796 if (!(flags & HAS_RADIUS))
1797 lookup_variable("arcrad", &radius);
1798 // compute the end
1799 position endpos;
1800 if (flags & HAS_TO)
1801 endpos = to;
1802 else {
1803 position m(radius, radius);
1804 // Adjust the signs.
1805 if (cw) {
1806 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1807 m.x = -m.x;
1808 if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1809 m.y = -m.y;
1810 *dirp = direction((dir + 3) % 4);
1811 }
1812 else {
1813 if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1814 m.x = -m.x;
1815 if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1816 m.y = -m.y;
1817 *dirp = direction((dir + 1) % 4);
1818 }
1819 endpos = startpos + m;
1820 }
1821 // compute the center
1822 position centerpos;
1823 if (flags & HAS_AT)
1824 centerpos = at;
1825 else if (startpos == endpos)
1826 centerpos = startpos;
1827 else {
1828 position h = (endpos - startpos)/2.0;
1829 double d = hypot(h);
1830 if (radius <= 0)
1831 radius = .25;
1832 // make the radius big enough
1833 while (radius < d)
1834 radius *= 2.0;
1835 double alpha = acos(d/radius);
1836 double theta = atan2(h.y, h.x);
1837 if (cw)
1838 theta -= alpha;
1839 else
1840 theta += alpha;
1841 centerpos = position(cos(theta), sin(theta))*radius + startpos;
1842 }
1843 arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1844 *curpos = endpos;
1845 return p;
1846}
1847
1848graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1849{
1850 linear_object *obj;
1851 if (type == ARC_OBJECT)
1852 obj = make_arc(curpos, dirp);
1853 else
1854 obj = make_line(curpos, dirp);
1855 if (type == ARROW_OBJECT
1856 && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1857 flags |= HAS_RIGHT_ARROW_HEAD;
1858 if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1859 arrow_head_type a;
1860 int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1861 int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1862 if (flags & HAS_HEIGHT)
1863 a.height = height;
1864 else
1865 lookup_variable("arrowht", &a.height);
1866 if (flags & HAS_WIDTH)
1867 a.width = width;
1868 else
1869 lookup_variable("arrowwid", &a.width);
1870 double solid;
1871 lookup_variable("arrowhead", &solid);
1872 a.solid = solid != 0.0;
1873 obj->add_arrows(at_start, at_end, a);
1874 }
1875 return obj;
1876}
1877
1878object *object_spec::make_object(position *curpos, direction *dirp)
1879{
1880 graphic_object *obj = 0;
1881 switch (type) {
1882 case BLOCK_OBJECT:
1883 obj = make_block(curpos, dirp);
1884 break;
1885 case BOX_OBJECT:
1886 obj = make_box(curpos, dirp);
1887 break;
1888 case TEXT_OBJECT:
1889 obj = make_text(curpos, dirp);
1890 break;
1891 case ELLIPSE_OBJECT:
1892 obj = make_ellipse(curpos, dirp);
1893 break;
1894 case CIRCLE_OBJECT:
1895 obj = make_circle(curpos, dirp);
1896 break;
1897 case MOVE_OBJECT:
1898 obj = make_move(curpos, dirp);
1899 break;
1900 case ARC_OBJECT:
1901 case LINE_OBJECT:
1902 case SPLINE_OBJECT:
1903 case ARROW_OBJECT:
1904 obj = make_linear(curpos, dirp);
1905 break;
1906 case MARK_OBJECT:
1907 case OTHER_OBJECT:
1908 default:
1909 assert(0);
1910 break;
1911 }
1912 if (obj) {
1913 if (flags & IS_INVISIBLE)
1914 obj->set_invisible();
1915 if (text != 0)
1916 obj->add_text(text, (flags & IS_ALIGNED) != 0);
1917 if (flags & IS_DOTTED)
1918 obj->set_dotted(dash_width);
1919 else if (flags & IS_DASHED)
1920 obj->set_dashed(dash_width);
1921 double th;
1922 if (flags & HAS_THICKNESS)
1923 th = thickness;
1924 else
1925 lookup_variable("linethick", &th);
1926 obj->set_thickness(th);
1927 if (flags & IS_OUTLINED)
1928 obj->set_outline_color(outlined);
4d3e9548
JL
1929 if (flags & IS_XSLANTED)
1930 obj->set_xslanted(xslanted);
1931 if (flags & IS_YSLANTED)
1932 obj->set_yslanted(yslanted);
465b256c 1933 if (flags & (IS_DEFAULT_FILLED | IS_FILLED)) {
92d0a6a6
JR
1934 if (flags & IS_SHADED)
1935 obj->set_fill_color(shaded);
1936 else {
1937 if (flags & IS_DEFAULT_FILLED)
1938 lookup_variable("fillval", &fill);
1939 if (fill < 0.0)
1940 error("bad fill value %1", fill);
1941 else
1942 obj->set_fill(fill);
1943 }
1944 }
1945 }
1946 return obj;
1947}
1948
1949struct string_list {
1950 string_list *next;
1951 char *str;
1952 string_list(char *);
1953 ~string_list();
1954};
1955
1956string_list::string_list(char *s)
1957: next(0), str(s)
1958{
1959}
1960
1961string_list::~string_list()
1962{
1963 a_delete str;
1964}
1965
1966/* A path is used to hold the argument to the `with' attribute. For
1967 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1968 take a place and follow the path through the place to place within the
1969 place. Note that `.A.B.C.sw' will work.
1970
1971 For compatibility with DWB pic, `with' accepts positions also (this
1972 is incorrectly documented in CSTR 116). */
1973
1974path::path(corner c)
1975: crn(c), label_list(0), ypath(0), is_position(0)
1976{
1977}
1978
1979path::path(position p)
1980: crn(0), label_list(0), ypath(0), is_position(1)
1981{
1982 pos.x = p.x;
1983 pos.y = p.y;
1984}
1985
1986path::path(char *l, corner c)
1987: crn(c), ypath(0), is_position(0)
1988{
1989 label_list = new string_list(l);
1990}
1991
1992path::~path()
1993{
1994 while (label_list) {
1995 string_list *tem = label_list;
1996 label_list = label_list->next;
1997 delete tem;
1998 }
1999 delete ypath;
2000}
2001
2002void path::append(corner c)
2003{
2004 assert(crn == 0);
2005 crn = c;
2006}
2007
2008void path::append(char *s)
2009{
2010 string_list **p;
2011 for (p = &label_list; *p; p = &(*p)->next)
2012 ;
2013 *p = new string_list(s);
2014}
2015
2016void path::set_ypath(path *p)
2017{
2018 ypath = p;
2019}
2020
2021// return non-zero for success
2022
2023int path::follow(const place &pl, place *result) const
2024{
2025 if (is_position) {
2026 result->x = pos.x;
2027 result->y = pos.y;
2028 result->obj = 0;
2029 return 1;
2030 }
2031 const place *p = &pl;
2032 for (string_list *lb = label_list; lb; lb = lb->next)
2033 if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
2034 lex_error("object does not contain a place `%1'", lb->str);
2035 return 0;
2036 }
2037 if (crn == 0 || p->obj == 0)
2038 *result = *p;
2039 else {
2040 position ps = ((p->obj)->*(crn))();
2041 result->x = ps.x;
2042 result->y = ps.y;
2043 result->obj = 0;
2044 }
2045 if (ypath) {
2046 place tem;
2047 if (!ypath->follow(pl, &tem))
2048 return 0;
2049 result->y = tem.y;
2050 if (result->obj != tem.obj)
2051 result->obj = 0;
2052 }
2053 return 1;
2054}
2055
2056void print_object_list(object *p)
2057{
2058 for (; p; p = p->next) {
2059 p->print();
2060 p->print_text();
2061 }
2062}
2063
2064void print_picture(object *obj)
2065{
2066 bounding_box bb;
2067 for (object *p = obj; p; p = p->next)
2068 p->update_bounding_box(&bb);
2069 double scale;
2070 lookup_variable("scale", &scale);
2071 out->start_picture(scale, bb.ll, bb.ur);
2072 print_object_list(obj);
2073 out->finish_picture();
2074}
2075