File: a_path.c

package info (click to toggle)
plotutils 2.6-11
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 13,184 kB
  • sloc: ansic: 68,670; sh: 20,086; cpp: 12,382; yacc: 2,588; makefile: 838; lex: 137
file content (330 lines) | stat: -rw-r--r-- 10,952 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* This file is part of the GNU plotutils package.  Copyright (C) 1995,
   1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.

   The GNU plotutils package is free software.  You may redistribute it
   and/or modify it under the terms of the GNU General Public License as
   published by the Free Software foundation; either version 2, or (at your
   option) any later version.

   The GNU plotutils package is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with the GNU plotutils package; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
   Boston, MA 02110-1301, USA. */

/* This file contains the internal paint_path() and paint_paths() methods,
   which the public method endpath() is a wrapper around. */

/* This version is for AIPlotters.  By construction, for AIPlotters our
   path storage buffer may include only a segment list: a list of line
   segments and/or cubic Bezier segments.  No primitives such as ellipses,
   circles, and boxes are allowed: any such primitive, if drawn, is
   replaced by a segment list at a higher level. */

#include "sys-defines.h"
#include "extern.h"

/* Maximum value for squared sine of angle between two segments, if their
   juncture is to be a `smooth point' rather than a corner point.  (In
   Illustrator, smooth points have only one direction handle; not two.) */
#define MAX_SQUARED_SINE (1e-6)

