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 internal method is invoked before drawing any path.  It sets the
20    relevant attributes of an HP-GL or HP-GL/2 plotter (fill rule, line
21    type, cap type, join type, line width) to what they should be. */
22 
23 #include "sys-defines.h"
24 #include "extern.h"
25 
26 /* Each dash and gap in our canonical line modes ("shortdashed",
27    "dotdashed") etc. has length that we take to be an integer multiple of
28    the line width.  (For the integers, see g_dash2.c).  Actually, when
29    performing this computation we impose a floor on the line width
30    (measured in the device frame, in scaled HP-GL coordinates. */
31 #define MIN_DASH_UNIT (PL_MIN_DASH_UNIT_AS_FRACTION_OF_DISPLAY_SIZE * (HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT))
32 
33 /* HP-GL's native line types, indexed into by our internal line style
34    number (PL_L_SOLID/PL_L_DOTTED/
35    PL_L_DOTDASHED/PL_L_SHORTDASHED/PL_L_LONGDASHED/PL_L_DOTDOTDASHED/PL_L_DOTDOTDOTDASHED).
36    We use HP-GL's native line types only if we aren't emitting HP-GL/2,
37    since HP-GL/2 supports programmatic definition of line styles. */
38 static const int _hpgl_line_type[PL_NUM_LINE_TYPES] =
39 { HPGL_L_SOLID, HPGL_L_DOTTED, HPGL_L_DOTDASHED,
40     HPGL_L_SHORTDASHED, HPGL_L_LONGDASHED, HPGL_L_DOTDOTDASHED,
41     HPGL_L_DOTDOTDOTDASHED };
42 
43 /* In HP-GL/2, native line type (HP-GL/2 numbering) that will be redefined
44    programmatically as the dashed line style which we'll use.  Should not
45    be any of the preceding, and should be in range 1..8. */
46 #define SPECIAL_HPGL_LINE_TYPE 8
47 
48 /* HP-GL/2 joinstyles, indexed by internal number(miter/rd./bevel/triangular)*/
49 static const int _hpgl_join_style[] =
50 { HPGL_JOIN_MITER_BEVEL, HPGL_JOIN_ROUND, HPGL_JOIN_BEVEL, HPGL_JOIN_TRIANGULAR };
51 
52 /* HP-GL/2 capstyles, indexed by internal number(butt/rd./project/triangular)*/
53 static const int _hpgl_cap_style[] =
54 { HPGL_CAP_BUTT, HPGL_CAP_ROUND, HPGL_CAP_PROJECT, HPGL_CAP_TRIANGULAR };
55 
56 #define FUZZ 0.0000001
57 
58 void
_pl_h_set_attributes(S___ (Plotter * _plotter))59 _pl_h_set_attributes (S___(Plotter *_plotter))
60 {
61   double desired_hpgl_pen_width;
62   double width, height, diagonal_p1_p2_distance;
63 
64   /* first, compute desired linewidth in scaled HP-GL coors (i.e. as
65      fraction of diagonal distance between P1,P2) */
66   width = (double)(HPGL_SCALED_DEVICE_RIGHT - HPGL_SCALED_DEVICE_LEFT);
67   height = (double)(HPGL_SCALED_DEVICE_TOP - HPGL_SCALED_DEVICE_BOTTOM);
68   diagonal_p1_p2_distance = sqrt (width * width + height * height);
69   desired_hpgl_pen_width
70     = _plotter->drawstate->device_line_width / diagonal_p1_p2_distance;
71 
72   /* if plotter's policy on dashing lines needs to be adjusted, do so */
73 
74   if (_plotter->hpgl_version == 2
75       && (_plotter->drawstate->dash_array_in_effect
76 	  || (_plotter->hpgl_line_type !=
77 	      _hpgl_line_type[_plotter->drawstate->line_type])
78 	  || (_plotter->hpgl_pen_width != desired_hpgl_pen_width)))
79     /* HP-GL/2 case, and we need to emit HP-GL/2 instructions that define a
80        new line type.  Why?  Several possibilities: (1) user called
81        linedash(), in which case we always define the line type here, or
82        (2) user called linemod() to change the canonical line style, in
83        which case we need to define a line type here containing the
84        corresponding dash array, or (3) user called linewidth(), in which
85        case we need to define the new line type here because (in the
86        canonical line style case) the dash lengths we'll use depend on the
87        line width. */
88     {
89       double min_sing_val, max_sing_val;
90       double *dashbuf, dash_cycle_length;
91       int i, num_dashes;
92 
93       /* compute minimum singular value of user->device coordinate map,
94 	 which we use as a multiplicative factor to convert line widths
95 	 (cf. g_linewidth.c), dash lengths, etc. */
96       _matrix_sing_vals (_plotter->drawstate->transform.m,
97 			 &min_sing_val, &max_sing_val);
98 
99       if (_plotter->drawstate->dash_array_in_effect)
100 	/* user invoked linedash() */
101 	{
102 	  num_dashes = _plotter->drawstate->dash_array_len;
103 	  if (num_dashes > 0)
104 	    dashbuf = (double *)_pl_xmalloc (num_dashes * sizeof(double));
105 	  else
106 	    dashbuf = NULL;	/* solid line */
107 
108 	  dash_cycle_length = 0.0;
109 	  for (i = 0; i < num_dashes; i++)
110 	    {
111 	      /* convert dash length to device coordinates */
112 	      dashbuf[i] = min_sing_val * _plotter->drawstate->dash_array[i];
113 	      dash_cycle_length += dashbuf[i];
114 	    }
115 	}
116       else
117 	/* have a canonical line type, but since this is HP-GL/2, rather
118 	   than pre-HP-GL/2 or generic HP-GL, we'll implement it as a
119 	   user-defined line type for accuracy */
120 	{
121 	  if (_plotter->drawstate->line_type == PL_L_SOLID)
122 	    {
123 	      num_dashes = 0;
124 	      dash_cycle_length = 0.0;
125 	      dashbuf = NULL;
126 	    }
127 	  else
128 	    {
129 	      const int *dash_array;
130 	      double scale;
131 
132 	      num_dashes =
133 		_pl_g_line_styles[_plotter->drawstate->line_type].dash_array_len;
134 	      dashbuf = (double *)_pl_xmalloc (num_dashes * sizeof(double));
135 
136 	      /* scale the array of integers by line width (actually by
137 		 floored line width; see comments at head of file) */
138 	      dash_array = _pl_g_line_styles[_plotter->drawstate->line_type].dash_array;
139 	      scale = DMAX(MIN_DASH_UNIT,_plotter->drawstate->device_line_width);
140 
141 	      dash_cycle_length = 0.0;
142 	      for (i = 0; i < num_dashes; i++)
143 		{
144 		  dashbuf[i] = scale * dash_array[i];
145 		  dash_cycle_length += dashbuf[i];
146 		}
147 	    }
148 	}
149 
150       if (num_dashes == 0 || dash_cycle_length == 0.0)
151 	/* just switch to solid line type */
152 	{
153 	  strcpy (_plotter->data->page->point, "LT;");
154 	  _update_buffer (_plotter->data->page);
155 	  _plotter->hpgl_line_type = HPGL_L_SOLID;
156 	}
157       else
158 	/* create user-defined line-type, and switch to it */
159 	{
160 	  bool odd_length = (num_dashes & 1 ? true : false);
161 
162 	  /* create user-defined line type */
163 	  sprintf (_plotter->data->page->point, "UL%d",
164 		   SPECIAL_HPGL_LINE_TYPE);
165 	  _update_buffer (_plotter->data->page);
166 	  for (i = 0; i < num_dashes; i++)
167 	    {
168 	      sprintf (_plotter->data->page->point, ",%.3f",
169 		       /* dash length as frac of iteration interval */
170 		       100.0 * (odd_length ? 0.5 : 1.0)
171 		       * dashbuf[i] / dash_cycle_length);
172 	      _update_buffer (_plotter->data->page);
173 	    }
174 	  if (odd_length)
175 	    /* if an odd number of dashes, emit the dash array twice
176 	       (HP-GL/2 doesn't handle odd-length patterns the way that
177 	       Postscript does, so an even-length pattern is better) */
178 	    {
179 	      for (i = 0; i < num_dashes; i++)
180 		{
181 		  sprintf (_plotter->data->page->point, ",%.3f",
182 			   /* dash length as frac of iteration interval */
183 			   100.0 * (odd_length ? 0.5 : 1.0)
184 			   * dashbuf[i] / dash_cycle_length);
185 		  _update_buffer (_plotter->data->page);
186 		}
187 	    }
188 	  sprintf (_plotter->data->page->point, ";");
189 	  _update_buffer (_plotter->data->page);
190 
191 	  /* switch to new line type */
192 	  {
193 	    double width, height, diagonal_p1_p2_distance;
194 	    double iter_interval;
195 
196 	    /* specify iteration interval as percentage of P1-P2 distance */
197 	    width = (double)(HPGL_SCALED_DEVICE_RIGHT-HPGL_SCALED_DEVICE_LEFT);
198 	    height = (double)(HPGL_SCALED_DEVICE_TOP-HPGL_SCALED_DEVICE_BOTTOM);
199 	    diagonal_p1_p2_distance = sqrt (width * width + height * height);
200 	    iter_interval = 100 * (odd_length ? 2 : 1) * (dash_cycle_length/diagonal_p1_p2_distance);
201 	    sprintf (_plotter->data->page->point, "LT%d,%.4f;",
202 		     SPECIAL_HPGL_LINE_TYPE, iter_interval);
203 	    _update_buffer (_plotter->data->page);
204 	    if (_plotter->drawstate->dash_array_in_effect)
205 	      _plotter->hpgl_line_type = SPECIAL_HPGL_LINE_TYPE;
206 	    else
207 	      /* keep track of plotter's line type as if it were
208 		 one of the built-in ones */
209 	      _plotter->hpgl_line_type =
210 		_hpgl_line_type[_plotter->drawstate->line_type];
211 	  }
212 	}
213 
214       free (dashbuf);
215     }
216 
217   /* Not HP-GL/2, so the only line types at our disposal are HP-GL's
218      traditional line types.  Check whether we need to switch. */
219 
220   if (_plotter->hpgl_version < 2
221       && ((_plotter->hpgl_line_type !=
222 	   _hpgl_line_type[_plotter->drawstate->line_type])
223 	  ||			/* special case #1, mapped to "shortdashed" */
224 	  (_plotter->drawstate->dash_array_in_effect
225 	   && _plotter->drawstate->dash_array_len == 2
226 	   && (_plotter->drawstate->dash_array[1]
227 	       == _plotter->drawstate->dash_array[0]))
228 	  ||			/* special case #2, mapped to "dotted" */
229 	  (_plotter->drawstate->dash_array_in_effect
230 	   && _plotter->drawstate->dash_array_len == 2
231 	   && (_plotter->drawstate->dash_array[1]
232 	       > (3 - FUZZ) * _plotter->drawstate->dash_array[0])
233 	   && (_plotter->drawstate->dash_array[1]
234 	       < (3 + FUZZ) * _plotter->drawstate->dash_array[0]))))
235     /* switch to one of HP-GL's traditional line types */
236     {
237       double dash_cycle_length, iter_interval;
238       double min_sing_val, max_sing_val;
239       int line_type;
240 
241       if (_plotter->drawstate->dash_array_in_effect
242 	  && _plotter->drawstate->dash_array_len == 2
243 	  && (_plotter->drawstate->dash_array[1]
244 	      == _plotter->drawstate->dash_array[0]))
245 	/* special case #1, user-specified dashing (equal on/off lengths):
246 	   treat effectively as "shortdashed" line mode */
247 	{
248 	  /* Minimum singular value is the nominal device-frame line width
249 	     divided by the actual user-frame line-width (see
250 	     g_linewidth.c), so it's the user->device frame conversion
251 	     factor. */
252 	  _matrix_sing_vals (_plotter->drawstate->transform.m,
253 			     &min_sing_val, &max_sing_val);
254 	  dash_cycle_length =
255 	    min_sing_val * 2.0 * _plotter->drawstate->dash_array[0];
256 	  line_type = PL_L_SHORTDASHED;
257 	}
258       else if (_plotter->drawstate->dash_array_in_effect
259 	       && _plotter->drawstate->dash_array_len == 2
260 	       && (_plotter->drawstate->dash_array[1]
261 		   > (3 - FUZZ) * _plotter->drawstate->dash_array[0])
262 	       && (_plotter->drawstate->dash_array[1]
263 		   < (3 + FUZZ) * _plotter->drawstate->dash_array[0]))
264 	/* special case #2, user-specified dashing (dash on length = 1/4 of
265 	   cycle length): treat effectively as "dotted" line mode */
266 	{
267 	  /* Minimum singular value is the nominal device-frame line width
268 	     divided by the actual user-frame line-width (see
269 	     g_linewidth.c), so it's the user->device frame conversion
270 	     factor. */
271 	  _matrix_sing_vals (_plotter->drawstate->transform.m,
272 			     &min_sing_val, &max_sing_val);
273 	  dash_cycle_length =
274 	    min_sing_val * 2.0 * 4.0 * _plotter->drawstate->dash_array[0];
275 	  line_type = PL_L_DOTTED;
276 	}
277       else
278 	/* general case: user must have changed canonical line types by
279 	   invoking linemod(); will implement new line style as one of the
280 	   traditional HP-GL line types. */
281 	{
282 	  const int *dash_array;
283 	  int i, num_dashes;
284 	  double scale;
285 
286 	  dash_array = _pl_g_line_styles[_plotter->drawstate->line_type].dash_array;
287 	  num_dashes =
288 	    _pl_g_line_styles[_plotter->drawstate->line_type].dash_array_len;
289 
290 	  /* compute iter interval in device coors, scaling by floored line
291              width (see comments at head of file) */
292 	  scale = DMAX(MIN_DASH_UNIT,_plotter->drawstate->device_line_width);
293 	  if (scale < 1.0)
294 	    scale = 1.0;
295 	  dash_cycle_length = 0.0;
296 	  for (i = 0; i < num_dashes; i++)
297 	    dash_cycle_length += scale * dash_array[i];
298 
299 	  line_type = _plotter->drawstate->line_type;
300 	}
301 
302       /* compute iteration interval as percentage of P1-P2 distance */
303       {
304 	double width, height, diagonal_p1_p2_distance;
305 
306 	width = (double)(HPGL_SCALED_DEVICE_RIGHT-HPGL_SCALED_DEVICE_LEFT);
307 	height = (double)(HPGL_SCALED_DEVICE_TOP-HPGL_SCALED_DEVICE_BOTTOM);
308 	diagonal_p1_p2_distance = sqrt (width * width + height * height);
309 	iter_interval = 100 * (dash_cycle_length/diagonal_p1_p2_distance);
310       }
311 
312       switch (line_type)
313 	{
314 	case PL_L_SOLID:
315 	  /* "solid" */
316 	  strcpy (_plotter->data->page->point, "LT;");
317 	  break;
318 	case PL_L_DOTTED:
319 	  /* "dotted": emulate dots by selecting shortdashed pattern with a
320 	     short iteration interval */
321 	  sprintf (_plotter->data->page->point,
322 		   "LT%d,%.4f;",
323 		   HPGL_L_SHORTDASHED,
324 		   0.5 * iter_interval);
325 	  break;
326 	case PL_L_DOTDOTDOTDASHED:
327 	  /* not a native line type before HP-GL/2; use "dotdotdashed" */
328 	  sprintf (_plotter->data->page->point,
329 		   "LT%d,%.4f;",
330 		   HPGL_L_DOTDOTDASHED,
331 		   iter_interval);
332 	  break;
333 	default:
334 	  sprintf (_plotter->data->page->point,
335 		   "LT%d,%.4f;",
336 		   _hpgl_line_type[_plotter->drawstate->line_type],
337 		   iter_interval);
338 	}
339       _update_buffer (_plotter->data->page);
340       _plotter->hpgl_line_type =
341 	_hpgl_line_type[_plotter->drawstate->line_type];
342     }
343 
344   /* if plotter's line attributes don't agree with what they should be,
345      adjust them (HP-GL/2 only) */
346   if (_plotter->hpgl_version == 2)
347     {
348       if ((_plotter->hpgl_cap_style
349 	   != _hpgl_cap_style[_plotter->drawstate->cap_type])
350 	  || (_plotter->hpgl_join_style
351 	      != _hpgl_join_style[_plotter->drawstate->join_type]))
352 	{
353 	  sprintf (_plotter->data->page->point, "LA1,%d,2,%d;",
354 		   _hpgl_cap_style[_plotter->drawstate->cap_type],
355 		   _hpgl_join_style[_plotter->drawstate->join_type]);
356 	  _update_buffer (_plotter->data->page);
357 	  _plotter->hpgl_cap_style =
358 	    _hpgl_cap_style[_plotter->drawstate->cap_type];
359 	  _plotter->hpgl_join_style =
360 	    _hpgl_join_style[_plotter->drawstate->join_type];
361 	}
362     }
363 
364   /* if plotter's miter limit doesn't agree with what it should be, update
365      it (HP-GL/2 only) */
366   if (_plotter->hpgl_version == 2
367       && _plotter->hpgl_miter_limit != _plotter->drawstate->miter_limit)
368     {
369       double new_limit = _plotter->drawstate->miter_limit;
370       int new_limit_integer;
371 
372       if (new_limit > 32767.0)	/* clamp */
373 	new_limit = 32767.0;
374       else if (new_limit < 1.0)
375 	new_limit = 1.0;
376       new_limit_integer = (int)new_limit; /* floor */
377 
378       sprintf (_plotter->data->page->point, "LA3,%d;", new_limit_integer);
379       _update_buffer (_plotter->data->page);
380       _plotter->hpgl_miter_limit = _plotter->drawstate->miter_limit;
381     }
382 
383   /* if plotter's pen width doesn't agree with what it should be (i.e. the
384      device-frame version of our line width), update it (HP-GL/2 only) */
385   if (_plotter->hpgl_version == 2)
386     {
387       if (_plotter->hpgl_pen_width != desired_hpgl_pen_width)
388 	{
389 	  sprintf (_plotter->data->page->point, "PW%.4f;",
390 		   100.0 * desired_hpgl_pen_width);
391 	  _update_buffer (_plotter->data->page);
392 	  _plotter->hpgl_pen_width = desired_hpgl_pen_width;
393 	}
394     }
395 }
396