s_path.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. /* This file is part of the GNU plotutils package. Copyright (C) 1995,
  2. 1996, 1997, 1998, 1999, 2000, 2005, 2008, 2009, Free Software
  3. Foundation, Inc.
  4. The GNU plotutils package is free software. You may redistribute it
  5. and/or modify it under the terms of the GNU General Public License as
  6. published by the Free Software foundation; either version 2, or (at your
  7. option) any later version.
  8. The GNU plotutils package is distributed in the hope that it will be
  9. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License along
  13. with the GNU plotutils package; see the file COPYING. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
  15. Boston, MA 02110-1301, USA. */
  16. /* This file contains the internal paint_path() and paint_paths() methods,
  17. which the public method endpath() is a wrapper around. */
  18. /* This version of paint_path() is for SVGPlotters. It renders a libplot
  19. path in terms of SVG shapes:
  20. path/rect/circle/ellipse/line/polyline/polygon. */
  21. #include "sys-defines.h"
  22. #include "extern.h"
  23. /* SVG join styles, i.e., stroke-linejoin attribute, indexed by internal
  24. number (miter/rd./bevel/triangular) */
  25. static const char * const svg_join_style[PL_NUM_JOIN_TYPES] =
  26. { "miter", "round", "bevel", "round" };
  27. /* SVG cap styles, i.e., stroke-linecap attribute, indexed by internal
  28. number (butt/rd./project/triangular) */
  29. static const char * const svg_cap_style[PL_NUM_CAP_TYPES] =
  30. { "butt", "round", "square", "round" };
  31. /* SVG fill rule styles, i.e., fill-rule attribute, indexed by internal
  32. number (evenodd/nonzero winding number) */
  33. static const char * const svg_fill_style[PL_NUM_FILL_RULES] =
  34. { "evenodd", "nonzero" };
  35. static const double identity_matrix[6] = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 };
  36. /* forward references */
  37. static void write_svg_path_data (plOutbuf *page, const plPath *path);
  38. static void write_svg_path_style (plOutbuf *page, const plDrawState *drawstate, bool need_cap, bool need_join);
  39. void
  40. _pl_s_paint_path (S___(Plotter *_plotter))
  41. {
  42. switch ((int)_plotter->drawstate->path->type)
  43. {
  44. case (int)PATH_SEGMENT_LIST:
  45. {
  46. bool closed, lines_only;
  47. int i;
  48. /* sanity checks */
  49. if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
  50. break;
  51. if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */
  52. break;
  53. if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
  54. && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
  55. && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
  56. closed = true;
  57. else
  58. closed = false; /* 2-point ones should be open */
  59. /* determine which sort of SVG primitive shape this should be:
  60. line/polyline/polygon, or general path */
  61. lines_only = true;
  62. for (i = 1; i < _plotter->drawstate->path->num_segments; i++)
  63. {
  64. plPathSegmentType element_type;
  65. element_type = _plotter->drawstate->path->segments[i].type;
  66. if (element_type != S_LINE)
  67. {
  68. lines_only = false;
  69. break;
  70. }
  71. }
  72. if (lines_only && _plotter->drawstate->path->num_segments == 2)
  73. /* SVG line */
  74. {
  75. sprintf (_plotter->data->page->point, "<line ");
  76. _update_buffer (_plotter->data->page);
  77. _pl_s_set_matrix (R___(_plotter) identity_matrix);
  78. sprintf (_plotter->data->page->point,
  79. "x1=\"%.5g\" y1=\"%.5g\" x2=\"%.5g\" y2=\"%.5g\" ",
  80. _plotter->drawstate->path->segments[0].p.x,
  81. _plotter->drawstate->path->segments[0].p.y,
  82. _plotter->drawstate->path->segments[1].p.x,
  83. _plotter->drawstate->path->segments[1].p.y);
  84. _update_buffer (_plotter->data->page);
  85. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  86. true, false);
  87. sprintf (_plotter->data->page->point, "/>\n");
  88. _update_buffer (_plotter->data->page);
  89. }
  90. else if (lines_only && !closed)
  91. /* SVG polyline */
  92. {
  93. sprintf (_plotter->data->page->point, "<polyline ");
  94. _update_buffer (_plotter->data->page);
  95. _pl_s_set_matrix (R___(_plotter) identity_matrix);
  96. sprintf (_plotter->data->page->point,
  97. "points=\"");
  98. _update_buffer (_plotter->data->page);
  99. for (i = 0; i < _plotter->drawstate->path->num_segments; i++)
  100. {
  101. plPoint p;
  102. p = _plotter->drawstate->path->segments[i].p;
  103. sprintf (_plotter->data->page->point,
  104. "%.5g,%.5g ",
  105. p.x, p.y);
  106. _update_buffer (_plotter->data->page);
  107. }
  108. sprintf (_plotter->data->page->point,
  109. "\" ");
  110. _update_buffer (_plotter->data->page);
  111. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  112. true, true);
  113. sprintf (_plotter->data->page->point,
  114. "/>\n");
  115. _update_buffer (_plotter->data->page);
  116. }
  117. else if (lines_only && closed)
  118. /* SVG polygon */
  119. {
  120. sprintf (_plotter->data->page->point, "<polygon ");
  121. _update_buffer (_plotter->data->page);
  122. _pl_s_set_matrix (R___(_plotter) identity_matrix);
  123. sprintf (_plotter->data->page->point,
  124. "points=\"");
  125. _update_buffer (_plotter->data->page);
  126. for (i = 0; i < _plotter->drawstate->path->num_segments - 1; i++)
  127. {
  128. plPoint p;
  129. p = _plotter->drawstate->path->segments[i].p;
  130. sprintf (_plotter->data->page->point,
  131. "%.5g,%.5g ",
  132. p.x, p.y);
  133. _update_buffer (_plotter->data->page);
  134. }
  135. sprintf (_plotter->data->page->point,
  136. "\" ");
  137. _update_buffer (_plotter->data->page);
  138. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  139. false, true);
  140. sprintf (_plotter->data->page->point,
  141. "/>\n");
  142. _update_buffer (_plotter->data->page);
  143. }
  144. else
  145. /* general SVG path */
  146. {
  147. sprintf (_plotter->data->page->point, "<path ");
  148. _update_buffer (_plotter->data->page);
  149. _pl_s_set_matrix (R___(_plotter) identity_matrix);
  150. sprintf (_plotter->data->page->point,
  151. "d=\"");
  152. _update_buffer (_plotter->data->page);
  153. /* write SVG path data string */
  154. write_svg_path_data (_plotter->data->page,
  155. _plotter->drawstate->path);
  156. sprintf (_plotter->data->page->point,
  157. "\" ");
  158. _update_buffer (_plotter->data->page);
  159. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  160. true, true);
  161. sprintf (_plotter->data->page->point,
  162. "/>\n");
  163. _update_buffer (_plotter->data->page);
  164. }
  165. }
  166. break;
  167. case (int)PATH_BOX:
  168. {
  169. plPoint p0, p1;
  170. double xmin, ymin, xmax, ymax;
  171. p0 = _plotter->drawstate->path->p0;
  172. p1 = _plotter->drawstate->path->p1;
  173. xmin = DMIN(p0.x, p1.x);
  174. ymin = DMIN(p0.y, p1.y);
  175. xmax = DMAX(p0.x, p1.x);
  176. ymax = DMAX(p0.y, p1.y);
  177. sprintf (_plotter->data->page->point, "<rect ");
  178. _update_buffer (_plotter->data->page);
  179. _pl_s_set_matrix (R___(_plotter) identity_matrix);
  180. sprintf (_plotter->data->page->point,
  181. "x=\"%.5g\" y=\"%.5g\" width=\"%.5g\" height=\"%.5g\" ",
  182. xmin, ymin, xmax - xmin, ymax - ymin);
  183. _update_buffer (_plotter->data->page);
  184. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  185. false, true);
  186. sprintf (_plotter->data->page->point,
  187. "/>\n");
  188. _update_buffer (_plotter->data->page);
  189. }
  190. break;
  191. case (int)PATH_CIRCLE:
  192. {
  193. plPoint pc;
  194. double radius = _plotter->drawstate->path->radius;
  195. sprintf (_plotter->data->page->point, "<circle ");
  196. _update_buffer (_plotter->data->page);
  197. _pl_s_set_matrix (R___(_plotter) identity_matrix);
  198. pc = _plotter->drawstate->path->pc;
  199. sprintf (_plotter->data->page->point,
  200. "cx=\"%.5g\" cy=\"%.5g\" r=\"%.5g\" ",
  201. pc.x, pc.y, radius);
  202. _update_buffer (_plotter->data->page);
  203. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  204. false, false);
  205. sprintf (_plotter->data->page->point,
  206. "/>\n");
  207. _update_buffer (_plotter->data->page);
  208. }
  209. break;
  210. case (int)PATH_ELLIPSE:
  211. {
  212. plPoint pc;
  213. double rx = _plotter->drawstate->path->rx;
  214. double ry = _plotter->drawstate->path->ry;
  215. double angle = _plotter->drawstate->path->angle;
  216. double local_matrix[6];
  217. sprintf (_plotter->data->page->point, "<ellipse ");
  218. _update_buffer (_plotter->data->page);
  219. pc = _plotter->drawstate->path->pc;
  220. local_matrix[0] = cos (M_PI * angle / 180.0);
  221. local_matrix[1] = sin (M_PI * angle / 180.0);
  222. local_matrix[2] = -sin (M_PI * angle / 180.0);
  223. local_matrix[3] = cos (M_PI * angle / 180.0);
  224. local_matrix[4] = pc.x;
  225. local_matrix[5] = pc.y;
  226. _pl_s_set_matrix (R___(_plotter) local_matrix);
  227. sprintf (_plotter->data->page->point, "rx=\"%.5g\" ry=\"%.5g\" ",
  228. rx, ry);
  229. _update_buffer (_plotter->data->page);
  230. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  231. false, false);
  232. sprintf (_plotter->data->page->point, "/>\n");
  233. _update_buffer (_plotter->data->page);
  234. }
  235. break;
  236. default: /* shouldn't happen */
  237. break;
  238. }
  239. }
  240. bool
  241. _pl_s_paint_paths (S___(Plotter *_plotter))
  242. {
  243. int i;
  244. sprintf (_plotter->data->page->point,
  245. "<path ");
  246. _update_buffer (_plotter->data->page);
  247. _pl_s_set_matrix (R___(_plotter) identity_matrix);
  248. sprintf (_plotter->data->page->point,
  249. "d=\"");
  250. _update_buffer (_plotter->data->page);
  251. for (i = 0; i < _plotter->drawstate->num_paths; i++)
  252. {
  253. plPath *path = _plotter->drawstate->paths[i];
  254. switch ((int)path->type)
  255. {
  256. case (int)PATH_SEGMENT_LIST:
  257. /* write SVG path data string */
  258. write_svg_path_data (_plotter->data->page, path);
  259. break;
  260. case (int)PATH_CIRCLE:
  261. /* draw as four quarter-circles */
  262. {
  263. plPoint pc;
  264. double radius;
  265. pc = path->pc;
  266. radius = path->radius;
  267. if (path->clockwise == false)
  268. /* counter-clockwise */
  269. sprintf (_plotter->data->page->point, "\
  270. M%.5g,%.5g \
  271. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  272. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  273. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  274. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g Z ",
  275. pc.x + radius, pc.y,
  276. radius, radius, 0.0, 0, 1, pc.x, pc.y + radius,
  277. radius, radius, 0.0, 0, 1, pc.x - radius, pc.y,
  278. radius, radius, 0.0, 0, 1, pc.x, pc.y - radius,
  279. radius, radius, 0.0, 0, 1, pc.x + radius, pc.y);
  280. else
  281. /* clockwise */
  282. sprintf (_plotter->data->page->point, "\
  283. M%.5g,%.5g \
  284. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  285. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  286. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  287. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g Z ",
  288. pc.x + radius, pc.y,
  289. radius, radius, 0.0, 0, 0, pc.x, pc.y - radius,
  290. radius, radius, 0.0, 0, 0, pc.x - radius, pc.y,
  291. radius, radius, 0.0, 0, 0, pc.x, pc.y + radius,
  292. radius, radius, 0.0, 0, 0, pc.x + radius, pc.y);
  293. _update_buffer (_plotter->data->page);
  294. }
  295. break;
  296. case (int)PATH_ELLIPSE:
  297. /* draw as four quarter-ellipses */
  298. {
  299. plPoint pc;
  300. double rx, ry, angle;
  301. plVector v1, v2;
  302. pc = path->pc;
  303. rx = path->rx;
  304. ry = path->ry;
  305. angle = path->angle;
  306. v1.x = rx * cos (M_PI * angle / 180.0);
  307. v1.y = rx * sin (M_PI * angle / 180.0);
  308. v2.x = -ry * sin (M_PI * angle / 180.0);
  309. v2.y = ry * cos (M_PI * angle / 180.0);
  310. if (path->clockwise == false)
  311. /* counter-clockwise */
  312. sprintf (_plotter->data->page->point, "\
  313. M%.5g,%.5g \
  314. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  315. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  316. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  317. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g Z ",
  318. pc.x + v1.x, pc.y + v1.y,
  319. rx, ry, 0.0, 0, 1, pc.x + v2.x, pc.y + v2.y,
  320. rx, ry, 0.0, 0, 1, pc.x - v1.x, pc.y - v1.y,
  321. rx, ry, 0.0, 0, 1, pc.x - v2.x, pc.y - v2.y,
  322. rx, ry, 0.0, 0, 1, pc.x + v1.x, pc.y + v1.y);
  323. else
  324. /* clockwise */
  325. sprintf (_plotter->data->page->point, "\
  326. M%.5g,%.5g \
  327. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  328. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  329. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g \
  330. A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g Z ",
  331. pc.x + v1.x, pc.y + v1.y,
  332. rx, ry, 0.0, 0, 0, pc.x - v2.x, pc.y - v2.y,
  333. rx, ry, 0.0, 0, 0, pc.x - v1.x, pc.y - v1.y,
  334. rx, ry, 0.0, 0, 0, pc.x + v2.x, pc.y + v2.y,
  335. rx, ry, 0.0, 0, 0, pc.x + v1.x, pc.y + v1.y);
  336. _update_buffer (_plotter->data->page);
  337. }
  338. break;
  339. case (int)PATH_BOX:
  340. {
  341. plPoint p0, p1;
  342. bool x_move_is_first;
  343. p0 = path->p0;
  344. p1 = path->p1;
  345. /* if counterclockwise, would first pen motion be in x
  346. direction? */
  347. x_move_is_first = ((p1.x >= p0.x && p1.y >= p0.y)
  348. || (p1.x < p0.x && p1.y < p0.y) ? true : false);
  349. if (path->clockwise)
  350. /* take complement */
  351. x_move_is_first = (x_move_is_first == true ? false : true);
  352. if (x_move_is_first)
  353. sprintf (_plotter->data->page->point,
  354. "M%.5g,%.5g H%.5g V%.5g H%.5g Z ",
  355. p0.x, p0.y, p1.x, p1.y, p0.x);
  356. else
  357. sprintf (_plotter->data->page->point,
  358. "M%.5g,%.5g V%.5g H%.5g V%.5g Z ",
  359. p0.x, p0.y, p1.y, p1.x, p0.y);
  360. _update_buffer (_plotter->data->page);
  361. }
  362. break;
  363. default: /* shouldn't happen */
  364. break;
  365. }
  366. }
  367. sprintf (_plotter->data->page->point,
  368. "\" ");
  369. _update_buffer (_plotter->data->page);
  370. write_svg_path_style (_plotter->data->page, _plotter->drawstate,
  371. true, true);
  372. sprintf (_plotter->data->page->point,
  373. "/>\n");
  374. _update_buffer (_plotter->data->page);
  375. return true;
  376. }
  377. /* Write an SVG path data string that specifies a single simple path. This
  378. may be called only on a libplot segment-list path, not on a libplot path
  379. that consists of a single closed path primitive (box/circle/ellipse). */
  380. static void
  381. write_svg_path_data (plOutbuf *page, const plPath *path)
  382. {
  383. bool closed;
  384. plPoint p, oldpoint;
  385. int i;
  386. /* sanity check */
  387. if (path->type != PATH_SEGMENT_LIST)
  388. return;
  389. if ((path->num_segments >= 3) /* check for closure */
  390. && (path->segments[path->num_segments - 1].p.x == path->segments[0].p.x)
  391. && (path->segments[path->num_segments - 1].p.y == path->segments[0].p.y))
  392. closed = true;
  393. else
  394. closed = false; /* 2-point ones should be open */
  395. p = path->segments[0].p; /* initial seg should be a moveto */
  396. sprintf (page->point, "M%.5g,%.5g ",
  397. p.x, p.y);
  398. _update_buffer (page);
  399. oldpoint = p;
  400. for (i = 1; i < path->num_segments; i++)
  401. {
  402. plPathSegmentType type;
  403. plPoint pc, pd;
  404. type = path->segments[i].type;
  405. p = path->segments[i].p;
  406. pc = path->segments[i].pc;
  407. pd = path->segments[i].pd;
  408. if (closed
  409. && i == path->num_segments - 1
  410. && type == S_LINE)
  411. continue; /* i.e. don't end with line-as-closepath */
  412. switch ((int)type)
  413. {
  414. case (int)S_LINE:
  415. if (p.y == oldpoint.y)
  416. sprintf (page->point, "H%.5g ",
  417. p.x);
  418. else if (p.x == oldpoint.x)
  419. sprintf (page->point, "V%.5g ",
  420. p.y);
  421. else
  422. sprintf (page->point, "L%.5g,%.5g ",
  423. p.x, p.y);
  424. break;
  425. case (int)S_ARC:
  426. {
  427. double radius;
  428. double angle;
  429. /* compute angle in radians, range -pi..pi */
  430. angle = _angle_of_arc (oldpoint, p, pc);
  431. radius = sqrt ((p.x - pc.x)*(p.x - pc.x)
  432. + (p.y - pc.y)*(p.y - pc.y));
  433. sprintf (page->point, "A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g ",
  434. radius, radius,
  435. 0.0, /* rotation of x-axis of ellipse */
  436. 0, /* large-arc-flag, 0/1 = small/large */
  437. angle >= 0.0 ? 1 : 0,/* sweep-flag, 0/1 = clock/c'clock */
  438. p.x, p.y);
  439. }
  440. break;
  441. case (int)S_ELLARC:
  442. {
  443. double cross, mixing_angle, rx, ry, theta;
  444. plVector u, v, semi_axis_1, semi_axis_2;
  445. bool clockwise;
  446. /* conjugate radial vectors for the quarter-ellipse */
  447. u.x = oldpoint.x - pc.x;
  448. u.y = oldpoint.y - pc.y;
  449. v.x = p.x - pc.x;
  450. v.y = p.y - pc.y;
  451. cross = u.x * v.y - v.x * u.y;
  452. clockwise = cross < 0.0 ? true : false;
  453. /* angle by which they should be mixed, to yield vectors along
  454. the major and minor axes */
  455. mixing_angle = 0.5 * _xatan2 (2.0 * (u.x * v.x + u.y * v.y),
  456. u.x * u.x + u.y * u.y
  457. - v.x * v.x + v.y * v.y);
  458. /* semi-axis vectors */
  459. semi_axis_1.x = u.x * cos(mixing_angle) + v.x * sin(mixing_angle);
  460. semi_axis_1.y = u.y * cos(mixing_angle) + v.y * sin(mixing_angle);
  461. semi_axis_2.x = (u.x * cos(mixing_angle + M_PI_2)
  462. + v.x * sin(mixing_angle + M_PI_2));
  463. semi_axis_2.y = (u.y * cos(mixing_angle + M_PI_2)
  464. + v.y * sin(mixing_angle + M_PI_2));
  465. /* semi-axis lengths */
  466. rx = sqrt (semi_axis_1.x * semi_axis_1.x
  467. + semi_axis_1.y * semi_axis_1.y);
  468. ry = sqrt (semi_axis_2.x * semi_axis_2.x
  469. + semi_axis_2.y * semi_axis_2.y);
  470. /* angle of inclination of first semi-axis */
  471. theta = _xatan2 (semi_axis_1.y, semi_axis_1.x);
  472. /* compensate for possible roundoff error: treat a very small inclination
  473. angle of the 1st semi-axis, relative to the x-axis, as zero */
  474. #define VERY_SMALL_ANGLE 1e-10
  475. if (theta < VERY_SMALL_ANGLE && theta > -(VERY_SMALL_ANGLE))
  476. theta = 0.0;
  477. sprintf (page->point, "A%.5g,%.5g,%.5g,%d,%d,%.5g,%.5g ",
  478. rx, ry,
  479. theta * 180.0 / M_PI, /* rotation of x-axis of ellipse */
  480. 0, /* large-arc-flag, 0/1 = small/large */
  481. clockwise ? 0 : 1, /* sweep-flag, 0/1 = clock/c'clock */
  482. p.x, p.y);
  483. }
  484. break;
  485. case (int)S_QUAD:
  486. sprintf (page->point, "Q%.5g,%.5g,%.5g,%.5g ",
  487. pc.x, pc.y, p.x, p.y);
  488. break;
  489. case (int)S_CUBIC:
  490. sprintf (page->point, "C%.5g,%.5g,%.5g,%.5g,%.5g,%.5g ",
  491. pc.x, pc.y, pd.x, pd.y, p.x, p.y);
  492. break;
  493. default: /* shouldn't happen */
  494. break;
  495. }
  496. _update_buffer (page);
  497. oldpoint = p;
  498. }
  499. if (closed)
  500. {
  501. sprintf (page->point, "Z ");
  502. _update_buffer (page);
  503. }
  504. }
  505. static void
  506. write_svg_path_style (plOutbuf *page, const plDrawState *drawstate, bool need_cap, bool need_join)
  507. {
  508. char color_buf[8]; /* enough room for "#ffffff", incl. NUL */
  509. if (drawstate->pen_type)
  510. {
  511. if (drawstate->fgcolor.red != 0
  512. || drawstate->fgcolor.green != 0
  513. || drawstate->fgcolor.blue != 0)
  514. /* non-black, i.e. non-default */
  515. {
  516. sprintf (page->point, "stroke=\"%s\" ",
  517. _libplot_color_to_svg_color (drawstate->fgcolor,
  518. color_buf));
  519. _update_buffer (page);
  520. }
  521. /* should use `px' here to specify user units, per the SVG Authoring
  522. Guide, but ImageMagick objects to that */
  523. sprintf (page->point, "stroke-width=\"%.5g\" ",
  524. drawstate->line_width);
  525. _update_buffer (page);
  526. if (need_cap)
  527. {
  528. if (drawstate->cap_type != PL_CAP_BUTT) /* i.e. not default */
  529. {
  530. sprintf (page->point, "stroke-linecap=\"%s\" ",
  531. svg_cap_style[drawstate->cap_type]);
  532. _update_buffer (page);
  533. }
  534. }
  535. if (need_join)
  536. {
  537. if (drawstate->join_type != PL_JOIN_MITER) /* i.e. not default */
  538. {
  539. sprintf (page->point, "stroke-linejoin=\"%s\" ",
  540. svg_join_style[drawstate->join_type]);
  541. _update_buffer (page);
  542. }
  543. if (drawstate->join_type == PL_JOIN_MITER
  544. && drawstate->miter_limit != PL_DEFAULT_MITER_LIMIT)
  545. {
  546. sprintf (page->point, "stroke-miterlimit=\"%.5g\" ",
  547. drawstate->miter_limit);
  548. _update_buffer (page);
  549. }
  550. }
  551. if ((drawstate->dash_array_in_effect /* user-specified dash array */
  552. && drawstate->dash_array_len > 0)
  553. ||
  554. (drawstate->dash_array_in_effect == false
  555. && drawstate->line_type != PL_L_SOLID)) /* non-solid builtin linetype*/
  556. /* need to specify stroke-array, maybe stroke-offset too */
  557. {
  558. int i;
  559. double *dashbuf, offset;
  560. int num_dashes;
  561. if (drawstate->dash_array_in_effect)
  562. {
  563. dashbuf = (double *)(drawstate->dash_array);
  564. num_dashes = drawstate->dash_array_len;
  565. offset = drawstate->dash_offset;
  566. }
  567. else
  568. /* builtin line type, handcraft a SVG-style dash array for it */
  569. {
  570. const int *dash_array;
  571. double min_sing_val, max_sing_val, min_width, scale;
  572. /* compute maximum singular value of user->device coordinate
  573. map, which we use as a divisive factor to convert size in
  574. NCD frame back to size in the user frame */
  575. _matrix_sing_vals (drawstate->transform.m_user_to_ndc,
  576. &min_sing_val, &max_sing_val);
  577. if (max_sing_val != 0.0)
  578. min_width =
  579. PL_DEFAULT_LINE_WIDTH_AS_FRACTION_OF_DISPLAY_SIZE / max_sing_val;
  580. else
  581. min_width = 0.0;
  582. scale = DMAX(drawstate->line_width, min_width);
  583. /* take normalized dash array (linemode-specific) from
  584. internal table */
  585. dash_array =
  586. _pl_g_line_styles[drawstate->line_type].dash_array;
  587. num_dashes =
  588. _pl_g_line_styles[drawstate->line_type].dash_array_len;
  589. dashbuf = (double *)_pl_xmalloc (num_dashes * sizeof(double));
  590. /* scale length of each dash by current line width, unless
  591. it's too small (see above computation) */
  592. for (i = 0; i < num_dashes; i++)
  593. dashbuf[i] = scale * dash_array[i];
  594. offset = 0.0; /* true for all builtin line types */
  595. }
  596. sprintf (page->point, "stroke-dasharray=\"");
  597. _update_buffer (page);
  598. for (i = 0; i < num_dashes; i++)
  599. {
  600. sprintf (page->point, "%.5g%s",
  601. dashbuf[i],
  602. i < num_dashes - 1 ? ", " : "\"");
  603. _update_buffer (page);
  604. }
  605. if (offset != 0.0) /* not default */
  606. {
  607. /* should use `px' here to specify user units, per the SVG
  608. Authoring Guide, but ImageMagick objects to that */
  609. sprintf (page->point, "stroke-dashoffset=\"%.5g\" ",
  610. offset);
  611. _update_buffer (page);
  612. }
  613. if (drawstate->dash_array_in_effect == false)
  614. /* have a handcrafted dash array to free */
  615. free (dashbuf);
  616. }
  617. else
  618. /* solid, so don't specify stroke-dasharray or stroke-offset */
  619. {
  620. }
  621. }
  622. else
  623. {
  624. sprintf (page->point, "stroke=\"none\" ");
  625. _update_buffer (page);
  626. }
  627. if (drawstate->fill_type)
  628. {
  629. sprintf (page->point, "fill=\"%s\" ",
  630. _libplot_color_to_svg_color (drawstate->fillcolor, color_buf));
  631. _update_buffer (page);
  632. if (drawstate->fill_rule_type != PL_FILL_ODD_WINDING) /* not default */
  633. {
  634. sprintf (page->point, "fill-rule=\"%s\" ",
  635. svg_fill_style[drawstate->fill_rule_type]);
  636. _update_buffer (page);
  637. }
  638. }
  639. }