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