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