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 27 line_type::line_type() 28 : type(solid), thickness(1.0) 29 { 30 } 31 32 output::output() : desired_height(0.0), desired_width(0.0), args(0) 33 { 34 } 35 36 output::~output() 37 { 38 a_delete args; 39 } 40 41 void output::set_desired_width_height(double wid, double ht) 42 { 43 desired_width = wid; 44 desired_height = ht; 45 } 46 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 56 void output::command(const char *, const char *, int) 57 { 58 } 59 60 void output::set_location(const char *, int) 61 { 62 } 63 64 int output::supports_filled_polygons() 65 { 66 return 0; 67 } 68 69 void output::begin_block(const position &, const position &) 70 { 71 } 72 73 void output::end_block() 74 { 75 } 76 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 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 131 position::position() : x(0.0), y(0.0) 132 { 133 } 134 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 */ 143 position &posref(position p) { return p; } 144 145 146 int operator==(const position &a, const position &b) 147 { 148 return a.x == b.x && a.y == b.y; 149 } 150 151 int operator!=(const position &a, const position &b) 152 { 153 return a.x != b.x || a.y != b.y; 154 } 155 156 position &position::operator+=(const position &a) 157 { 158 x += a.x; 159 y += a.y; 160 return *this; 161 } 162 163 position &position::operator-=(const position &a) 164 { 165 x -= a.x; 166 y -= a.y; 167 return *this; 168 } 169 170 position &position::operator*=(double a) 171 { 172 x *= a; 173 y *= a; 174 return *this; 175 } 176 177 position &position::operator/=(double a) 178 { 179 x /= a; 180 y /= a; 181 return *this; 182 } 183 184 position operator-(const position &a) 185 { 186 return position(-a.x, -a.y); 187 } 188 189 position operator+(const position &a, const position &b) 190 { 191 return position(a.x + b.x, a.y + b.y); 192 } 193 194 position operator-(const position &a, const position &b) 195 { 196 return position(a.x - b.x, a.y - b.y); 197 } 198 199 position operator/(const position &a, double n) 200 { 201 return position(a.x/n, a.y/n); 202 } 203 204 position operator*(const position &a, double n) 205 { 206 return position(a.x*n, a.y*n); 207 } 208 209 // dot product 210 211 double operator*(const position &a, const position &b) 212 { 213 return a.x*b.x + a.y*b.y; 214 } 215 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 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 257 object::object() : prev(0), next(0) 258 { 259 } 260 261 object::~object() 262 { 263 } 264 265 void object::move_by(const position &) 266 { 267 } 268 269 void object::print() 270 { 271 } 272 273 void object::print_text() 274 { 275 } 276 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 291 bounding_box::bounding_box() 292 : blank(1) 293 { 294 } 295 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 315 void object::update_bounding_box(bounding_box *) 316 { 317 } 318 319 position object::origin() 320 { 321 return position(0.0,0.0); 322 } 323 324 position object::north() 325 { 326 return origin(); 327 } 328 329 position object::south() 330 { 331 return origin(); 332 } 333 334 position object::east() 335 { 336 return origin(); 337 } 338 339 position object::west() 340 { 341 return origin(); 342 } 343 344 position object::north_east() 345 { 346 return origin(); 347 } 348 349 position object::north_west() 350 { 351 return origin(); 352 } 353 354 position object::south_east() 355 { 356 return origin(); 357 } 358 359 position object::south_west() 360 { 361 return origin(); 362 } 363 364 position object::start() 365 { 366 return origin(); 367 } 368 369 position object::end() 370 { 371 return origin(); 372 } 373 374 position object::center() 375 { 376 return origin(); 377 } 378 379 double object::width() 380 { 381 return 0.0; 382 } 383 384 double object::radius() 385 { 386 return 0.0; 387 } 388 389 double object::height() 390 { 391 return 0.0; 392 } 393 394 place *object::find_label(const char *) 395 { 396 return 0; 397 } 398 399 segment::segment(const position &a, int n, segment *p) 400 : pos(a), is_absolute(n), next(p) 401 { 402 } 403 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 411 text_item::~text_item() 412 { 413 a_delete text; 414 } 415 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 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(); 457 object_type type() { return OTHER_OBJECT; } 458 void print(); 459 }; 460 461 command_object::command_object(char *p, const char *fn, int ln) 462 : s(p), filename(fn), lineno(ln) 463 { 464 } 465 466 command_object::~command_object() 467 { 468 a_delete s; 469 } 470 471 void command_object::print() 472 { 473 out->command(s, filename, lineno); 474 } 475 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 487 object *make_mark_object() 488 { 489 return new mark_object(); 490 } 491 492 mark_object::mark_object() 493 { 494 } 495 496 object_type mark_object::type() 497 { 498 return MARK_OBJECT; 499 } 500 501 object_list::object_list() : head(0), tail(0) 502 { 503 } 504 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 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 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 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 570 graphic_object::graphic_object() : ntext(0), text(0), aligned(0) 571 { 572 } 573 574 void graphic_object::set_dotted(double wid) 575 { 576 lt.type = line_type::dotted; 577 lt.dash_width = wid; 578 } 579 580 void graphic_object::set_dashed(double wid) 581 { 582 lt.type = line_type::dashed; 583 lt.dash_width = wid; 584 } 585 586 void graphic_object::set_thickness(double th) 587 { 588 lt.thickness = th; 589 } 590 591 void graphic_object::set_fill(double) 592 { 593 } 594 595 void graphic_object::set_invisible() 596 { 597 lt.type = line_type::invisible; 598 } 599 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 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 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 &); 645 double width() { return dim.x; } 646 double height() { return dim.y; } 647 position origin() { return cent; } 648 position center() { return cent; } 649 position north() { return position(cent.x, cent.y + dim.y/2.0); } 650 position south() { return position(cent.x, cent.y - dim.y/2.0); } 651 position east() { return position(cent.x + dim.x/2.0, cent.y); } 652 position west() { return position(cent.x - dim.x/2.0, cent.y); } 653 position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); } 654 position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); } 655 position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); } 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 662 rectangle_object::rectangle_object(const position &d) 663 : dim(d) 664 { 665 } 666 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 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 687 closed_object::closed_object(const position &pos) 688 : rectangle_object(pos), fill(-1.0) 689 { 690 } 691 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); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 906 place *block_object::find_label(const char *name) 907 { 908 return tbl->lookup(name); 909 } 910 911 object_type block_object::type() 912 { 913 return BLOCK_OBJECT; 914 } 915 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 &); 946 object_type type() { return TEXT_OBJECT; } 947 }; 948 949 text_object::text_object(const position &d) 950 : rectangle_object(d) 951 { 952 } 953 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 &); 977 position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0), 978 cent.y + dim.y/(M_SQRT2*2.0)); } 979 position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0), 980 cent.y + dim.y/(M_SQRT2*2.0)); } 981 position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0), 982 cent.y - dim.y/(M_SQRT2*2.0)); } 983 position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0), 984 cent.y - dim.y/(M_SQRT2*2.0)); } 985 double radius() { return dim.x/2.0; } 986 object_type type() { return ELLIPSE_OBJECT; } 987 void print(); 988 }; 989 990 ellipse_object::ellipse_object(const position &d) 991 : closed_object(d) 992 { 993 } 994 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 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); 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 */ 1041 circle_object::circle_object(double diam) 1042 : ellipse_object(posref(position(diam, diam))) 1043 { 1044 } 1045 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 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); 1078 position origin() { return en; } 1079 object_type type() { return MOVE_OBJECT; } 1080 void update_bounding_box(bounding_box *); 1081 void move_by(const position &); 1082 }; 1083 1084 move_object::move_object(const position &s, const position &e) 1085 : strt(s), en(e) 1086 { 1087 } 1088 1089 void move_object::update_bounding_box(bounding_box *p) 1090 { 1091 p->encompass(strt); 1092 p->encompass(en); 1093 } 1094 1095 void move_object::move_by(const position &a) 1096 { 1097 strt += a; 1098 en += a; 1099 } 1100 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); 1163 position start() { return strt; } 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(); 1178 position origin() { return strt; } 1179 position center() { return (strt + en)/2.0; } 1180 position north() { return (en.y - strt.y) > 0 ? en : strt; } 1181 position south() { return (en.y - strt.y) < 0 ? en : strt; } 1182 position east() { return (en.x - strt.x) > 0 ? en : strt; } 1183 position west() { return (en.x - strt.x) < 0 ? en : strt; } 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); 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); 1199 object_type type() { return SPLINE_OBJECT; } 1200 void print(); 1201 void update_bounding_box(bounding_box *); 1202 }; 1203 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 1209 void linear_object::move_by(const position &a) 1210 { 1211 strt += a; 1212 en += a; 1213 } 1214 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 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 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 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 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 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 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 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 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 1307 line_object::~line_object() 1308 { 1309 a_delete v; 1310 } 1311 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 &); 1413 position origin() { return cent; } 1414 position center() { return cent; } 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 *); 1425 object_type type() { return ARC_OBJECT; } 1426 void print(); 1427 void move_by(const position &pos); 1428 }; 1429 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 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 1445 position arc_object::north() 1446 { 1447 position result(cent); 1448 result.y += rad; 1449 return result; 1450 } 1451 1452 position arc_object::south() 1453 { 1454 position result(cent); 1455 result.y -= rad; 1456 return result; 1457 } 1458 1459 position arc_object::east() 1460 { 1461 position result(cent); 1462 result.x += rad; 1463 return result; 1464 } 1465 1466 position arc_object::west() 1467 { 1468 position result(cent); 1469 result.x -= rad; 1470 return result; 1471 } 1472 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 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 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 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 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 1528 inline double max(double a, double b) 1529 { 1530 return a > b ? a : b; 1531 } 1532 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 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 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 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 1737 string_list::string_list(char *s) 1738 : next(0), str(s) 1739 { 1740 } 1741 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 1752 path::path(corner c) 1753 : label_list(0), crn(c) 1754 { 1755 } 1756 1757 path::path(char *l, corner c) 1758 : crn(c) 1759 { 1760 label_list = new string_list(l); 1761 } 1762 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 1772 void path::append(corner c) 1773 { 1774 assert(crn == 0); 1775 crn = c; 1776 } 1777 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 1787 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 1806 void print_object_list(object *p) 1807 { 1808 for (; p; p = p->next) { 1809 p->print(); 1810 p->print_text(); 1811 } 1812 } 1813 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