1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru#include "pic.h" 23114402Sru#include "ptable.h" 24114402Sru#include "object.h" 25114402Sru 26114402Sruvoid print_object_list(object *); 27114402Sru 28114402Sruline_type::line_type() 29114402Sru: type(solid), thickness(1.0) 30114402Sru{ 31114402Sru} 32114402Sru 33114402Sruoutput::output() : args(0), desired_height(0.0), desired_width(0.0) 34114402Sru{ 35114402Sru} 36114402Sru 37114402Sruoutput::~output() 38114402Sru{ 39114402Sru a_delete args; 40114402Sru} 41114402Sru 42114402Sruvoid output::set_desired_width_height(double wid, double ht) 43114402Sru{ 44114402Sru desired_width = wid; 45114402Sru desired_height = ht; 46114402Sru} 47114402Sru 48114402Sruvoid output::set_args(const char *s) 49114402Sru{ 50114402Sru a_delete args; 51114402Sru if (s == 0 || *s == '\0') 52114402Sru args = 0; 53114402Sru else 54114402Sru args = strsave(s); 55114402Sru} 56114402Sru 57114402Sruint output::supports_filled_polygons() 58114402Sru{ 59114402Sru return 0; 60114402Sru} 61114402Sru 62114402Sruvoid output::begin_block(const position &, const position &) 63114402Sru{ 64114402Sru} 65114402Sru 66114402Sruvoid output::end_block() 67114402Sru{ 68114402Sru} 69114402Sru 70114402Srudouble output::compute_scale(double sc, const position &ll, const position &ur) 71114402Sru{ 72114402Sru distance dim = ur - ll; 73114402Sru if (desired_width != 0.0 || desired_height != 0.0) { 74114402Sru sc = 0.0; 75114402Sru if (desired_width != 0.0) { 76114402Sru if (dim.x == 0.0) 77114402Sru error("width specified for picture with zero width"); 78114402Sru else 79114402Sru sc = dim.x/desired_width; 80114402Sru } 81114402Sru if (desired_height != 0.0) { 82114402Sru if (dim.y == 0.0) 83114402Sru error("height specified for picture with zero height"); 84114402Sru else { 85114402Sru double tem = dim.y/desired_height; 86114402Sru if (tem > sc) 87114402Sru sc = tem; 88114402Sru } 89114402Sru } 90114402Sru return sc == 0.0 ? 1.0 : sc; 91114402Sru } 92114402Sru else { 93114402Sru if (sc <= 0.0) 94114402Sru sc = 1.0; 95114402Sru distance sdim = dim/sc; 96114402Sru double max_width = 0.0; 97114402Sru lookup_variable("maxpswid", &max_width); 98114402Sru double max_height = 0.0; 99114402Sru lookup_variable("maxpsht", &max_height); 100114402Sru if ((max_width > 0.0 && sdim.x > max_width) 101114402Sru || (max_height > 0.0 && sdim.y > max_height)) { 102114402Sru double xscale = dim.x/max_width; 103114402Sru double yscale = dim.y/max_height; 104114402Sru return xscale > yscale ? xscale : yscale; 105114402Sru } 106114402Sru else 107114402Sru return sc; 108114402Sru } 109114402Sru} 110114402Sru 111114402Sruposition::position(const place &pl) 112114402Sru{ 113114402Sru if (pl.obj != 0) { 114114402Sru // Use two statements to work around bug in SGI C++. 115114402Sru object *tem = pl.obj; 116114402Sru *this = tem->origin(); 117114402Sru } 118114402Sru else { 119114402Sru x = pl.x; 120114402Sru y = pl.y; 121114402Sru } 122114402Sru} 123114402Sru 124114402Sruposition::position() : x(0.0), y(0.0) 125114402Sru{ 126114402Sru} 127114402Sru 128114402Sruposition::position(double a, double b) : x(a), y(b) 129114402Sru{ 130114402Sru} 131114402Sru 132114402Sru 133114402Sruint operator==(const position &a, const position &b) 134114402Sru{ 135114402Sru return a.x == b.x && a.y == b.y; 136114402Sru} 137114402Sru 138114402Sruint operator!=(const position &a, const position &b) 139114402Sru{ 140114402Sru return a.x != b.x || a.y != b.y; 141114402Sru} 142114402Sru 143114402Sruposition &position::operator+=(const position &a) 144114402Sru{ 145114402Sru x += a.x; 146114402Sru y += a.y; 147114402Sru return *this; 148114402Sru} 149114402Sru 150114402Sruposition &position::operator-=(const position &a) 151114402Sru{ 152114402Sru x -= a.x; 153114402Sru y -= a.y; 154114402Sru return *this; 155114402Sru} 156114402Sru 157114402Sruposition &position::operator*=(double a) 158114402Sru{ 159114402Sru x *= a; 160114402Sru y *= a; 161114402Sru return *this; 162114402Sru} 163114402Sru 164114402Sruposition &position::operator/=(double a) 165114402Sru{ 166114402Sru x /= a; 167114402Sru y /= a; 168114402Sru return *this; 169114402Sru} 170114402Sru 171114402Sruposition operator-(const position &a) 172114402Sru{ 173114402Sru return position(-a.x, -a.y); 174114402Sru} 175114402Sru 176114402Sruposition operator+(const position &a, const position &b) 177114402Sru{ 178114402Sru return position(a.x + b.x, a.y + b.y); 179114402Sru} 180114402Sru 181114402Sruposition operator-(const position &a, const position &b) 182114402Sru{ 183114402Sru return position(a.x - b.x, a.y - b.y); 184114402Sru} 185114402Sru 186114402Sruposition operator/(const position &a, double n) 187114402Sru{ 188114402Sru return position(a.x/n, a.y/n); 189114402Sru} 190114402Sru 191114402Sruposition operator*(const position &a, double n) 192114402Sru{ 193114402Sru return position(a.x*n, a.y*n); 194114402Sru} 195114402Sru 196114402Sru// dot product 197114402Sru 198114402Srudouble operator*(const position &a, const position &b) 199114402Sru{ 200114402Sru return a.x*b.x + a.y*b.y; 201114402Sru} 202114402Sru 203114402Srudouble hypot(const position &a) 204114402Sru{ 205151497Sru return groff_hypot(a.x, a.y); 206114402Sru} 207114402Sru 208114402Srustruct arrow_head_type { 209114402Sru double height; 210114402Sru double width; 211114402Sru int solid; 212114402Sru}; 213114402Sru 214114402Sruvoid draw_arrow(const position &pos, const distance &dir, 215114402Sru const arrow_head_type &aht, const line_type <, 216114402Sru char *outline_color_for_fill) 217114402Sru{ 218114402Sru double hyp = hypot(dir); 219114402Sru if (hyp == 0.0) { 220114402Sru error("cannot draw arrow on object with zero length"); 221114402Sru return; 222114402Sru } 223114402Sru position base = -dir; 224114402Sru base *= aht.height/hyp; 225114402Sru position n(dir.y, -dir.x); 226114402Sru n *= aht.width/(hyp*2.0); 227114402Sru line_type slt = lt; 228114402Sru slt.type = line_type::solid; 229114402Sru if (aht.solid && out->supports_filled_polygons()) { 230114402Sru position v[3]; 231114402Sru v[0] = pos; 232114402Sru v[1] = pos + base + n; 233114402Sru v[2] = pos + base - n; 234114402Sru // fill with outline color 235114402Sru out->set_color(outline_color_for_fill, outline_color_for_fill); 236151497Sru // make stroke thin to avoid arrow sticking 237151497Sru slt.thickness = 0.1; 238114402Sru out->polygon(v, 3, slt, 1); 239114402Sru } 240114402Sru else { 241151497Sru // use two line segments to avoid arrow sticking 242151497Sru out->line(pos + base - n, &pos, 1, slt); 243151497Sru out->line(pos + base + n, &pos, 1, slt); 244114402Sru } 245114402Sru} 246114402Sru 247114402Sruobject::object() : prev(0), next(0) 248114402Sru{ 249114402Sru} 250114402Sru 251114402Sruobject::~object() 252114402Sru{ 253114402Sru} 254114402Sru 255114402Sruvoid object::move_by(const position &) 256114402Sru{ 257114402Sru} 258114402Sru 259114402Sruvoid object::print() 260114402Sru{ 261114402Sru} 262114402Sru 263114402Sruvoid object::print_text() 264114402Sru{ 265114402Sru} 266114402Sru 267114402Sruint object::blank() 268114402Sru{ 269114402Sru return 0; 270114402Sru} 271114402Sru 272114402Srustruct bounding_box { 273114402Sru int blank; 274114402Sru position ll; 275114402Sru position ur; 276114402Sru 277114402Sru bounding_box(); 278114402Sru void encompass(const position &); 279114402Sru}; 280114402Sru 281114402Srubounding_box::bounding_box() 282114402Sru: blank(1) 283114402Sru{ 284114402Sru} 285114402Sru 286114402Sruvoid bounding_box::encompass(const position &pos) 287114402Sru{ 288114402Sru if (blank) { 289114402Sru ll = pos; 290114402Sru ur = pos; 291114402Sru blank = 0; 292114402Sru } 293114402Sru else { 294114402Sru if (pos.x < ll.x) 295114402Sru ll.x = pos.x; 296114402Sru if (pos.y < ll.y) 297114402Sru ll.y = pos.y; 298114402Sru if (pos.x > ur.x) 299114402Sru ur.x = pos.x; 300114402Sru if (pos.y > ur.y) 301114402Sru ur.y = pos.y; 302114402Sru } 303114402Sru} 304114402Sru 305114402Sruvoid object::update_bounding_box(bounding_box *) 306114402Sru{ 307114402Sru} 308114402Sru 309114402Sruposition object::origin() 310114402Sru{ 311114402Sru return position(0.0,0.0); 312114402Sru} 313114402Sru 314114402Sruposition object::north() 315114402Sru{ 316114402Sru return origin(); 317114402Sru} 318114402Sru 319114402Sruposition object::south() 320114402Sru{ 321114402Sru return origin(); 322114402Sru} 323114402Sru 324114402Sruposition object::east() 325114402Sru{ 326114402Sru return origin(); 327114402Sru} 328114402Sru 329114402Sruposition object::west() 330114402Sru{ 331114402Sru return origin(); 332114402Sru} 333114402Sru 334114402Sruposition object::north_east() 335114402Sru{ 336114402Sru return origin(); 337114402Sru} 338114402Sru 339114402Sruposition object::north_west() 340114402Sru{ 341114402Sru return origin(); 342114402Sru} 343114402Sru 344114402Sruposition object::south_east() 345114402Sru{ 346114402Sru return origin(); 347114402Sru} 348114402Sru 349114402Sruposition object::south_west() 350114402Sru{ 351114402Sru return origin(); 352114402Sru} 353114402Sru 354114402Sruposition object::start() 355114402Sru{ 356114402Sru return origin(); 357114402Sru} 358114402Sru 359114402Sruposition object::end() 360114402Sru{ 361114402Sru return origin(); 362114402Sru} 363114402Sru 364114402Sruposition object::center() 365114402Sru{ 366114402Sru return origin(); 367114402Sru} 368114402Sru 369114402Srudouble object::width() 370114402Sru{ 371114402Sru return 0.0; 372114402Sru} 373114402Sru 374114402Srudouble object::radius() 375114402Sru{ 376114402Sru return 0.0; 377114402Sru} 378114402Sru 379114402Srudouble object::height() 380114402Sru{ 381114402Sru return 0.0; 382114402Sru} 383114402Sru 384114402Sruplace *object::find_label(const char *) 385114402Sru{ 386114402Sru return 0; 387114402Sru} 388114402Sru 389114402Srusegment::segment(const position &a, int n, segment *p) 390114402Sru: is_absolute(n), pos(a), next(p) 391114402Sru{ 392114402Sru} 393114402Sru 394114402Srutext_item::text_item(char *t, const char *fn, int ln) 395114402Sru: next(0), text(t), filename(fn), lineno(ln) 396114402Sru{ 397114402Sru adj.h = CENTER_ADJUST; 398114402Sru adj.v = NONE_ADJUST; 399114402Sru} 400114402Sru 401114402Srutext_item::~text_item() 402114402Sru{ 403114402Sru a_delete text; 404114402Sru} 405114402Sru 406114402Sruobject_spec::object_spec(object_type t) : type(t) 407114402Sru{ 408114402Sru flags = 0; 409114402Sru tbl = 0; 410114402Sru segment_list = 0; 411114402Sru segment_width = segment_height = 0.0; 412114402Sru segment_is_absolute = 0; 413114402Sru text = 0; 414114402Sru shaded = 0; 415114402Sru outlined = 0; 416114402Sru with = 0; 417114402Sru dir = RIGHT_DIRECTION; 418114402Sru} 419114402Sru 420114402Sruobject_spec::~object_spec() 421114402Sru{ 422114402Sru delete tbl; 423114402Sru while (segment_list != 0) { 424114402Sru segment *tem = segment_list; 425114402Sru segment_list = segment_list->next; 426114402Sru delete tem; 427114402Sru } 428114402Sru object *p = oblist.head; 429114402Sru while (p != 0) { 430114402Sru object *tem = p; 431114402Sru p = p->next; 432114402Sru delete tem; 433114402Sru } 434114402Sru while (text != 0) { 435114402Sru text_item *tem = text; 436114402Sru text = text->next; 437114402Sru delete tem; 438114402Sru } 439114402Sru delete with; 440114402Sru a_delete shaded; 441114402Sru a_delete outlined; 442114402Sru} 443114402Sru 444114402Sruclass command_object : public object { 445114402Sru char *s; 446114402Sru const char *filename; 447114402Sru int lineno; 448114402Srupublic: 449114402Sru command_object(char *, const char *, int); 450114402Sru ~command_object(); 451114402Sru object_type type() { return OTHER_OBJECT; } 452114402Sru void print(); 453114402Sru}; 454114402Sru 455114402Srucommand_object::command_object(char *p, const char *fn, int ln) 456114402Sru: s(p), filename(fn), lineno(ln) 457114402Sru{ 458114402Sru} 459114402Sru 460114402Srucommand_object::~command_object() 461114402Sru{ 462114402Sru a_delete s; 463114402Sru} 464114402Sru 465114402Sruvoid command_object::print() 466114402Sru{ 467114402Sru out->command(s, filename, lineno); 468114402Sru} 469114402Sru 470114402Sruobject *make_command_object(char *s, const char *fn, int ln) 471114402Sru{ 472114402Sru return new command_object(s, fn, ln); 473114402Sru} 474114402Sru 475114402Sruclass mark_object : public object { 476114402Srupublic: 477114402Sru mark_object(); 478114402Sru object_type type(); 479114402Sru}; 480114402Sru 481114402Sruobject *make_mark_object() 482114402Sru{ 483114402Sru return new mark_object(); 484114402Sru} 485114402Sru 486114402Srumark_object::mark_object() 487114402Sru{ 488114402Sru} 489114402Sru 490114402Sruobject_type mark_object::type() 491114402Sru{ 492114402Sru return MARK_OBJECT; 493114402Sru} 494114402Sru 495114402Sruobject_list::object_list() : head(0), tail(0) 496114402Sru{ 497114402Sru} 498114402Sru 499114402Sruvoid object_list::append(object *obj) 500114402Sru{ 501114402Sru if (tail == 0) { 502114402Sru obj->next = obj->prev = 0; 503114402Sru head = tail = obj; 504114402Sru } 505114402Sru else { 506114402Sru obj->prev = tail; 507114402Sru obj->next = 0; 508114402Sru tail->next = obj; 509114402Sru tail = obj; 510114402Sru } 511114402Sru} 512114402Sru 513114402Sruvoid object_list::wrap_up_block(object_list *ol) 514114402Sru{ 515114402Sru object *p; 516114402Sru for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev) 517114402Sru ; 518114402Sru assert(p != 0); 519114402Sru ol->head = p->next; 520114402Sru if (ol->head) { 521114402Sru ol->tail = tail; 522114402Sru ol->head->prev = 0; 523114402Sru } 524114402Sru else 525114402Sru ol->tail = 0; 526114402Sru tail = p->prev; 527114402Sru if (tail) 528114402Sru tail->next = 0; 529114402Sru else 530114402Sru head = 0; 531114402Sru delete p; 532114402Sru} 533114402Sru 534114402Srutext_piece::text_piece() 535114402Sru: text(0), filename(0), lineno(-1) 536114402Sru{ 537114402Sru adj.h = CENTER_ADJUST; 538114402Sru adj.v = NONE_ADJUST; 539114402Sru} 540114402Sru 541114402Srutext_piece::~text_piece() 542114402Sru{ 543114402Sru a_delete text; 544114402Sru} 545114402Sru 546114402Sruclass graphic_object : public object { 547114402Sru int ntext; 548114402Sru text_piece *text; 549114402Sru int aligned; 550114402Sruprotected: 551114402Sru line_type lt; 552114402Sru char *outline_color; 553114402Sru char *color_fill; 554114402Srupublic: 555114402Sru graphic_object(); 556114402Sru ~graphic_object(); 557114402Sru object_type type() = 0; 558114402Sru void print_text(); 559114402Sru void add_text(text_item *, int); 560114402Sru void set_dotted(double); 561114402Sru void set_dashed(double); 562114402Sru void set_thickness(double); 563114402Sru void set_invisible(); 564114402Sru void set_outline_color(char *); 565114402Sru char *get_outline_color(); 566114402Sru virtual void set_fill(double); 567114402Sru virtual void set_fill_color(char *); 568114402Sru}; 569114402Sru 570114402Srugraphic_object::graphic_object() 571114402Sru: ntext(0), text(0), aligned(0), outline_color(0), color_fill(0) 572114402Sru{ 573114402Sru} 574114402Sru 575114402Sruvoid graphic_object::set_dotted(double wid) 576114402Sru{ 577114402Sru lt.type = line_type::dotted; 578114402Sru lt.dash_width = wid; 579114402Sru} 580114402Sru 581114402Sruvoid graphic_object::set_dashed(double wid) 582114402Sru{ 583114402Sru lt.type = line_type::dashed; 584114402Sru lt.dash_width = wid; 585114402Sru} 586114402Sru 587114402Sruvoid graphic_object::set_thickness(double th) 588114402Sru{ 589114402Sru lt.thickness = th; 590114402Sru} 591114402Sru 592114402Sruvoid graphic_object::set_fill(double) 593114402Sru{ 594114402Sru} 595114402Sru 596114402Sruvoid graphic_object::set_fill_color(char *c) 597114402Sru{ 598114402Sru color_fill = strsave(c); 599114402Sru} 600114402Sru 601114402Sruvoid graphic_object::set_outline_color(char *c) 602114402Sru{ 603114402Sru outline_color = strsave(c); 604114402Sru} 605114402Sru 606114402Sruchar *graphic_object::get_outline_color() 607114402Sru{ 608114402Sru return outline_color; 609114402Sru} 610114402Sru 611114402Sruvoid graphic_object::set_invisible() 612114402Sru{ 613114402Sru lt.type = line_type::invisible; 614114402Sru} 615114402Sru 616114402Sruvoid graphic_object::add_text(text_item *t, int a) 617114402Sru{ 618114402Sru aligned = a; 619114402Sru int len = 0; 620114402Sru text_item *p; 621114402Sru for (p = t; p; p = p->next) 622114402Sru len++; 623114402Sru if (len == 0) 624114402Sru text = 0; 625114402Sru else { 626114402Sru text = new text_piece[len]; 627114402Sru for (p = t, len = 0; p; p = p->next, len++) { 628114402Sru text[len].text = p->text; 629114402Sru p->text = 0; 630114402Sru text[len].adj = p->adj; 631114402Sru text[len].filename = p->filename; 632114402Sru text[len].lineno = p->lineno; 633114402Sru } 634114402Sru } 635114402Sru ntext = len; 636114402Sru} 637114402Sru 638114402Sruvoid graphic_object::print_text() 639114402Sru{ 640114402Sru double angle = 0.0; 641114402Sru if (aligned) { 642114402Sru position d(end() - start()); 643114402Sru if (d.x != 0.0 || d.y != 0.0) 644114402Sru angle = atan2(d.y, d.x); 645114402Sru } 646114402Sru if (text != 0) { 647114402Sru out->set_color(color_fill, get_outline_color()); 648114402Sru out->text(center(), text, ntext, angle); 649114402Sru out->reset_color(); 650114402Sru } 651114402Sru} 652114402Sru 653114402Srugraphic_object::~graphic_object() 654114402Sru{ 655114402Sru if (text) 656114402Sru ad_delete(ntext) text; 657114402Sru} 658114402Sru 659114402Sruclass rectangle_object : public graphic_object { 660114402Sruprotected: 661114402Sru position cent; 662114402Sru position dim; 663114402Srupublic: 664114402Sru rectangle_object(const position &); 665114402Sru double width() { return dim.x; } 666114402Sru double height() { return dim.y; } 667114402Sru position origin() { return cent; } 668114402Sru position center() { return cent; } 669114402Sru position north() { return position(cent.x, cent.y + dim.y/2.0); } 670114402Sru position south() { return position(cent.x, cent.y - dim.y/2.0); } 671114402Sru position east() { return position(cent.x + dim.x/2.0, cent.y); } 672114402Sru position west() { return position(cent.x - dim.x/2.0, cent.y); } 673114402Sru position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); } 674114402Sru position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); } 675114402Sru position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); } 676114402Sru position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); } 677114402Sru object_type type() = 0; 678114402Sru void update_bounding_box(bounding_box *); 679114402Sru void move_by(const position &); 680114402Sru}; 681114402Sru 682114402Srurectangle_object::rectangle_object(const position &d) 683114402Sru: dim(d) 684114402Sru{ 685114402Sru} 686114402Sru 687114402Sruvoid rectangle_object::update_bounding_box(bounding_box *p) 688114402Sru{ 689114402Sru p->encompass(cent - dim/2.0); 690114402Sru p->encompass(cent + dim/2.0); 691114402Sru} 692114402Sru 693114402Sruvoid rectangle_object::move_by(const position &a) 694114402Sru{ 695114402Sru cent += a; 696114402Sru} 697114402Sru 698114402Sruclass closed_object : public rectangle_object { 699114402Srupublic: 700114402Sru closed_object(const position &); 701114402Sru object_type type() = 0; 702114402Sru void set_fill(double); 703114402Sru void set_fill_color(char *fill); 704114402Sruprotected: 705114402Sru double fill; // < 0 if not filled 706114402Sru char *color_fill; // = 0 if not colored 707114402Sru}; 708114402Sru 709114402Sruclosed_object::closed_object(const position &pos) 710114402Sru: rectangle_object(pos), fill(-1.0), color_fill(0) 711114402Sru{ 712114402Sru} 713114402Sru 714114402Sruvoid closed_object::set_fill(double f) 715114402Sru{ 716114402Sru assert(f >= 0.0); 717114402Sru fill = f; 718114402Sru} 719114402Sru 720151497Sruvoid closed_object::set_fill_color(char *f) 721114402Sru{ 722151497Sru color_fill = strsave(f); 723114402Sru} 724114402Sru 725114402Sruclass box_object : public closed_object { 726114402Sru double xrad; 727114402Sru double yrad; 728114402Srupublic: 729114402Sru box_object(const position &, double); 730114402Sru object_type type() { return BOX_OBJECT; } 731114402Sru void print(); 732114402Sru position north_east(); 733114402Sru position north_west(); 734114402Sru position south_east(); 735114402Sru position south_west(); 736114402Sru}; 737114402Sru 738114402Srubox_object::box_object(const position &pos, double r) 739114402Sru: closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r) 740114402Sru{ 741114402Sru} 742114402Sru 743114402Sruconst double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2; 744114402Sru 745114402Sruposition box_object::north_east() 746114402Sru{ 747114402Sru return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad, 748114402Sru cent.y + dim.y/2.0 - CHOP_FACTOR*yrad); 749114402Sru} 750114402Sru 751114402Sruposition box_object::north_west() 752114402Sru{ 753114402Sru return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad, 754114402Sru cent.y + dim.y/2.0 - CHOP_FACTOR*yrad); 755114402Sru} 756114402Sru 757114402Sruposition box_object::south_east() 758114402Sru{ 759114402Sru return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad, 760114402Sru cent.y - dim.y/2.0 + CHOP_FACTOR*yrad); 761114402Sru} 762114402Sru 763114402Sruposition box_object::south_west() 764114402Sru{ 765114402Sru return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad, 766114402Sru cent.y - dim.y/2.0 + CHOP_FACTOR*yrad); 767114402Sru} 768114402Sru 769114402Sruvoid box_object::print() 770114402Sru{ 771114402Sru if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0) 772114402Sru return; 773114402Sru out->set_color(color_fill, graphic_object::get_outline_color()); 774114402Sru if (xrad == 0.0) { 775114402Sru distance dim2 = dim/2.0; 776114402Sru position vec[4]; 777114402Sru vec[0] = cent + position(dim2.x, -dim2.y); 778114402Sru vec[1] = cent + position(dim2.x, dim2.y); 779114402Sru vec[2] = cent + position(-dim2.x, dim2.y); 780114402Sru vec[3] = cent + position(-dim2.x, -dim2.y); 781114402Sru out->polygon(vec, 4, lt, fill); 782114402Sru } 783114402Sru else { 784114402Sru distance abs_dim(fabs(dim.x), fabs(dim.y)); 785114402Sru out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill); 786114402Sru } 787114402Sru out->reset_color(); 788114402Sru} 789114402Sru 790114402Srugraphic_object *object_spec::make_box(position *curpos, direction *dirp) 791114402Sru{ 792114402Sru static double last_box_height; 793114402Sru static double last_box_width; 794114402Sru static double last_box_radius; 795114402Sru static int have_last_box = 0; 796114402Sru if (!(flags & HAS_HEIGHT)) { 797114402Sru if ((flags & IS_SAME) && have_last_box) 798114402Sru height = last_box_height; 799114402Sru else 800114402Sru lookup_variable("boxht", &height); 801114402Sru } 802114402Sru if (!(flags & HAS_WIDTH)) { 803114402Sru if ((flags & IS_SAME) && have_last_box) 804114402Sru width = last_box_width; 805114402Sru else 806114402Sru lookup_variable("boxwid", &width); 807114402Sru } 808114402Sru if (!(flags & HAS_RADIUS)) { 809114402Sru if ((flags & IS_SAME) && have_last_box) 810114402Sru radius = last_box_radius; 811114402Sru else 812114402Sru lookup_variable("boxrad", &radius); 813114402Sru } 814114402Sru last_box_width = width; 815114402Sru last_box_height = height; 816114402Sru last_box_radius = radius; 817114402Sru have_last_box = 1; 818114402Sru radius = fabs(radius); 819114402Sru if (radius*2.0 > fabs(width)) 820114402Sru radius = fabs(width/2.0); 821114402Sru if (radius*2.0 > fabs(height)) 822114402Sru radius = fabs(height/2.0); 823114402Sru box_object *p = new box_object(position(width, height), radius); 824114402Sru if (!position_rectangle(p, curpos, dirp)) { 825114402Sru delete p; 826114402Sru p = 0; 827114402Sru } 828114402Sru return p; 829114402Sru} 830114402Sru 831114402Sru// return non-zero for success 832114402Sru 833114402Sruint object_spec::position_rectangle(rectangle_object *p, 834114402Sru position *curpos, direction *dirp) 835114402Sru{ 836114402Sru position pos; 837114402Sru dir = *dirp; // ignore any direction in attribute list 838114402Sru position motion; 839114402Sru switch (dir) { 840114402Sru case UP_DIRECTION: 841114402Sru motion.y = p->height()/2.0; 842114402Sru break; 843114402Sru case DOWN_DIRECTION: 844114402Sru motion.y = -p->height()/2.0; 845114402Sru break; 846114402Sru case LEFT_DIRECTION: 847114402Sru motion.x = -p->width()/2.0; 848114402Sru break; 849114402Sru case RIGHT_DIRECTION: 850114402Sru motion.x = p->width()/2.0; 851114402Sru break; 852114402Sru default: 853114402Sru assert(0); 854114402Sru } 855114402Sru if (flags & HAS_AT) { 856114402Sru pos = at; 857114402Sru if (flags & HAS_WITH) { 858114402Sru place offset; 859114402Sru place here; 860114402Sru here.obj = p; 861114402Sru if (!with->follow(here, &offset)) 862114402Sru return 0; 863114402Sru pos -= offset; 864114402Sru } 865114402Sru } 866114402Sru else { 867114402Sru pos = *curpos; 868114402Sru pos += motion; 869114402Sru } 870114402Sru p->move_by(pos); 871114402Sru pos += motion; 872114402Sru *curpos = pos; 873114402Sru return 1; 874114402Sru} 875114402Sru 876114402Sruclass block_object : public rectangle_object { 877114402Sru object_list oblist; 878114402Sru PTABLE(place) *tbl; 879114402Srupublic: 880114402Sru block_object(const position &, const object_list &ol, PTABLE(place) *t); 881114402Sru ~block_object(); 882114402Sru place *find_label(const char *); 883114402Sru object_type type(); 884114402Sru void move_by(const position &); 885114402Sru void print(); 886114402Sru}; 887114402Sru 888114402Srublock_object::block_object(const position &d, const object_list &ol, 889114402Sru PTABLE(place) *t) 890114402Sru: rectangle_object(d), oblist(ol), tbl(t) 891114402Sru{ 892114402Sru} 893114402Sru 894114402Srublock_object::~block_object() 895114402Sru{ 896114402Sru delete tbl; 897114402Sru object *p = oblist.head; 898114402Sru while (p != 0) { 899114402Sru object *tem = p; 900114402Sru p = p->next; 901114402Sru delete tem; 902114402Sru } 903114402Sru} 904114402Sru 905114402Sruvoid block_object::print() 906114402Sru{ 907114402Sru out->begin_block(south_west(), north_east()); 908114402Sru print_object_list(oblist.head); 909114402Sru out->end_block(); 910114402Sru} 911114402Sru 912114402Srustatic void adjust_objectless_places(PTABLE(place) *tbl, const position &a) 913114402Sru{ 914114402Sru // Adjust all the labels that aren't attached to objects. 915114402Sru PTABLE_ITERATOR(place) iter(tbl); 916114402Sru const char *key; 917114402Sru place *pl; 918114402Sru while (iter.next(&key, &pl)) 919114402Sru if (key && csupper(key[0]) && pl->obj == 0) { 920114402Sru pl->x += a.x; 921114402Sru pl->y += a.y; 922114402Sru } 923114402Sru} 924114402Sru 925114402Sruvoid block_object::move_by(const position &a) 926114402Sru{ 927114402Sru cent += a; 928114402Sru for (object *p = oblist.head; p; p = p->next) 929114402Sru p->move_by(a); 930114402Sru adjust_objectless_places(tbl, a); 931114402Sru} 932114402Sru 933114402Sru 934114402Sruplace *block_object::find_label(const char *name) 935114402Sru{ 936114402Sru return tbl->lookup(name); 937114402Sru} 938114402Sru 939114402Sruobject_type block_object::type() 940114402Sru{ 941114402Sru return BLOCK_OBJECT; 942114402Sru} 943114402Sru 944114402Srugraphic_object *object_spec::make_block(position *curpos, direction *dirp) 945114402Sru{ 946114402Sru bounding_box bb; 947114402Sru for (object *p = oblist.head; p; p = p->next) 948114402Sru p->update_bounding_box(&bb); 949114402Sru position dim; 950114402Sru if (!bb.blank) { 951114402Sru position m = -(bb.ll + bb.ur)/2.0; 952114402Sru for (object *p = oblist.head; p; p = p->next) 953114402Sru p->move_by(m); 954114402Sru adjust_objectless_places(tbl, m); 955114402Sru dim = bb.ur - bb.ll; 956114402Sru } 957114402Sru if (flags & HAS_WIDTH) 958114402Sru dim.x = width; 959114402Sru if (flags & HAS_HEIGHT) 960114402Sru dim.y = height; 961114402Sru block_object *block = new block_object(dim, oblist, tbl); 962114402Sru if (!position_rectangle(block, curpos, dirp)) { 963114402Sru delete block; 964114402Sru block = 0; 965114402Sru } 966114402Sru tbl = 0; 967114402Sru oblist.head = oblist.tail = 0; 968114402Sru return block; 969114402Sru} 970114402Sru 971114402Sruclass text_object : public rectangle_object { 972114402Srupublic: 973114402Sru text_object(const position &); 974114402Sru object_type type() { return TEXT_OBJECT; } 975114402Sru}; 976114402Sru 977114402Srutext_object::text_object(const position &d) 978114402Sru: rectangle_object(d) 979114402Sru{ 980114402Sru} 981114402Sru 982114402Srugraphic_object *object_spec::make_text(position *curpos, direction *dirp) 983114402Sru{ 984114402Sru if (!(flags & HAS_HEIGHT)) { 985114402Sru lookup_variable("textht", &height); 986114402Sru int nitems = 0; 987114402Sru for (text_item *t = text; t; t = t->next) 988114402Sru nitems++; 989114402Sru height *= nitems; 990114402Sru } 991114402Sru if (!(flags & HAS_WIDTH)) 992114402Sru lookup_variable("textwid", &width); 993114402Sru text_object *p = new text_object(position(width, height)); 994114402Sru if (!position_rectangle(p, curpos, dirp)) { 995114402Sru delete p; 996114402Sru p = 0; 997114402Sru } 998114402Sru return p; 999114402Sru} 1000114402Sru 1001114402Sru 1002114402Sruclass ellipse_object : public closed_object { 1003114402Srupublic: 1004114402Sru ellipse_object(const position &); 1005114402Sru position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0), 1006114402Sru cent.y + dim.y/(M_SQRT2*2.0)); } 1007114402Sru position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0), 1008114402Sru cent.y + dim.y/(M_SQRT2*2.0)); } 1009114402Sru position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0), 1010114402Sru cent.y - dim.y/(M_SQRT2*2.0)); } 1011114402Sru position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0), 1012114402Sru cent.y - dim.y/(M_SQRT2*2.0)); } 1013114402Sru double radius() { return dim.x/2.0; } 1014114402Sru object_type type() { return ELLIPSE_OBJECT; } 1015114402Sru void print(); 1016114402Sru}; 1017114402Sru 1018114402Sruellipse_object::ellipse_object(const position &d) 1019114402Sru: closed_object(d) 1020114402Sru{ 1021114402Sru} 1022114402Sru 1023114402Sruvoid ellipse_object::print() 1024114402Sru{ 1025114402Sru if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0) 1026114402Sru return; 1027114402Sru out->set_color(color_fill, graphic_object::get_outline_color()); 1028114402Sru out->ellipse(cent, dim, lt, fill); 1029114402Sru out->reset_color(); 1030114402Sru} 1031114402Sru 1032114402Srugraphic_object *object_spec::make_ellipse(position *curpos, direction *dirp) 1033114402Sru{ 1034114402Sru static double last_ellipse_height; 1035114402Sru static double last_ellipse_width; 1036114402Sru static int have_last_ellipse = 0; 1037114402Sru if (!(flags & HAS_HEIGHT)) { 1038114402Sru if ((flags & IS_SAME) && have_last_ellipse) 1039114402Sru height = last_ellipse_height; 1040114402Sru else 1041114402Sru lookup_variable("ellipseht", &height); 1042114402Sru } 1043114402Sru if (!(flags & HAS_WIDTH)) { 1044114402Sru if ((flags & IS_SAME) && have_last_ellipse) 1045114402Sru width = last_ellipse_width; 1046114402Sru else 1047114402Sru lookup_variable("ellipsewid", &width); 1048114402Sru } 1049114402Sru last_ellipse_width = width; 1050114402Sru last_ellipse_height = height; 1051114402Sru have_last_ellipse = 1; 1052114402Sru ellipse_object *p = new ellipse_object(position(width, height)); 1053114402Sru if (!position_rectangle(p, curpos, dirp)) { 1054114402Sru delete p; 1055114402Sru return 0; 1056114402Sru } 1057114402Sru return p; 1058114402Sru} 1059114402Sru 1060114402Sruclass circle_object : public ellipse_object { 1061114402Srupublic: 1062114402Sru circle_object(double); 1063114402Sru object_type type() { return CIRCLE_OBJECT; } 1064114402Sru void print(); 1065114402Sru}; 1066114402Sru 1067114402Srucircle_object::circle_object(double diam) 1068114402Sru: ellipse_object(position(diam, diam)) 1069114402Sru{ 1070114402Sru} 1071114402Sru 1072114402Sruvoid circle_object::print() 1073114402Sru{ 1074114402Sru if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0) 1075114402Sru return; 1076114402Sru out->set_color(color_fill, graphic_object::get_outline_color()); 1077114402Sru out->circle(cent, dim.x/2.0, lt, fill); 1078114402Sru out->reset_color(); 1079114402Sru} 1080114402Sru 1081114402Srugraphic_object *object_spec::make_circle(position *curpos, direction *dirp) 1082114402Sru{ 1083114402Sru static double last_circle_radius; 1084114402Sru static int have_last_circle = 0; 1085114402Sru if (!(flags & HAS_RADIUS)) { 1086114402Sru if ((flags & IS_SAME) && have_last_circle) 1087114402Sru radius = last_circle_radius; 1088114402Sru else 1089114402Sru lookup_variable("circlerad", &radius); 1090114402Sru } 1091114402Sru last_circle_radius = radius; 1092114402Sru have_last_circle = 1; 1093114402Sru circle_object *p = new circle_object(radius*2.0); 1094114402Sru if (!position_rectangle(p, curpos, dirp)) { 1095114402Sru delete p; 1096114402Sru return 0; 1097114402Sru } 1098114402Sru return p; 1099114402Sru} 1100114402Sru 1101114402Sruclass move_object : public graphic_object { 1102114402Sru position strt; 1103114402Sru position en; 1104114402Srupublic: 1105114402Sru move_object(const position &s, const position &e); 1106114402Sru position origin() { return en; } 1107114402Sru object_type type() { return MOVE_OBJECT; } 1108114402Sru void update_bounding_box(bounding_box *); 1109114402Sru void move_by(const position &); 1110114402Sru}; 1111114402Sru 1112114402Srumove_object::move_object(const position &s, const position &e) 1113114402Sru: strt(s), en(e) 1114114402Sru{ 1115114402Sru} 1116114402Sru 1117114402Sruvoid move_object::update_bounding_box(bounding_box *p) 1118114402Sru{ 1119114402Sru p->encompass(strt); 1120114402Sru p->encompass(en); 1121114402Sru} 1122114402Sru 1123114402Sruvoid move_object::move_by(const position &a) 1124114402Sru{ 1125114402Sru strt += a; 1126114402Sru en += a; 1127114402Sru} 1128114402Sru 1129114402Srugraphic_object *object_spec::make_move(position *curpos, direction *dirp) 1130114402Sru{ 1131114402Sru static position last_move; 1132114402Sru static int have_last_move = 0; 1133114402Sru *dirp = dir; 1134114402Sru // No need to look at at since `at' attribute sets `from' attribute. 1135114402Sru position startpos = (flags & HAS_FROM) ? from : *curpos; 1136114402Sru if (!(flags & HAS_SEGMENT)) { 1137114402Sru if ((flags & IS_SAME) && have_last_move) 1138114402Sru segment_pos = last_move; 1139114402Sru else { 1140114402Sru switch (dir) { 1141114402Sru case UP_DIRECTION: 1142114402Sru segment_pos.y = segment_height; 1143114402Sru break; 1144114402Sru case DOWN_DIRECTION: 1145114402Sru segment_pos.y = -segment_height; 1146114402Sru break; 1147114402Sru case LEFT_DIRECTION: 1148114402Sru segment_pos.x = -segment_width; 1149114402Sru break; 1150114402Sru case RIGHT_DIRECTION: 1151114402Sru segment_pos.x = segment_width; 1152114402Sru break; 1153114402Sru default: 1154114402Sru assert(0); 1155114402Sru } 1156114402Sru } 1157114402Sru } 1158114402Sru segment_list = new segment(segment_pos, segment_is_absolute, segment_list); 1159114402Sru // Reverse the segment_list so that it's in forward order. 1160114402Sru segment *old = segment_list; 1161114402Sru segment_list = 0; 1162114402Sru while (old != 0) { 1163114402Sru segment *tem = old->next; 1164114402Sru old->next = segment_list; 1165114402Sru segment_list = old; 1166114402Sru old = tem; 1167114402Sru } 1168114402Sru // Compute the end position. 1169114402Sru position endpos = startpos; 1170114402Sru for (segment *s = segment_list; s; s = s->next) 1171114402Sru if (s->is_absolute) 1172114402Sru endpos = s->pos; 1173114402Sru else 1174114402Sru endpos += s->pos; 1175114402Sru have_last_move = 1; 1176114402Sru last_move = endpos - startpos; 1177114402Sru move_object *p = new move_object(startpos, endpos); 1178114402Sru *curpos = endpos; 1179114402Sru return p; 1180114402Sru} 1181114402Sru 1182114402Sruclass linear_object : public graphic_object { 1183114402Sruprotected: 1184114402Sru char arrow_at_start; 1185114402Sru char arrow_at_end; 1186114402Sru arrow_head_type aht; 1187114402Sru position strt; 1188114402Sru position en; 1189114402Srupublic: 1190114402Sru linear_object(const position &s, const position &e); 1191114402Sru position start() { return strt; } 1192114402Sru position end() { return en; } 1193114402Sru void move_by(const position &); 1194114402Sru void update_bounding_box(bounding_box *) = 0; 1195114402Sru object_type type() = 0; 1196114402Sru void add_arrows(int at_start, int at_end, const arrow_head_type &); 1197114402Sru}; 1198114402Sru 1199114402Sruclass line_object : public linear_object { 1200114402Sruprotected: 1201114402Sru position *v; 1202114402Sru int n; 1203114402Srupublic: 1204114402Sru line_object(const position &s, const position &e, position *, int); 1205114402Sru ~line_object(); 1206114402Sru position origin() { return strt; } 1207114402Sru position center() { return (strt + en)/2.0; } 1208114402Sru position north() { return (en.y - strt.y) > 0 ? en : strt; } 1209114402Sru position south() { return (en.y - strt.y) < 0 ? en : strt; } 1210114402Sru position east() { return (en.x - strt.x) > 0 ? en : strt; } 1211114402Sru position west() { return (en.x - strt.x) < 0 ? en : strt; } 1212114402Sru object_type type() { return LINE_OBJECT; } 1213114402Sru void update_bounding_box(bounding_box *); 1214114402Sru void print(); 1215114402Sru void move_by(const position &); 1216114402Sru}; 1217114402Sru 1218114402Sruclass arrow_object : public line_object { 1219114402Srupublic: 1220114402Sru arrow_object(const position &, const position &, position *, int); 1221114402Sru object_type type() { return ARROW_OBJECT; } 1222114402Sru}; 1223114402Sru 1224114402Sruclass spline_object : public line_object { 1225114402Srupublic: 1226114402Sru spline_object(const position &, const position &, position *, int); 1227114402Sru object_type type() { return SPLINE_OBJECT; } 1228114402Sru void print(); 1229114402Sru void update_bounding_box(bounding_box *); 1230114402Sru}; 1231114402Sru 1232114402Srulinear_object::linear_object(const position &s, const position &e) 1233114402Sru: arrow_at_start(0), arrow_at_end(0), strt(s), en(e) 1234114402Sru{ 1235114402Sru} 1236114402Sru 1237114402Sruvoid linear_object::move_by(const position &a) 1238114402Sru{ 1239114402Sru strt += a; 1240114402Sru en += a; 1241114402Sru} 1242114402Sru 1243114402Sruvoid linear_object::add_arrows(int at_start, int at_end, 1244114402Sru const arrow_head_type &a) 1245114402Sru{ 1246114402Sru arrow_at_start = at_start; 1247114402Sru arrow_at_end = at_end; 1248114402Sru aht = a; 1249114402Sru} 1250114402Sru 1251114402Sruline_object::line_object(const position &s, const position &e, 1252114402Sru position *p, int i) 1253114402Sru: linear_object(s, e), v(p), n(i) 1254114402Sru{ 1255114402Sru} 1256114402Sru 1257114402Sruvoid line_object::print() 1258114402Sru{ 1259114402Sru if (lt.type == line_type::invisible) 1260114402Sru return; 1261114402Sru out->set_color(0, graphic_object::get_outline_color()); 1262151497Sru // shorten line length to avoid arrow sticking. 1263151497Sru position sp = strt; 1264151497Sru if (arrow_at_start) { 1265151497Sru position base = v[0] - strt; 1266151497Sru double hyp = hypot(base); 1267151497Sru if (hyp == 0.0) { 1268151497Sru error("cannot draw arrow on object with zero length"); 1269151497Sru return; 1270151497Sru } 1271151497Sru if (aht.solid && out->supports_filled_polygons()) { 1272151497Sru base *= aht.height / hyp; 1273151497Sru draw_arrow(strt, strt - v[0], aht, lt, 1274151497Sru graphic_object::get_outline_color()); 1275151497Sru sp = strt + base; 1276151497Sru } else { 1277151497Sru base *= fabs(lt.thickness) / hyp / 72 / 4; 1278151497Sru sp = strt + base; 1279151497Sru draw_arrow(sp, sp - v[0], aht, lt, 1280151497Sru graphic_object::get_outline_color()); 1281151497Sru } 1282151497Sru } 1283151497Sru if (arrow_at_end) { 1284151497Sru position base = v[n-1] - (n > 1 ? v[n-2] : strt); 1285151497Sru double hyp = hypot(base); 1286151497Sru if (hyp == 0.0) { 1287151497Sru error("cannot draw arrow on object with zero length"); 1288151497Sru return; 1289151497Sru } 1290151497Sru if (aht.solid && out->supports_filled_polygons()) { 1291151497Sru base *= aht.height / hyp; 1292151497Sru draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt, 1293151497Sru graphic_object::get_outline_color()); 1294151497Sru v[n-1] = en - base; 1295151497Sru } else { 1296151497Sru base *= fabs(lt.thickness) / hyp / 72 / 4; 1297151497Sru v[n-1] = en - base; 1298151497Sru draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt, 1299151497Sru graphic_object::get_outline_color()); 1300151497Sru } 1301151497Sru } 1302151497Sru out->line(sp, v, n, lt); 1303114402Sru out->reset_color(); 1304114402Sru} 1305114402Sru 1306114402Sruvoid line_object::update_bounding_box(bounding_box *p) 1307114402Sru{ 1308114402Sru p->encompass(strt); 1309114402Sru for (int i = 0; i < n; i++) 1310114402Sru p->encompass(v[i]); 1311114402Sru} 1312114402Sru 1313114402Sruvoid line_object::move_by(const position &pos) 1314114402Sru{ 1315114402Sru linear_object::move_by(pos); 1316114402Sru for (int i = 0; i < n; i++) 1317114402Sru v[i] += pos; 1318114402Sru} 1319114402Sru 1320114402Sruvoid spline_object::update_bounding_box(bounding_box *p) 1321114402Sru{ 1322114402Sru p->encompass(strt); 1323114402Sru p->encompass(en); 1324114402Sru /* 1325114402Sru 1326114402Sru If 1327114402Sru 1328114402Sru p1 = q1/2 + q2/2 1329114402Sru p2 = q1/6 + q2*5/6 1330114402Sru p3 = q2*5/6 + q3/6 1331114402Sru p4 = q2/2 + q3/2 1332114402Sru [ the points for the Bezier cubic ] 1333114402Sru 1334114402Sru and 1335114402Sru 1336114402Sru t = .5 1337114402Sru 1338114402Sru then 1339114402Sru 1340114402Sru (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4 1341114402Sru [ the equation for the Bezier cubic ] 1342114402Sru 1343114402Sru = .125*q1 + .75*q2 + .125*q3 1344114402Sru 1345114402Sru */ 1346114402Sru for (int i = 1; i < n; i++) 1347114402Sru p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125); 1348114402Sru} 1349114402Sru 1350114402Sruarrow_object::arrow_object(const position &s, const position &e, 1351114402Sru position *p, int i) 1352114402Sru: line_object(s, e, p, i) 1353114402Sru{ 1354114402Sru} 1355114402Sru 1356114402Sruspline_object::spline_object(const position &s, const position &e, 1357114402Sru position *p, int i) 1358114402Sru: line_object(s, e, p, i) 1359114402Sru{ 1360114402Sru} 1361114402Sru 1362114402Sruvoid spline_object::print() 1363114402Sru{ 1364114402Sru if (lt.type == line_type::invisible) 1365114402Sru return; 1366114402Sru out->set_color(0, graphic_object::get_outline_color()); 1367151497Sru // shorten line length for spline to avoid arrow sticking 1368151497Sru position sp = strt; 1369151497Sru if (arrow_at_start) { 1370151497Sru position base = v[0] - strt; 1371151497Sru double hyp = hypot(base); 1372151497Sru if (hyp == 0.0) { 1373151497Sru error("cannot draw arrow on object with zero length"); 1374151497Sru return; 1375151497Sru } 1376151497Sru if (aht.solid && out->supports_filled_polygons()) { 1377151497Sru base *= aht.height / hyp; 1378151497Sru draw_arrow(strt, strt - v[0], aht, lt, 1379151497Sru graphic_object::get_outline_color()); 1380151497Sru sp = strt + base*0.1; // to reserve spline shape 1381151497Sru } else { 1382151497Sru base *= fabs(lt.thickness) / hyp / 72 / 4; 1383151497Sru sp = strt + base; 1384151497Sru draw_arrow(sp, sp - v[0], aht, lt, 1385151497Sru graphic_object::get_outline_color()); 1386151497Sru } 1387151497Sru } 1388151497Sru if (arrow_at_end) { 1389151497Sru position base = v[n-1] - (n > 1 ? v[n-2] : strt); 1390151497Sru double hyp = hypot(base); 1391151497Sru if (hyp == 0.0) { 1392151497Sru error("cannot draw arrow on object with zero length"); 1393151497Sru return; 1394151497Sru } 1395151497Sru if (aht.solid && out->supports_filled_polygons()) { 1396151497Sru base *= aht.height / hyp; 1397151497Sru draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt, 1398151497Sru graphic_object::get_outline_color()); 1399151497Sru v[n-1] = en - base*0.1; // to reserve spline shape 1400151497Sru } else { 1401151497Sru base *= fabs(lt.thickness) / hyp / 72 / 4; 1402151497Sru v[n-1] = en - base; 1403151497Sru draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt, 1404151497Sru graphic_object::get_outline_color()); 1405151497Sru } 1406151497Sru } 1407151497Sru out->spline(sp, v, n, lt); 1408114402Sru out->reset_color(); 1409114402Sru} 1410114402Sru 1411114402Sruline_object::~line_object() 1412114402Sru{ 1413114402Sru a_delete v; 1414114402Sru} 1415114402Sru 1416114402Srulinear_object *object_spec::make_line(position *curpos, direction *dirp) 1417114402Sru{ 1418114402Sru static position last_line; 1419114402Sru static int have_last_line = 0; 1420114402Sru *dirp = dir; 1421114402Sru // No need to look at at since `at' attribute sets `from' attribute. 1422114402Sru position startpos = (flags & HAS_FROM) ? from : *curpos; 1423114402Sru if (!(flags & HAS_SEGMENT)) { 1424114402Sru if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT) 1425114402Sru && have_last_line) 1426114402Sru segment_pos = last_line; 1427114402Sru else 1428114402Sru switch (dir) { 1429114402Sru case UP_DIRECTION: 1430114402Sru segment_pos.y = segment_height; 1431114402Sru break; 1432114402Sru case DOWN_DIRECTION: 1433114402Sru segment_pos.y = -segment_height; 1434114402Sru break; 1435114402Sru case LEFT_DIRECTION: 1436114402Sru segment_pos.x = -segment_width; 1437114402Sru break; 1438114402Sru case RIGHT_DIRECTION: 1439114402Sru segment_pos.x = segment_width; 1440114402Sru break; 1441114402Sru default: 1442114402Sru assert(0); 1443114402Sru } 1444114402Sru } 1445114402Sru segment_list = new segment(segment_pos, segment_is_absolute, segment_list); 1446114402Sru // reverse the segment_list so that it's in forward order 1447114402Sru segment *old = segment_list; 1448114402Sru segment_list = 0; 1449114402Sru while (old != 0) { 1450114402Sru segment *tem = old->next; 1451114402Sru old->next = segment_list; 1452114402Sru segment_list = old; 1453114402Sru old = tem; 1454114402Sru } 1455114402Sru // Absolutise all movements 1456114402Sru position endpos = startpos; 1457114402Sru int nsegments = 0; 1458114402Sru segment *s; 1459114402Sru for (s = segment_list; s; s = s->next, nsegments++) 1460114402Sru if (s->is_absolute) 1461114402Sru endpos = s->pos; 1462114402Sru else { 1463114402Sru endpos += s->pos; 1464114402Sru s->pos = endpos; 1465114402Sru s->is_absolute = 1; // to avoid confusion 1466114402Sru } 1467114402Sru // handle chop 1468114402Sru line_object *p = 0; 1469114402Sru position *v = new position[nsegments]; 1470114402Sru int i = 0; 1471114402Sru for (s = segment_list; s; s = s->next, i++) 1472114402Sru v[i] = s->pos; 1473114402Sru if (flags & IS_DEFAULT_CHOPPED) { 1474114402Sru lookup_variable("circlerad", &start_chop); 1475114402Sru end_chop = start_chop; 1476114402Sru flags |= IS_CHOPPED; 1477114402Sru } 1478114402Sru if (flags & IS_CHOPPED) { 1479114402Sru position start_chop_vec, end_chop_vec; 1480114402Sru if (start_chop != 0.0) { 1481114402Sru start_chop_vec = v[0] - startpos; 1482114402Sru start_chop_vec *= start_chop / hypot(start_chop_vec); 1483114402Sru } 1484114402Sru if (end_chop != 0.0) { 1485114402Sru end_chop_vec = (v[nsegments - 1] 1486114402Sru - (nsegments > 1 ? v[nsegments - 2] : startpos)); 1487114402Sru end_chop_vec *= end_chop / hypot(end_chop_vec); 1488114402Sru } 1489114402Sru startpos += start_chop_vec; 1490114402Sru v[nsegments - 1] -= end_chop_vec; 1491114402Sru endpos -= end_chop_vec; 1492114402Sru } 1493114402Sru switch (type) { 1494114402Sru case SPLINE_OBJECT: 1495114402Sru p = new spline_object(startpos, endpos, v, nsegments); 1496114402Sru break; 1497114402Sru case ARROW_OBJECT: 1498114402Sru p = new arrow_object(startpos, endpos, v, nsegments); 1499114402Sru break; 1500114402Sru case LINE_OBJECT: 1501114402Sru p = new line_object(startpos, endpos, v, nsegments); 1502114402Sru break; 1503114402Sru default: 1504114402Sru assert(0); 1505114402Sru } 1506114402Sru have_last_line = 1; 1507114402Sru last_line = endpos - startpos; 1508114402Sru *curpos = endpos; 1509114402Sru return p; 1510114402Sru} 1511114402Sru 1512114402Sruclass arc_object : public linear_object { 1513114402Sru int clockwise; 1514114402Sru position cent; 1515114402Sru double rad; 1516114402Srupublic: 1517114402Sru arc_object(int, const position &, const position &, const position &); 1518114402Sru position origin() { return cent; } 1519114402Sru position center() { return cent; } 1520114402Sru double radius() { return rad; } 1521114402Sru position north(); 1522114402Sru position south(); 1523114402Sru position east(); 1524114402Sru position west(); 1525114402Sru position north_east(); 1526114402Sru position north_west(); 1527114402Sru position south_east(); 1528114402Sru position south_west(); 1529114402Sru void update_bounding_box(bounding_box *); 1530114402Sru object_type type() { return ARC_OBJECT; } 1531114402Sru void print(); 1532114402Sru void move_by(const position &pos); 1533114402Sru}; 1534114402Sru 1535114402Sruarc_object::arc_object(int cw, const position &s, const position &e, 1536114402Sru const position &c) 1537114402Sru: linear_object(s, e), clockwise(cw), cent(c) 1538114402Sru{ 1539114402Sru rad = hypot(c - s); 1540114402Sru} 1541114402Sru 1542114402Sruvoid arc_object::move_by(const position &pos) 1543114402Sru{ 1544114402Sru linear_object::move_by(pos); 1545114402Sru cent += pos; 1546114402Sru} 1547114402Sru 1548114402Sru// we get arc corners from the corresponding circle 1549114402Sru 1550114402Sruposition arc_object::north() 1551114402Sru{ 1552114402Sru position result(cent); 1553114402Sru result.y += rad; 1554114402Sru return result; 1555114402Sru} 1556114402Sru 1557114402Sruposition arc_object::south() 1558114402Sru{ 1559114402Sru position result(cent); 1560114402Sru result.y -= rad; 1561114402Sru return result; 1562114402Sru} 1563114402Sru 1564114402Sruposition arc_object::east() 1565114402Sru{ 1566114402Sru position result(cent); 1567114402Sru result.x += rad; 1568114402Sru return result; 1569114402Sru} 1570114402Sru 1571114402Sruposition arc_object::west() 1572114402Sru{ 1573114402Sru position result(cent); 1574114402Sru result.x -= rad; 1575114402Sru return result; 1576114402Sru} 1577114402Sru 1578114402Sruposition arc_object::north_east() 1579114402Sru{ 1580114402Sru position result(cent); 1581114402Sru result.x += rad/M_SQRT2; 1582114402Sru result.y += rad/M_SQRT2; 1583114402Sru return result; 1584114402Sru} 1585114402Sru 1586114402Sruposition arc_object::north_west() 1587114402Sru{ 1588114402Sru position result(cent); 1589114402Sru result.x -= rad/M_SQRT2; 1590114402Sru result.y += rad/M_SQRT2; 1591114402Sru return result; 1592114402Sru} 1593114402Sru 1594114402Sruposition arc_object::south_east() 1595114402Sru{ 1596114402Sru position result(cent); 1597114402Sru result.x += rad/M_SQRT2; 1598114402Sru result.y -= rad/M_SQRT2; 1599114402Sru return result; 1600114402Sru} 1601114402Sru 1602114402Sruposition arc_object::south_west() 1603114402Sru{ 1604114402Sru position result(cent); 1605114402Sru result.x -= rad/M_SQRT2; 1606114402Sru result.y -= rad/M_SQRT2; 1607114402Sru return result; 1608114402Sru} 1609114402Sru 1610114402Sru 1611114402Sruvoid arc_object::print() 1612114402Sru{ 1613114402Sru if (lt.type == line_type::invisible) 1614114402Sru return; 1615114402Sru out->set_color(0, graphic_object::get_outline_color()); 1616151497Sru // handle arrow direction; make shorter line for arc 1617151497Sru position sp, ep, b; 1618151497Sru if (clockwise) { 1619151497Sru sp = en; 1620151497Sru ep = strt; 1621151497Sru } else { 1622151497Sru sp = strt; 1623151497Sru ep = en; 1624151497Sru } 1625114402Sru if (arrow_at_start) { 1626151497Sru double theta = aht.height / rad; 1627151497Sru if (clockwise) 1628151497Sru theta = - theta; 1629151497Sru b = strt - cent; 1630151497Sru b = position(b.x*cos(theta) - b.y*sin(theta), 1631151497Sru b.x*sin(theta) + b.y*cos(theta)) + cent; 1632151497Sru if (clockwise) 1633151497Sru ep = b; 1634151497Sru else 1635151497Sru sp = b; 1636151497Sru if (aht.solid && out->supports_filled_polygons()) { 1637151497Sru draw_arrow(strt, strt - b, aht, lt, 1638151497Sru graphic_object::get_outline_color()); 1639151497Sru } else { 1640151497Sru position v = b; 1641151497Sru theta = fabs(lt.thickness) / 72 / 4 / rad; 1642151497Sru if (clockwise) 1643151497Sru theta = - theta; 1644151497Sru b = strt - cent; 1645151497Sru b = position(b.x*cos(theta) - b.y*sin(theta), 1646151497Sru b.x*sin(theta) + b.y*cos(theta)) + cent; 1647151497Sru draw_arrow(b, b - v, aht, lt, 1648151497Sru graphic_object::get_outline_color()); 1649151497Sru out->line(b, &v, 1, lt); 1650151497Sru } 1651114402Sru } 1652114402Sru if (arrow_at_end) { 1653151497Sru double theta = aht.height / rad; 1654151497Sru if (!clockwise) 1655151497Sru theta = - theta; 1656151497Sru b = en - cent; 1657151497Sru b = position(b.x*cos(theta) - b.y*sin(theta), 1658151497Sru b.x*sin(theta) + b.y*cos(theta)) + cent; 1659151497Sru if (clockwise) 1660151497Sru sp = b; 1661151497Sru else 1662151497Sru ep = b; 1663151497Sru if (aht.solid && out->supports_filled_polygons()) { 1664151497Sru draw_arrow(en, en - b, aht, lt, 1665151497Sru graphic_object::get_outline_color()); 1666151497Sru } else { 1667151497Sru position v = b; 1668151497Sru theta = fabs(lt.thickness) / 72 / 4 / rad; 1669151497Sru if (!clockwise) 1670151497Sru theta = - theta; 1671151497Sru b = en - cent; 1672151497Sru b = position(b.x*cos(theta) - b.y*sin(theta), 1673151497Sru b.x*sin(theta) + b.y*cos(theta)) + cent; 1674151497Sru draw_arrow(b, b - v, aht, lt, 1675151497Sru graphic_object::get_outline_color()); 1676151497Sru out->line(b, &v, 1, lt); 1677151497Sru } 1678114402Sru } 1679151497Sru out->arc(sp, cent, ep, lt); 1680114402Sru out->reset_color(); 1681114402Sru} 1682114402Sru 1683114402Sruinline double max(double a, double b) 1684114402Sru{ 1685114402Sru return a > b ? a : b; 1686114402Sru} 1687114402Sru 1688114402Sruvoid arc_object::update_bounding_box(bounding_box *p) 1689114402Sru{ 1690114402Sru p->encompass(strt); 1691114402Sru p->encompass(en); 1692114402Sru position start_offset = strt - cent; 1693114402Sru if (start_offset.x == 0.0 && start_offset.y == 0.0) 1694114402Sru return; 1695114402Sru position end_offset = en - cent; 1696114402Sru if (end_offset.x == 0.0 && end_offset.y == 0.0) 1697114402Sru return; 1698114402Sru double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0); 1699114402Sru double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0); 1700114402Sru if (clockwise) { 1701114402Sru double temp = start_quad; 1702114402Sru start_quad = end_quad; 1703114402Sru end_quad = temp; 1704114402Sru } 1705114402Sru if (start_quad < 0.0) 1706114402Sru start_quad += 4.0; 1707114402Sru while (end_quad <= start_quad) 1708114402Sru end_quad += 4.0; 1709151497Sru double r = max(hypot(start_offset), hypot(end_offset)); 1710114402Sru for (int q = int(start_quad) + 1; q < end_quad; q++) { 1711114402Sru position offset; 1712114402Sru switch (q % 4) { 1713114402Sru case 0: 1714151497Sru offset.x = r; 1715114402Sru break; 1716114402Sru case 1: 1717151497Sru offset.y = r; 1718114402Sru break; 1719114402Sru case 2: 1720151497Sru offset.x = -r; 1721114402Sru break; 1722114402Sru case 3: 1723151497Sru offset.y = -r; 1724114402Sru break; 1725114402Sru } 1726114402Sru p->encompass(cent + offset); 1727114402Sru } 1728114402Sru} 1729114402Sru 1730114402Sru// We ignore the with attribute. The at attribute always refers to the center. 1731114402Sru 1732114402Srulinear_object *object_spec::make_arc(position *curpos, direction *dirp) 1733114402Sru{ 1734114402Sru *dirp = dir; 1735114402Sru int cw = (flags & IS_CLOCKWISE) != 0; 1736114402Sru // compute the start 1737114402Sru position startpos; 1738114402Sru if (flags & HAS_FROM) 1739114402Sru startpos = from; 1740114402Sru else 1741114402Sru startpos = *curpos; 1742114402Sru if (!(flags & HAS_RADIUS)) 1743114402Sru lookup_variable("arcrad", &radius); 1744114402Sru // compute the end 1745114402Sru position endpos; 1746114402Sru if (flags & HAS_TO) 1747114402Sru endpos = to; 1748114402Sru else { 1749114402Sru position m(radius, radius); 1750114402Sru // Adjust the signs. 1751114402Sru if (cw) { 1752114402Sru if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION) 1753114402Sru m.x = -m.x; 1754114402Sru if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION) 1755114402Sru m.y = -m.y; 1756114402Sru *dirp = direction((dir + 3) % 4); 1757114402Sru } 1758114402Sru else { 1759114402Sru if (dir == UP_DIRECTION || dir == LEFT_DIRECTION) 1760114402Sru m.x = -m.x; 1761114402Sru if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION) 1762114402Sru m.y = -m.y; 1763114402Sru *dirp = direction((dir + 1) % 4); 1764114402Sru } 1765114402Sru endpos = startpos + m; 1766114402Sru } 1767114402Sru // compute the center 1768114402Sru position centerpos; 1769114402Sru if (flags & HAS_AT) 1770114402Sru centerpos = at; 1771114402Sru else if (startpos == endpos) 1772114402Sru centerpos = startpos; 1773114402Sru else { 1774114402Sru position h = (endpos - startpos)/2.0; 1775114402Sru double d = hypot(h); 1776114402Sru if (radius <= 0) 1777114402Sru radius = .25; 1778114402Sru // make the radius big enough 1779114402Sru while (radius < d) 1780114402Sru radius *= 2.0; 1781114402Sru double alpha = acos(d/radius); 1782114402Sru double theta = atan2(h.y, h.x); 1783114402Sru if (cw) 1784114402Sru theta -= alpha; 1785114402Sru else 1786114402Sru theta += alpha; 1787114402Sru centerpos = position(cos(theta), sin(theta))*radius + startpos; 1788114402Sru } 1789114402Sru arc_object *p = new arc_object(cw, startpos, endpos, centerpos); 1790114402Sru *curpos = endpos; 1791114402Sru return p; 1792114402Sru} 1793114402Sru 1794114402Srugraphic_object *object_spec::make_linear(position *curpos, direction *dirp) 1795114402Sru{ 1796114402Sru linear_object *obj; 1797114402Sru if (type == ARC_OBJECT) 1798114402Sru obj = make_arc(curpos, dirp); 1799114402Sru else 1800114402Sru obj = make_line(curpos, dirp); 1801114402Sru if (type == ARROW_OBJECT 1802114402Sru && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0) 1803114402Sru flags |= HAS_RIGHT_ARROW_HEAD; 1804114402Sru if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) { 1805114402Sru arrow_head_type a; 1806114402Sru int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0; 1807114402Sru int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0; 1808114402Sru if (flags & HAS_HEIGHT) 1809114402Sru a.height = height; 1810114402Sru else 1811114402Sru lookup_variable("arrowht", &a.height); 1812114402Sru if (flags & HAS_WIDTH) 1813114402Sru a.width = width; 1814114402Sru else 1815114402Sru lookup_variable("arrowwid", &a.width); 1816114402Sru double solid; 1817114402Sru lookup_variable("arrowhead", &solid); 1818114402Sru a.solid = solid != 0.0; 1819114402Sru obj->add_arrows(at_start, at_end, a); 1820114402Sru } 1821114402Sru return obj; 1822114402Sru} 1823114402Sru 1824114402Sruobject *object_spec::make_object(position *curpos, direction *dirp) 1825114402Sru{ 1826114402Sru graphic_object *obj = 0; 1827114402Sru switch (type) { 1828114402Sru case BLOCK_OBJECT: 1829114402Sru obj = make_block(curpos, dirp); 1830114402Sru break; 1831114402Sru case BOX_OBJECT: 1832114402Sru obj = make_box(curpos, dirp); 1833114402Sru break; 1834114402Sru case TEXT_OBJECT: 1835114402Sru obj = make_text(curpos, dirp); 1836114402Sru break; 1837114402Sru case ELLIPSE_OBJECT: 1838114402Sru obj = make_ellipse(curpos, dirp); 1839114402Sru break; 1840114402Sru case CIRCLE_OBJECT: 1841114402Sru obj = make_circle(curpos, dirp); 1842114402Sru break; 1843114402Sru case MOVE_OBJECT: 1844114402Sru obj = make_move(curpos, dirp); 1845114402Sru break; 1846114402Sru case ARC_OBJECT: 1847114402Sru case LINE_OBJECT: 1848114402Sru case SPLINE_OBJECT: 1849114402Sru case ARROW_OBJECT: 1850114402Sru obj = make_linear(curpos, dirp); 1851114402Sru break; 1852114402Sru case MARK_OBJECT: 1853114402Sru case OTHER_OBJECT: 1854114402Sru default: 1855114402Sru assert(0); 1856114402Sru break; 1857114402Sru } 1858114402Sru if (obj) { 1859114402Sru if (flags & IS_INVISIBLE) 1860114402Sru obj->set_invisible(); 1861114402Sru if (text != 0) 1862114402Sru obj->add_text(text, (flags & IS_ALIGNED) != 0); 1863114402Sru if (flags & IS_DOTTED) 1864114402Sru obj->set_dotted(dash_width); 1865114402Sru else if (flags & IS_DASHED) 1866114402Sru obj->set_dashed(dash_width); 1867114402Sru double th; 1868114402Sru if (flags & HAS_THICKNESS) 1869114402Sru th = thickness; 1870114402Sru else 1871114402Sru lookup_variable("linethick", &th); 1872114402Sru obj->set_thickness(th); 1873114402Sru if (flags & IS_OUTLINED) 1874114402Sru obj->set_outline_color(outlined); 1875151497Sru if (flags & (IS_DEFAULT_FILLED | IS_FILLED)) { 1876114402Sru if (flags & IS_SHADED) 1877114402Sru obj->set_fill_color(shaded); 1878114402Sru else { 1879114402Sru if (flags & IS_DEFAULT_FILLED) 1880114402Sru lookup_variable("fillval", &fill); 1881114402Sru if (fill < 0.0) 1882114402Sru error("bad fill value %1", fill); 1883114402Sru else 1884114402Sru obj->set_fill(fill); 1885114402Sru } 1886114402Sru } 1887114402Sru } 1888114402Sru return obj; 1889114402Sru} 1890114402Sru 1891114402Srustruct string_list { 1892114402Sru string_list *next; 1893114402Sru char *str; 1894114402Sru string_list(char *); 1895114402Sru ~string_list(); 1896114402Sru}; 1897114402Sru 1898114402Srustring_list::string_list(char *s) 1899114402Sru: next(0), str(s) 1900114402Sru{ 1901114402Sru} 1902114402Sru 1903114402Srustring_list::~string_list() 1904114402Sru{ 1905114402Sru a_delete str; 1906114402Sru} 1907114402Sru 1908114402Sru/* A path is used to hold the argument to the `with' attribute. For 1909114402Sru example, `.nw' or `.A.s' or `.A'. The major operation on a path is to 1910114402Sru take a place and follow the path through the place to place within the 1911114402Sru place. Note that `.A.B.C.sw' will work. 1912114402Sru 1913114402Sru For compatibility with DWB pic, `with' accepts positions also (this 1914114402Sru is incorrectly documented in CSTR 116). */ 1915114402Sru 1916114402Srupath::path(corner c) 1917114402Sru: crn(c), label_list(0), ypath(0), is_position(0) 1918114402Sru{ 1919114402Sru} 1920114402Sru 1921114402Srupath::path(position p) 1922114402Sru: crn(0), label_list(0), ypath(0), is_position(1) 1923114402Sru{ 1924114402Sru pos.x = p.x; 1925114402Sru pos.y = p.y; 1926114402Sru} 1927114402Sru 1928114402Srupath::path(char *l, corner c) 1929114402Sru: crn(c), ypath(0), is_position(0) 1930114402Sru{ 1931114402Sru label_list = new string_list(l); 1932114402Sru} 1933114402Sru 1934114402Srupath::~path() 1935114402Sru{ 1936114402Sru while (label_list) { 1937114402Sru string_list *tem = label_list; 1938114402Sru label_list = label_list->next; 1939114402Sru delete tem; 1940114402Sru } 1941114402Sru delete ypath; 1942114402Sru} 1943114402Sru 1944114402Sruvoid path::append(corner c) 1945114402Sru{ 1946114402Sru assert(crn == 0); 1947114402Sru crn = c; 1948114402Sru} 1949114402Sru 1950114402Sruvoid path::append(char *s) 1951114402Sru{ 1952114402Sru string_list **p; 1953114402Sru for (p = &label_list; *p; p = &(*p)->next) 1954114402Sru ; 1955114402Sru *p = new string_list(s); 1956114402Sru} 1957114402Sru 1958114402Sruvoid path::set_ypath(path *p) 1959114402Sru{ 1960114402Sru ypath = p; 1961114402Sru} 1962114402Sru 1963114402Sru// return non-zero for success 1964114402Sru 1965114402Sruint path::follow(const place &pl, place *result) const 1966114402Sru{ 1967114402Sru if (is_position) { 1968114402Sru result->x = pos.x; 1969114402Sru result->y = pos.y; 1970114402Sru result->obj = 0; 1971114402Sru return 1; 1972114402Sru } 1973114402Sru const place *p = &pl; 1974114402Sru for (string_list *lb = label_list; lb; lb = lb->next) 1975114402Sru if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) { 1976114402Sru lex_error("object does not contain a place `%1'", lb->str); 1977114402Sru return 0; 1978114402Sru } 1979114402Sru if (crn == 0 || p->obj == 0) 1980114402Sru *result = *p; 1981114402Sru else { 1982114402Sru position ps = ((p->obj)->*(crn))(); 1983114402Sru result->x = ps.x; 1984114402Sru result->y = ps.y; 1985114402Sru result->obj = 0; 1986114402Sru } 1987114402Sru if (ypath) { 1988114402Sru place tem; 1989114402Sru if (!ypath->follow(pl, &tem)) 1990114402Sru return 0; 1991114402Sru result->y = tem.y; 1992114402Sru if (result->obj != tem.obj) 1993114402Sru result->obj = 0; 1994114402Sru } 1995114402Sru return 1; 1996114402Sru} 1997114402Sru 1998114402Sruvoid print_object_list(object *p) 1999114402Sru{ 2000114402Sru for (; p; p = p->next) { 2001114402Sru p->print(); 2002114402Sru p->print_text(); 2003114402Sru } 2004114402Sru} 2005114402Sru 2006114402Sruvoid print_picture(object *obj) 2007114402Sru{ 2008114402Sru bounding_box bb; 2009114402Sru for (object *p = obj; p; p = p->next) 2010114402Sru p->update_bounding_box(&bb); 2011114402Sru double scale; 2012114402Sru lookup_variable("scale", &scale); 2013114402Sru out->start_picture(scale, bb.ll, bb.ur); 2014114402Sru print_object_list(obj); 2015114402Sru out->finish_picture(); 2016114402Sru} 2017114402Sru 2018