plot.cc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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. // The `plot_output' class, subclassed from the `common_output' class.
  16. // In this class we invoke GNU libplot operations to draw objects.
  17. // If the `precision_dashing' flag is set, we draw some types of object
  18. // (arcs, polygons, circles, rounded boxes) in a special way. The object
  19. // boundary is drawn as a sequence of line segments (if it's to be
  20. // "dashed") or a sequence of filled circles (if it's to be "dotted").
  21. // This is done by invoking e.g. the dashed_arc, dotted_arc, dashed_circle,
  22. // dotted_circle, and rounded_box operations in the `common_output'
  23. // superclass.
  24. // This is the only reason why we subclass from `common_output', rather
  25. // than directly from `output'.
  26. #include "pic.h"
  27. #include "output.h"
  28. #include "common.h"
  29. #include "plot.h" // libplot header file
  30. // Plotter parameter array, set from command line in main.cc
  31. extern plPlotterParams *plotter_params;
  32. // size of graphics display in `virtual inches'
  33. #define DISPLAY_SIZE_IN_INCHES 8.0
  34. #define POINTS_PER_INCH 72.0
  35. // color name array in libplot; undocumented but accessible to programmers
  36. typedef struct
  37. {
  38. const char *name;
  39. unsigned char red;
  40. unsigned char green;
  41. unsigned char blue;
  42. } Colornameinfo;
  43. extern const Colornameinfo _colornames[];
  44. // our libplot driver
  45. class plot_output : public common_output
  46. {
  47. public:
  48. // ctor, dtor
  49. plot_output();
  50. ~plot_output();
  51. // basic interface
  52. void start_picture (double sc, const position &ll, const position &ur);
  53. void finish_picture (void);
  54. // draw objects
  55. void arc (const position &start, const position &cent, const position &end,
  56. const line_type &lt);
  57. void circle (const position &cent, double rad, const line_type &lt,
  58. double fill);
  59. void ellipse (const position &cent, const distance &dim,
  60. const line_type &lt, double fill);
  61. void line (const position &start, const position *v, int n,
  62. const line_type &lt);
  63. void polygon (const position *v, int n,
  64. const line_type &lt, double fill);
  65. void spline (const position &start, const position *v, int n,
  66. const line_type &lt);
  67. void text (const position &center, text_piece *v, int n, double angle);
  68. void rounded_box (const position &cent, const distance &dim,
  69. double rad, const line_type &lt, double fill);
  70. // attribute-querying function
  71. int supports_filled_polygons (void);
  72. private:
  73. // parameters
  74. plPlotter *plotter; // pointer to opaque libplot Plotter object
  75. double default_plotter_line_thickness; // line thickness in virtual points
  76. int pen_red, pen_green, pen_blue; // 48-bit pen color
  77. // dynamic variables, keep track of Plotter drawing state
  78. int plotter_line_type; // one of line_type::solid etc.
  79. int plotter_fill_fraction; // libplot fill fraction
  80. double plotter_line_thickness; // in virtual points
  81. bool plotter_visible_pen; // default is `yes'
  82. bool plotter_path_in_progress; // need to break?
  83. // internal functions, modify Plotter drawing state
  84. void set_line_type_and_thickness (const line_type &lt);
  85. void set_fill (double fill);
  86. void set_pen_visibility (bool visible);
  87. // invoked by common_output dotting methods
  88. void dot (const position &pos, const line_type &lt);
  89. };
  90. output *
  91. make_plot_output()
  92. {
  93. return new plot_output;
  94. }
  95. plot_output::plot_output()
  96. {
  97. if ((plotter = pl_newpl_r (output_format, NULL, stdout, stderr,
  98. plotter_params)) == NULL)
  99. {
  100. fprintf (stderr, "%s: error: could not open plot device\n",
  101. program_name);
  102. exit (EXIT_FAILURE);
  103. }
  104. }
  105. plot_output::~plot_output()
  106. {
  107. pl_deletepl_r (plotter);
  108. }
  109. void
  110. plot_output::start_picture(double sc, const position &ll,
  111. const position &ur)
  112. {
  113. double xcen, ycen, xmin, xmax, ymin, ymax;
  114. double scale;
  115. // open Plotter; record Plotter drawing state defaults
  116. pl_openpl_r (plotter);
  117. plotter_line_type = line_type::solid;
  118. plotter_fill_fraction = 0; // i.e. unfilled
  119. plotter_visible_pen = true;
  120. plotter_path_in_progress = false;
  121. // Compute scale factor via compute_scale() method of output
  122. // class; see object.cc. .PS line may contain desired width/height
  123. // in virtual inches; if so, scale to it. If .PS line doesn't contain
  124. // desired width/height, scale according to the global `scale' variable
  125. // (normally set at top of pic file. But on no account violate
  126. // the bounds maxpswid/maxpsht.
  127. scale = compute_scale(sc, ll, ur);
  128. /* Initialize map from user space to device space, by specifying
  129. rectangle in user space that will be mapped to graphics display in
  130. device space. Possibly choose rectangle so that plot will be
  131. centered on the display. */
  132. if (no_centering_flag)
  133. {
  134. xmin = 0.0;
  135. ymin = 0.0;
  136. }
  137. else // center
  138. {
  139. xcen = 0.5 * (ll.x + ur.x);
  140. ycen = 0.5 * (ll.y + ur.y);
  141. xmin = xcen - 0.5 * DISPLAY_SIZE_IN_INCHES * scale;
  142. ymin = ycen - 0.5 * DISPLAY_SIZE_IN_INCHES * scale;
  143. }
  144. xmax = xmin + DISPLAY_SIZE_IN_INCHES * scale;
  145. ymax = ymin + DISPLAY_SIZE_IN_INCHES * scale;
  146. pl_fspace_r (plotter, xmin, ymin, xmax, ymax);
  147. // clear Plotter of objects; initialize font name
  148. pl_erase_r (plotter);
  149. if (font_name)
  150. pl_fontname_r (plotter, font_name);
  151. // set pen/fill color (will modify later only by invoking pl_filltype_r)
  152. if (pen_color_name)
  153. pl_colorname_r (plotter, pen_color_name);
  154. // initialize font size and line thickness from values that can be set on
  155. // the command line (latter is dynamic, can be altered in pic file)
  156. font_size *= scale;
  157. line_width *= scale;
  158. if (font_size >= 0.0)
  159. // `font size', as set on command line, is in terms of display width,
  160. // but libplot, according to our scaling, uses virtual inches; so we
  161. // convert
  162. pl_ffontsize_r (plotter, DISPLAY_SIZE_IN_INCHES * font_size);
  163. else
  164. // use Plotter default; no need to issue a fontsize() instruction
  165. {
  166. }
  167. if (line_width >= 0.0)
  168. {
  169. // `line_width', as set on command line, is in terms of display
  170. // width, but libplot, according to our scaling, uses virtual inches;
  171. // pic2plot, uses virtual points both internally and in pic scripts
  172. pl_flinewidth_r (plotter, DISPLAY_SIZE_IN_INCHES * line_width);
  173. default_plotter_line_thickness
  174. = DISPLAY_SIZE_IN_INCHES * POINTS_PER_INCH * line_width;
  175. }
  176. else
  177. // use Plotter default, represented internally by pic2plot as -1;
  178. // no need to issue a linewidth() instruction
  179. default_plotter_line_thickness = -1.0;
  180. /* store initial line thickness as a default, for later use */
  181. plotter_line_thickness = default_plotter_line_thickness;
  182. }
  183. void
  184. plot_output::finish_picture()
  185. {
  186. pl_closepl_r (plotter);
  187. }
  188. //////////////////////////////////////////////////////////////////////
  189. // SET PLOTTER DRAWING ATTRIBUTES
  190. //////////////////////////////////////////////////////////////////////
  191. // Manipulate fill color (idempotent, so may not actually do anything,
  192. // i.e. may not break the path in progress, if any).
  193. void
  194. plot_output::set_fill (double fill)
  195. {
  196. int fill_fraction;
  197. if (fill < 0.0)
  198. fill_fraction = 0; // unfilled
  199. else
  200. {
  201. if (fill > 1.0)
  202. fill = 1.0;
  203. /* fill=0.0 is white, fraction=0xffff;
  204. fill=1.0 is solid color, fraction = 1 */
  205. fill_fraction = 0xffff - IROUND(0xfffe * fill);
  206. }
  207. if (fill_fraction != plotter_fill_fraction)
  208. {
  209. // manipulate fill color by setting the fill fraction
  210. pl_filltype_r (plotter, fill_fraction);
  211. plotter_fill_fraction = fill_fraction;
  212. plotter_path_in_progress = false;
  213. }
  214. }
  215. // Set line type (solid/dashed/dotted) and thickness. May not invoke a
  216. // libplot operation if neither needs to be changed, so may not break the
  217. // path in progress (if any).
  218. void
  219. plot_output::set_line_type_and_thickness (const line_type &lt)
  220. {
  221. switch (lt.type)
  222. {
  223. case line_type::solid:
  224. default:
  225. if (plotter_line_type != line_type::solid)
  226. {
  227. pl_linemod_r (plotter, "solid");
  228. plotter_line_type = line_type::solid;
  229. plotter_path_in_progress = false;
  230. }
  231. break;
  232. case line_type::dotted:
  233. if (plotter_line_type != line_type::dotted)
  234. {
  235. double dashbuf[2];
  236. pl_linemod_r (plotter, "dotted");
  237. dashbuf[0] = 0.25 * lt.dash_width;
  238. dashbuf[1] = 0.75 * lt.dash_width;
  239. pl_flinedash_r (plotter, 2, dashbuf, 0.0);
  240. plotter_line_type = line_type::dotted;
  241. plotter_path_in_progress = false;
  242. }
  243. break;
  244. case line_type::dashed:
  245. if (plotter_line_type != line_type::dashed)
  246. {
  247. double dashbuf[2];
  248. pl_linemod_r (plotter, "shortdashed");
  249. dashbuf[0] = dashbuf[1] = lt.dash_width;
  250. pl_flinedash_r (plotter, 2, dashbuf, 0.0);
  251. plotter_line_type = line_type::dashed;
  252. plotter_path_in_progress = false;
  253. }
  254. break;
  255. }
  256. if (lt.thickness != plotter_line_thickness
  257. &&
  258. !(lt.thickness < 0.0 && plotter_line_thickness < 0.0))
  259. // need to change (recall negative thickness means `default')
  260. {
  261. if (lt.thickness < 0)
  262. pl_flinewidth_r (plotter,
  263. default_plotter_line_thickness / POINTS_PER_INCH);
  264. else
  265. pl_flinewidth_r (plotter, lt.thickness / POINTS_PER_INCH);
  266. plotter_line_thickness = lt.thickness;
  267. plotter_path_in_progress = false;
  268. }
  269. }
  270. // Set pen visibility (true/false). This is needed for precision dashing
  271. // around the boundary of any closed object; provided that it is filled, at
  272. // least. When first drawing the closed object itself, pen visibility
  273. // needs to be set to `false'.
  274. void
  275. plot_output::set_pen_visibility (bool visible)
  276. {
  277. if (visible != plotter_visible_pen)
  278. {
  279. if (visible)
  280. pl_pentype_r (plotter, 1);
  281. else
  282. pl_pentype_r (plotter, 0);
  283. plotter_visible_pen = visible;
  284. }
  285. }
  286. //////////////////////////////////////////////////////////////////////
  287. // TEXT
  288. //////////////////////////////////////////////////////////////////////
  289. // Draw a text object.
  290. void
  291. plot_output::text(const position &center, text_piece *v, int n, double angle)
  292. {
  293. int horizontal_adj, vertical_adj;
  294. double line_spacing;
  295. // convert from fraction of width of display, to virtual inches
  296. // also multiply by 1.2 (cf. 10pt with 12pt leading)
  297. line_spacing = 1.2 * (DISPLAY_SIZE_IN_INCHES * font_size);
  298. if (n > 0)
  299. {
  300. pl_ftextangle_r (plotter, 180 * angle / M_PI);
  301. plotter_path_in_progress = false;
  302. set_pen_visibility (true); // libplot may need this
  303. }
  304. for (int i = 0; i < n; i++)
  305. {
  306. pl_fmove_r (plotter,
  307. center.x - (0.5*(n-1) - i) * line_spacing * sin(angle),
  308. center.y + (0.5*(n-1) - i) * line_spacing * cos(angle));
  309. plotter_path_in_progress = false;
  310. switch ((int)(v[i].adj.h))
  311. {
  312. case (int)CENTER_ADJUST:
  313. default:
  314. horizontal_adj = 'c';
  315. break;
  316. case (int)LEFT_ADJUST:
  317. horizontal_adj = 'l';
  318. break;
  319. case (int)RIGHT_ADJUST:
  320. horizontal_adj = 'r';
  321. break;
  322. }
  323. switch ((int)(v[i].adj.v))
  324. {
  325. case (int)NONE_ADJUST:
  326. default:
  327. vertical_adj = 'c';
  328. break;
  329. case (int)ABOVE_ADJUST:
  330. vertical_adj = 'b';
  331. break;
  332. case (int)BELOW_ADJUST:
  333. vertical_adj = 't';
  334. break;
  335. }
  336. pl_alabel_r (plotter, horizontal_adj, vertical_adj, v[i].text);
  337. plotter_path_in_progress = false;
  338. }
  339. }
  340. //////////////////////////////////////////////////////////////////////
  341. // OPEN PIC OBJECTS
  342. //////////////////////////////////////////////////////////////////////
  343. // Draw a polyline ("open" in pic's sense, i.e., unfilled, may be part of a
  344. // continuing path).
  345. void
  346. plot_output::line(const position &start, const position *v, int n,
  347. const line_type &lt)
  348. {
  349. if (n == 0)
  350. return;
  351. if (lt.type == line_type::invisible)
  352. {
  353. pl_fmove_r (plotter, v[n-1].x, v[n-1].y);
  354. plotter_path_in_progress = false;
  355. return;
  356. }
  357. set_fill (-1.0); // unfilled, pic convention
  358. set_pen_visibility (true);
  359. if (!precision_dashing || lt.type == line_type::solid)
  360. {
  361. set_line_type_and_thickness (lt);
  362. pl_fline_r (plotter, start.x, start.y, v[0].x, v[0].y);
  363. for (int i = 1; i < n; i++)
  364. pl_fcont_r (plotter, v[i].x, v[i].y);
  365. plotter_path_in_progress = true;
  366. }
  367. else
  368. {
  369. switch (lt.type)
  370. {
  371. case line_type::dashed:
  372. {
  373. // edge polyline, with dashes
  374. line_type slt = lt;
  375. slt.type = line_type::solid;
  376. set_line_type_and_thickness (slt);
  377. position from_point = start, to_point = v[0];
  378. for (int i = 0; i < n; i++)
  379. {
  380. distance vec(to_point - from_point);
  381. double dist = hypot(vec);
  382. if (dist <= lt.dash_width*2.0)
  383. pl_fline_r (plotter,
  384. from_point.x, from_point.y, to_point.x, to_point.y);
  385. else
  386. {
  387. // round number of dashes to integer, along each segment
  388. int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
  389. distance dash_vec = vec*(lt.dash_width/dist);
  390. double dash_gap = (dist - lt.dash_width)/ndashes;
  391. distance dash_gap_vec = vec*(dash_gap/dist);
  392. for (int j = 0; j <= ndashes; j++)
  393. {
  394. position s(from_point + dash_gap_vec*j);
  395. pl_fline_r (plotter,
  396. s.x, s.y, s.x + dash_vec.x, s.y + dash_vec.y);
  397. }
  398. }
  399. from_point = v[i];
  400. to_point = v[i+1];
  401. }
  402. pl_endpath_r (plotter);
  403. plotter_path_in_progress = false;
  404. }
  405. break;
  406. case line_type::dotted:
  407. {
  408. // edge polyline, with dots
  409. position from_point = start, to_point = v[0];
  410. for (int i = 0; i < n; i++)
  411. {
  412. distance vec(to_point - from_point);
  413. double dist = hypot(vec);
  414. // round dot spacings to integer, along line segment
  415. int ndots = IROUND(dist/lt.dash_width);
  416. if (ndots == 0)
  417. dot (from_point, lt);
  418. else
  419. {
  420. vec /= double(ndots);
  421. for (int j = 0; j <= ndots; j++)
  422. dot (from_point + vec*j, lt);
  423. }
  424. from_point = v[i];
  425. to_point = v[i+1];
  426. }
  427. }
  428. break;
  429. default:
  430. break;
  431. }
  432. }
  433. }
  434. // Draw a spline ("open" in pic's sense, i.e. unfilled, may be part
  435. // of a continuing path).
  436. void
  437. plot_output::spline(const position &start, const position *v, int n,
  438. const line_type &lt)
  439. {
  440. if (n == 0)
  441. return;
  442. if (lt.type == line_type::invisible)
  443. {
  444. pl_fmove_r (plotter, v[n-1].x, v[n-1].y);
  445. plotter_path_in_progress = false;
  446. return;
  447. }
  448. set_fill (-1.0); // unfilled, pic convention
  449. set_pen_visibility (true);
  450. set_line_type_and_thickness (lt);
  451. if (n == 1)
  452. pl_fline_r (plotter, start.x, start.y, v[0].x, v[0].y);
  453. else if (n == 2)
  454. pl_fbezier2_r (plotter,
  455. start.x, start.y, v[0].x, v[0].y, v[1].x, v[1].y);
  456. else
  457. {
  458. pl_fbezier2_r (plotter,
  459. start.x, start.y,
  460. v[0].x, v[0].y,
  461. 0.5 * (v[0].x + v[1].x), 0.5 * (v[0].y + v[1].y));
  462. for (int i = 0; i < n - 3; i++)
  463. pl_fbezier2_r (plotter,
  464. 0.5 * (v[i].x + v[i+1].x), 0.5 * (v[i].y + v[i+1].y),
  465. v[i+1].x, v[i+1].y,
  466. 0.5 * (v[i+1].x + v[i+2].x), 0.5 * (v[i+1].y + v[i+2].y));
  467. pl_fbezier2_r (plotter,
  468. 0.5 * (v[n-3].x + v[n-2].x), 0.5 * (v[n-3].y + v[n-2].y),
  469. v[n-2].x, v[n-2].y,
  470. v[n-1].x, v[n-1].y);
  471. }
  472. plotter_path_in_progress = true;
  473. }
  474. // Draw an arc object ("open" in pic's sense, i.e., unfilled, may
  475. // be part of a continuing path).
  476. void
  477. plot_output::arc (const position &start, const position &cent,
  478. const position &end, const line_type &lt)
  479. // in libplot, arcs don't subtend >= 180 degrees, but that's OK
  480. // because they don't subtend >=180 degrees in pic either
  481. {
  482. if (lt.type == line_type::invisible)
  483. {
  484. pl_fmove_r (plotter, end.x, end.y);
  485. plotter_path_in_progress = false;
  486. return;
  487. }
  488. set_fill (-1.0); // unfilled (pic convention)
  489. set_pen_visibility (true);
  490. if (!precision_dashing || lt.type == line_type::solid)
  491. {
  492. set_line_type_and_thickness (lt);
  493. pl_farc_r (plotter, cent.x, cent.y, start.x, start.y, end.x, end.y);
  494. plotter_path_in_progress = true;
  495. }
  496. else
  497. {
  498. line_type slt;
  499. slt = lt;
  500. slt.type = line_type::solid;
  501. set_line_type_and_thickness (slt);
  502. switch (lt.type)
  503. {
  504. case line_type::dashed:
  505. // edge arc, with dashes
  506. if (plotter_path_in_progress)
  507. pl_endpath_r (plotter);
  508. dashed_arc (start, cent, end, lt);
  509. pl_endpath_r (plotter);
  510. plotter_path_in_progress = false;
  511. break;
  512. case line_type::dotted:
  513. // edge arc, with dots
  514. dotted_arc (start, cent, end, lt);
  515. plotter_path_in_progress = false;
  516. break;
  517. default:
  518. break;
  519. }
  520. }
  521. }
  522. //////////////////////////////////////////////////////////////////////
  523. // CLOSED PIC OBJECTS
  524. // (some drawn differently if we do `precision dashing')
  525. //////////////////////////////////////////////////////////////////////
  526. // Draw a polyline object ("closed" in pic's sense).
  527. void
  528. plot_output::polygon(const position *v, int n,
  529. const line_type &lt, double fill)
  530. {
  531. if (lt.type == line_type::invisible)
  532. {
  533. pl_fmove_r (plotter, v[n-1].x, v[n-1].y);
  534. plotter_path_in_progress = false;
  535. return;
  536. }
  537. if (!precision_dashing || lt.type == line_type::solid)
  538. {
  539. set_fill (fill);
  540. set_pen_visibility (true);
  541. set_line_type_and_thickness (lt);
  542. if (n == 4
  543. && v[0].x == v[1].x && v[2].x == v[3].x
  544. && v[0].y == v[3].y && v[1].y == v[2].y)
  545. {
  546. pl_fbox_r (plotter, v[3].x, v[3].y, v[1].x, v[1].y);
  547. plotter_path_in_progress = false;
  548. }
  549. else
  550. {
  551. pl_fmove_r (plotter, v[n-1].x, v[n-1].y);
  552. for (int i = 0; i < n; i++)
  553. pl_fcont_r (plotter, v[i].x, v[i].y);
  554. pl_endpath_r (plotter);
  555. plotter_path_in_progress = false;
  556. }
  557. }
  558. else
  559. // precision dashing (or dotting)
  560. {
  561. line_type slt;
  562. if (fill >= 0.0)
  563. // fill polygon, but don't edge it
  564. {
  565. set_fill (fill);
  566. slt.type = line_type::solid;
  567. slt.thickness = 0.0;
  568. set_line_type_and_thickness (slt);
  569. set_pen_visibility (false); // edge will not be drawn
  570. pl_fmove_r (plotter, v[n-1].x, v[n-1].y);
  571. for (int i = 0; i < n; i++)
  572. pl_fcont_r (plotter, v[i].x, v[i].y);
  573. pl_endpath_r (plotter);
  574. plotter_path_in_progress = false;
  575. }
  576. // draw polygon boundary (unfilled)
  577. set_fill (-1.0);
  578. set_pen_visibility (true);
  579. switch (lt.type)
  580. {
  581. case line_type::dashed:
  582. {
  583. // edge polygon, with dashes
  584. slt = lt;
  585. slt.type = line_type::solid;
  586. set_line_type_and_thickness (slt);
  587. position from_point = v[n-1], to_point = v[0];
  588. for (int i = 0; i < n; i++)
  589. {
  590. distance vec(to_point - from_point);
  591. double dist = hypot(vec);
  592. if (dist <= lt.dash_width*2.0)
  593. pl_fline_r (plotter,
  594. from_point.x, from_point.y, to_point.x, to_point.y);
  595. else
  596. {
  597. // round number of dashes to integer, along each segment
  598. int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
  599. distance dash_vec = vec*(lt.dash_width/dist);
  600. double dash_gap = (dist - lt.dash_width)/ndashes;
  601. distance dash_gap_vec = vec*(dash_gap/dist);
  602. for (int j = 0; j <= ndashes; j++)
  603. {
  604. position s(from_point + dash_gap_vec*j);
  605. pl_fline_r (plotter,
  606. s.x, s.y, s.x + dash_vec.x, s.y + dash_vec.y);
  607. }
  608. }
  609. from_point = v[i];
  610. to_point = v[i+1];
  611. }
  612. pl_endpath_r (plotter);
  613. plotter_path_in_progress = false;
  614. }
  615. break;
  616. case line_type::dotted:
  617. {
  618. // edge polygon, with dots
  619. position from_point = v[n-1], to_point = v[0];
  620. for (int i = 0; i < n; i++)
  621. {
  622. distance vec(to_point - from_point);
  623. double dist = hypot(vec);
  624. // round dot spacings to integer, along line segment
  625. int ndots = IROUND(dist/lt.dash_width);
  626. if (ndots == 0)
  627. dot (from_point, lt);
  628. else
  629. {
  630. vec /= double(ndots);
  631. for (int j = 0; j <= ndots; j++)
  632. dot (from_point + vec*j, lt);
  633. }
  634. from_point = v[i];
  635. to_point = v[i+1];
  636. }
  637. }
  638. break;
  639. default: // shouldn't happen
  640. break;
  641. }
  642. }
  643. }
  644. // Draw a circle object ("closed" in pic's sense).
  645. void
  646. plot_output::circle (const position &cent, double rad,
  647. const line_type &lt, double fill)
  648. {
  649. if (lt.type == line_type::invisible)
  650. {
  651. pl_fmove_r (plotter, cent.x, cent.y);
  652. plotter_path_in_progress = false;
  653. return;
  654. }
  655. if (!precision_dashing || lt.type == line_type::solid)
  656. {
  657. set_fill (fill);
  658. set_pen_visibility (true);
  659. set_line_type_and_thickness (lt);
  660. pl_fcircle_r (plotter, cent.x, cent.y, rad);
  661. plotter_path_in_progress = false;
  662. }
  663. else
  664. // precision dashing (or dotting)
  665. {
  666. line_type slt;
  667. if (fill >= 0.0)
  668. // fill circle, but don't edge it
  669. {
  670. set_fill (fill);
  671. set_pen_visibility (false); // edge will not be drawn
  672. slt = lt;
  673. slt.type = line_type::solid;
  674. slt.thickness = 0.0;
  675. set_line_type_and_thickness (slt);
  676. pl_fcircle_r (plotter, cent.x, cent.y, rad);
  677. plotter_path_in_progress = false;
  678. }
  679. // draw circle boundary (unfilled)
  680. set_fill (-1.0);
  681. set_pen_visibility (true);
  682. slt = lt;
  683. slt.type = line_type::solid;
  684. set_line_type_and_thickness (slt);
  685. switch (lt.type)
  686. {
  687. case line_type::dashed:
  688. // edge circle, with dashes
  689. if (plotter_path_in_progress)
  690. pl_endpath_r (plotter);
  691. dashed_circle(cent, rad, lt);
  692. pl_endpath_r (plotter);
  693. plotter_path_in_progress = false;
  694. break;
  695. case line_type::dotted:
  696. // edge circle, with dots
  697. dotted_circle (cent, rad, lt);
  698. break;
  699. default: // shouldn't happen
  700. break;
  701. }
  702. }
  703. }
  704. // Draw a rounded box object ("closed" in pic's sense).
  705. void
  706. plot_output::rounded_box(const position &cent, const distance &dim, double rad, const line_type &lt, double fill)
  707. {
  708. static bool recursive = false;
  709. position tem, arc_start, arc_cent, arc_end;
  710. position line_start, line_end;
  711. if (lt.type == line_type::invisible)
  712. {
  713. pl_fmove_r (plotter, cent.x, cent.y);
  714. plotter_path_in_progress = false;
  715. return;
  716. }
  717. if (plotter_path_in_progress)
  718. {
  719. pl_endpath_r (plotter);
  720. plotter_path_in_progress = false;
  721. }
  722. if (!precision_dashing || lt.type == line_type::solid)
  723. {
  724. set_fill (fill);
  725. if (!recursive)
  726. // _not_ invoked recursively on account of precision dashing
  727. {
  728. set_pen_visibility (true);
  729. set_line_type_and_thickness (lt);
  730. }
  731. tem = cent - dim/2.0;
  732. arc_start = tem + position(0.0, rad);
  733. arc_cent = tem + position(rad, rad);
  734. arc_end = tem + position(rad, 0.0);
  735. pl_farc_r (plotter, arc_cent.x, arc_cent.y,
  736. arc_start.x, arc_start.y, arc_end.x, arc_end.y);
  737. line_start = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
  738. line_end = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
  739. pl_fline_r (plotter, arc_end.x, arc_end.y, line_end.x, line_end.y);
  740. tem = cent + position(dim.x/2.0, -dim.y/2.0);
  741. arc_start = tem + position(-rad, 0.0);
  742. arc_cent = tem + position(-rad, rad);
  743. arc_end = tem + position(0.0, rad);
  744. pl_farc_r (plotter, arc_cent.x, arc_cent.y,
  745. line_end.x, line_end.y, arc_end.x, arc_end.y);
  746. line_start = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
  747. line_end = cent + position(dim.x/2.0, dim.y/2.0 - rad);
  748. pl_fline_r (plotter, arc_end.x, arc_end.y, line_end.x, line_end.y);
  749. tem = cent + dim/2.0;
  750. arc_start = tem + position(0.0, -rad);
  751. arc_cent = tem + position(-rad, -rad);
  752. arc_end = tem + position(-rad, 0.0);
  753. pl_farc_r (plotter, arc_cent.x, arc_cent.y,
  754. line_end.x, line_end.y, arc_end.x, arc_end.y);
  755. line_start = cent + position(dim.x/2.0 - rad, dim.y/2.0);
  756. line_end = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
  757. pl_fline_r (plotter, arc_end.x, arc_end.y, line_end.x, line_end.y);
  758. tem = cent + position(-dim.x/2.0, dim.y/2.0);
  759. arc_start = tem + position(rad, 0.0);
  760. arc_cent = tem + position(rad, -rad);
  761. arc_end = tem + position(0.0, -rad);
  762. pl_farc_r (plotter, arc_cent.x, arc_cent.y,
  763. line_end.x, line_end.y, arc_end.x, arc_end.y);
  764. line_start = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
  765. line_end = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
  766. pl_fline_r (plotter, arc_end.x, arc_end.y, line_end.x, line_end.y);
  767. pl_endpath_r (plotter);
  768. plotter_path_in_progress = false;
  769. }
  770. else
  771. // precision dashing (or dotting)
  772. {
  773. if (fill >= 0.0)
  774. {
  775. // fill rounded box (boundary solid, thickness 0) via recursive call
  776. set_fill (fill);
  777. set_pen_visibility (false); // edge will not be drawn
  778. line_type slt = lt;
  779. slt.type = line_type::solid;
  780. slt.thickness = 0.0;
  781. recursive = true;
  782. rounded_box(cent, dim, rad, slt, fill);
  783. recursive = false;
  784. plotter_path_in_progress = false;
  785. }
  786. // draw rounded box boundary, unfilled
  787. set_pen_visibility (true);
  788. set_line_type_and_thickness (lt); // only thickness is relevant
  789. common_output::rounded_box(cent, dim, rad, lt, -1.0); //-1 means unfilled
  790. if (plotter_path_in_progress)
  791. {
  792. pl_endpath_r (plotter);
  793. plotter_path_in_progress = false;
  794. }
  795. }
  796. }
  797. // Draw an ellipse object ("closed" in pic's sense).
  798. // No support for precision dashing, but there should be.
  799. void
  800. plot_output::ellipse(const position &cent, const distance &dim,
  801. const line_type &lt, double fill)
  802. {
  803. if (lt.type == line_type::invisible)
  804. {
  805. pl_fmove_r (plotter, cent.x, cent.y);
  806. plotter_path_in_progress = false;
  807. return;
  808. }
  809. set_fill (fill);
  810. set_pen_visibility (true);
  811. set_line_type_and_thickness (lt);
  812. pl_fellipse_r (plotter, cent.x, cent.y, 0.5 * dim.x, 0.5 * dim.y, 0.0);
  813. plotter_path_in_progress = false;
  814. }
  815. //////////////////////////////////////////////////////////////////////
  816. // MISC.
  817. //////////////////////////////////////////////////////////////////////
  818. // Internal function, used for precision dotting; also invoked by
  819. // precision dotting methods in the common_output superclass.
  820. void
  821. plot_output::dot (const position &cent, const line_type &lt)
  822. // lt arg determines diameter of dot
  823. {
  824. line_type slt;
  825. set_fill (1.0);
  826. set_pen_visibility (true);
  827. slt.type = line_type::solid;
  828. slt.thickness = 0.0;
  829. set_line_type_and_thickness (slt);
  830. pl_fcircle_r (plotter, cent.x, cent.y, 0.5 * lt.thickness / POINTS_PER_INCH);
  831. plotter_path_in_progress = false;
  832. }
  833. int
  834. plot_output::supports_filled_polygons()
  835. {
  836. return 1;
  837. }