File: p_path.c

package info (click to toggle)
plotutils 2.4.1-11
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 11,676 kB
  • ctags: 6,967
  • sloc: ansic: 76,305; sh: 15,172; cpp: 12,403; yacc: 2,604; makefile: 888; lex: 144
file content (768 lines) | stat: -rw-r--r-- 26,213 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
/* This file contains the internal paint_path() and paint_paths() methods,
   which the public method endpath() is a wrapper around. */

/* This version is for PSPlotters.  By construction, for PSPlotters our
   path storage buffer, if it contains a path of segment array type,
   contains a sequence of line segments only (no other path elements such
   as arcs or Beziers are allowed).  That's because our PS driver draws
   objects by calling PS functions defined in the idraw header, and the
   header doesn't include functions that draw arcs or Beziers. */

#include "sys-defines.h"
#include "extern.h"

/* 16-bit brush patterns for idraw (1 = on, 0 = off), indexed by our
   internal numbering of line styles, i.e. by L_{SOLID,DOTTED,DOTDASHED,
   SHORTDASHED,LONGDASHED,DOTDOTDASHED,DOTDOTDOTDASHED} */ 
const long _idraw_brush_pattern[NUM_LINE_STYLES] = 
{ 0xffff, 0x8888, 0xfc30, 0xf0f0, 0xffc0, 0xfccc, 0xfdb6 };

/* PS join styles, indexed by internal number (miter/rd./bevel/triangular) */
const int _ps_join_style[] =
{ PS_LINE_JOIN_MITER, PS_LINE_JOIN_ROUND, PS_LINE_JOIN_BEVEL, PS_LINE_JOIN_ROUND };

/* PS cap styles, indexed by internal number (butt/rd./project/triangular) */
const int _ps_cap_style[] =
{ PS_LINE_CAP_BUTT, PS_LINE_CAP_ROUND, PS_LINE_CAP_PROJECT, PS_LINE_CAP_ROUND };

void
#ifdef _HAVE_PROTOS
_p_paint_path (S___(Plotter *_plotter))
#else
_p_paint_path (S___(_plotter))
     S___(Plotter *_plotter;)
