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