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