#endif
{
  double granularity;

  if (_plotter->drawstate->pen_type == 0
      && _plotter->drawstate->fill_type == 0)
    /* nothing to draw */
    return;

  /* Compute `granularity': factor by which user-frame coordinates will be
     scaled up, so that when they're emitted as integers (which idraw
     requires), resolution loss won't be excessive.  CTM factors will be
     scaled down by this factor. */
  {
    /* compute norm of user->device affine transformation */
    double norm, min_sing_val, max_sing_val;
    
    /* This minimum singular value isn't really the norm.  But it's the
       nominal device-frame line width divided by the actual user-frame
       line-width (see g_linewidth.c), and that's what we need. */
    _matrix_sing_vals (_plotter->drawstate->transform.m,
		       &min_sing_val, &max_sing_val);
    norm = min_sing_val;
    
    granularity = norm / (PS_MIN_RESOLUTION);
  }

  if (granularity == 0.0)
    /* must have norm = 0, quit now to avoid division by zero */
    return;

  switch ((int)_plotter->drawstate->path->type)
    {
    case (int)PATH_SEGMENT_LIST:
      {
	bool closed, closed_int;
	int i, numpoints, index_start, index_increment;
	int polyline_len;
	plIntPoint *xarray;
  
	/* sanity checks */
	if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
	  break;
	if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */
	  break;

	if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
	  closed = true;
	else
	  closed = false;		/* 2-point ones should be open */
	
	/* scale up each point coordinate by granularity factor and round
	   it to closest integer, removing runs of points with the same
	   scaled integer coordinates */
	xarray = (plIntPoint *)_plot_xmalloc (_plotter->drawstate->path->num_segments * sizeof(plIntPoint));
	polyline_len = 0;
	for (i = 0; i < _plotter->drawstate->path->num_segments; i++)
	  {
	    plPoint datapoint;
	    int x_int, y_int;
	    
	    datapoint = _plotter->drawstate->path->segments[i].p;
	    x_int = IROUND(granularity * datapoint.x);
	    y_int = IROUND(granularity * datapoint.y);
	    
	    if ((polyline_len == 0) 
		|| (x_int != xarray[polyline_len-1].x) 
		|| (y_int != xarray[polyline_len-1].y))
	      /* add point, in integer coordinates, to the array */
	      {
		xarray[polyline_len].x = x_int;
		xarray[polyline_len].y = y_int;
		polyline_len++;
	      }
	  }

	/* Handle awkward cases: due to rounding and elimination of runs,
	   may be only 1 or 2 distinct vertices left in the polyline. */
	if (polyline_len == 1)
	  /* add a second point */
	  {
	    xarray[1] = xarray[0];
	    polyline_len = 2;
	  }
	if (polyline_len == 2)
	  closed_int = false;	/* 2-point ones should be open */
	else
	  closed_int = closed;

	/* number of points to be output, given that we're quantizing
	   coordinates and removing runs */
	numpoints = polyline_len - (closed_int ? 1 : 0);

	/* emit prolog and idraw instructions: start of MLine or Poly */
	if (closed_int)
	  strcpy (_plotter->data->page->point, "Begin %I Poly\n");
	else
	  strcpy (_plotter->data->page->point, "Begin %I MLine\n");
	_update_buffer (_plotter->data->page);
	
	/* emit common attributes: CTM, fill rule, cap and join styles and
	   miter limit, dash array, foreground and background colors, and
	   idraw brush. */
	_p_emit_common_attributes (S___(_plotter));
	
	/* emit transformation matrix (all 6 elements) */
	strcpy (_plotter->data->page->point, "%I t\n["); 
	_update_buffer (_plotter->data->page);
	for (i = 0; i < 6; i++)
	  {
	    if ((i==0) || (i==1) || (i==2) || (i==3))
	      sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i] / granularity);
	    else
	      sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i]);
	    _update_buffer (_plotter->data->page);
	  }
	strcpy (_plotter->data->page->point, "\
] concat\n");
	_update_buffer (_plotter->data->page);
	
	/* emit idraw instruction: number of points in line */
	sprintf (_plotter->data->page->point, "\
%%I %d\n", 
		 numpoints);
	_update_buffer (_plotter->data->page);
	
	/* if polyline is closed, loop through points _backward_, since the
	   `Poly' function in the idraw prologue draws closed polylines in
	   reverse, and we want the dasharray to be interpreted correctly */
	if (closed_int)
	  {
	    index_start = numpoints - 1;
	    index_increment = -1;
	  }
	else
	  {
	    index_start = 0;
	    index_increment = 1;
	  }
	for (i = index_start; 
	     i >= 0 && i <= numpoints - 1; 
	     i += index_increment)
	  {
	    /* output the data point */
	    sprintf (_plotter->data->page->point, "\
%d %d\n",
		     xarray[i].x, xarray[i].y);
	    _update_buffer (_plotter->data->page);
	  }
	
	if (closed_int)
	  sprintf (_plotter->data->page->point, "\
%d Poly\n\
End\n\n", numpoints);
	else
	  sprintf (_plotter->data->page->point, "\
%d MLine\n\
End\n\n", numpoints);
	_update_buffer (_plotter->data->page);

	/* free temporary storage for quantized points */
	free (xarray);

	/* Update bounding box, by iterating over segments in the original
	   segment array (no quantizing, please).  But for consistency,
	   iterate in much the same way as above. */

	/* number of points that we'd have emitted, had we not quantized
	   and removed runs */
	numpoints = 
	  _plotter->drawstate->path->num_segments - (closed ? 1 : 0);

	if (closed)
	  {
	    index_start = numpoints - 1;
	    index_increment = -1;
	  }
	else
	  {
	    index_start = 0;
	    index_increment = 1;
	  }
	for (i = index_start; 
	     i >= 0 && i <= numpoints - 1; 
	     i += index_increment)
	  {
	    if (!closed && ((i == 0) || (i == numpoints - 1)))
	      /* an end rather than a join */
	      {
		int j;
		
		j = (i == 0 ? 1 : numpoints - 2);
		_set_line_end_bbox (_plotter->data->page,
				    _plotter->drawstate->path->segments[i].p.x,
				    _plotter->drawstate->path->segments[i].p.y,
				    _plotter->drawstate->path->segments[j].p.x,
				    _plotter->drawstate->path->segments[j].p.y,
				    _plotter->drawstate->line_width,
				    _plotter->drawstate->cap_type,
				    _plotter->drawstate->transform.m);
	      }
	    else
	      /* a join rather than an end */
	      {
		int a, b, c;
		
		if (closed && i == 0) /* wrap */
		  {
		    a = numpoints - 1;
		    b = 0;
		    c = 1;
		  }
		else		/* normal join */
		  {
		    a = i - 1;
		    b = i;
		    c = i + 1;
		  }
		_set_line_join_bbox(_plotter->data->page,
				    _plotter->drawstate->path->segments[a].p.x,
				    _plotter->drawstate->path->segments[a].p.y,
				    _plotter->drawstate->path->segments[b].p.x,
				    _plotter->drawstate->path->segments[b].p.y,
				    _plotter->drawstate->path->segments[c].p.x,
				    _plotter->drawstate->path->segments[c].p.y,
				    _plotter->drawstate->line_width,
				    _plotter->drawstate->join_type,
				    _plotter->drawstate->miter_limit,
				    _plotter->drawstate->transform.m);
	      }
	  }
      }
      break;

    case (int)PATH_BOX:
      {
	int i;

	/* emit prolog and idraw instructions: start of Rect */
	strcpy (_plotter->data->page->point, "Begin %I Rect\n");
	_update_buffer (_plotter->data->page);
	
	/* emit common attributes: CTM, fill rule, cap and join styles and
	   miter limit, dash array, foreground and background colors, and
	   idraw brush. */
	_p_emit_common_attributes (S___(_plotter));
	
	/* emit transformation matrix (all 6 elements) */
	strcpy (_plotter->data->page->point, "%I t\n["); 
	_update_buffer (_plotter->data->page);
	for (i = 0; i < 6; i++)
	  {
	    if ((i==0) || (i==1) || (i==2) || (i==3))
	      sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i] / granularity);
	    else
	      sprintf (_plotter->data->page->point, "%.7g ", _plotter->drawstate->transform.m[i]);
	    _update_buffer (_plotter->data->page);
	  }
	strcpy (_plotter->data->page->point, "\
] concat\n");
	_update_buffer (_plotter->data->page);
	
	/* output the two defining vertices (preceded by an empty idraw
           instruction), and wind things up */
	sprintf (_plotter->data->page->point, "\
%%I\n\
%d %d %d %d Rect\n\
End\n\n",
		 IROUND(granularity * _plotter->drawstate->path->p0.x),
		 IROUND(granularity * _plotter->drawstate->path->p0.y),
		 IROUND(granularity * _plotter->drawstate->path->p1.x),
		 IROUND(granularity * _plotter->drawstate->path->p1.y));
	_update_buffer (_plotter->data->page);
		 
	/* update bounding box */
	_set_line_join_bbox(_plotter->data->page,
			    _plotter->drawstate->path->p0.x,
			    _plotter->drawstate->path->p1.y,
			    _plotter->drawstate->path->p0.x,
			    _plotter->drawstate->path->p0.y,
			    _plotter->drawstate->path->p1.x,
			    _plotter->drawstate->path->p0.y,
			    _plotter->drawstate->line_width,
			    _plotter->drawstate->join_type,
			    _plotter->drawstate->miter_limit,
			    _plotter->drawstate->transform.m);
	_set_line_join_bbox(_plotter->data->page,
			    _plotter->drawstate->path->p0.x,
			    _plotter->drawstate->path->p0.y,
			    _plotter->drawstate->path->p1.x,
			    _plotter->drawstate->path->p0.y,
			    _plotter->drawstate->path->p1.x,
			    _plotter->drawstate->path->p1.y,
			    _plotter->drawstate->line_width,
			    _plotter->drawstate->join_type,
			    _plotter->drawstate->miter_limit,
			    _plotter->drawstate->transform.m);
	_set_line_join_bbox(_plotter->data->page,
			    _plotter->drawstate->path->p1.x,
			    _plotter->drawstate->path->p0.y,
			    _plotter->drawstate->path->p1.x,
			    _plotter->drawstate->path->p1.y,
			    _plotter->drawstate->path->p0.x,
			    _plotter->drawstate->path->p1.y,
			    _plotter->drawstate->line_width,
			    _plotter->drawstate->join_type,
			    _plotter->drawstate->miter_limit,
			    _plotter->drawstate->transform.m);
	_set_line_join_bbox(_plotter->data->page,
			    _plotter->drawstate->path->p1.x,
			    _plotter->drawstate->path->p1.y,
			    _plotter->drawstate->path->p0.x,
			    _plotter->drawstate->path->p1.y,
			    _plotter->drawstate->path->p0.x,
			    _plotter->drawstate->path->p0.y,
			    _plotter->drawstate->line_width,
			    _plotter->drawstate->join_type,
			    _plotter->drawstate->miter_limit,
			    _plotter->drawstate->transform.m);
      }
      break;

    case (int)PATH_CIRCLE:
      {
	plPoint pc;
	double radius;

	pc = _plotter->drawstate->path->pc;
	radius = _plotter->drawstate->path->radius;
	
	/* final arg flags this for idraw as a circle, not an ellipse */
	_p_fellipse_internal (R___(_plotter) pc.x, pc.y, radius, radius,
			      0.0, true);
      }
      break;
      
    case (int)PATH_ELLIPSE:
      {
	double x = _plotter->drawstate->path->pc.x;
	double y = _plotter->drawstate->path->pc.y;
	double rx = _plotter->drawstate->path->rx;
	double ry = _plotter->drawstate->path->ry;
	double angle = _plotter->drawstate->path->angle;	

	/* final arg flags this for idraw as an ellipse, not a circle */
	_p_fellipse_internal (R___(_plotter) x, y, rx, ry, angle, false);
      }
      break;

    default:			/* shouldn't happen */
      break;
    }
}
      
