p_path.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  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 file contains the internal paint_path() and paint_paths() methods,
  16. which the public method endpath() is a wrapper around. */
  17. /* This version is for PSPlotters. By construction, for PSPlotters our
  18. path storage buffer, if it contains a path of segment array type,
  19. contains a sequence of line segments only (no other path elements such
  20. as arcs or Beziers are allowed). That's because our PS driver draws
  21. objects by calling PS functions defined in the idraw header, and the
  22. header doesn't include functions that draw arcs or Beziers. */
  23. #include "sys-defines.h"
  24. #include "extern.h"
  25. /* 16-bit brush patterns for idraw (1 = on, 0 = off), indexed by our
  26. internal numbering of line types, i.e. by L_{SOLID,DOTTED,DOTDASHED,
  27. SHORTDASHED,LONGDASHED,DOTDOTDASHED,DOTDOTDOTDASHED} */
  28. static const long idraw_brush_pattern[PL_NUM_LINE_TYPES] =
  29. { 0xffff, 0x8888, 0xfc30, 0xf0f0, 0xffc0, 0xfccc, 0xfdb6 };
  30. /* PS join styles, indexed by internal number (miter/rd./bevel/triangular) */
  31. static const int ps_join_style[PL_NUM_JOIN_TYPES] =
  32. { PS_LINE_JOIN_MITER, PS_LINE_JOIN_ROUND, PS_LINE_JOIN_BEVEL, PS_LINE_JOIN_ROUND };
  33. /* PS cap styles, indexed by internal number (butt/rd./project/triangular) */
  34. static const int ps_cap_style[PL_NUM_CAP_TYPES] =
  35. { PS_LINE_CAP_BUTT, PS_LINE_CAP_ROUND, PS_LINE_CAP_PROJECT, PS_LINE_CAP_ROUND };
  36. void
  37. _pl_p_paint_path (S___(Plotter *_plotter))
  38. {
  39. double granularity;
  40. if (_plotter->drawstate->pen_type == 0
  41. && _plotter->drawstate->fill_type == 0)
  42. /* nothing to draw */
  43. return;
  44. /* Compute `granularity': factor by which user-frame coordinates will be
  45. scaled up, so that when they're emitted as integers (which idraw
  46. requires), resolution loss won't be excessive. CTM factors will be
  47. scaled down by this factor. */
  48. {
  49. /* compute norm of user->device affine transformation */
  50. double norm, min_sing_val, max_sing_val;
  51. /* This minimum singular value isn't really the norm. But it's the
  52. nominal device-frame line width divided by the actual user-frame
  53. line-width (see g_linewidth.c), and that's what we need. */
  54. _matrix_sing_vals (_plotter->drawstate->transform.m,
  55. &min_sing_val, &max_sing_val);
  56. norm = min_sing_val;
  57. granularity = norm / (PS_MIN_RESOLUTION);
  58. }
  59. if (granularity == 0.0)
  60. /* must have norm = 0, quit now to avoid division by zero */
  61. return;
  62. switch ((int)_plotter->drawstate->path->type)
  63. {
  64. case (int)PATH_SEGMENT_LIST:
  65. {
  66. bool closed, closed_int;
  67. int i, numpoints, index_start, index_increment;
  68. int polyline_len;
  69. plIntPoint *xarray;
  70. /* sanity checks */
  71. if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
  72. break;
  73. if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */
  74. break;
  75. if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
  76. && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
  77. && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
  78. closed = true;
  79. else
  80. closed = false; /* 2-point ones should be open */
  81. /* scale up each point coordinate by granularity factor and round
  82. it to closest integer, removing runs of points with the same
  83. scaled integer coordinates */
  84. xarray = (plIntPoint *)_pl_xmalloc (_plotter->drawstate->path->num_segments * sizeof(plIntPoint));
  85. polyline_len = 0;
  86. for (i = 0; i < _plotter->drawstate->path->num_segments; i++)
  87. {
  88. plPoint datapoint;
  89. int x_int, y_int;
  90. datapoint = _plotter->drawstate->path->segments[i].p;
  91. x_int = IROUND(granularity * datapoint.x);
  92. y_int = IROUND(granularity * datapoint.y);
  93. if ((polyline_len == 0)
  94. || (x_int != xarray[polyline_len-1].x)
  95. || (y_int != xarray[polyline_len-1].y))
  96. /* add point, in integer coordinates, to the array */
  97. {
  98. xarray[polyline_len].x = x_int;
  99. xarray[polyline_len].y = y_int;
  100. polyline_len++;
  101. }
  102. }
  103. /* Handle awkward cases: due to rounding and elimination of runs,
  104. may be only 1 or 2 distinct vertices left in the polyline. */
  105. if (polyline_len == 1)
  106. /* add a second point */
  107. {
  108. xarray[1] = xarray[0];
  109. polyline_len = 2;
  110. }
  111. if (polyline_len == 2)
  112. closed_int = false; /* 2-point ones should be open */
  113. else
  114. closed_int = closed;
  115. /* number of points to be output, given that we're quantizing
  116. coordinates and removing runs */
  117. numpoints = polyline_len - (closed_int ? 1 : 0);
  118. /* emit prolog and idraw instructions: start of MLine or Poly */
  119. if (closed_int)
  120. strcpy (_plotter->data->page->point, "Begin %I Poly\n");
  121. else
  122. strcpy (_plotter->data->page->point, "Begin %I MLine\n");
  123. _update_buffer (_plotter->data->page);
  124. /* emit common attributes: CTM, fill rule, cap and join styles and
  125. miter limit, dash array, foreground and background colors, and
  126. idraw brush. */
  127. _pl_p_emit_common_attributes (S___(_plotter));
  128. /* emit transformation matrix (all 6 elements) */
  129. strcpy (_plotter->data->page->point, "%I t\n[");
  130. _update_buffer (_plotter->data->page);
  131. for (i = 0; i < 6; i++)
  132. {
  133. if ((i==0) || (i==1) || (i==2) || (i==3))
  134. sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i] / granularity);
  135. else
  136. sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i]);
  137. _update_buffer (_plotter->data->page);
  138. }
  139. strcpy (_plotter->data->page->point, "\
  140. ] concat\n");
  141. _update_buffer (_plotter->data->page);
  142. /* emit idraw instruction: number of points in line */
  143. sprintf (_plotter->data->page->point, "\
  144. %%I %d\n",
  145. numpoints);
  146. _update_buffer (_plotter->data->page);
  147. /* if polyline is closed, loop through points _backward_, since the
  148. `Poly' function in the idraw prologue draws closed polylines in
  149. reverse, and we want the dasharray to be interpreted correctly */
  150. if (closed_int)
  151. {
  152. index_start = numpoints - 1;
  153. index_increment = -1;
  154. }
  155. else
  156. {
  157. index_start = 0;
  158. index_increment = 1;
  159. }
  160. for (i = index_start;
  161. i >= 0 && i <= numpoints - 1;
  162. i += index_increment)
  163. {
  164. /* output the data point */
  165. sprintf (_plotter->data->page->point, "\
  166. %d %d\n",
  167. xarray[i].x, xarray[i].y);
  168. _update_buffer (_plotter->data->page);
  169. }
  170. if (closed_int)
  171. sprintf (_plotter->data->page->point, "\
  172. %d Poly\n\
  173. End\n\n", numpoints);
  174. else
  175. sprintf (_plotter->data->page->point, "\
  176. %d MLine\n\
  177. End\n\n", numpoints);
  178. _update_buffer (_plotter->data->page);
  179. /* free temporary storage for quantized points */
  180. free (xarray);
  181. /* Update bounding box, by iterating over segments in the original
  182. segment array (no quantizing, please). But for consistency,
  183. iterate in much the same way as above. */
  184. /* number of points that we'd have emitted, had we not quantized
  185. and removed runs */
  186. numpoints =
  187. _plotter->drawstate->path->num_segments - (closed ? 1 : 0);
  188. if (closed)
  189. {
  190. index_start = numpoints - 1;
  191. index_increment = -1;
  192. }
  193. else
  194. {
  195. index_start = 0;
  196. index_increment = 1;
  197. }
  198. for (i = index_start;
  199. i >= 0 && i <= numpoints - 1;
  200. i += index_increment)
  201. {
  202. if (!closed && ((i == 0) || (i == numpoints - 1)))
  203. /* an end rather than a join */
  204. {
  205. int j;
  206. j = (i == 0 ? 1 : numpoints - 2);
  207. _set_line_end_bbox (_plotter->data->page,
  208. _plotter->drawstate->path->segments[i].p.x,
  209. _plotter->drawstate->path->segments[i].p.y,
  210. _plotter->drawstate->path->segments[j].p.x,
  211. _plotter->drawstate->path->segments[j].p.y,
  212. _plotter->drawstate->line_width,
  213. _plotter->drawstate->cap_type,
  214. _plotter->drawstate->transform.m);
  215. }
  216. else
  217. /* a join rather than an end */
  218. {
  219. int a, b, c;
  220. if (closed && i == 0) /* wrap */
  221. {
  222. a = numpoints - 1;
  223. b = 0;
  224. c = 1;
  225. }
  226. else /* normal join */
  227. {
  228. a = i - 1;
  229. b = i;
  230. c = i + 1;
  231. }
  232. _set_line_join_bbox(_plotter->data->page,
  233. _plotter->drawstate->path->segments[a].p.x,
  234. _plotter->drawstate->path->segments[a].p.y,
  235. _plotter->drawstate->path->segments[b].p.x,
  236. _plotter->drawstate->path->segments[b].p.y,
  237. _plotter->drawstate->path->segments[c].p.x,
  238. _plotter->drawstate->path->segments[c].p.y,
  239. _plotter->drawstate->line_width,
  240. _plotter->drawstate->join_type,
  241. _plotter->drawstate->miter_limit,
  242. _plotter->drawstate->transform.m);
  243. }
  244. }
  245. }
  246. break;
  247. case (int)PATH_BOX:
  248. {
  249. int i;
  250. /* emit prolog and idraw instructions: start of Rect */
  251. strcpy (_plotter->data->page->point, "Begin %I Rect\n");
  252. _update_buffer (_plotter->data->page);
  253. /* emit common attributes: CTM, fill rule, cap and join styles and
  254. miter limit, dash array, foreground and background colors, and
  255. idraw brush. */
  256. _pl_p_emit_common_attributes (S___(_plotter));
  257. /* emit transformation matrix (all 6 elements) */
  258. strcpy (_plotter->data->page->point, "%I t\n[");
  259. _update_buffer (_plotter->data->page);
  260. for (i = 0; i < 6; i++)
  261. {
  262. if ((i==0) || (i==1) || (i==2) || (i==3))
  263. sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i] / granularity);
  264. else
  265. sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i]);
  266. _update_buffer (_plotter->data->page);
  267. }
  268. strcpy (_plotter->data->page->point, "\
  269. ] concat\n");
  270. _update_buffer (_plotter->data->page);
  271. /* output the two defining vertices (preceded by an empty idraw
  272. instruction), and wind things up */
  273. sprintf (_plotter->data->page->point, "\
  274. %%I\n\
  275. %d %d %d %d Rect\n\
  276. End\n\n",
  277. IROUND(granularity * _plotter->drawstate->path->p0.x),
  278. IROUND(granularity * _plotter->drawstate->path->p0.y),
  279. IROUND(granularity * _plotter->drawstate->path->p1.x),
  280. IROUND(granularity * _plotter->drawstate->path->p1.y));
  281. _update_buffer (_plotter->data->page);
  282. /* update bounding box */
  283. _set_line_join_bbox(_plotter->data->page,
  284. _plotter->drawstate->path->p0.x,
  285. _plotter->drawstate->path->p1.y,
  286. _plotter->drawstate->path->p0.x,
  287. _plotter->drawstate->path->p0.y,
  288. _plotter->drawstate->path->p1.x,
  289. _plotter->drawstate->path->p0.y,
  290. _plotter->drawstate->line_width,
  291. _plotter->drawstate->join_type,
  292. _plotter->drawstate->miter_limit,
  293. _plotter->drawstate->transform.m);
  294. _set_line_join_bbox(_plotter->data->page,
  295. _plotter->drawstate->path->p0.x,
  296. _plotter->drawstate->path->p0.y,
  297. _plotter->drawstate->path->p1.x,
  298. _plotter->drawstate->path->p0.y,
  299. _plotter->drawstate->path->p1.x,
  300. _plotter->drawstate->path->p1.y,
  301. _plotter->drawstate->line_width,
  302. _plotter->drawstate->join_type,
  303. _plotter->drawstate->miter_limit,
  304. _plotter->drawstate->transform.m);
  305. _set_line_join_bbox(_plotter->data->page,
  306. _plotter->drawstate->path->p1.x,
  307. _plotter->drawstate->path->p0.y,
  308. _plotter->drawstate->path->p1.x,
  309. _plotter->drawstate->path->p1.y,
  310. _plotter->drawstate->path->p0.x,
  311. _plotter->drawstate->path->p1.y,
  312. _plotter->drawstate->line_width,
  313. _plotter->drawstate->join_type,
  314. _plotter->drawstate->miter_limit,
  315. _plotter->drawstate->transform.m);
  316. _set_line_join_bbox(_plotter->data->page,
  317. _plotter->drawstate->path->p1.x,
  318. _plotter->drawstate->path->p1.y,
  319. _plotter->drawstate->path->p0.x,
  320. _plotter->drawstate->path->p1.y,
  321. _plotter->drawstate->path->p0.x,
  322. _plotter->drawstate->path->p0.y,
  323. _plotter->drawstate->line_width,
  324. _plotter->drawstate->join_type,
  325. _plotter->drawstate->miter_limit,
  326. _plotter->drawstate->transform.m);
  327. }
  328. break;
  329. case (int)PATH_CIRCLE:
  330. {
  331. plPoint pc;
  332. double radius;
  333. pc = _plotter->drawstate->path->pc;
  334. radius = _plotter->drawstate->path->radius;
  335. /* final arg flags this for idraw as a circle, not an ellipse */
  336. _pl_p_fellipse_internal (R___(_plotter) pc.x, pc.y, radius, radius,
  337. 0.0, true);
  338. }
  339. break;
  340. case (int)PATH_ELLIPSE:
  341. {
  342. double x = _plotter->drawstate->path->pc.x;
  343. double y = _plotter->drawstate->path->pc.y;
  344. double rx = _plotter->drawstate->path->rx;
  345. double ry = _plotter->drawstate->path->ry;
  346. double angle = _plotter->drawstate->path->angle;
  347. /* final arg flags this for idraw as an ellipse, not a circle */
  348. _pl_p_fellipse_internal (R___(_plotter) x, y, rx, ry, angle, false);
  349. }
  350. break;
  351. default: /* shouldn't happen */
  352. break;
  353. }
  354. }
  355. /* ARGS: circlep = drawn as a circle in user frame? */
  356. void
  357. _pl_p_fellipse_internal (R___(Plotter *_plotter) double x, double y, double rx, double ry, double angle, bool circlep)
  358. {
  359. if (_plotter->drawstate->pen_type || _plotter->drawstate->fill_type)
  360. /* have something to draw */
  361. {
  362. double granularity;
  363. double costheta, sintheta;
  364. double offcenter_rotation_matrix[6];
  365. double ellipse_transformation_matrix[6];
  366. int i;
  367. /* emit prolog instruction and idraw directive: start of Elli or Circ */
  368. if (circlep)
  369. strcpy (_plotter->data->page->point, "Begin %I Circ\n");
  370. else
  371. strcpy (_plotter->data->page->point, "Begin %I Elli\n");
  372. _update_buffer(_plotter->data->page);
  373. /* emit common attributes: CTM, fill rule, cap and join styles and
  374. miter limit, dash array, foreground and background colors, and
  375. idraw brush. */
  376. granularity = _pl_p_emit_common_attributes (S___(_plotter));
  377. /* An affine tranformation must be applied to the ellipse produced by
  378. the Elli routine in the idraw prologue, to turn it into the
  379. ellipse we want. The Elli routine produces an ellipse with
  380. specified semi-axes, aligned parallel to the coordinate axes in
  381. user space, and centered on the point (x,y). I.e. it produces,
  382. symbolically,
  383. [unit circle centered on (0,0)] S T
  384. where S is a diagonal matrix that scales the unit circle to give
  385. the specified semi-axis lengths, and T translates (0,0) to (x,y).
  386. This is not what we want, since the ellipse is not rotated (it has
  387. zero inclination angle). What we want is
  388. [unit circle centered on (0,0)] S R T
  389. where R is a rotation matrix. This may be rewritten as
  390. [unit circle centered on (0,0)] S T (T^{-1} R T)
  391. where T^{-1} R T is a so-called offcenter rotation matrix, which
  392. rotates about the point (x,y). So the ellipse transformation
  393. matrix we'll place in the PS code will be (T^{-1} R T) times the
  394. matrix that transforms from user space to device space. */
  395. costheta = cos (M_PI * angle / 180.0);
  396. sintheta = sin (M_PI * angle / 180.0);
  397. offcenter_rotation_matrix[0] = costheta; /* 1st 4 els are those of R */
  398. offcenter_rotation_matrix[1] = sintheta;
  399. offcenter_rotation_matrix[2] = - sintheta;
  400. offcenter_rotation_matrix[3] = costheta;
  401. offcenter_rotation_matrix[4] = x * (1.0 - costheta) + y * sintheta;
  402. offcenter_rotation_matrix[5] = y * (1.0 - costheta) - x * sintheta;
  403. _matrix_product (offcenter_rotation_matrix,
  404. _plotter->drawstate->transform.m,
  405. ellipse_transformation_matrix);
  406. /* emit idraw directive: transformation matrix (all 6 elements) */
  407. sprintf (_plotter->data->page->point, "%%I t\n[");
  408. _update_buffer(_plotter->data->page);
  409. for (i = 0; i < 6; i++)
  410. {
  411. if ((i==0) || (i==1) || (i==2) || (i==3))
  412. sprintf (_plotter->data->page->point, "%.7g ",
  413. ellipse_transformation_matrix[i] / granularity);
  414. else
  415. sprintf (_plotter->data->page->point, "%.7g ",
  416. ellipse_transformation_matrix[i]);
  417. _update_buffer(_plotter->data->page);
  418. }
  419. sprintf (_plotter->data->page->point, "] concat\n");
  420. _update_buffer(_plotter->data->page);
  421. /* emit idraw directive: draw Elli, and end Elli (or same for Circ) */
  422. if (circlep)
  423. sprintf (_plotter->data->page->point, "%%I\n%d %d %d Circ\nEnd\n\n",
  424. IROUND(granularity * x), IROUND(granularity * y),
  425. IROUND(granularity * rx));
  426. else
  427. sprintf (_plotter->data->page->point, "%%I\n%d %d %d %d Elli\nEnd\n\n",
  428. IROUND(granularity * x), IROUND(granularity * y),
  429. IROUND(granularity * rx), IROUND(granularity * ry));
  430. _update_buffer(_plotter->data->page);
  431. /* update bounding box */
  432. _set_ellipse_bbox (_plotter->data->page, x, y, rx, ry, costheta, sintheta,
  433. _plotter->drawstate->line_width,
  434. _plotter->drawstate->transform.m);
  435. }
  436. }
  437. /* Emit the common attributes, for PS and idraw, of any path object, either
  438. polyline, ellipse, or box. This includes the CTM, fill rule, cap and
  439. join styles and miter limit, dash array, foreground and background
  440. colors, and idraw brush.
  441. Return value is the `granularity': a factor by which user-frame
  442. coordinates, when emitted to the output file as integers, should be
  443. scaled up. This is to avoid loss of precision when using integer
  444. coordinates. The CTM emitted here will automatically compensate for the
  445. granularity factor.
  446. Note: some of the functions that call this one (see _pl_p_paint_path()
  447. above) need to compute the granularity themselves, since they can't need
  448. to quit if the granularity is zero, without calling this function . */
  449. double
  450. _pl_p_emit_common_attributes (S___(Plotter *_plotter))
  451. {
  452. bool singular_map;
  453. int i;
  454. double invnorm = 0.0, granularity = 1.0;
  455. double linewidth_adjust = 1.0;
  456. double min_sing_val, max_sing_val, norm;
  457. /* compute norm of user->device affine transformation */
  458. /* This minimum singular value isn't really the norm. But it's the
  459. nominal device-frame line width divided by the actual user-frame
  460. line-width (see g_linewidth.c), and that's what we need. */
  461. _matrix_sing_vals (_plotter->drawstate->transform.m,
  462. &min_sing_val, &max_sing_val);
  463. norm = min_sing_val;
  464. /* granularity = scaleup factor for user coordinates, so that when
  465. they're emitted as integers, resolution loss won't be excessive.
  466. CTM entries will be scaled down by this factor. */
  467. granularity = norm / (PS_MIN_RESOLUTION);
  468. if (norm != 0.0)
  469. {
  470. /* invnorm is `norm' of device->user coordinate transformation */
  471. invnorm = 1.0 / norm;
  472. singular_map = false;
  473. }
  474. else
  475. singular_map = true;
  476. /* redefine `originalCTM' matrix, which is the CTM applied when the
  477. polyline is stroked (as opposed to drawn). We define it to be the
  478. same as the one in effect when the polyline was drawn. */
  479. if (singular_map != true)
  480. {
  481. int integer_linewidth = _plotter->drawstate->quantized_device_line_width;
  482. double double_linewidth = _plotter->drawstate->device_line_width;
  483. /* adjustment to CTM needed, due to our specifying line widths as
  484. integers */
  485. if (integer_linewidth != 0)
  486. linewidth_adjust = double_linewidth / integer_linewidth;
  487. else
  488. linewidth_adjust = 1.0;
  489. strcpy (_plotter->data->page->point, "[");
  490. _update_buffer (_plotter->data->page);
  491. for (i = 0; i < 4; i++)
  492. {
  493. sprintf (_plotter->data->page->point, "%.7g ",
  494. linewidth_adjust * invnorm * _plotter->drawstate->transform.m[i]);
  495. _update_buffer (_plotter->data->page);
  496. }
  497. _update_buffer (_plotter->data->page);
  498. strcpy (_plotter->data->page->point, "\
  499. 0 0 ] trueoriginalCTM originalCTM\n\
  500. concatmatrix pop\n");
  501. _update_buffer (_plotter->data->page);
  502. }
  503. /* specify cap style and join style, and miter limit if mitering */
  504. if (_plotter->drawstate->join_type == PL_JOIN_MITER)
  505. sprintf (_plotter->data->page->point, "\
  506. %d setlinecap %d setlinejoin %.4g setmiterlimit\n",
  507. ps_cap_style[_plotter->drawstate->cap_type],
  508. ps_join_style[_plotter->drawstate->join_type],
  509. _plotter->drawstate->miter_limit);
  510. else
  511. sprintf (_plotter->data->page->point, "\
  512. %d setlinecap %d setlinejoin\n",
  513. ps_cap_style[_plotter->drawstate->cap_type],
  514. ps_join_style[_plotter->drawstate->join_type]);
  515. _update_buffer (_plotter->data->page);
  516. /* specify fill rule (i.e. whether to use even-odd filling) */
  517. if (_plotter->drawstate->fill_rule_type == PL_FILL_NONZERO_WINDING)
  518. sprintf (_plotter->data->page->point, "\
  519. /eoFillRule false def\n");
  520. else
  521. sprintf (_plotter->data->page->point, "\
  522. /eoFillRule true def\n");
  523. _update_buffer (_plotter->data->page);
  524. if (_plotter->drawstate->pen_type != 0)
  525. /* pen is present, so will brush an outline of the path */
  526. {
  527. int num_dashes;
  528. double scale;
  529. double *dashbuf, dash_cycle_length, offset;
  530. if (_plotter->drawstate->dash_array_in_effect)
  531. /* have user-specified dash array */
  532. {
  533. /* idraw instruction: brush type (spec'd as bit vector, but for now
  534. we just use a solid brush */
  535. sprintf (_plotter->data->page->point, "\
  536. %%I b %ld\n",
  537. (long int)0xffff);
  538. _update_buffer (_plotter->data->page);
  539. num_dashes = _plotter->drawstate->dash_array_len;
  540. if (num_dashes > 0)
  541. dashbuf = (double *)_pl_xmalloc (num_dashes * sizeof(double));
  542. else
  543. dashbuf = NULL; /* solid line */
  544. /* take the adjustment to the CTM into account */
  545. scale = norm / linewidth_adjust;
  546. dash_cycle_length = 0.0;
  547. for (i = 0; i < num_dashes; i++)
  548. {
  549. double dashlen;
  550. dashlen = _plotter->drawstate->dash_array[i];
  551. dash_cycle_length += dashlen;
  552. dashbuf[i] = scale * dashlen;
  553. }
  554. if (dash_cycle_length > 0.0)
  555. /* choose an offset in range 0..true_cycle_length */
  556. {
  557. double true_cycle_length;
  558. offset = _plotter->drawstate->dash_offset;
  559. true_cycle_length =
  560. dash_cycle_length * (num_dashes % 2 == 1 ? 2 : 1);
  561. while (offset < 0.0)
  562. offset += true_cycle_length;
  563. offset = fmod (offset, true_cycle_length);
  564. offset *= scale;
  565. }
  566. else
  567. offset = 0.0;
  568. }
  569. else
  570. /* have one of the canonical line types */
  571. {
  572. /* idraw brush type (spec'd as bit vector) */
  573. sprintf (_plotter->data->page->point, "\
  574. %%I b %ld\n",
  575. idraw_brush_pattern[_plotter->drawstate->line_type]);
  576. _update_buffer (_plotter->data->page);
  577. if (_plotter->drawstate->line_type == PL_L_SOLID)
  578. {
  579. num_dashes = 0;
  580. dashbuf = NULL;
  581. offset = 0.0;
  582. }
  583. else
  584. {
  585. const int *dash_array;
  586. double display_size_in_points, min_dash_unit;
  587. /* compute PS dash array for this line type */
  588. dash_array =
  589. _pl_g_line_styles[_plotter->drawstate->line_type].dash_array;
  590. num_dashes =
  591. _pl_g_line_styles[_plotter->drawstate->line_type].dash_array_len;
  592. dashbuf = (double *)_pl_xmalloc (num_dashes * sizeof(double));
  593. /* scale the array of integers by line width (actually by
  594. floored line width) */
  595. display_size_in_points =
  596. DMIN(_plotter->data->xmax - _plotter->data->xmin,
  597. _plotter->data->ymax - _plotter->data->ymin);
  598. min_dash_unit = (PL_MIN_DASH_UNIT_AS_FRACTION_OF_DISPLAY_SIZE
  599. * display_size_in_points);
  600. scale = DMAX(min_dash_unit,
  601. _plotter->drawstate->device_line_width);
  602. /* take the adjustment to the CTM into account */
  603. scale /= linewidth_adjust;
  604. for (i = 0; i < num_dashes; i++)
  605. dashbuf[i] = scale * dash_array[i];
  606. offset = 0.0;
  607. }
  608. }
  609. /* PS instruction: SetB (i.e. setbrush), with args
  610. LineWidth, LeftArrow, RightArrow, DashArray, DashOffset. */
  611. /* Note LineWidth must be an integer for idraw compatibility. */
  612. /* emit dash array */
  613. sprintf (_plotter->data->page->point, "%d 0 0 [ ",
  614. _plotter->drawstate->quantized_device_line_width);
  615. _update_buffer (_plotter->data->page);
  616. for (i = 0; i < num_dashes; i++)
  617. {
  618. sprintf (_plotter->data->page->point, "%.3g ", dashbuf[i]);
  619. _update_buffer (_plotter->data->page);
  620. }
  621. sprintf (_plotter->data->page->point, "] %.3g SetB\n", offset);
  622. _update_buffer (_plotter->data->page);
  623. free (dashbuf);
  624. }
  625. else
  626. /* pen_type = 0, we have no pen to draw with (though we may do filling) */
  627. {
  628. sprintf (_plotter->data->page->point, "\
  629. %%I b n\n\
  630. none SetB\n");
  631. _update_buffer (_plotter->data->page);
  632. }
  633. /* idraw instruction: set foreground color */
  634. _pl_p_set_pen_color (S___(_plotter)); /* invoked lazily, when needed */
  635. sprintf (_plotter->data->page->point, "\
  636. %%I cfg %s\n\
  637. %g %g %g SetCFg\n",
  638. _pl_p_idraw_stdcolornames[_plotter->drawstate->ps_idraw_fgcolor],
  639. _plotter->drawstate->ps_fgcolor_red,
  640. _plotter->drawstate->ps_fgcolor_green,
  641. _plotter->drawstate->ps_fgcolor_blue);
  642. _update_buffer (_plotter->data->page);
  643. /* idraw instruction: set background color */
  644. _pl_p_set_fill_color (S___(_plotter)); /* invoked lazily, when needed */
  645. sprintf (_plotter->data->page->point, "\
  646. %%I cbg %s\n\
  647. %g %g %g SetCBg\n",
  648. _pl_p_idraw_stdcolornames[_plotter->drawstate->ps_idraw_bgcolor],
  649. _plotter->drawstate->ps_fillcolor_red,
  650. _plotter->drawstate->ps_fillcolor_green,
  651. _plotter->drawstate->ps_fillcolor_blue);
  652. _update_buffer (_plotter->data->page);
  653. /* includes idraw instruction: set fill pattern */
  654. if (_plotter->drawstate->fill_type == 0) /* transparent */
  655. sprintf (_plotter->data->page->point, "\
  656. %%I p\n\
  657. none SetP\n");
  658. else /* filled, i.e. shaded, in the sense of idraw */
  659. sprintf (_plotter->data->page->point, "\
  660. %%I p\n\
  661. %f SetP\n",
  662. _pl_p_idraw_stdshadings[_plotter->drawstate->ps_idraw_shading]);
  663. _update_buffer (_plotter->data->page);
  664. /* return factor we'll later use to scale up user-frame coordinates */
  665. return granularity;
  666. }
  667. bool
  668. _pl_p_paint_paths (S___(Plotter *_plotter))
  669. {
  670. return false;
  671. }