object.cc 40 KB

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