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 by a CGMPlotter before drawing any path.
20 It sets the relevant CGM attributes (line type, cap type, join type,
21 line width) to what they should be. */
22
23 #include "sys-defines.h"
24 #include "extern.h"
25
26 /* CGM join styles, indexed by internal number (miter/rd./bevel/triangular) */
27 static const int _cgm_join_style[PL_NUM_JOIN_TYPES] =
28 { CGM_JOIN_MITER, CGM_JOIN_ROUND, CGM_JOIN_BEVEL, CGM_JOIN_ROUND };
29
30 /* CGM cap styles, indexed by internal number (butt/rd./project/triangular) */
31 /* Note: we map libplot's triangular cap style to CGM's round cap style,
32 not CGM's triangular cap style, since the latter plots an equilateral
33 triangle, which is not libplot's convention. */
34 static const int _cgm_cap_style[PL_NUM_CAP_TYPES] =
35 { CGM_CAP_BUTT, CGM_CAP_ROUND, CGM_CAP_PROJECTING, CGM_CAP_ROUND };
36
37 /* ARGS: object type = close path / open path / marker etc. */
38 void
_pl_c_set_attributes(R___ (Plotter * _plotter)int object_type)39 _pl_c_set_attributes (R___(Plotter *_plotter) int object_type)
40 {
41 int desired_width = _plotter->drawstate->quantized_device_line_width;
42 int desired_line_type = CGM_L_SOLID; /* keep compiler happy */
43 double desired_dash_offset = 0.0;
44
45 if (_plotter->drawstate->pen_type == 0)
46 /* won't be edging; at most, will be filling; so nothing to do */
47 return;
48
49 /* alter CGM line width if necessary */
50
51 switch (object_type)
52 {
53 case CGM_OBJECT_OPEN:
54 if (_plotter->cgm_line_width != desired_width)
55 /* emit "LINE WIDTH" command */
56 {
57 int byte_count, data_byte_count, data_len;
58
59 data_len = CGM_BINARY_BYTES_PER_INTEGER;
60 byte_count = data_byte_count = 0;
61 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
62 CGM_ATTRIBUTE_ELEMENT, 3,
63 data_len, &byte_count,
64 "LINEWIDTH");
65 _cgm_emit_integer (_plotter->data->page, false, _plotter->cgm_encoding,
66 desired_width,
67 data_len, &data_byte_count, &byte_count);
68 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
69 &byte_count);
70 /* update line width */
71 _plotter->cgm_line_width = desired_width;
72 }
73 break;
74 case CGM_OBJECT_CLOSED:
75 if (_plotter->cgm_edge_width != desired_width)
76 /* emit "EDGE WIDTH" command */
77 {
78 int byte_count, data_byte_count, data_len;
79
80 data_len = CGM_BINARY_BYTES_PER_INTEGER;
81 byte_count = data_byte_count = 0;
82 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
83 CGM_ATTRIBUTE_ELEMENT, 28,
84 data_len, &byte_count,
85 "EDGEWIDTH");
86 _cgm_emit_integer (_plotter->data->page, false, _plotter->cgm_encoding,
87 desired_width,
88 data_len, &data_byte_count, &byte_count);
89 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
90 &byte_count);
91 /* update edge width */
92 _plotter->cgm_edge_width = desired_width;
93 }
94 break;
95 default: /* shouldn't happen */
96 break;
97 }
98
99 /* determine line type */
100
101 if (_plotter->data->have_dash_array
102 && _plotter->drawstate->dash_array_in_effect)
103 /* user specified a dash array, and this version of CGM supports them;
104 so compute a CGM-style dash array, and maybe add a new line type to
105 page-specific line type table */
106 {
107 int num_dashes = _plotter->drawstate->dash_array_len;
108 int our_num_dashes = num_dashes; /* will double if array length is odd */
109
110 if (num_dashes > 0)
111 /* non-solid line type */
112 {
113 double min_sing_val, max_sing_val;
114 int i, *dashbuf;
115 plCGMCustomLineType *line_type_ptr, *old_line_type_ptr;
116 int line_type;
117 bool odd_length, matched_line_type;
118
119 /* compute minimum singular value of user->device coordinate
120 map, which we use as a multiplicative factor to convert
121 line widths (cf. g_linewidth.c), dash lengths, etc. */
122 _matrix_sing_vals (_plotter->drawstate->transform.m,
123 &min_sing_val, &max_sing_val);
124
125 /* double array length if odd (we don't trust CGM interpreters to
126 handle odd-length dash arrays in the way that PS does) */
127 odd_length = (num_dashes % 2 != 0 ? true : false);
128 if (odd_length)
129 our_num_dashes *= 2;
130
131 dashbuf = (int *)_pl_xmalloc (our_num_dashes * sizeof(int));
132 for (i = 0; i < num_dashes; i++)
133 {
134 double dashlen;
135 int i_dashlen;
136
137 dashlen =
138 min_sing_val * _plotter->drawstate->dash_array[i];
139 i_dashlen = IROUND(dashlen);
140 if (i_dashlen == 0 && dashlen > 0.0)
141 i_dashlen = 1; /* don't use 0 if user specified non-0 */
142 dashbuf[i] = i_dashlen;
143 if (odd_length)
144 dashbuf[i + num_dashes] = i_dashlen;
145 }
146
147 /* compute offset as fraction of cycle length; must be in range
148 [0.0,1.0] (CGM convention) */
149 {
150 int cycle_length = 0;
151
152 for (i = 0; i < our_num_dashes; i++)
153 cycle_length += dashbuf[i];
154 /* cycle length now guaranteed to be > 0 */
155
156 desired_dash_offset =
157 min_sing_val * _plotter->drawstate->dash_offset / cycle_length;
158 desired_dash_offset -= IFLOOR(desired_dash_offset);
159
160 if (desired_dash_offset < 0.0 || desired_dash_offset >= 1.0)
161 desired_dash_offset = 0.0;
162 }
163
164 /* search table of user-defined, page-specific CGM line types;
165 they're numbered -1, -2, -3, ... */
166 line_type_ptr = (plCGMCustomLineType *)_plotter->data->page->extra;
167 old_line_type_ptr = (plCGMCustomLineType *)NULL;
168 line_type = 0;
169 matched_line_type = false;
170 while (line_type_ptr != (plCGMCustomLineType *)NULL)
171 {
172 line_type--;
173
174 if (line_type_ptr->dash_array_len == our_num_dashes)
175 {
176 bool foundit = true;
177
178 for (i = 0; i < our_num_dashes; i++)
179 {
180 if (dashbuf[i] != line_type_ptr->dashes[i])
181 {
182 foundit = false;
183 break; /* break out of for loop */
184 }
185 }
186 if (foundit)
187 {
188 matched_line_type = true;
189 break; /* break out of while loop */
190 }
191 }
192
193 /* on to next entry in line type table */
194 old_line_type_ptr = line_type_ptr;
195 line_type_ptr = line_type_ptr->next;
196 }
197 /* on exit from while(), either matched_line_type = true (with
198 line_type set correctly), or old_line_type_ptr points to tail
199 of line type list, and `line_type' is the last valid type */
200
201 if (matched_line_type)
202 {
203 desired_line_type = line_type;
204 free (dashbuf);
205 }
206 else
207 {
208 /* construct new record from `dashbuf', add to tail of list;
209 `dashbuf' and the record will be freed when the page is
210 written out */
211 plCGMCustomLineType *newguy;
212
213 newguy = (plCGMCustomLineType *)_pl_xmalloc (sizeof(plCGMCustomLineType));
214 newguy->dashes = dashbuf;
215 newguy->dash_array_len = our_num_dashes;
216 newguy->next = (plCGMCustomLineType *)NULL;
217 if (old_line_type_ptr != (plCGMCustomLineType *)NULL)
218 old_line_type_ptr->next = newguy;
219 else
220 _plotter->data->page->extra = newguy;
221
222 /* new line type index is one less than most negative
223 previously defined index */
224 desired_line_type = line_type - 1;
225 }
226 }
227 else
228 /* zero-length dash array, i.e. solid line type */
229 {
230 desired_line_type = CGM_L_SOLID;
231 desired_dash_offset = 0.0;
232 }
233 }
234 else
235 /* dash array not in effect or cannot be used, use one of CGM's
236 canonical line types instead */
237 {
238 switch (_plotter->drawstate->line_type)
239 {
240 case PL_L_SOLID:
241 default:
242 desired_line_type = CGM_L_SOLID;
243 break;
244 case PL_L_DOTTED:
245 desired_line_type = CGM_L_DOTTED;
246 break;
247 case PL_L_DOTDASHED:
248 desired_line_type = CGM_L_DOTDASHED;
249 break;
250 case PL_L_SHORTDASHED:
251 desired_line_type = CGM_L_DASHED;
252 break;
253 case PL_L_LONGDASHED:
254 /* can't distinguish from shortdashed */
255 desired_line_type = CGM_L_DASHED;
256 break;
257 case PL_L_DOTDOTDASHED:
258 desired_line_type = CGM_L_DOTDOTDASHED;
259 break;
260 case PL_L_DOTDOTDOTDASHED:
261 /* map to "dotdotdashed" */
262 desired_line_type = CGM_L_DOTDOTDASHED;
263 break;
264 }
265
266 desired_dash_offset = 0.0;
267 }
268
269 switch (object_type)
270 {
271 case CGM_OBJECT_OPEN:
272 if (_plotter->cgm_line_type != desired_line_type)
273 /* emit "LINE TYPE" command */
274 {
275 int byte_count, data_byte_count, data_len;
276
277 data_len = 2; /* 2 bytes per index */
278 byte_count = data_byte_count = 0;
279 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
280 CGM_ATTRIBUTE_ELEMENT, 2,
281 data_len, &byte_count,
282 "LINETYPE");
283 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
284 desired_line_type,
285 data_len, &data_byte_count, &byte_count);
286 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
287 &byte_count);
288 /* update line type */
289 _plotter->cgm_line_type = desired_line_type;
290 }
291
292 if (_plotter->cgm_max_version >= 3
293 && _plotter->cgm_dash_offset != desired_dash_offset)
294 /* emit "LINE TYPE INITIAL OFFSET" command */
295 {
296 int byte_count, data_byte_count, data_len;
297
298 data_len = 4; /* 4 bytes per fixed-pt. real */
299 byte_count = data_byte_count = 0;
300 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
301 CGM_ATTRIBUTE_ELEMENT, 40,
302 data_len, &byte_count,
303 "LINETYPEINITOFFSET");
304 _cgm_emit_real_fixed_point (_plotter->data->page, false, _plotter->cgm_encoding,
305 desired_dash_offset,
306 data_len, &data_byte_count, &byte_count);
307 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
308 &byte_count);
309 /* update dash offset, and CGM version needed for this page */
310 _plotter->cgm_dash_offset = desired_dash_offset;
311 _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version);
312 }
313 break;
314
315 case CGM_OBJECT_CLOSED:
316 if (_plotter->cgm_edge_type != desired_line_type)
317 /* emit "EDGE TYPE" command */
318 {
319 int byte_count, data_byte_count, data_len;
320
321 data_len = 2; /* 2 bytes per index */
322 byte_count = data_byte_count = 0;
323 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
324 CGM_ATTRIBUTE_ELEMENT, 27,
325 data_len, &byte_count,
326 "EDGETYPE");
327 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
328 desired_line_type,
329 data_len, &data_byte_count, &byte_count);
330 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
331 &byte_count);
332 /* update edge type */
333 _plotter->cgm_edge_type = desired_line_type;
334 }
335
336 if (_plotter->cgm_max_version >= 3
337 && _plotter->cgm_edge_dash_offset != desired_dash_offset)
338 /* emit "EDGE TYPE INITIAL OFFSET" command */
339 {
340 int byte_count, data_byte_count, data_len;
341
342 data_len = 4; /* 4 bytes per fixed-pt. real */
343 byte_count = data_byte_count = 0;
344 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
345 CGM_ATTRIBUTE_ELEMENT, 47,
346 data_len, &byte_count,
347 "EDGETYPEINITOFFSET");
348 _cgm_emit_real_fixed_point (_plotter->data->page, false, _plotter->cgm_encoding,
349 desired_dash_offset,
350 data_len, &data_byte_count, &byte_count);
351 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
352 &byte_count);
353 /* update dash offset, and CGM version needed for this page */
354 _plotter->cgm_edge_dash_offset = desired_dash_offset;
355 _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version);
356 }
357 break;
358
359 default:
360 break;
361 }
362
363 if (_plotter->cgm_max_version >= 3)
364 /* have line/edge cap/join style, and miter limit commands */
365 {
366 int desired_join_style = _cgm_join_style[_plotter->drawstate->join_type];
367 int desired_cap_style = _cgm_cap_style[_plotter->drawstate->cap_type];
368 double desired_miter_limit = _plotter->drawstate->miter_limit;
369
370 switch (object_type)
371 {
372 case CGM_OBJECT_OPEN:
373 if (_plotter->cgm_cap_style != desired_cap_style)
374 /* emit "LINE CAP" command */
375 {
376 int byte_count, data_byte_count, data_len;
377
378 /* set line cap style */
379 data_len = 2 * 2; /* 2 bytes per index */
380 byte_count = data_byte_count = 0;
381 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
382 CGM_ATTRIBUTE_ELEMENT, 37,
383 data_len, &byte_count,
384 "LINECAP");
385 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
386 desired_cap_style,
387 data_len, &data_byte_count, &byte_count);
388 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
389 CGM_DASH_CAP_MATCH,
390 data_len, &data_byte_count, &byte_count);
391 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
392 &byte_count);
393 /* update cap style, and CGM version needed for this page */
394 _plotter->cgm_cap_style = desired_cap_style;
395 _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version);
396 }
397 break;
398 case CGM_OBJECT_CLOSED:
399 if (_plotter->cgm_edge_cap_style != desired_cap_style)
400 /* emit "EDGE CAP" command */
401 {
402 int byte_count, data_byte_count, data_len;
403
404 data_len = 2 * 2; /* 2 bytes per index */
405 byte_count = data_byte_count = 0;
406 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
407 CGM_ATTRIBUTE_ELEMENT, 44,
408 data_len, &byte_count,
409 "EDGECAP");
410 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
411 desired_cap_style,
412 data_len, &data_byte_count, &byte_count);
413 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
414 CGM_DASH_CAP_MATCH,
415 data_len, &data_byte_count, &byte_count);
416 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
417 &byte_count);
418 /* update edge cap style, and CGM version needed for this page */
419 _plotter->cgm_edge_cap_style = desired_cap_style;
420 _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version);
421 }
422 break;
423 default:
424 break;
425 }
426
427 switch (object_type)
428 {
429 case CGM_OBJECT_OPEN:
430 if (_plotter->cgm_join_style != desired_join_style)
431 /* emit "LINE JOIN" command */
432 {
433 int byte_count, data_byte_count, data_len;
434
435 /* set line join style */
436 data_len = 2; /* 2 bytes per index */
437 byte_count = data_byte_count = 0;
438 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
439 CGM_ATTRIBUTE_ELEMENT, 38,
440 data_len, &byte_count,
441 "LINEJOIN");
442 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
443 desired_join_style,
444 data_len, &data_byte_count, &byte_count);
445 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
446 &byte_count);
447 /* update join style, and CGM version needed for this page */
448 _plotter->cgm_join_style = desired_join_style;
449 _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version);
450 }
451 break;
452 case CGM_OBJECT_CLOSED:
453 if (_plotter->cgm_edge_join_style != desired_join_style)
454 /* emit "EDGE JOIN" command */
455 {
456 int byte_count, data_byte_count, data_len;
457
458 /* do it over again, this time for edge join style */
459 data_len = 2; /* 2 bytes per index */
460 byte_count = data_byte_count = 0;
461 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
462 CGM_ATTRIBUTE_ELEMENT, 45,
463 data_len, &byte_count,
464 "EDGEJOIN");
465 _cgm_emit_index (_plotter->data->page, false, _plotter->cgm_encoding,
466 desired_join_style,
467 data_len, &data_byte_count, &byte_count);
468 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
469 &byte_count);
470 /* update edge join style, and CGM version needed for this page*/
471 _plotter->cgm_edge_join_style = desired_join_style;
472 _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version);
473 }
474 break;
475 default:
476 break;
477 }
478
479 if (_plotter->cgm_miter_limit != desired_miter_limit)
480 /* emit "MITRE LIMIT" command */
481 {
482 int byte_count, data_byte_count, data_len;
483
484 data_len = 4; /* 4 bytes per fixed-point real */
485 byte_count = data_byte_count = 0;
486 _cgm_emit_command_header (_plotter->data->page, _plotter->cgm_encoding,
487 CGM_CONTROL_ELEMENT, 19,
488 data_len, &byte_count,
489 "MITRELIMIT");
490 _cgm_emit_real_fixed_point (_plotter->data->page, false, _plotter->cgm_encoding,
491 desired_miter_limit,
492 data_len, &data_byte_count, &byte_count);
493 _cgm_emit_command_terminator (_plotter->data->page, _plotter->cgm_encoding,
494 &byte_count);
495 /* update miter limit, and CGM version needed for this page */
496 _plotter->cgm_miter_limit = desired_miter_limit;
497 _plotter->cgm_page_version = IMAX(3, _plotter->cgm_page_version);
498 }
499 }
500 }
501
502
503