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