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