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