1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, 2009, Free Software
3    Foundation, Inc.
4 
5    The GNU plotutils package is free software.  You may redistribute it
6    and/or modify it under the terms of the GNU General Public License as
7    published by the Free Software foundation; either version 2, or (at your
8    option) any later version.
9 
10    The GNU plotutils package is distributed in the hope that it will be
11    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with the GNU plotutils package; see the file COPYING.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
18    Boston, MA 02110-1301, USA. */
19 
20 /* This file contains the internal paint_path() and paint_paths() methods,
21    which the public method endpath() is a wrapper around. */
22 
23 /* This version is for FigPlotters.  By construction, for FigPlotters our
24    path buffer always contains either a segment list, or a rectangle or
25    circle or ellipse object.  If it's a segment list, it consists of either
26    (1) a sequence of line segments, or (2) a single circular arc segment.
27    Those are the only sorts of path that xfig can handle.  (For the last to
28    be included, the map from user to device coordinates must be uniform.)  */
29 
30 #include "sys-defines.h"
31 #include "extern.h"
32 
33 /* subtypes of xfig POLYLINE object type (xfig numbering) */
34 #define P_OPEN 1
35 #define P_BOX 2
36 #define P_CLOSED 3
37 
38 /* subtypes of xfig ELLIPSE object type (xfig numbering) */
39 #define SUBTYPE_ELLIPSE 1	/* ellipse defined by radii */
40 #define SUBTYPE_CIRCLE  3	/* circle defined by radius */
41 
42 /* Fig's line styles, indexed into by internal line number
43    (PL_L_SOLID/PL_L_DOTTED/PL_L_DOTDASHED/PL_L_SHORTDASHED/PL_L_LONGDASHED/PL_L_DOTDOTDASHED) */
44 const int _pl_f_fig_line_style[PL_NUM_LINE_TYPES] =
45 { FIG_L_SOLID, FIG_L_DOTTED, FIG_L_DASHDOTTED, FIG_L_DASHED, FIG_L_DASHED,
46     FIG_L_DASHDOUBLEDOTTED, FIG_L_DASHTRIPLEDOTTED };
47 
48 /* Fig join styles, indexed by internal number (miter/rd./bevel/triangular) */
49 const int _pl_f_fig_join_style[PL_NUM_JOIN_TYPES] =
50 { FIG_JOIN_MITER, FIG_JOIN_ROUND, FIG_JOIN_BEVEL, FIG_JOIN_ROUND };
51 
52 /* Fig cap styles, indexed by internal number (butt/rd./project/triangular) */
53 const int _pl_f_fig_cap_style[PL_NUM_CAP_TYPES] =
54 { FIG_CAP_BUTT, FIG_CAP_ROUND, FIG_CAP_PROJECT, FIG_CAP_ROUND };
55 
56 #define FUZZ 0.0000001
57 
58 void
_pl_f_paint_path(S___ (Plotter * _plotter))59 _pl_f_paint_path (S___(Plotter *_plotter))
60 {
61   if (_plotter->drawstate->pen_type == 0
62       && _plotter->drawstate->fill_type == 0)
63     /* nothing to draw */
64     return;
65 
66   switch ((int)_plotter->drawstate->path->type)
67     {
68     case (int)PATH_SEGMENT_LIST:
69       {
70 	bool closed;
71 	const char *format;
72 	int i, polyline_subtype, line_style;
73 	double nominal_spacing;
74 	double device_line_width;
75 	int quantized_device_line_width;
76 
77 	/* sanity checks */
78 	if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
79 	  break;
80 	if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */
81 	  break;
82 
83 	if (_plotter->drawstate->path->num_segments == 2
84 	    && _plotter->drawstate->path->segments[1].type == S_ARC)
85 	  /* segment buffer contains a single arc, not a polyline */
86 	  {
87 	    double x0 = _plotter->drawstate->path->segments[0].p.x;
88 	    double y0 = _plotter->drawstate->path->segments[0].p.y;
89 	    double x1 = _plotter->drawstate->path->segments[1].p.x;
90 	    double y1 = _plotter->drawstate->path->segments[1].p.y;
91 	    double xc = _plotter->drawstate->path->segments[1].pc.x;
92 	    double yc = _plotter->drawstate->path->segments[1].pc.y;
93 
94 	    _pl_f_draw_arc_internal (R___(_plotter) xc, yc, x0, y0, x1, y1);
95 	    break;
96 	  }
97 
98 	if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
99 	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
100 	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
101 	  closed = true;
102 	else
103 	  closed = false;		/* 2-point ones should be open */
104 
105 	if (closed)
106 	  {
107 	    polyline_subtype = P_CLOSED;
108 	    format = "#POLYLINE [CLOSED]\n%d %d %d %d %d %d %d %d %d %.3f %d %d %d %d %d %d";
109 	  }
110 	else
111 	  {
112 	    polyline_subtype = P_OPEN;
113 	    format = "#POLYLINE [OPEN]\n%d %d %d %d %d %d %d %d %d %.3f %d %d %d %d %d %d";
114 	  }
115 
116 	/* evaluate fig colors lazily, i.e. only when needed */
117 	_pl_f_set_pen_color (S___(_plotter));
118 	_pl_f_set_fill_color (S___(_plotter));
119 
120 	/* In a .fig file, the width of a line is expressed as a
121 	   non-negative integer (a positive integer, if the line is to be
122 	   visible).  Originally, the width, if positive, was interpreted
123 	   as a multiple of a fundamental `Fig display unit', namely 1/80
124 	   inch.  However, the interpretation of the in-file line width was
125 	   subsequently changed, thus:
126 
127 	   Width in .fig file 		Width as actually displayed by xfig
128 	   1				0.5
129 	   2				1
130 	   3				2
131 	   4				3
132 	   etc.
133 
134 	   In consequence, our line width in terms of Fig display units
135 	   usually needs to be adjusted upward, before we round it to the
136 	   closest integer.  Thanks to Wolfgang Glunz and Bart De Schutter
137 	   for pointing this out.  (See the addition of 0.75 below, which
138 	   is what they recommend.)
139 	*/
140 
141 	device_line_width =
142 	  FIG_UNITS_TO_FIG_DISPLAY_UNITS(_plotter->drawstate->device_line_width);
143 	if (device_line_width > 0.75)
144 	  device_line_width += 1.0;
145 
146 	/* round xfig's notion of the line width to the closest integer;
147 	   but never round it down to 0 (which would yield an invisible
148 	   line) if the line width in user coordinates is positive;
149 	   instead, round it upward to 1  */
150 	quantized_device_line_width = IROUND(device_line_width);
151 	if (quantized_device_line_width == 0 && device_line_width > 0.0)
152 	  quantized_device_line_width = 1;
153 
154 	/* compute line style (type of dotting/dashing, spacing of
155            dots/dashes)*/
156 	_pl_f_compute_line_style (R___(_plotter) &line_style, &nominal_spacing);
157 
158 	/* update xfig's `depth' attribute */
159 	if (_plotter->fig_drawing_depth > 0)
160 	  (_plotter->fig_drawing_depth)--;
161 
162 	sprintf(_plotter->data->page->point,
163 		format,
164 		2,		/* polyline object */
165 		polyline_subtype, /* polyline subtype */
166 		line_style,	/* Fig line style */
167 	  			/* thickness, in Fig display units */
168 		(_plotter->drawstate->pen_type == 0 ? 0 :
169 		 quantized_device_line_width),
170 		_plotter->drawstate->fig_fgcolor, /* pen color */
171 		_plotter->drawstate->fig_fillcolor, /* fill color */
172 		_plotter->fig_drawing_depth, /* depth */
173 		0,		/* pen style, ignored */
174 		_plotter->drawstate->fig_fill_level, /* area fill */
175 		nominal_spacing, /* style val, in Fig display units (float) */
176 		_pl_f_fig_join_style[_plotter->drawstate->join_type],/*join style */
177 		_pl_f_fig_cap_style[_plotter->drawstate->cap_type], /* cap style */
178 		0,		/* radius(of arc boxes, ignored here) */
179 		0,		/* forward arrow */
180 		0,		/* backward arrow */
181 		_plotter->drawstate->path->num_segments /*num points in line */
182 		);
183 	_update_buffer (_plotter->data->page);
184 
185 	for (i=0; i<_plotter->drawstate->path->num_segments; i++)
186 	  {
187 	    plPathSegment datapoint;
188 	    double xu, yu, xd, yd;
189 	    int device_x, device_y;
190 
191 	    datapoint = _plotter->drawstate->path->segments[i];
192 	    xu = datapoint.p.x;
193 	    yu = datapoint.p.y;
194 	    xd = XD(xu, yu);
195 	    yd = YD(xu, yu);
196 	    device_x = IROUND(xd);
197 	    device_y = IROUND(yd);
198 
199 	    if ((i%5) == 0)
200 	      sprintf (_plotter->data->page->point, "\n\t");/* make human-readable */
201 	    else
202 	      sprintf (_plotter->data->page->point, " ");
203 	    _update_buffer (_plotter->data->page);
204 
205 	    sprintf (_plotter->data->page->point, "%d %d", device_x, device_y);
206 	    _update_buffer (_plotter->data->page);
207 	  }
208 	sprintf (_plotter->data->page->point, "\n");
209 	_update_buffer (_plotter->data->page);
210       }
211       break;
212 
213     case (int)PATH_BOX:
214       {
215 	plPoint p0, p1;
216 
217 	p0 = _plotter->drawstate->path->p0;
218 	p1 = _plotter->drawstate->path->p1;
219 
220 	_pl_f_draw_box_internal (R___(_plotter) p0, p1);
221       }
222       break;
223 
224     case (int)PATH_CIRCLE:
225       {
226 	double x = _plotter->drawstate->path->pc.x;
227 	double y = _plotter->drawstate->path->pc.y;
228 	double r = _plotter->drawstate->path->radius;
229 
230 	_pl_f_draw_ellipse_internal (R___(_plotter)
231 				  x, y, r, r, 0.0, SUBTYPE_CIRCLE);
232       }
233       break;
234 
235     case (int)PATH_ELLIPSE:
236       {
237 	double x = _plotter->drawstate->path->pc.x;
238 	double y = _plotter->drawstate->path->pc.y;
239 	double rx = _plotter->drawstate->path->rx;
240 	double ry = _plotter->drawstate->path->ry;
241 	double angle = _plotter->drawstate->path->angle;
242 
243 	_pl_f_draw_ellipse_internal (R___(_plotter)
244 				  x, y, rx, ry, angle, SUBTYPE_ELLIPSE);
245       }
246       break;
247 
248     default:			/* shouldn't happen */
249       break;
250     }
251 }
252 
253 /* Emit Fig code for an arc.  This is called if the segment buffer contains
254    not a polyline, but a single circular arc.  If an arc was placed there,
255    we can count on the map from the user frame to the device frame being
256    isotropic (so the arc will be circular in the device frame too), and we
257    can count on the arc not being of zero length.  See g_arc.c. */
258 
259 #define DIST(p1, p2) sqrt( ((p1).x - (p2).x) * ((p1).x - (p2).x) \
260 			  + ((p1).y - (p2).y) * ((p1).y - (p2).y))
261 
262 void
_pl_f_draw_arc_internal(R___ (Plotter * _plotter)double xc,double yc,double x0,double y0,double x1,double y1)263 _pl_f_draw_arc_internal (R___(Plotter *_plotter) double xc, double yc, double x0, double y0, double x1, double y1)
264 {
265   plPoint p0, p1, pc, pb;
266   plVector v, v0, v1;
267   double cross, radius, nominal_spacing;
268   int line_style, orientation;
269   double device_line_width;
270   int quantized_device_line_width;
271 
272   pc.x = xc, pc.y = yc;
273   p0.x = x0, p0.y = y0;
274   p1.x = x1, p1.y = y1;
275 
276   /* vectors from pc to p0, and pc to p1 */
277   v0.x = p0.x - pc.x;
278   v0.y = p0.y - pc.y;
279   v1.x = p1.x - pc.x;
280   v1.y = p1.y - pc.y;
281 
282   /* cross product, zero means points are collinear */
283   cross = v0.x * v1.y - v1.x * v0.y;
284 
285   /* Compute orientation.  Note libplot convention: if p0, p1, pc are
286      collinear then arc goes counterclockwise from p0 to p1. */
287   orientation = (cross >= 0.0 ? 1 : -1);
288 
289   radius = DIST(pc, p0);	/* radius is distance to p0 or p1 */
290 
291   v.x = p1.x - p0.x;		/* chord vector from p0 to p1 */
292   v.y = p1.y - p0.y;
293 
294   _vscale(&v, radius);
295   pb.x = pc.x + orientation * v.y; /* bisection point of arc */
296   pb.y = pc.y - orientation * v.x;
297 
298   /* evaluate fig colors lazily, i.e. only when needed */
299   _pl_f_set_pen_color (S___(_plotter));
300   _pl_f_set_fill_color (S___(_plotter));
301 
302   /* xfig expresses the width of a line as an integer number of `Fig
303      display units', so convert the width to those units */
304   device_line_width =
305     FIG_UNITS_TO_FIG_DISPLAY_UNITS(_plotter->drawstate->device_line_width);
306 
307   /* the interpretation of line width in a .fig file is now more
308      complicated (see comments in _pl_f_paint_path() above), so this value
309      must usually be incremented */
310   if (device_line_width > 0.75)
311     device_line_width += 1.0;
312 
313   /* round xfig's notion of the line width to the closest integer; but
314      never round it down to 0 (which would yield an invisible line) if the
315      line width in user coordinates is positive; instead, round it upward
316      to 1  */
317   quantized_device_line_width = IROUND(device_line_width);
318   if (quantized_device_line_width == 0 && device_line_width > 0.0)
319     quantized_device_line_width = 1;
320 
321   /* compute line style (type of dotting/dashing, spacing of dots/dashes) */
322   _pl_f_compute_line_style (R___(_plotter) &line_style, &nominal_spacing);
323 
324   /* update xfig's `depth' attribute */
325     if (_plotter->fig_drawing_depth > 0)
326       (_plotter->fig_drawing_depth)--;
327 
328   /* compute orientation in NDC frame */
329   orientation *= (_plotter->drawstate->transform.nonreflection ? 1 : -1);
330 
331   if (orientation == -1)
332     /* interchange p0, p1 (since xfig insists that p0, pb, p1 must appear
333        in counterclockwise order around the arc) */
334     {
335       plPoint ptmp;
336 
337       ptmp = p0;
338       p0 = p1;
339       p1 = ptmp;
340     }
341 
342   sprintf(_plotter->data->page->point,
343 	  "#ARC\n%d %d %d %d %d %d %d %d %d %.3f %d %d %d %d %.3f %.3f %d %d %d %d %d %d\n",
344 	  5,			/* arc object */
345 	  1,			/* open-ended arc subtype */
346 	  line_style,		/* Fig line style */
347 	  			/* thickness, in Fig display units */
348 	  (_plotter->drawstate->pen_type == 0 ? 0 :
349 	   quantized_device_line_width),
350 	  _plotter->drawstate->fig_fgcolor, /* pen color */
351 	  _plotter->drawstate->fig_fillcolor, /* fill color */
352 	  _plotter->fig_drawing_depth, /* depth */
353 	  0,			/* pen style, ignored */
354 	  _plotter->drawstate->fig_fill_level, /* area fill */
355 	  nominal_spacing,	/* style val, in Fig display units (float) */
356 	  _pl_f_fig_cap_style[_plotter->drawstate->cap_type], /* cap style */
357 	  1,			/* counterclockwise */
358 	  0,			/* no forward arrow */
359 	  0,			/* no backward arrow */
360 	  XD(pc.x, pc.y),	/* center_x (float) */
361 	  YD(pc.x, pc.y),	/* center_y (float) */
362 	  IROUND(XD(p0.x, p0.y)), /* 1st point user entered (p0) */
363 	  IROUND(YD(p0.x, p0.y)),
364 	  IROUND(XD(pb.x, pb.y)), /* 2nd point user entered (bisection point)*/
365 	  IROUND(YD(pb.x, pb.y)),
366 	  IROUND(XD(p1.x, p1.y)), /* last point user entered (p1) */
367 	  IROUND(YD(p1.x, p1.y)));
368   _update_buffer (_plotter->data->page);
369 }
370 
371 void
_pl_f_draw_box_internal(R___ (Plotter * _plotter)plPoint p0,plPoint p1)372 _pl_f_draw_box_internal (R___(Plotter *_plotter) plPoint p0, plPoint p1)
373 {
374   int xd0, xd1, yd0, yd1;	/* in device coordinates */
375   double nominal_spacing;
376   int line_style;
377   double device_line_width;
378   int quantized_device_line_width;
379 
380   /* evaluate fig colors lazily, i.e. only when needed */
381   _pl_f_set_pen_color (S___(_plotter));
382   _pl_f_set_fill_color (S___(_plotter));
383 
384   /* xfig expresses the width of a line as an integer number of `Fig
385      display units', so convert the width to those units */
386   device_line_width =
387     FIG_UNITS_TO_FIG_DISPLAY_UNITS(_plotter->drawstate->device_line_width);
388 
389   /* the interpretation of line width in a .fig file is now more
390      complicated (see comments in _pl_f_paint_path() above), so this value
391      must usually be incremented */
392   if (device_line_width > 0.75)
393     device_line_width += 1.0;
394 
395   /* round xfig's notion of the line width to the closest integer; but
396      never round it down to 0 (which would yield an invisible line) if the
397      line width in user coordinates is positive; instead, round it upward
398      to 1  */
399   quantized_device_line_width = IROUND(device_line_width);
400   if (quantized_device_line_width == 0 && device_line_width > 0.0)
401     quantized_device_line_width = 1;
402 
403   /* compute line style (type of dotting/dashing, spacing of dots/dashes)*/
404   _pl_f_compute_line_style (R___(_plotter) &line_style, &nominal_spacing);
405 
406   /* update xfig's `depth' attribute */
407   if (_plotter->fig_drawing_depth > 0)
408     (_plotter->fig_drawing_depth)--;
409 
410   sprintf(_plotter->data->page->point,
411 	  "#POLYLINE [BOX]\n%d %d %d %d %d %d %d %d %d %.3f %d %d %d %d %d %d\n",
412 	  2,			/* polyline object */
413 	  P_BOX,		/* polyline subtype */
414 	  line_style,		/* Fig line style */
415 	  			/* thickness, in Fig display units */
416 	  (_plotter->drawstate->pen_type == 0 ? 0 :
417 	  quantized_device_line_width),
418 	  _plotter->drawstate->fig_fgcolor,	/* pen color */
419 	  _plotter->drawstate->fig_fillcolor, /* fill color */
420 	  _plotter->fig_drawing_depth, /* depth */
421 	  0,			/* pen style, ignored */
422 	  _plotter->drawstate->fig_fill_level, /* area fill */
423 	  nominal_spacing,	/* style val, in Fig display units (float) */
424 	  _pl_f_fig_join_style[_plotter->drawstate->join_type], /* join style */
425 	  _pl_f_fig_cap_style[_plotter->drawstate->cap_type], /* cap style */
426 	  0,			/* radius (of arc boxes, ignored here) */
427 	  0,			/* forward arrow */
428 	  0,			/* backward arrow */
429 	  5			/* number of points in line */
430 	  );
431   _update_buffer (_plotter->data->page);
432 
433   p0 = _plotter->drawstate->path->p0;
434   p1 = _plotter->drawstate->path->p1;
435   xd0 = IROUND(XD(p0.x, p0.y));
436   yd0 = IROUND(YD(p0.x, p0.y));
437   xd1 = IROUND(XD(p1.x, p1.y));
438   yd1 = IROUND(YD(p1.x, p1.y));
439 
440   sprintf (_plotter->data->page->point, "\t%d %d ", xd0, yd0);
441   _update_buffer (_plotter->data->page);
442   sprintf (_plotter->data->page->point, "%d %d ", xd0, yd1);
443   _update_buffer (_plotter->data->page);
444   sprintf (_plotter->data->page->point, "%d %d ", xd1, yd1);
445   _update_buffer (_plotter->data->page);
446   sprintf (_plotter->data->page->point, "%d %d ", xd1, yd0);
447   _update_buffer (_plotter->data->page);
448   sprintf (_plotter->data->page->point, "%d %d\n", xd0, yd0);
449   _update_buffer (_plotter->data->page);
450 }
451 
452 void
_pl_f_draw_ellipse_internal(R___ (Plotter * _plotter)double x,double y,double rx,double ry,double angle,int subtype)453 _pl_f_draw_ellipse_internal (R___(Plotter *_plotter) double x, double y, double rx, double ry, double angle, int subtype)
454 {
455   const char *format;
456   double theta, mixing_angle;
457   double ux, uy, vx, vy;
458   double semi_axis_1_x, semi_axis_1_y;
459   double semi_axis_2_x, semi_axis_2_y;
460   double rx_device, ry_device, theta_device;
461   double costheta, sintheta;
462   double nominal_spacing;
463   int line_style;
464   double device_line_width;
465   int quantized_device_line_width;
466 
467   /* inclination angle (radians), in user frame */
468   theta = M_PI * angle / 180.0;
469   costheta = cos (theta);
470   sintheta = sin (theta);
471 
472   /* perform affine user->device coor transformation; (ux,uy) and (vx,vy)
473      are forward images of the semiaxes, i.e. they are conjugate radial
474      vectors in the device frame */
475 
476   ux = XDV(rx * costheta, rx * sintheta);
477   uy = YDV(rx * costheta, rx * sintheta);
478 
479   vx = XDV(-ry * sintheta, ry * costheta);
480   vy = YDV(-ry * sintheta, ry * costheta);
481 
482   /* angle by which the conjugate radial vectors should be mixed, in order
483      to yield vectors along the major and minor axes in the device frame */
484   mixing_angle = 0.5 * _xatan2 (2.0 * (ux * vx + uy * vy),
485 				ux * ux + uy * uy - vx * vx + vy * vy);
486 
487   /* semi-axis vectors in device coordinates */
488   semi_axis_1_x = ux * cos(mixing_angle) + vx * sin(mixing_angle);
489   semi_axis_1_y = uy * cos(mixing_angle) + vy * sin(mixing_angle);
490   semi_axis_2_x = ux * cos(mixing_angle + M_PI_2)
491     + vx * sin(mixing_angle + M_PI_2);
492   semi_axis_2_y = uy * cos(mixing_angle + M_PI_2)
493     + vy * sin(mixing_angle + M_PI_2);
494 
495   /* semi-axis lengths in device coordinates */
496   rx_device = sqrt (semi_axis_1_x * semi_axis_1_x
497 		    + semi_axis_1_y * semi_axis_1_y);
498   ry_device = sqrt (semi_axis_2_x * semi_axis_2_x
499 		    + semi_axis_2_y * semi_axis_2_y);
500 
501   /* angle of inclination of the first semi-axis, in device frame
502      (note flipped-y convention) */
503   theta_device = - _xatan2 (semi_axis_1_y, semi_axis_1_x);
504   if (theta_device == 0.0)
505     theta_device = 0.0;		/* remove sign bit if any */
506 
507   if (subtype == SUBTYPE_CIRCLE &&
508       IROUND (rx_device) != IROUND (ry_device))
509     subtype = SUBTYPE_ELLIPSE;
510 
511   /* evaluate fig colors lazily, i.e. only when needed */
512   _pl_f_set_pen_color (S___(_plotter));
513   _pl_f_set_fill_color (S___(_plotter));
514 
515   /* xfig expresses the width of a line as an integer number of `Fig
516      display units', so convert the width to those units */
517   device_line_width =
518     FIG_UNITS_TO_FIG_DISPLAY_UNITS(_plotter->drawstate->device_line_width);
519 
520   /* the interpretation of line width in a .fig file is now more
521      complicated (see comments in _pl_f_paint_path() above), so this value
522      must usually be incremented */
523   if (device_line_width > 0.75)
524     device_line_width += 1.0;
525 
526   /* round xfig's notion of the line width to the closest integer; but
527      never round it down to 0 (which would yield an invisible line) if the
528      line width in user coordinates is positive; instead, round it upward
529      to 1  */
530   quantized_device_line_width = IROUND(device_line_width);
531   if (quantized_device_line_width == 0 && device_line_width > 0.0)
532     quantized_device_line_width = 1;
533 
534   /* compute line style (type of dotting/dashing, spacing of dots/dashes) */
535   _pl_f_compute_line_style (R___(_plotter) &line_style, &nominal_spacing);
536 
537   /* update xfig's `depth' attribute */
538     if (_plotter->fig_drawing_depth > 0)
539       (_plotter->fig_drawing_depth)--;
540 
541   if (subtype == SUBTYPE_CIRCLE)
542     format = "#ELLIPSE [CIRCLE]\n%d %d %d %d %d %d %d %d %d %.3f %d %.3f %d %d %d %d %d %d %d %d\n";
543   else
544     format = "#ELLIPSE\n%d %d %d %d %d %d %d %d %d %.3f %d %.3f %d %d %d %d %d %d %d %d\n";
545 
546   sprintf(_plotter->data->page->point,
547 	  format,
548 	  1,			/* ellipse object */
549 	  subtype,		/* subtype, see above */
550 	  line_style,		/* Fig line style */
551 	  			/* thickness, in Fig display units */
552 	  (_plotter->drawstate->pen_type == 0 ? 0 :
553 	   quantized_device_line_width),
554 	  _plotter->drawstate->fig_fgcolor,	/* pen color */
555 	  _plotter->drawstate->fig_fillcolor, /* fill color */
556 	  _plotter->fig_drawing_depth, /* depth */
557 	  0,			/* pen style, ignored */
558 	  _plotter->drawstate->fig_fill_level, /* area fill */
559 	  nominal_spacing,	/* style val, in Fig display units (float) */
560 	  1,			/* direction, always 1 */
561 	  theta_device,		/* inclination angle, in radians (float) */
562 	  IROUND(XD(x,y)),	/* center_x (not float, unlike arc) */
563 	  IROUND(YD(x,y)),	/* center_y (not float, unlike arc) */
564 	  IROUND(rx_device),	/* radius_x */
565 	  IROUND(ry_device),	/* radius_y */
566 	  IROUND(XD(x,y)),	/* start_x, 1st point entered */
567 	  IROUND(YD(x,y)),	/* start_y, 1st point entered */
568 	  IROUND(XD(x,y)	/* end_x, last point entered */
569 		 + semi_axis_1_x + semi_axis_2_x),
570 	  IROUND(YD(x,y)	/* end_y, last point entered */
571 		 + semi_axis_1_y + semi_axis_2_y)
572 	  );
573   _update_buffer(_plotter->data->page);
574 }
575 
576 /* compute appropriate Fig line style, and also appropriate value for Fig's
577    notion of `dash length/dot gap' (in Fig display units) */
578 void
_pl_f_compute_line_style(R___ (Plotter * _plotter)int * style,double * spacing)579 _pl_f_compute_line_style (R___(Plotter *_plotter) int *style, double *spacing)
580 {
581   int fig_line_style;
582   double fig_nominal_spacing;
583 
584   if (_plotter->drawstate->dash_array_in_effect
585       && _plotter->drawstate->dash_array_len == 2
586       && (_plotter->drawstate->dash_array[1]
587 	  == _plotter->drawstate->dash_array[0]))
588     /* special case of user-specified dashing (equal on/off lengths);
589        we map this into Fig's `dashed' line type */
590     {
591       double min_sing_val, max_sing_val;
592 
593       /* Minimum singular value is the nominal device-frame line width
594 	 divided by the actual user-frame line-width (see g_linewidth.c),
595 	 so it's the user->device frame conversion factor. */
596       _matrix_sing_vals (_plotter->drawstate->transform.m,
597 			 &min_sing_val, &max_sing_val);
598 
599       /* desired cycle length in Fig display units */
600       fig_nominal_spacing =
601 	FIG_UNITS_TO_FIG_DISPLAY_UNITS(min_sing_val * 2.0 * _plotter->drawstate->dash_array[0]);
602       fig_line_style = FIG_L_DASHED;
603     }
604   else if (_plotter->drawstate->dash_array_in_effect
605 	   && _plotter->drawstate->dash_array_len == 2
606 	   && (_plotter->drawstate->dash_array[1]
607 	       > (3 - FUZZ) * _plotter->drawstate->dash_array[0])
608 	   && (_plotter->drawstate->dash_array[1]
609 	       < (3 + FUZZ) * _plotter->drawstate->dash_array[0]))
610     /* special case of user-specified dashing (gap length = 3 * dash length);
611        we map this into Fig's `dotted' line type, since it agrees with
612        libplot's convention for dashing `dotted' lines (see g_dash2.c) */
613     {
614       double min_sing_val, max_sing_val;
615 
616       _matrix_sing_vals (_plotter->drawstate->transform.m,
617 			 &min_sing_val, &max_sing_val);
618 
619       /* desired cycle length in Fig display units */
620       fig_nominal_spacing =
621 	FIG_UNITS_TO_FIG_DISPLAY_UNITS(min_sing_val * 4.0 * _plotter->drawstate->dash_array[0]);
622       fig_line_style = FIG_L_DOTTED;
623     }
624   else
625     /* canonical line type; retrieve dash array from database (in g_dash2.c) */
626     {
627       int i, num_dashes, cycle_length;
628       const int *dash_array;
629       double display_size_in_fig_units, min_dash_unit, dash_unit;
630 
631       num_dashes =
632 	_pl_g_line_styles[_plotter->drawstate->line_type].dash_array_len;
633       dash_array = _pl_g_line_styles[_plotter->drawstate->line_type].dash_array;
634       cycle_length = 0;
635       for (i = 0; i < num_dashes; i++)
636 	cycle_length += dash_array[i];
637       /* multiply cycle length of dash array by device-frame line width in
638 	 Fig display units, with a floor on the latter (see comments at
639 	 head of file) */
640       display_size_in_fig_units = DMIN(_plotter->data->xmax - _plotter->data->xmin,
641 				       /* flipped y */
642 				       _plotter->data->ymin - _plotter->data->ymax);
643       min_dash_unit = PL_MIN_DASH_UNIT_AS_FRACTION_OF_DISPLAY_SIZE
644 	* FIG_UNITS_TO_FIG_DISPLAY_UNITS(display_size_in_fig_units);
645       dash_unit = DMAX(min_dash_unit,
646 		       FIG_UNITS_TO_FIG_DISPLAY_UNITS(_plotter->drawstate->device_line_width));
647 
648       /* desired cycle length in Fig display units */
649       fig_nominal_spacing = cycle_length * dash_unit;
650       fig_line_style = _pl_f_fig_line_style[_plotter->drawstate->line_type];
651     }
652 
653   /* compensate for Fig's (or fig2dev's) peculiarities; value stored in Fig
654      output file isn't really the cycle length */
655   switch (fig_line_style)
656     {
657     case FIG_L_SOLID:
658     default:			/* shouldn't happen */
659       break;
660     case FIG_L_DOTTED:
661       fig_nominal_spacing -= 1.0;
662       break;
663     case FIG_L_DASHDOTTED:
664       fig_nominal_spacing -= 1.0;
665       /* fall thru */
666     case FIG_L_DASHED:
667       fig_nominal_spacing *= 0.5;
668       break;
669     case FIG_L_DASHDOUBLEDOTTED:
670       fig_nominal_spacing -= 2.0;
671       fig_nominal_spacing /= (1.9 + 1/3.0); /* really */
672       break;
673     case FIG_L_DASHTRIPLEDOTTED:
674       fig_nominal_spacing -= 3.0;
675       fig_nominal_spacing /= 2.4;
676       break;
677     }
678   if (fig_nominal_spacing <= 1.0)
679     fig_nominal_spacing = 1.0;
680 
681   /* pass back what Fig will need */
682   *style = fig_line_style;
683   *spacing = fig_nominal_spacing;
684 }
685 
686 bool
_pl_f_paint_paths(S___ (Plotter * _plotter))687 _pl_f_paint_paths (S___(Plotter *_plotter))
688 {
689   return false;
690 }
691