void
#ifdef _HAVE_PROTOS
_p_fellipse_internal (R___(Plotter *_plotter) double x, double y, double rx, double ry, double angle, bool circlep)
#else
_p_fellipse_internal (R___(_plotter) x, y, rx, ry, angle, circlep)
     S___(Plotter *_plotter;)
     double x, y, rx, ry, angle;
     bool circlep;		/* drawn as a circle in user frame? */
#endif
{  
  if (_plotter->drawstate->pen_type || _plotter->drawstate->fill_type)
    /* have something to draw */
    {
      double granularity;
      double costheta, sintheta;
      double offcenter_rotation_matrix[6];
      double ellipse_transformation_matrix[6];
      int i;

      /* emit prolog instruction and idraw directive: start of Elli or Circ */
      if (circlep)
	strcpy (_plotter->data->page->point, "Begin %I Circ\n");
      else
	strcpy (_plotter->data->page->point, "Begin %I Elli\n");
      _update_buffer(_plotter->data->page);
      
      /* emit common attributes: CTM, fill rule, cap and join styles and
	 miter limit, dash array, foreground and background colors, and
	 idraw brush. */
      granularity = _p_emit_common_attributes (S___(_plotter));

      /* An affine tranformation must be applied to the ellipse produced by
	 the Elli routine in the idraw prologue, to turn it into the
	 ellipse we want.  The Elli routine produces an ellipse with
	 specified semi-axes, aligned parallel to the coordinate axes in
	 user space, and centered on the point (x,y).  I.e. it produces,
	 symbolically,

	 [unit circle centered on (0,0)] S T

	 where S is a diagonal matrix that scales the unit circle to give
	 the specified semi-axis lengths, and T translates (0,0) to (x,y).
	 This is not what we want, since the ellipse is not rotated (it has
	 zero inclination angle).  What we want is
 
	 [unit circle centered on (0,0)] S R T

	 where R is a rotation matrix.  This may be rewritten as
	 
	 [unit circle centered on (0,0)] S T  (T^{-1} R T)

	 where T^{-1} R T is a so-called offcenter rotation matrix, which
	 rotates about the point (x,y).  So the ellipse transformation
	 matrix we'll place in the PS code will be (T^{-1} R T) times the
	 matrix that transforms from user space to device space. */

      costheta = cos (M_PI * angle / 180.0);
      sintheta = sin (M_PI * angle / 180.0);
      
      offcenter_rotation_matrix[0] = costheta; /* 1st 4 els are those of R */
      offcenter_rotation_matrix[1] = sintheta;
      offcenter_rotation_matrix[2] = - sintheta;
      offcenter_rotation_matrix[3] = costheta;
      offcenter_rotation_matrix[4] = x * (1.0 - costheta) + y * sintheta;
      offcenter_rotation_matrix[5] = y * (1.0 - costheta) - x * sintheta;
  
      _matrix_product (offcenter_rotation_matrix,
		       _plotter->drawstate->transform.m,
		       ellipse_transformation_matrix);
  
      /* emit idraw directive: transformation matrix (all 6 elements) */
      sprintf (_plotter->data->page->point, "%%I t\n[");
      _update_buffer(_plotter->data->page);
      for (i = 0; i < 6; i++)
	{
	  if ((i==0) || (i==1) || (i==2) || (i==3))
	    sprintf (_plotter->data->page->point, "%.7g ", 
		     ellipse_transformation_matrix[i] / granularity);
	  else
	    sprintf (_plotter->data->page->point, "%.7g ", 
		     ellipse_transformation_matrix[i]);
	  _update_buffer(_plotter->data->page);
	}
      sprintf (_plotter->data->page->point, "] concat\n");
      _update_buffer(_plotter->data->page);
      
      /* emit idraw directive: draw Elli, and end Elli (or same for Circ) */
      if (circlep)
	sprintf (_plotter->data->page->point, "%%I\n%d %d %d Circ\nEnd\n\n", 
		 IROUND(granularity * x), IROUND(granularity * y), 
		 IROUND(granularity * rx));
      else
	sprintf (_plotter->data->page->point, "%%I\n%d %d %d %d Elli\nEnd\n\n", 
		 IROUND(granularity * x), IROUND(granularity * y), 
		 IROUND(granularity * rx), IROUND(granularity * ry));
      _update_buffer(_plotter->data->page);
      
      /* update bounding box */
      _set_ellipse_bbox (_plotter->data->page, x, y, rx, ry, costheta, sintheta, 
			 _plotter->drawstate->line_width,
			 _plotter->drawstate->transform.m);
    }
}