void
_pl_a_paint_path (S___(Plotter *_plotter))
{
  if (_plotter->drawstate->pen_type == 0
      && _plotter->drawstate->fill_type == 0)
    /* nothing to draw */
    return;

  switch ((int)_plotter->drawstate->path->type)
    {
    case (int)PATH_SEGMENT_LIST:
      {
	int i, numpoints;
	bool closed;
	double linewidth;

	/* sanity checks */
	if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
	  break;
	if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */
	  break;

	if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
	  closed = true;
	else
	  closed = false;		/* 2-point ones should be open */
	
	/* set fill color and pen color */
	if (_plotter->drawstate->fill_type)
	  /* will be filling the path */
	  _pl_a_set_fill_color (R___(_plotter) false);
	else
	  /* won't be filling the path, but set AI's fill color anyway; in
	     particular, to be the same as the pen color (this is a
	     convenience for AI users who may wish e.g. to switch from
	     stroking to filling) */
	  _pl_a_set_fill_color (R___(_plotter) true);

	_pl_a_set_pen_color (S___(_plotter));
	
	/* update line attributes (cap style, join style, line width), if
	   necessary */
	_pl_a_set_attributes (S___(_plotter));
	
	linewidth = _plotter->drawstate->line_width;
	numpoints = _plotter->drawstate->path->num_segments;
	
	/* loop over segments in path */
	for (i = 0; i < numpoints; i++)
	  {
	    bool smooth_join_point; /* if a path join point, a smooth one? */
	    
	    /* update bounding box to take into account the segment's
	       terminal point (which is either a path join point or a path
	       end point) */

	    if (!closed && (i == 0 || i == numpoints - 1))
	      /* for the path, an end rather than a join */
	      {
		double xcurrent, ycurrent, xother, yother;
		
		smooth_join_point = false;
		
		/* compute path end point, and a nearby point, the vector
		   to which will determine the shape of the path end */
		xcurrent = _plotter->drawstate->path->segments[i].p.x;
		ycurrent = _plotter->drawstate->path->segments[i].p.y;	  
		
		if (i == 0)	/* i = 0, initial end point */
		  {
		    if (_plotter->drawstate->path->segments[i+1].type == S_CUBIC)
		      {
			xother = _plotter->drawstate->path->segments[i+1].pc.x;
			yother = _plotter->drawstate->path->segments[i+1].pc.y;
		      }
		    else	/* line segment */
		      {
			xother = _plotter->drawstate->path->segments[i+1].p.x;
			yother = _plotter->drawstate->path->segments[i+1].p.y;
		      }
		  }
		else		/* i = numpoints - 1, final end point */
		  {
		    if (_plotter->drawstate->path->segments[i].type == S_CUBIC)
		      {
			xother = _plotter->drawstate->path->segments[i].pd.x;
			yother = _plotter->drawstate->path->segments[i].pd.y;
		      }
		    else	/* line segment */
		      {
			xother = _plotter->drawstate->path->segments[i-1].p.x;
			yother = _plotter->drawstate->path->segments[i-1].p.y;
		      }
		  }
		/* take path end into account: update bounding box */
		_set_line_end_bbox (_plotter->data->page,
				    xcurrent, ycurrent, xother, yother,
				    linewidth, _plotter->drawstate->cap_type,
				    _plotter->drawstate->transform.m);
	      }
	    else
	      /* for the path, a join rather than an end */
	      {
		int a, b, c;
		double xcurrent, ycurrent, xleft, yleft, xright, yright;
		
		if (closed && (i == 0 || i == numpoints - 1)) /* wrap */
		  {
		    a = numpoints - 2;
		    b = numpoints - 1;
		    c = 1;
		  }
		else		/* normal join */
		  {
		    a = i - 1;
		    b = i;
		    c = i + 1;
		  }
		
		xcurrent = _plotter->drawstate->path->segments[b].p.x;
		ycurrent = _plotter->drawstate->path->segments[b].p.y;
		
		/* compute points to left and right, vectors to which will
		   determine the shape of the path join */
		switch ((int)_plotter->drawstate->path->segments[b].type)
		  {
		  case (int)S_LINE:
		  default:
		    xleft = _plotter->drawstate->path->segments[a].p.x;
		    yleft = _plotter->drawstate->path->segments[a].p.y;
		    break;
		  case (int)S_CUBIC:
		    xleft = _plotter->drawstate->path->segments[b].pd.x;
		    yleft = _plotter->drawstate->path->segments[b].pd.y;
		    break;
		  }
		switch ((int)_plotter->drawstate->path->segments[c].type)
		  {
		  case (int)S_LINE:
		  default:
		    xright = _plotter->drawstate->path->segments[c].p.x;
		    yright = _plotter->drawstate->path->segments[c].p.y;
		    break;
		  case (int)S_CUBIC:
		    xright = _plotter->drawstate->path->segments[c].pc.x;
		    yright = _plotter->drawstate->path->segments[c].pc.y;
		    break;
		  }
		
		/* take path join into account: update bounding box */
		_set_line_join_bbox(_plotter->data->page,
				    xleft, yleft, xcurrent, ycurrent, xright, yright,
				    linewidth, 
				    _plotter->drawstate->join_type,
				    _plotter->drawstate->miter_limit,
				    _plotter->drawstate->transform.m);
		
		/* is join smooth? */
		{
		  double ux, uy, vx, vy, cross, dot, uselfdot, vselfdot;
		  
		  ux = xleft - xcurrent;
		  uy = yleft - ycurrent;
		  vx = xright - xcurrent;
		  vy = yright - ycurrent;
		  
		  cross = ux * vy - uy * vx;
		  dot = ux * vx + uy * vy;
		  uselfdot = ux * ux + uy * uy;
		  vselfdot = vx * vx + vy * vy;
		  
		  if (cross * cross < MAX_SQUARED_SINE * uselfdot * vselfdot 
		      && dot < 0.0)
		    smooth_join_point = true;
		  else
		    smooth_join_point = false;
		}
	      }
	    
	    /* output to Illustrator the points that define this segment */
	    
	    if (i != 0 
		&& (_plotter->drawstate->path->segments)[i].type == S_CUBIC)
	      /* cubic Bezier segment, so output control points */
	      {
		sprintf (_plotter->data->page->point, 
			 "%.4f %.4f %.4f %.4f ", 
			 XD(_plotter->drawstate->path->segments[i].pc.x,
			    _plotter->drawstate->path->segments[i].pc.y),
			 YD(_plotter->drawstate->path->segments[i].pc.x,
			    _plotter->drawstate->path->segments[i].pc.y),
			 XD(_plotter->drawstate->path->segments[i].pd.x,
		      _plotter->drawstate->path->segments[i].pd.y),
			 YD(_plotter->drawstate->path->segments[i].pd.x,
			    _plotter->drawstate->path->segments[i].pd.y));
		_update_buffer (_plotter->data->page);
		/* update bounding box due to extremal x/y values in device
                   frame */
		_set_bezier3_bbox (_plotter->data->page, 
				   _plotter->drawstate->path->segments[i-1].p.x,
				   _plotter->drawstate->path->segments[i-1].p.y,
				   _plotter->drawstate->path->segments[i].pc.x,
				   _plotter->drawstate->path->segments[i].pc.y,
				   _plotter->drawstate->path->segments[i].pd.x,
				   _plotter->drawstate->path->segments[i].pd.y,
				   _plotter->drawstate->path->segments[i].p.x,
				   _plotter->drawstate->path->segments[i].p.y,
				   _plotter->drawstate->device_line_width,
				   _plotter->drawstate->transform.m);
	      }
	    
	    /* output terminal point of segment */
	    sprintf (_plotter->data->page->point, 
		     "%.4f %.4f ", 
		     XD(_plotter->drawstate->path->segments[i].p.x,
			_plotter->drawstate->path->segments[i].p.y),
		     YD(_plotter->drawstate->path->segments[i].p.x,
			_plotter->drawstate->path->segments[i].p.y));
	    _update_buffer (_plotter->data->page);
	    
	    /* tell Illustrator what sort of path segment this is */
	    if (i == 0)
	      /* start of path, so just move to point */
	      sprintf (_plotter->data->page->point, "m\n");
	    else
	      /* append line segment or Bezier segment to path */
	      switch ((int)_plotter->drawstate->path->segments[i].type)
		{
		case (int)S_LINE:
		default:
		  sprintf (_plotter->data->page->point, 
			   smooth_join_point ? "l\n" : "L\n");
		  break;
		case (int)S_CUBIC:
		  sprintf (_plotter->data->page->point, 
			   smooth_join_point ? "c\n" : "C\n");
		  break;	    
		}
	    _update_buffer (_plotter->data->page);
	    
	  } /* end of loop over segments */
	
	if (_plotter->drawstate->pen_type)
	  /* have a pen to draw with */
	  {
	    /* emit `closepath' if path is closed; stroke and maybe fill */
	    if (_plotter->drawstate->fill_type)
	      {
		if (closed)
		  /* close path, fill and stroke */
		  sprintf (_plotter->data->page->point, "b\n");
		else
		  /* fill and stroke */
		  sprintf (_plotter->data->page->point, "B\n");
	      }
	    else
	      {
		if (closed)
		  /* close path, stroke */
		  sprintf (_plotter->data->page->point, "s\n");
		else
		  /* stroke */
		  sprintf (_plotter->data->page->point, "S\n");
	      }
	  }
	else
	  /* no pen to draw with, but we may do filling */
	  {
	    /* emit `closepath' if path is closed; don't stroke */
	    if (_plotter->drawstate->fill_type)
	      {
		if (closed)
		  /* close path, fill */
		  sprintf (_plotter->data->page->point, "f\n");
		else
		  /* fill */
		  sprintf (_plotter->data->page->point, "F\n");
	      }
	  }
	_update_buffer (_plotter->data->page);
      }
      break;
      
    default:			/* shouldn't happen */
      break;
    }
}

bool
_pl_a_paint_paths (S___(Plotter *_plotter))
{
  return false;
}