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