/* Emit the common attributes, for PS and idraw, of any path object, either
   polyline, ellipse, or box.  This includes the CTM, fill rule, cap and
   join styles and miter limit, dash array, foreground and background
   colors, and idraw brush.
   
   Return value is the `granularity': a factor by which user-frame
   coordinates, when emitted to the output file as integers, should be
   scaled up.  This is to avoid loss of precision when using integer
   coordinates.  The CTM emitted here will automatically compensate for the
   granularity factor.

   Note: some of the functions that call this one (see _p_paint_path()
   above) need to compute the granularity themselves, since they can't need
   to quit if the granularity is zero, without calling this function . */

double
#ifdef _HAVE_PROTOS
_p_emit_common_attributes (S___(Plotter *_plotter))
#else
_p_emit_common_attributes (S___(_plotter))
     S___(Plotter *_plotter;)
#endif
{
  bool singular_map;
  int i;
  double invnorm = 0.0, granularity = 1.0;
  double linewidth_adjust = 1.0;
  double min_sing_val, max_sing_val, norm;

  /* compute norm of user->device affine transformation */

  /* This minimum singular value isn't really the norm.  But it's the
     nominal device-frame line width divided by the actual user-frame
     line-width (see g_linewidth.c), and that's what we need. */
  _matrix_sing_vals (_plotter->drawstate->transform.m,
		     &min_sing_val, &max_sing_val);
  norm = min_sing_val;

  /* granularity = scaleup factor for user coordinates, so that when
     they're emitted as integers, resolution loss won't be excessive.
     CTM entries will be scaled down by this factor. */
  granularity = norm / (PS_MIN_RESOLUTION);
  if (norm != 0.0)
    {
      /* invnorm is `norm' of device->user coordinate transformation */
      invnorm = 1.0 / norm;
      singular_map = false;
    }
  else
    singular_map = true;

  /* redefine `originalCTM' matrix, which is the CTM applied when the
     polyline is stroked (as opposed to drawn).  We define it to be the
     same as the one in effect when the polyline was drawn. */
  if (singular_map != true)
    {
      int integer_linewidth = _plotter->drawstate->quantized_device_line_width;
      double double_linewidth = _plotter->drawstate->device_line_width;

      /* adjustment to CTM needed, due to our specifying line widths as
         integers */
      if (integer_linewidth != 0)
	linewidth_adjust = double_linewidth / integer_linewidth;
      else
	linewidth_adjust = 1.0;

      strcpy (_plotter->data->page->point, "[");
      _update_buffer (_plotter->data->page);

      for (i = 0; i < 4; i++)
	{
	  sprintf (_plotter->data->page->point, "%.7g ", 
		   linewidth_adjust * invnorm * _plotter->drawstate->transform.m[i]);
	  _update_buffer (_plotter->data->page);
	}
      _update_buffer (_plotter->data->page);
      strcpy (_plotter->data->page->point, "\
0 0 ] trueoriginalCTM originalCTM\n\
concatmatrix pop\n");
      _update_buffer (_plotter->data->page);
    }
  
  /* specify cap style and join style, and miter limit if mitering */
  if (_plotter->drawstate->join_type == JOIN_MITER)
    sprintf (_plotter->data->page->point, "\
%d setlinecap %d setlinejoin %.4g setmiterlimit\n",
	     _ps_cap_style[_plotter->drawstate->cap_type], 
	     _ps_join_style[_plotter->drawstate->join_type],
	     _plotter->drawstate->miter_limit);
  else
    sprintf (_plotter->data->page->point, "\
%d setlinecap %d setlinejoin\n",
	     _ps_cap_style[_plotter->drawstate->cap_type], 
	     _ps_join_style[_plotter->drawstate->join_type]);
  _update_buffer (_plotter->data->page);
  
  /* specify fill rule (i.e. whether to use even-odd filling) */
  if (_plotter->drawstate->fill_rule_type == FILL_NONZERO_WINDING)
    sprintf (_plotter->data->page->point, "\
/eoFillRule false def\n");
  else
    sprintf (_plotter->data->page->point, "\
/eoFillRule true def\n");
  _update_buffer (_plotter->data->page);
  
  if (_plotter->drawstate->pen_type != 0)
    /* pen is present, so will brush an outline of the path */
    {
      int num_dashes;
      double scale;
      double *dashbuf, dash_cycle_length, offset;

      if (_plotter->drawstate->dash_array_in_effect)
	/* have user-specified dash array */
	{
	  /* idraw instruction: brush type (spec'd as bit vector, but for now
	     we just use a solid brush */
	  sprintf (_plotter->data->page->point, "\
%%I b %ld\n", 
		   (long int)0xffff);
	  _update_buffer (_plotter->data->page);
	  
	  num_dashes = _plotter->drawstate->dash_array_len;
	  if (num_dashes > 0)
	    dashbuf = (double *)_plot_xmalloc (num_dashes * sizeof(double));
	  else
	    dashbuf = NULL;	/* solid line */
	  /* take the adjustment to the CTM into account */
	  scale = norm / linewidth_adjust;
	  
	  dash_cycle_length = 0.0;
	  for (i = 0; i < num_dashes; i++)
	    {
	      double dashlen;
	      
	      dashlen = _plotter->drawstate->dash_array[i];
	      dash_cycle_length += dashlen;
	      dashbuf[i] = scale * dashlen;
	    }
	  
	  if (dash_cycle_length > 0.0)
	    /* choose an offset in range 0..true_cycle_length */
	    {
	      double true_cycle_length;
	      
	      offset = _plotter->drawstate->dash_offset;
	      true_cycle_length = 
		dash_cycle_length * (num_dashes % 2 == 1 ? 2 : 1);
	      while (offset < 0.0)
		offset += true_cycle_length;
	      offset = fmod (offset, true_cycle_length);
	      offset *= scale;
	    }
	  else
	    offset = 0.0;
	}
      else
	/* have one of the canonical line types */
	{
	  /* idraw brush type (spec'd as bit vector) */
	  sprintf (_plotter->data->page->point, "\
%%I b %ld\n", 
		   _idraw_brush_pattern[_plotter->drawstate->line_type]);
	  _update_buffer (_plotter->data->page);
	  
	  if (_plotter->drawstate->line_type == L_SOLID)
	    {
	      num_dashes = 0;
	      dashbuf = NULL;
	      offset = 0.0;
	    }
	  else
	    {
	      const int *dash_array;
	      double display_size_in_points, min_dash_unit;
	      
	      /* compute PS dash array for this line type */
	      dash_array = 
		_line_styles[_plotter->drawstate->line_type].dash_array;
	      num_dashes =
		_line_styles[_plotter->drawstate->line_type].dash_array_len;
	      dashbuf = (double *)_plot_xmalloc (num_dashes * sizeof(double));
	      
	      /* scale the array of integers by line width (actually by
		 floored line width) */
	      display_size_in_points = 
		DMIN(_plotter->data->xmax - _plotter->data->xmin,
		     _plotter->data->ymax - _plotter->data->ymin);
	      min_dash_unit = (MIN_DASH_UNIT_AS_FRACTION_OF_DISPLAY_SIZE 
			       * display_size_in_points);
	      scale = DMAX(min_dash_unit,
			   _plotter->drawstate->device_line_width);
	      /* take the adjustment to the CTM into account */
	      scale /= linewidth_adjust;
	      
	      for (i = 0; i < num_dashes; i++)
		dashbuf[i] = scale * dash_array[i];
	      offset = 0.0;
	    }
	}

      /* PS instruction: SetB (i.e. setbrush), with args
	 LineWidth, LeftArrow, RightArrow, DashArray, DashOffset. */
      /* Note LineWidth must be an integer for idraw compatibility. */
      
      /* emit dash array */
      sprintf (_plotter->data->page->point, "%d 0 0 [ ", 
	       _plotter->drawstate->quantized_device_line_width);
      _update_buffer (_plotter->data->page);
      for (i = 0; i < num_dashes; i++)
	{
	  sprintf (_plotter->data->page->point, "%.3g ", dashbuf[i]);
	  _update_buffer (_plotter->data->page);
	}
      sprintf (_plotter->data->page->point, "] %.3g SetB\n", offset);
      _update_buffer (_plotter->data->page);
      free (dashbuf);
    }
  else
    /* pen_type = 0, we have no pen to draw with (though we may do filling) */
    {
      sprintf (_plotter->data->page->point, "\
%%I b n\n\
none SetB\n");
      _update_buffer (_plotter->data->page);
    }
  
  /* idraw instruction: set foreground color */
  _p_set_pen_color (S___(_plotter)); /* invoked lazily, when needed */
  sprintf (_plotter->data->page->point, "\
%%I cfg %s\n\
%g %g %g SetCFg\n",
	   _idraw_stdcolornames[_plotter->drawstate->ps_idraw_fgcolor],
	   _plotter->drawstate->ps_fgcolor_red, 
	   _plotter->drawstate->ps_fgcolor_green, 
	   _plotter->drawstate->ps_fgcolor_blue);
  _update_buffer (_plotter->data->page);
  
  /* idraw instruction: set background color */
  _p_set_fill_color (S___(_plotter)); /* invoked lazily, when needed */
  sprintf (_plotter->data->page->point, "\
%%I cbg %s\n\
%g %g %g SetCBg\n",
	   _idraw_stdcolornames[_plotter->drawstate->ps_idraw_bgcolor],
	   _plotter->drawstate->ps_fillcolor_red, 
	   _plotter->drawstate->ps_fillcolor_green, 
	   _plotter->drawstate->ps_fillcolor_blue);
  _update_buffer (_plotter->data->page);
  
  /* includes idraw instruction: set fill pattern */
  if (_plotter->drawstate->fill_type == 0)	/* transparent */
    sprintf (_plotter->data->page->point, "\
%%I p\n\
none SetP\n");
  else			/* filled, i.e. shaded, in the sense of idraw */
    sprintf (_plotter->data->page->point, "\
%%I p\n\
%f SetP\n", 
	     _idraw_stdshadings[_plotter->drawstate->ps_idraw_shading]);
  _update_buffer (_plotter->data->page);
  
  /* return factor we'll later use to scale up user-frame coordinates */
  return granularity;
}

bool
#ifdef _HAVE_PROTOS
_p_paint_paths (S___(Plotter *_plotter))
#else
_p_paint_paths (S___(_plotter))
     S___(Plotter *_plotter;)
#endif
{
  return false;
}