common.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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 common_output class: subclassed from the output class,
  16. // providing support for dotted/dashed arcs and circles, and for
  17. // rounded boxes (possible dotted/dashed).
  18. #include "pic.h"
  19. #include "output.h"
  20. #include "common.h"
  21. // output a dashed circle as a series of arcs
  22. void
  23. common_output::dashed_circle(const position &cent, double rad, const line_type &lt)
  24. {
  25. assert (lt.type == line_type::dashed);
  26. line_type slt = lt;
  27. slt.type = line_type::solid;
  28. double dash_angle = lt.dash_width/rad; // dash angle in radians
  29. int ndashes;
  30. double gap_angle;
  31. if (dash_angle >= M_PI/4.0)
  32. {
  33. if (dash_angle < M_PI/2.0)
  34. {
  35. gap_angle = M_PI/2.0 - dash_angle;
  36. ndashes = 4;
  37. }
  38. else if (dash_angle < M_PI)
  39. {
  40. gap_angle = M_PI - dash_angle;
  41. ndashes = 2;
  42. }
  43. else
  44. {
  45. circle (cent, rad, slt, -1.0);
  46. return;
  47. }
  48. }
  49. else
  50. {
  51. ndashes = 4 * int(ceil(M_PI/(4.0*dash_angle)));
  52. gap_angle = (M_PI*2.0)/ndashes - dash_angle;
  53. }
  54. for (int i = 0; i < ndashes; i++)
  55. {
  56. double start_angle = i*(dash_angle+gap_angle) - 0.5 * dash_angle;
  57. solid_arc (cent, rad, start_angle, start_angle + dash_angle, lt);
  58. }
  59. }
  60. // output a dotted circle as a series of dots
  61. void
  62. common_output::dotted_circle(const position &cent, double rad, const line_type &lt)
  63. {
  64. double gap_angle = lt.dash_width/rad; // gap angle in radians
  65. int ndots;
  66. double ang = 0.0;
  67. assert (lt.type == line_type::dotted);
  68. if (gap_angle >= M_PI/2.0)
  69. {
  70. // always have at least 2 dots
  71. gap_angle = M_PI;
  72. ndots = 2;
  73. }
  74. else
  75. {
  76. ndots = 4*int(M_PI/(2.0*gap_angle));
  77. gap_angle = (M_PI*2.0)/ndots;
  78. }
  79. for (int i = 0; i < ndots; i++, ang += gap_angle)
  80. dot (cent + position(cos(ang), sin(ang))*rad, lt);
  81. }
  82. // return non-zero iff we can compute a center
  83. int
  84. compute_arc_center(const position &start, const position &cent, const position &end, position *result)
  85. {
  86. // This finds the point along the vector from start to cent that
  87. // is equidistant between start and end.
  88. distance c = cent - start;
  89. distance e = end - start;
  90. double n = c*e;
  91. if (n == 0.0)
  92. return 0;
  93. else
  94. {
  95. *result = start + c*((e*e)/(2.0*n));
  96. return 1;
  97. }
  98. }
  99. // output a dashed arc as a series of arcs
  100. void
  101. common_output::dashed_arc(const position &start, const position &cent, const position &end, const line_type &lt)
  102. {
  103. assert (lt.type == line_type::dashed);
  104. position c;
  105. if (!compute_arc_center(start, cent, end, &c))
  106. {
  107. line(start, &end, 1, lt);
  108. return;
  109. }
  110. distance start_offset = start - c;
  111. distance end_offset = end - c;
  112. double start_angle = atan2(start_offset.y, start_offset.x);
  113. double end_angle = atan2(end_offset.y, end_offset.x);
  114. double rad = hypot(c - start);
  115. double dash_angle = lt.dash_width/rad;
  116. double total_angle = end_angle - start_angle;
  117. while (total_angle < 0)
  118. total_angle += M_PI + M_PI;
  119. if (total_angle <= dash_angle*2.0)
  120. {
  121. solid_arc(cent, rad, start_angle, end_angle, lt);
  122. return;
  123. }
  124. int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + 0.5);
  125. double dash_and_gap_angle = (total_angle - dash_angle)/ndashes;
  126. for (int i = 0; i <= ndashes; i++)
  127. solid_arc(cent, rad, start_angle + i*dash_and_gap_angle,
  128. start_angle + i*dash_and_gap_angle + dash_angle, lt);
  129. }
  130. // output a dotted arc as a series of dots
  131. void
  132. common_output::dotted_arc(const position &start, const position &cent, const position &end, const line_type &lt)
  133. {
  134. assert (lt.type == line_type::dotted);
  135. position c;
  136. if (!compute_arc_center(start, cent, end, &c))
  137. {
  138. line(start, &end, 1, lt);
  139. return;
  140. }
  141. distance start_offset = start - c;
  142. distance end_offset = end - c;
  143. double start_angle = atan2(start_offset.y, start_offset.x);
  144. double total_angle = atan2(end_offset.y, end_offset.x) - start_angle;
  145. while (total_angle < 0)
  146. total_angle += M_PI + M_PI;
  147. double rad = hypot(c - start);
  148. int ndots = int(total_angle/(lt.dash_width/rad) + .5);
  149. if (ndots == 0)
  150. dot (start, lt);
  151. else
  152. {
  153. for (int i = 0; i <= ndots; i++)
  154. {
  155. double a = start_angle + (total_angle*i)/ndots;
  156. dot (cent + position(cos(a), sin(a))*rad, lt);
  157. }
  158. }
  159. }
  160. // output a solid arc, of the sort used in dashing
  161. void
  162. common_output::solid_arc(const position &cent, double rad, double start_angle, double end_angle, const line_type &lt)
  163. {
  164. line_type slt = lt;
  165. slt.type = line_type::solid;
  166. arc (cent + position(cos(start_angle), sin(start_angle))*rad,
  167. cent,
  168. cent + position(cos(end_angle), sin(end_angle))*rad,
  169. slt);
  170. }
  171. // output a rounded box (of one of several line types)
  172. void
  173. common_output::rounded_box(const position &cent, const distance &dim, double rad, const line_type &lt, double fill)
  174. {
  175. if (fill >= 0.0)
  176. filled_rounded_box(cent, dim, rad, fill);
  177. switch (lt.type)
  178. {
  179. case line_type::invisible:
  180. break;
  181. case line_type::dashed:
  182. dashed_rounded_box(cent, dim, rad, lt);
  183. break;
  184. case line_type::dotted:
  185. dotted_rounded_box(cent, dim, rad, lt);
  186. break;
  187. case line_type::solid:
  188. solid_rounded_box(cent, dim, rad, lt);
  189. break;
  190. default:
  191. assert (0);
  192. }
  193. }
  194. // output a dashed rounded box as a series of arcs
  195. void
  196. common_output::dashed_rounded_box(const position &cent, const distance &dim, double rad, const line_type &lt)
  197. {
  198. line_type slt = lt;
  199. slt.type = line_type::solid;
  200. double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
  201. int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + 0.5);
  202. double hor_gap_width = (n_hor_dashes != 0
  203. ? hor_length/n_hor_dashes - lt.dash_width
  204. : 0.0);
  205. double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
  206. int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + 0.5);
  207. double vert_gap_width = (n_vert_dashes != 0
  208. ? vert_length/n_vert_dashes - lt.dash_width
  209. : 0.0);
  210. // Each corner arc must be split into two for dashing,
  211. // with one part dashed using vert_gap_width, and the other
  212. // using hor_gap_width.
  213. double offset = lt.dash_width/2.0;
  214. dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
  215. -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset);
  216. dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
  217. cent + position(dim.x/2.0, dim.y/2.0 - rad),
  218. slt, lt.dash_width, vert_gap_width, &offset);
  219. dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
  220. 0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
  221. offset = lt.dash_width/2.0;
  222. dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
  223. M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset);
  224. dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
  225. cent + position(-dim.x/2.0 + rad, dim.y/2.0),
  226. slt, lt.dash_width, hor_gap_width, &offset);
  227. dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
  228. M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset);
  229. offset = lt.dash_width/2.0;
  230. dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
  231. 3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset);
  232. dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
  233. cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
  234. slt, lt.dash_width, vert_gap_width, &offset);
  235. dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
  236. M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
  237. offset = lt.dash_width/2.0;
  238. dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
  239. 5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset);
  240. dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
  241. cent + position(dim.x/2.0 - rad, -dim.y/2.0),
  242. slt, lt.dash_width, hor_gap_width, &offset);
  243. dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
  244. 3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset);
  245. }
  246. // Private, used by dashed_rounded_box.
  247. void
  248. common_output::dash_arc(const position &cent, double rad, double start_angle, double end_angle, const line_type &lt, double dash_width, double gap_width, double *offsetp)
  249. {
  250. double length = (end_angle - start_angle)*rad;
  251. double pos = 0.0;
  252. for (;;)
  253. {
  254. if (*offsetp >= dash_width)
  255. {
  256. double rem = dash_width + gap_width - *offsetp;
  257. if (pos + rem > length)
  258. {
  259. *offsetp += length - pos;
  260. break;
  261. }
  262. else
  263. {
  264. pos += rem;
  265. *offsetp = 0.0;
  266. }
  267. }
  268. else
  269. {
  270. double rem = dash_width - *offsetp;
  271. if (pos + rem > length)
  272. {
  273. solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt);
  274. *offsetp += length - pos;
  275. break;
  276. }
  277. else
  278. {
  279. solid_arc(cent, rad, start_angle + pos/rad,
  280. start_angle + (pos + rem)/rad, lt);
  281. pos += rem;
  282. *offsetp = dash_width;
  283. }
  284. }
  285. }
  286. }
  287. // Private, used by dashed_rounded_box.
  288. void
  289. common_output::dash_line(const position &start, const position &end, const line_type &lt, double dash_width, double gap_width, double *offsetp)
  290. {
  291. distance dist = end - start;
  292. double length = hypot(dist);
  293. if (length == 0.0)
  294. return;
  295. double pos = 0.0;
  296. for (;;)
  297. {
  298. if (*offsetp >= dash_width)
  299. {
  300. double rem = dash_width + gap_width - *offsetp;
  301. if (pos + rem > length)
  302. {
  303. *offsetp += length - pos;
  304. break;
  305. }
  306. else
  307. {
  308. pos += rem;
  309. *offsetp = 0.0;
  310. }
  311. }
  312. else
  313. {
  314. double rem = dash_width - *offsetp;
  315. if (pos + rem > length)
  316. {
  317. line(start + dist*(pos/length), &end, 1, lt);
  318. *offsetp += length - pos;
  319. break;
  320. }
  321. else
  322. {
  323. position p(start + dist*((pos + rem)/length));
  324. line(start + dist*(pos/length), &p, 1, lt);
  325. pos += rem;
  326. *offsetp = dash_width;
  327. }
  328. }
  329. }
  330. }
  331. // output a dotted rounded box as a series of dots
  332. void
  333. common_output::dotted_rounded_box(const position &cent, const distance &dim, double rad, const line_type &lt)
  334. {
  335. line_type slt = lt;
  336. slt.type = line_type::solid;
  337. double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
  338. int n_hor_dots = int(hor_length/lt.dash_width + .5);
  339. double hor_gap_width = (n_hor_dots != 0
  340. ? hor_length/n_hor_dots
  341. : lt.dash_width);
  342. double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
  343. int n_vert_dots = int(vert_length/lt.dash_width + .5);
  344. double vert_gap_width = (n_vert_dots != 0
  345. ? vert_length/n_vert_dots
  346. : lt.dash_width);
  347. double epsilon = lt.dash_width/(rad*100.0);
  348. double offset = 0.0;
  349. dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
  350. -M_PI/4.0, 0, slt, vert_gap_width, &offset);
  351. dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
  352. cent + position(dim.x/2.0, dim.y/2.0 - rad),
  353. slt, vert_gap_width, &offset);
  354. dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
  355. 0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
  356. offset = 0.0;
  357. dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
  358. M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset);
  359. dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
  360. cent + position(-dim.x/2.0 + rad, dim.y/2.0),
  361. slt, hor_gap_width, &offset);
  362. dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
  363. M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset);
  364. offset = 0.0;
  365. dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
  366. 3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset);
  367. dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
  368. cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
  369. slt, vert_gap_width, &offset);
  370. dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
  371. M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
  372. offset = 0.0;
  373. dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
  374. 5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset);
  375. dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
  376. cent + position(dim.x/2.0 - rad, -dim.y/2.0),
  377. slt, hor_gap_width, &offset);
  378. dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
  379. 3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset);
  380. }
  381. // Private, used by dotted_rounded_box.
  382. void
  383. common_output::dot_arc(const position &cent, double rad, double start_angle, double end_angle, const line_type &lt, double gap_width, double *offsetp)
  384. {
  385. double length = (end_angle - start_angle)*rad;
  386. double pos = 0.0;
  387. for (;;)
  388. {
  389. if (*offsetp == 0.0)
  390. {
  391. double ang = start_angle + pos/rad;
  392. dot (cent + position(cos(ang), sin(ang))*rad, lt);
  393. }
  394. double rem = gap_width - *offsetp;
  395. if (pos + rem > length)
  396. {
  397. *offsetp += length - pos;
  398. break;
  399. }
  400. else
  401. {
  402. pos += rem;
  403. *offsetp = 0.0;
  404. }
  405. }
  406. }
  407. // Private, used by dotted_rounded_box.
  408. void
  409. common_output::dot_line(const position &start, const position &end, const line_type &lt, double gap_width, double *offsetp)
  410. {
  411. distance dist = end - start;
  412. double length = hypot(dist);
  413. if (length == 0.0)
  414. return;
  415. double pos = 0.0;
  416. for (;;)
  417. {
  418. if (*offsetp == 0.0)
  419. dot (start + dist*(pos/length), lt);
  420. double rem = gap_width - *offsetp;
  421. if (pos + rem > length)
  422. {
  423. *offsetp += length - pos;
  424. break;
  425. }
  426. else
  427. {
  428. pos += rem;
  429. *offsetp = 0.0;
  430. }
  431. }
  432. }
  433. // draw a solid rounded box, from arcs and lines
  434. void
  435. common_output::solid_rounded_box(const position &cent, const distance &dim, double rad, const line_type &lt)
  436. {
  437. position tem = cent - dim/2.0;
  438. arc(tem + position(0.0, rad),
  439. tem + position(rad, rad),
  440. tem + position(rad, 0.0),
  441. lt);
  442. tem = cent + position(-dim.x/2.0, dim.y/2.0);
  443. arc(tem + position(rad, 0.0),
  444. tem + position(rad, -rad),
  445. tem + position(0.0, -rad),
  446. lt);
  447. tem = cent + dim/2.0;
  448. arc(tem + position(0.0, -rad),
  449. tem + position(-rad, -rad),
  450. tem + position(-rad, 0.0),
  451. lt);
  452. tem = cent + position(dim.x/2.0, -dim.y/2.0);
  453. arc(tem + position(-rad, 0.0),
  454. tem + position(-rad, rad),
  455. tem + position(0.0, rad),
  456. lt);
  457. position end;
  458. end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
  459. line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
  460. end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
  461. line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
  462. end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
  463. line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
  464. end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
  465. line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
  466. }
  467. // Draw a filled rounded box, by drawing filled circles etc.
  468. // Not used by libplot driver.
  469. void
  470. common_output::filled_rounded_box(const position &cent, const distance &dim, double rad, double fill)
  471. {
  472. line_type ilt;
  473. ilt.type = line_type::invisible;
  474. circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill);
  475. circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill);
  476. circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill);
  477. circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
  478. position vec[4];
  479. vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
  480. vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
  481. vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
  482. vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
  483. polygon(vec, 4, ilt, fill);
  484. vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
  485. vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
  486. vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
  487. vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
  488. polygon(vec, 4, ilt, fill);
  489. }