1 /* $Id: plline.c,v 1.3 2007/05/08 09:09:37 rice Exp $
2 
3 	Routines dealing with line generation.
4 
5    Copyright (C) 2004  Maurice LeBrun
6 
7    This file is part of PLplot.
8 
9    PLplot is free software; you can redistribute it and/or modify
10    it under the terms of the GNU Library General Public License as published
11    by the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13 
14    PLplot is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU Library General Public License for more details.
18 
19    You should have received a copy of the GNU Library General Public License
20    along with PLplot; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 
23 */
24 
25 #include "plplotP.h"
26 
27 #define INSIDE(ix,iy) (BETW(ix,xmin,xmax) && BETW(iy,ymin,ymax))
28 
29 static PLINT xline[PL_MAXPOLY], yline[PL_MAXPOLY];
30 
31 static PLINT lastx = PL_UNDEFINED, lasty = PL_UNDEFINED;
32 
33 /* Function prototypes */
34 
35 /* Draws a polyline within the clip limits. */
36 
37 static void
38 pllclp(PLINT *x, PLINT *y, PLINT npts);
39 
40 /* Get clipped endpoints */
41 
42 static int
43 clipline(PLINT *p_x1, PLINT *p_y1, PLINT *p_x2, PLINT *p_y2,
44 	 PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax);
45 
46 /* General line-drawing routine.  Takes line styles into account. */
47 
48 static void
49 genlin(short *x, short *y, PLINT npts);
50 
51 /* Draws a dashed line to the specified point from the previous one. */
52 
53 static void
54 grdashline(short *x, short *y);
55 
56 /* Determines if a point is inside a polygon or not */
57 
58 static int
59 pointinpolygon( int n, PLINT *x, PLINT *y, PLINT xp, PLINT yp );
60 
61 /*----------------------------------------------------------------------*\
62  * void pljoin()
63  *
64  * Draws a line segment from (x1, y1) to (x2, y2).
65 \*----------------------------------------------------------------------*/
66 
67 void
c_pljoin(PLFLT xx1,PLFLT yy1,PLFLT xx2,PLFLT yy2)68 c_pljoin(PLFLT xx1, PLFLT yy1, PLFLT xx2, PLFLT yy2)
69 {
70     plP_movwor(xx1, yy1);
71     plP_drawor(xx2, yy2);
72 }
73 
74 /*----------------------------------------------------------------------*\
75  * void plline()
76  *
77  * Draws line segments connecting a series of points.
78 \*----------------------------------------------------------------------*/
79 
80 void
c_plline(PLINT n,PLFLT * x,PLFLT * y)81 c_plline(PLINT n, PLFLT *x, PLFLT *y)
82 {
83     if (plsc->level < 3) {
84 	plabort("plline: Please set up window first");
85 	return;
86     }
87     plP_drawor_poly(x, y, n);
88 }
89 
90 /*----------------------------------------------------------------------*\
91  * void plline3(n, x, y, z)
92  *
93  * Draws a line in 3 space.  You must first set up the viewport, the
94  * 2d viewing window (in world coordinates), and the 3d normalized
95  * coordinate box.  See x18c.c for more info.
96  *
97  * This version adds clipping against the 3d bounding box specified in plw3d
98 \*----------------------------------------------------------------------*/
99 void
c_plline3(PLINT n,PLFLT * x,PLFLT * y,PLFLT * z)100 c_plline3(PLINT n, PLFLT *x, PLFLT *y, PLFLT *z)
101 {
102     int i;
103     PLFLT vmin[3], vmax[3], zscale;
104 
105     if (plsc->level < 3) {
106 	plabort("plline3: Please set up window first");
107 	return;
108     }
109 
110     /* get the bounding box in 3d */
111     plP_gdom(&vmin[0], &vmax[0], &vmin[1], &vmax[1]);
112     plP_grange(&zscale, &vmin[2], &vmax[2]);
113 
114     /* interate over the vertices */
115     for( i=0; i < n-1; i++ ) {
116       PLFLT p0[3], p1[3];
117       int axis;
118 
119       /* copy the end points of the segment to allow clipping */
120       p0[0] = x[i]; p0[1] = y[i]; p0[2] = z[i];
121       p1[0] = x[i+1]; p1[1] = y[i+1]; p1[2] = z[i+1];
122 
123       /* check against each axis of the bounding box */
124       for(axis = 0; axis < 3; axis++) {
125 	if(p0[axis] < vmin[axis]) { /* first out */
126 	  if(p1[axis] < vmin[axis]) {
127 	    break; /* both endpoints out so quit */
128 	  } else {
129 	    int j;
130 	    /* interpolate to find intersection with box */
131 	    PLFLT t = (vmin[axis] - p0[axis]) / (p1[axis] - p0[axis]);
132 	    p0[axis] = vmin[axis];
133 	    for(j = 1; j<3; j++) {
134 	      int k = (axis+j)%3;
135 	      p0[k] = (1-t)*p0[k] + t*p1[k];
136 	    }
137 	  }
138 	} else if(p1[axis] < vmin[axis]) { /* second out */
139 	  int j;
140 	  /* interpolate to find intersection with box */
141 	  PLFLT t = (vmin[axis] - p0[axis]) / (p1[axis] - p0[axis]);
142 	  p1[axis] = vmin[axis];
143 	  for(j = 1; j<3; j++) {
144 	    int k = (axis+j)%3;
145 	    p1[k] = (1-t)*p0[k] + t*p1[k];
146 	  }
147 	}
148 	if(p0[axis] > vmax[axis]) { /* first out */
149 	  if(p1[axis] > vmax[axis]) {
150 	    break; /* both out so quit */
151 	  } else {
152 	    int j;
153 	    /* interpolate to find intersection with box */
154 	    PLFLT t = (vmax[axis] - p0[axis]) / (p1[axis] - p0[axis]);
155 	    p0[axis] = vmax[axis];
156 	    for(j = 1; j<3; j++) {
157 	      int k = (axis+j)%3;
158 	      p0[k] = (1-t)*p0[k] + t*p1[k];
159 	    }
160 	  }
161 	} else if(p1[axis] > vmax[axis]) { /* second out */
162 	  int j;
163 	  /* interpolate to find intersection with box */
164 	  PLFLT t = (vmax[axis] - p0[axis]) / (p1[axis] - p0[axis]);
165 	  p1[axis] = vmax[axis];
166 	  for(j = 1; j<3; j++) {
167 	    int k = (axis+j)%3;
168 	    p1[k] = (1-t)*p0[k] + t*p1[k];
169 	  }
170 	}
171       }
172       /* if we made it to here without "break"ing out of the loop, the
173 	 remaining segment is visible */
174       if( axis == 3 ) { /*  not clipped away */
175 	PLFLT u0, v0, u1, v1;
176 	u0 = plP_wcpcx(plP_w3wcx( p0[0], p0[1], p0[2] ));
177 	v0 = plP_wcpcy(plP_w3wcy( p0[0], p0[1], p0[2] ));
178 	u1 = plP_wcpcx(plP_w3wcx( p1[0], p1[1], p1[2] ));
179 	v1 = plP_wcpcy(plP_w3wcy( p1[0], p1[1], p1[2] ));
180 	plP_movphy(u0,v0);
181 	plP_draphy(u1,v1);
182       }
183     }
184     return;
185 }
186 /*----------------------------------------------------------------------*\
187  * void plpoly3( n, x, y, z, draw, ifcc )
188  *
189  * Draws a polygon in 3 space.  This differs from plline3() in that
190  * this attempts to determine if the polygon is viewable.  If the back
191  * of polygon is facing the viewer, then it isn't drawn.  If this
192  * isn't what you want, then use plline3 instead.
193  *
194  * n specifies the number of points.  They are assumed to be in a
195  * plane, and the directionality of the plane is determined from the
196  * first three points.  Additional points do not /have/ to lie on the
197  * plane defined by the first three, but if they do not, then the
198  * determiniation of visibility obviously can't be 100% accurate...
199  * So if you're 3 space polygons are too far from planar, consider
200  * breaking them into smaller polygons.  "3 points define a plane" :-).
201  *
202  * For ifcc == 1, the directionality of the polygon is determined by assuming
203  * the points are laid out in counter-clockwise order.
204  *
205  * For ifcc == 0, the directionality of the polygon is determined by assuming
206  * the points are laid out in clockwise order.
207  *
208  * BUGS:  If one of the first two segments is of zero length, or if
209  * they are colinear, the calculation of visibility has a 50/50 chance
210  * of being correct.  Avoid such situations :-).  See x18c for an
211  * example of this problem.  (Search for "20.1").
212 \*----------------------------------------------------------------------*/
213 
214 void
c_plpoly3(PLINT n,PLFLT * x,PLFLT * y,PLFLT * z,PLBOOL * draw,PLBOOL ifcc)215 c_plpoly3(PLINT n, PLFLT *x, PLFLT *y, PLFLT *z, PLBOOL *draw, PLBOOL ifcc)
216 {
217     int i;
218     PLFLT vmin[3], vmax[3], zscale;
219     PLFLT u1, v1, u2, v2, u3, v3;
220     PLFLT c;
221 
222     if (plsc->level < 3) {
223 	plabort("plpoly3: Please set up window first");
224 	return;
225     }
226 
227     if ( n < 3 ) {
228 	plabort("plpoly3: Must specify at least 3 points");
229 	return;
230     }
231 
232 /* Now figure out which side this is. */
233 
234     u1 = plP_wcpcx(plP_w3wcx( x[0], y[0], z[0] ));
235     v1 = plP_wcpcy(plP_w3wcy( x[0], y[0], z[0] ));
236 
237     u2 = plP_wcpcx(plP_w3wcx( x[1], y[1], z[1] ));
238     v2 = plP_wcpcy(plP_w3wcy( x[1], y[1], z[1] ));
239 
240     u3 = plP_wcpcx(plP_w3wcx( x[2], y[2], z[2] ));
241     v3 = plP_wcpcy(plP_w3wcy( x[2], y[2], z[2] ));
242 
243     c = (u1-u2)*(v3-v2)-(v1-v2)*(u3-u2);
244 
245     if ( c *(1 - 2*ABS(ifcc)) < 0. )
246         return;
247 
248     /* get the bounding box in 3d */
249     plP_gdom(&vmin[0], &vmax[0], &vmin[1], &vmax[1]);
250     plP_grange(&zscale, &vmin[2], &vmax[2]);
251 
252     /* interate over the vertices */
253     for( i=0; i < n-1; i++ ) {
254       PLFLT p0[3], p1[3];
255       int axis;
256 
257       /* copy the end points of the segment to allow clipping */
258       p0[0] = x[i]; p0[1] = y[i]; p0[2] = z[i];
259       p1[0] = x[i+1]; p1[1] = y[i+1]; p1[2] = z[i+1];
260 
261       /* check against each axis of the bounding box */
262       for(axis = 0; axis < 3; axis++) {
263 	if(p0[axis] < vmin[axis]) { /* first out */
264 	  if(p1[axis] < vmin[axis]) {
265 	    break; /* both endpoints out so quit */
266 	  } else {
267 	    int j;
268 	    /* interpolate to find intersection with box */
269 	    PLFLT t = (vmin[axis] - p0[axis]) / (p1[axis] - p0[axis]);
270 	    p0[axis] = vmin[axis];
271 	    for(j = 1; j<3; j++) {
272 	      int k = (axis+j)%3;
273 	      p0[k] = (1-t)*p0[k] + t*p1[k];
274 	    }
275 	  }
276 	} else if(p1[axis] < vmin[axis]) { /* second out */
277 	  int j;
278 	  /* interpolate to find intersection with box */
279 	  PLFLT t = (vmin[axis] - p0[axis]) / (p1[axis] - p0[axis]);
280 	  p1[axis] = vmin[axis];
281 	  for(j = 1; j<3; j++) {
282 	    int k = (axis+j)%3;
283 	    p1[k] = (1-t)*p0[k] + t*p1[k];
284 	  }
285 	}
286 	if(p0[axis] > vmax[axis]) { /* first out */
287 	  if(p1[axis] > vmax[axis]) {
288 	    break; /* both out so quit */
289 	  } else {
290 	    int j;
291 	    /* interpolate to find intersection with box */
292 	    PLFLT t = (vmax[axis] - p0[axis]) / (p1[axis] - p0[axis]);
293 	    p0[axis] = vmax[axis];
294 	    for(j = 1; j<3; j++) {
295 	      int k = (axis+j)%3;
296 	      p0[k] = (1-t)*p0[k] + t*p1[k];
297 	    }
298 	  }
299 	} else if(p1[axis] > vmax[axis]) { /* second out */
300 	  int j;
301 	  /* interpolate to find intersection with box */
302 	  PLFLT t = (vmax[axis] - p0[axis]) / (p1[axis] - p0[axis]);
303 	  p1[axis] = vmax[axis];
304 	  for(j = 1; j<3; j++) {
305 	    int k = (axis+j)%3;
306 	    p1[k] = (1-t)*p0[k] + t*p1[k];
307 	  }
308 	}
309       }
310       /* if we made it to here without "break"ing out of the loop, the
311 	 remaining segment is visible */
312       if( axis == 3 && draw[i] ) { /*  not clipped away */
313 	PLFLT myu0, myv0, myu1, myv1;
314 	myu0 = plP_wcpcx(plP_w3wcx( p0[0], p0[1], p0[2] ));
315 	myv0 = plP_wcpcy(plP_w3wcy( p0[0], p0[1], p0[2] ));
316 	myu1 = plP_wcpcx(plP_w3wcx( p1[0], p1[1], p1[2] ));
317 	myv1 = plP_wcpcy(plP_w3wcy( p1[0], p1[1], p1[2] ));
318 	plP_movphy(myu0,myv0);
319 	plP_draphy(myu1,myv1);
320       }
321     }
322     return;
323 }
324 
325 /*----------------------------------------------------------------------*\
326  * void plstyl()
327  *
328  * Set up a new line style of "nms" elements, with mark and space
329  * lengths given by arrays "mark" and "space".
330 \*----------------------------------------------------------------------*/
331 
332 void
c_plstyl(PLINT nms,PLINT * mark,PLINT * space)333 c_plstyl(PLINT nms, PLINT *mark, PLINT *space)
334 {
335     short int i;
336 
337     if (plsc->level < 1) {
338 	plabort("plstyl: Please call plinit first");
339 	return;
340     }
341     if ((nms < 0) || (nms > 10)) {
342 	plabort("plstyl: Broken lines cannot have <0 or >10 elements");
343 	return;
344     }
345     for (i = 0; i < nms; i++) {
346 	if ((mark[i] < 0) || (space[i] < 0)) {
347 	    plabort("plstyl: Mark and space lengths must be > 0");
348 	    return;
349 	}
350     }
351 
352     plsc->nms = nms;
353     for (i = 0; i < nms; i++) {
354 	plsc->mark[i] = mark[i];
355 	plsc->space[i] = space[i];
356     }
357 
358     plsc->curel = 0;
359     plsc->pendn = 1;
360     plsc->timecnt = 0;
361     plsc->alarm = nms > 0 ? mark[0] : 0;
362 }
363 
364 /*----------------------------------------------------------------------*\
365  * void plP_movphy()
366  *
367  * Move to physical coordinates (x,y).
368 \*----------------------------------------------------------------------*/
369 
370 void
plP_movphy(PLINT x,PLINT y)371 plP_movphy(PLINT x, PLINT y)
372 {
373     plsc->currx = x;
374     plsc->curry = y;
375 }
376 
377 /*----------------------------------------------------------------------*\
378  * void plP_draphy()
379  *
380  * Draw to physical coordinates (x,y).
381 \*----------------------------------------------------------------------*/
382 
383 void
plP_draphy(PLINT x,PLINT y)384 plP_draphy(PLINT x, PLINT y)
385 {
386     xline[0] = plsc->currx;
387     xline[1] = x;
388     yline[0] = plsc->curry;
389     yline[1] = y;
390 
391     pllclp(xline, yline, 2);
392 }
393 
394 /*----------------------------------------------------------------------*\
395  * void plP_movwor()
396  *
397  * Move to world coordinates (x,y).
398 \*----------------------------------------------------------------------*/
399 
400 void
plP_movwor(PLFLT x,PLFLT y)401 plP_movwor(PLFLT x, PLFLT y)
402 {
403     plsc->currx = plP_wcpcx(x);
404     plsc->curry = plP_wcpcy(y);
405 }
406 
407 /*----------------------------------------------------------------------*\
408  * void plP_drawor()
409  *
410  * Draw to world coordinates (x,y).
411 \*----------------------------------------------------------------------*/
412 
413 void
plP_drawor(PLFLT x,PLFLT y)414 plP_drawor(PLFLT x, PLFLT y)
415 {
416     xline[0] = plsc->currx;
417     xline[1] = plP_wcpcx(x);
418     yline[0] = plsc->curry;
419     yline[1] = plP_wcpcy(y);
420 
421     pllclp(xline, yline, 2);
422 }
423 
424 /*----------------------------------------------------------------------*\
425  * void plP_draphy_poly()
426  *
427  * Draw polyline in physical coordinates.
428  * Need to draw buffers in increments of (PL_MAXPOLY-1) since the
429  * last point must be repeated (for solid lines).
430 \*----------------------------------------------------------------------*/
431 
432 void
plP_draphy_poly(PLINT * x,PLINT * y,PLINT n)433 plP_draphy_poly(PLINT *x, PLINT *y, PLINT n)
434 {
435     PLINT i, j, ib, ilim;
436 
437     for (ib = 0; ib < n; ib += PL_MAXPOLY - 1) {
438 	ilim = MIN(PL_MAXPOLY, n - ib);
439 
440 	for (i = 0; i < ilim; i++) {
441 	    j = ib + i;
442 	    xline[i] = x[j];
443 	    yline[i] = y[j];
444 	}
445 	pllclp(xline, yline, ilim);
446     }
447 }
448 
449 /*----------------------------------------------------------------------*\
450  * void plP_drawor_poly()
451  *
452  * Draw polyline in world coordinates.
453  * Need to draw buffers in increments of (PL_MAXPOLY-1) since the
454  * last point must be repeated (for solid lines).
455 \*----------------------------------------------------------------------*/
456 
457 void
plP_drawor_poly(PLFLT * x,PLFLT * y,PLINT n)458 plP_drawor_poly(PLFLT *x, PLFLT *y, PLINT n)
459 {
460     PLINT i, j, ib, ilim;
461 
462     for (ib = 0; ib < n; ib += PL_MAXPOLY - 1) {
463 	ilim = MIN(PL_MAXPOLY, n - ib);
464 
465 	for (i = 0; i < ilim; i++) {
466 	    j = ib + i;
467 	    xline[i] = plP_wcpcx(x[j]);
468 	    yline[i] = plP_wcpcy(y[j]);
469 	}
470 	pllclp(xline, yline, ilim);
471     }
472 }
473 
474 /*----------------------------------------------------------------------*\
475  * void pllclp()
476  *
477  * Draws a polyline within the clip limits.
478  * Merely a front-end to plP_pllclp().
479 \*----------------------------------------------------------------------*/
480 
481 static void
pllclp(PLINT * x,PLINT * y,PLINT npts)482 pllclp(PLINT *x, PLINT *y, PLINT npts)
483 {
484     plP_pllclp(x, y, npts, plsc->clpxmi, plsc->clpxma,
485 	       plsc->clpymi, plsc->clpyma, genlin);
486 }
487 
488 /*----------------------------------------------------------------------*\
489  * void plP_pllclp()
490  *
491  * Draws a polyline within the clip limits.
492  *
493  * (AM)
494  * Wanted to change the type of xclp, yclp to avoid overflows!
495  * But that changes the type for the drawing routines too!
496 \*----------------------------------------------------------------------*/
497 
498 void
plP_pllclp(PLINT * x,PLINT * y,PLINT npts,PLINT xmin,PLINT xmax,PLINT ymin,PLINT ymax,void (* draw)(short *,short *,PLINT))499 plP_pllclp(PLINT *x, PLINT *y, PLINT npts,
500 	   PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax,
501 	   void (*draw) (short *, short *, PLINT))
502 {
503     PLINT xx1, xx2, yy1, yy2;
504     PLINT i, iclp = 0;
505 
506     short _xclp[PL_MAXPOLY], _yclp[PL_MAXPOLY];
507     short *xclp, *yclp;
508     int drawable;
509 
510     if ( npts < PL_MAXPOLY ) {
511         xclp = _xclp;
512         yclp = _yclp;
513     } else {
514         xclp = (short *) malloc( npts*sizeof(short) ) ;
515         yclp = (short *) malloc( npts*sizeof(short) ) ;
516     }
517 
518     for (i = 0; i < npts - 1; i++) {
519 	xx1 = x[i];
520 	xx2 = x[i + 1];
521 	yy1 = y[i];
522 	yy2 = y[i + 1];
523 
524 	drawable = (INSIDE(xx1, yy1) && INSIDE(xx2, yy2));
525 	if ( ! drawable)
526 	    drawable = ! clipline(&xx1, &yy1, &xx2, &yy2, xmin,
527 				  xmax, ymin, ymax);
528 
529 	if (drawable) {
530 
531 /* First point of polyline. */
532 
533 	    if (iclp == 0) {
534 		xclp[iclp] = xx1;
535 		yclp[iclp] = yy1;
536 		iclp++;
537 		xclp[iclp] = xx2;
538 		yclp[iclp] = yy2;
539 	    }
540 
541 /* Not first point.  Check if first point of this segment matches up to
542    previous point, and if so, add it to the current polyline buffer. */
543 
544 	    else if (xx1 == xclp[iclp] && yy1 == yclp[iclp]) {
545 		iclp++;
546 		xclp[iclp] = xx2;
547 		yclp[iclp] = yy2;
548 	    }
549 
550 /* Otherwise it's time to start a new polyline */
551 
552 	    else {
553 		if (iclp + 1 >= 2)
554 		    (*draw)(xclp, yclp, iclp + 1);
555 		iclp = 0;
556 		xclp[iclp] = xx1;
557 		yclp[iclp] = yy1;
558 		iclp++;
559 		xclp[iclp] = xx2;
560 		yclp[iclp] = yy2;
561 	    }
562 	}
563     }
564 
565 /* Handle remaining polyline */
566 
567     if (iclp + 1 >= 2)
568 	(*draw)(xclp, yclp, iclp + 1);
569 
570     plsc->currx = x[npts-1];
571     plsc->curry = y[npts-1];
572 
573     if ( xclp != _xclp ) {
574         free( xclp );
575         free( yclp );
576     }
577 }
578 
579 /*----------------------------------------------------------------------*\
580  * int circulation()
581  *
582  * Returns the circulation direction for a given polyline: positive is
583  * counterclockwise, negative is clockwise (right hand rule).
584  *
585  * Used to get the circulation of the fill polygon around the bounding box,
586  * when the fill polygon is larger than the bounding box.  Counts left
587  * (positive) vs right (negative) hand turns using a cross product, instead of
588  * performing all the expensive trig calculations needed to get this 100%
589  * correct.  For the fill cases encountered in plplot, this treatment should
590  * give the correct answer most of the time, by far.  When used with plshades,
591  * the typical return value is 3 or -3, since 3 turns are necessary in order
592  * to complete the fill region.  Only for really oddly shaped fill regions
593  * will it give the wrong answer.
594  *
595  * AM:
596  * Changed the computation: use the outer product to compute the surface
597  * area, the sign determines if the polygon is followed clockwise or
598  * counterclockwise. This is more reliable. Floating-point numbers
599  * are used to avoid overflow.
600 \*----------------------------------------------------------------------*/
601 
602 static int
circulation(PLINT * x,PLINT * y,PLINT npts)603 circulation(PLINT *x, PLINT *y, PLINT npts)
604 {
605     PLFLT xproduct;
606     int direction = 0;
607     PLFLT xx1, yy1, xx2, yy2, xx3, yy3;
608     int i;
609 
610     xproduct = 0.0 ;
611     for (i = 0; i < npts - 1; i++) {
612 	xx1 = x[i]; xx2 = x[i+1];
613 	yy1 = y[i]; yy2 = y[i+1];
614 	if (i < npts-2) {
615 	    xx3 = x[i+2]; yy3 = y[i+2];
616 	} else {
617 	    xx3 = x[0]; yy3 = y[0];
618 	}
619 	xproduct = xproduct + (xx2-xx1)*(yy3-yy2) - (yy2-yy1)*(xx3-xx2);
620     }
621 
622     if (xproduct > 0.0) direction = 1;
623     if (xproduct < 0.0) direction = -1;
624     return direction;
625 }
626 
627 /*----------------------------------------------------------------------*\
628  * void plP_plfclp()
629  *
630  * Fills a polygon within the clip limits.
631 \*----------------------------------------------------------------------*/
632 
633 void
plP_plfclp(PLINT * x,PLINT * y,PLINT npts,PLINT xmin,PLINT xmax,PLINT ymin,PLINT ymax,void (* draw)(short *,short *,PLINT))634 plP_plfclp(PLINT *x, PLINT *y, PLINT npts,
635 	   PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax,
636 	   void (*draw) (short *, short *, PLINT))
637 {
638     PLINT i, xx1, xx2, yy1, yy2;
639     int iclp = 0, iout = 2;
640     short _xclp[2*PL_MAXPOLY+2], _yclp[2*PL_MAXPOLY+2];
641     short *xclp, *yclp;
642     int drawable;
643     int crossed_xmin1 = 0, crossed_xmax1 = 0;
644     int crossed_ymin1 = 0, crossed_ymax1 = 0;
645     int crossed_xmin2 = 0, crossed_xmax2 = 0;
646     int crossed_ymin2 = 0, crossed_ymax2 = 0;
647     int crossed_up    = 0, crossed_down  = 0;
648     int crossed_left  = 0, crossed_right = 0;
649     int inside_lb ;
650     int inside_lu ;
651     int inside_rb ;
652     int inside_ru ;
653 
654 /* Must have at least 3 points and draw() specified */
655     if (npts < 3 || !draw) return;
656 
657     if ( npts < PL_MAXPOLY ) {
658         xclp = _xclp;
659         yclp = _yclp;
660     } else {
661         xclp = (short *) malloc( (2*npts+2)*sizeof(short) ) ;
662         yclp = (short *) malloc( (2*npts+2)*sizeof(short) ) ;
663     }
664 
665 
666     inside_lb = pointinpolygon(npts,x,y,xmin,ymin) ;
667     inside_lu = pointinpolygon(npts,x,y,xmin,ymax) ;
668     inside_rb = pointinpolygon(npts,x,y,xmax,ymin) ;
669     inside_ru = pointinpolygon(npts,x,y,xmax,ymax) ;
670 
671     for (i = 0; i < npts - 1; i++) {
672 	xx1 = x[i]; xx2 = x[i+1];
673 	yy1 = y[i]; yy2 = y[i+1];
674 
675 	drawable = (INSIDE(xx1, yy1) && INSIDE(xx2, yy2));
676 	if ( ! drawable)
677 	    drawable = ! clipline(&xx1, &yy1, &xx2, &yy2,
678 				  xmin, xmax, ymin, ymax);
679 
680 	if (drawable) {
681 	/* Boundary crossing condition -- coming in. */
682 	    crossed_xmin2 = (xx1 == xmin); crossed_xmax2 = (xx1 == xmax);
683 	    crossed_ymin2 = (yy1 == ymin); crossed_ymax2 = (yy1 == ymax);
684 
685 	    crossed_left  = (crossed_left  || crossed_xmin2);
686 	    crossed_right = (crossed_right || crossed_xmax2);
687 	    crossed_down  = (crossed_down  || crossed_ymin2);
688 	    crossed_up    = (crossed_up    || crossed_ymax2);
689 	    iout = iclp+2;
690 	/* If the first segment, just add it. */
691 
692 	    if (iclp == 0) {
693 		xclp[iclp] = xx1; yclp[iclp] = yy1; iclp++;
694 		xclp[iclp] = xx2; yclp[iclp] = yy2; iclp++;
695 	    }
696 
697 	/* Not first point.  If first point of this segment matches up to the
698 	   previous point, just add it.  */
699 
700 	    else if (xx1 == xclp[iclp-1] && yy1 == yclp[iclp-1]) {
701 		xclp[iclp] = xx2; yclp[iclp] = yy2; iclp++;
702 	    }
703 
704 	/* Otherwise, we need to add both points, to connect the points in the
705 	 * polygon along the clip boundary.  If we encircled a corner, we have
706 	 * to add that first.
707 	 */
708 
709 	    else {
710 	    /* Treat the case where we encircled two corners:
711 	       Construct a polygon out of the subset of vertices
712 	       Note that the direction is important too when adding
713 	       the extra points */
714 		xclp[iclp+1] = xx2; yclp[iclp+1] = yy2;
715 		xclp[iclp+2] = xx1; yclp[iclp+2] = yy1;
716 		iout = iout - iclp + 1;
717 	    /* Upper two */
718 		if ( ((crossed_xmin1 && crossed_xmax2) ||
719 			     (crossed_xmin2 && crossed_xmax1)) &&
720 			inside_lu )
721 		{
722 		    if ( crossed_xmin1 )
723 		    {
724 		        xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
725 		        xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
726 		    } else {
727 		        xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
728 		        xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
729 		    }
730 		}
731 	    /* Lower two */
732 		else if ( ((crossed_xmin1 && crossed_xmax2) ||
733 			          (crossed_xmin2 && crossed_xmax1)) &&
734 			inside_lb )
735 		{
736 		    if ( crossed_xmin1 )
737 		    {
738 		        xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
739 		        xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
740 		    } else {
741 		        xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
742 		        xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
743 		    }
744 		}
745 	    /* Left two */
746 		else if ( ((crossed_ymin1 && crossed_ymax2) ||
747 			          (crossed_ymin2 && crossed_ymax1)) &&
748 			inside_lb )
749 		{
750 		    if ( crossed_ymin1 )
751 		    {
752 		        xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
753 		        xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
754 		    } else {
755 		        xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
756 		        xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
757 		    }
758 		}
759 	    /* Right two */
760 		else if ( ((crossed_ymin1 && crossed_ymax2) ||
761 			          (crossed_ymin2 && crossed_ymax1)) &&
762 			inside_rb )
763 		{
764 		    if ( crossed_ymin1 )
765 		    {
766 		        xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
767 		        xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
768 		    } else {
769 		        xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
770 		        xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
771 		    }
772 		}
773 	    /* Now the case where we encircled one corner */
774 	    /* Lower left */
775 		else if ( (crossed_xmin1 && crossed_ymin2) ||
776 			  (crossed_ymin1 && crossed_xmin2) )
777 		{
778 		    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
779 		}
780 	    /* Lower right */
781 		else if ( (crossed_xmax1 && crossed_ymin2) ||
782 			  (crossed_ymin1 && crossed_xmax2) )
783 		{
784 		    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
785 		}
786 	    /* Upper left */
787 		else if ( (crossed_xmin1 && crossed_ymax2) ||
788 			  (crossed_ymax1 && crossed_xmin2) )
789 		{
790 		    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
791 		}
792 	    /* Upper right */
793 		else if ( (crossed_xmax1 && crossed_ymax2) ||
794 			  (crossed_ymax1 && crossed_xmax2) )
795 		{
796 		    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
797 		}
798 
799 	    /* Now add current segment. */
800 		xclp[iclp] = xx1; yclp[iclp] = yy1; iclp++;
801 		xclp[iclp] = xx2; yclp[iclp] = yy2; iclp++;
802 	    }
803 
804 	/* Boundary crossing condition -- going out. */
805 	    crossed_xmin1 = (xx2 == xmin); crossed_xmax1 = (xx2 == xmax);
806 	    crossed_ymin1 = (yy2 == ymin); crossed_ymax1 = (yy2 == ymax);
807 	}
808     }
809 
810 /* Limit case - all vertices are outside of bounding box.  So just fill entire
811    box, *if* the bounding box is completely encircled.
812 */
813 
814     if (iclp == 0) {
815 	PLINT xmin1, xmax1, ymin1, ymax1;
816 	xmin1 = xmax1 = x[0];
817 	ymin1 = ymax1 = y[0];
818 	for (i = 1; i < npts; i++) {
819 	    if (x[i] < xmin1) xmin1 = x[i];
820 	    if (x[i] > xmax1) xmax1 = x[i];
821 	    if (y[i] < ymin1) ymin1 = y[i];
822 	    if (y[i] > ymax1) ymax1 = y[i];
823 	}
824 	if (xmin1 <= xmin && xmax1 >= xmax && ymin1 <= ymin && ymax1 >= ymax ) {
825 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
826 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
827 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
828 	    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
829 	    (*draw)(xclp, yclp, iclp);
830 
831 	    if ( xclp != _xclp ) {
832 	        free( xclp );
833 	        free( yclp );
834 	    }
835 
836 
837 	    return;
838 	}
839     }
840 
841 /* Now handle cases where fill polygon intersects two sides of the box */
842 
843     if (iclp >= 2) {
844 	int debug=0;
845 	int dir = circulation(x, y, npts);
846 	if (debug) {
847 	    if ( (xclp[0] == xmin && xclp[iclp-1] == xmax) ||
848 		 (xclp[0] == xmax && xclp[iclp-1] == xmin) ||
849 		 (yclp[0] == ymin && yclp[iclp-1] == ymax) ||
850 		 (yclp[0] == ymax && yclp[iclp-1] == ymin) ||
851 		 (xclp[0] == xmin && yclp[iclp-1] == ymin) ||
852 		 (yclp[0] == ymin && xclp[iclp-1] == xmin) ||
853 		 (xclp[0] == xmax && yclp[iclp-1] == ymin) ||
854 		 (yclp[0] == ymin && xclp[iclp-1] == xmax) ||
855 		 (xclp[0] == xmax && yclp[iclp-1] == ymax) ||
856 		 (yclp[0] == ymax && xclp[iclp-1] == xmax) ||
857 		 (xclp[0] == xmin && yclp[iclp-1] == ymax) ||
858 		 (yclp[0] == ymax && xclp[iclp-1] == xmin) ) {
859 		printf("dir=%d, clipped points:\n", dir);
860 		for (i=0; i < iclp; i++)
861 		    printf(" x[%d]=%d y[%d]=%d", i, xclp[i], i, yclp[i]);
862 		printf("\n");
863 		printf("pre-clipped points:\n");
864 		for (i=0; i < npts; i++)
865 		    printf(" x[%d]=%d y[%d]=%d", i, x[i], i, y[i]);
866 		printf("\n");
867 	    }
868 	}
869 
870     /* The cases where the fill region is divided 2/2 */
871     /* Divided horizontally */
872 	if (xclp[0] == xmin && xclp[iclp-1] == xmax)
873 	{
874 	    if (dir > 0) {
875 		xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
876 		xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
877 	    }
878 	    else {
879 		xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
880 		xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
881 	    }
882 	}
883 	else if (xclp[0] == xmax && xclp[iclp-1] == xmin)
884 	{
885 	    if (dir > 0) {
886 		xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
887 		xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
888 	    }
889 	    else {
890 		xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
891 		xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
892 	    }
893 	}
894 
895     /* Divided vertically */
896 	else if (yclp[0] == ymin && yclp[iclp-1] == ymax)
897 	{
898 	    if (dir > 0) {
899 		xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
900 		xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
901 	    }
902 	    else {
903 		xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
904 		xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
905 	    }
906 	}
907 	else if (yclp[0] == ymax && yclp[iclp-1] == ymin)
908 	{
909 	    if (dir > 0) {
910 		xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
911 		xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
912 	    }
913 	    else {
914 		xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
915 		xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
916 	    }
917 	}
918 
919     /* The cases where the fill region is divided 3/1 --
920           LL           LR           UR           UL
921        +-----+      +-----+      +-----+      +-----+
922        |     |      |     |      |    \|      |/    |
923        |     |      |     |      |     |      |     |
924        |\    |      |    /|      |     |      |     |
925        +-----+      +-----+      +-----+      +-----+
926 
927        Note when we go the long way around, if the direction is reversed the
928        three vertices must be visited in the opposite order.
929     */
930     /* LL, short way around */
931 	else if ((xclp[0] == xmin && yclp[iclp-1] == ymin && dir < 0) ||
932 		 (yclp[0] == ymin && xclp[iclp-1] == xmin && dir > 0) )
933 	{
934 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
935 	}
936     /* LL, long way around, counterclockwise */
937 	else if ((xclp[0] == xmin && yclp[iclp-1] == ymin && dir > 0))
938 	{
939 	    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
940 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
941 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
942 	}
943     /* LL, long way around, clockwise */
944 	else if ((yclp[0] == ymin && xclp[iclp-1] == xmin && dir < 0))
945 	{
946 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
947 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
948 	    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
949 	}
950     /* LR, short way around */
951 	else if ((xclp[0] == xmax && yclp[iclp-1] == ymin && dir > 0) ||
952 		 (yclp[0] == ymin && xclp[iclp-1] == xmax && dir < 0) )
953 	{
954 	    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
955 	}
956     /* LR, long way around, counterclockwise */
957 	else if (yclp[0] == ymin && xclp[iclp-1] == xmax && dir > 0)
958 	{
959 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
960 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
961 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
962 	}
963     /* LR, long way around, clockwise */
964 	else if (xclp[0] == xmax && yclp[iclp-1] == ymin && dir < 0)
965 	{
966 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
967 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
968 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
969 	}
970     /* UR, short way around */
971 	else if ((xclp[0] == xmax && yclp[iclp-1] == ymax && dir < 0) ||
972 		 (yclp[0] == ymax && xclp[iclp-1] == xmax && dir > 0) )
973 	{
974 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
975 	}
976     /* UR, long way around, counterclockwise */
977 	else if (xclp[0] == xmax && yclp[iclp-1] == ymax && dir > 0)
978 	{
979 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
980 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
981 	    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
982 	}
983     /* UR, long way around, clockwise */
984 	else if (yclp[0] == ymax && xclp[iclp-1] == xmax && dir < 0)
985 	{
986 	    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
987 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
988 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
989 	}
990     /* UL, short way around */
991 	else if ((xclp[0] == xmin && yclp[iclp-1] == ymax && dir > 0) ||
992 		 (yclp[0] == ymax && xclp[iclp-1] == xmin && dir < 0) )
993 	{
994 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
995 	}
996     /* UL, long way around, counterclockwise */
997 	else if (yclp[0] == ymax && xclp[iclp-1] == xmin && dir > 0)
998 	{
999 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
1000 	    xclp[iclp] = xmax; yclp[iclp] = ymin; iclp++;
1001 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
1002 	}
1003     /* UL, long way around, clockwise */
1004 	else if (xclp[0] == xmin && yclp[iclp-1] == ymax && dir < 0)
1005 	{
1006 	    xclp[iclp] = xmax; yclp[iclp] = ymax; iclp++;
1007 	    xclp[iclp] = xmin; yclp[iclp] = ymax; iclp++;
1008 	    xclp[iclp] = xmin; yclp[iclp] = ymin; iclp++;
1009 	}
1010     }
1011 
1012 /* Check for the case that only one side has been crossed
1013    (AM) Just checking a single point turns out not to be
1014    enough, apparently the crossed_*1 and crossed_*2 variables
1015    are not quite what I expected.
1016 */
1017     if ( crossed_left+crossed_right+crossed_down+crossed_up == 1 &&
1018 		inside_lb+inside_rb+inside_lu+inside_ru == 4 ) {
1019 	int dir = circulation(x, y, npts);
1020 	PLINT xlim[4], ylim[4];
1021 	int insert;
1022 	int incr;
1023 
1024 	xlim[0] = xmin ; ylim[0] = ymin ;
1025 	xlim[1] = xmax ; ylim[1] = ymin ;
1026 	xlim[2] = xmax ; ylim[2] = ymax ;
1027 	xlim[3] = xmin ; ylim[3] = ymax ;
1028 	if ( dir > 0 ) {
1029 	    incr   = 1;
1030 	    insert = 0*crossed_left + 1*crossed_down + 2*crossed_right +
1031 	             3*crossed_up ;
1032 	} else {
1033 	    incr   = -1;
1034 	    insert = 3*crossed_left + 2*crossed_up + 1*crossed_right +
1035 	             0*crossed_down ;
1036 	}
1037 	for ( i=0; i < 4; i ++ ) {
1038 	    xclp[iclp] = xlim[insert] ;
1039 	    yclp[iclp] = ylim[insert] ;
1040 	    iclp   ++ ;
1041 	    insert += incr ;
1042 	    if ( insert > 3 ) insert = 0 ;
1043 	    if ( insert < 0 ) insert = 3 ;
1044 	}
1045     }
1046 
1047 /* Draw the sucker */
1048     if (iclp >= 3)
1049 	(*draw)(xclp, yclp, iclp);
1050 
1051     if ( xclp != _xclp ) {
1052         free( xclp );
1053         free( yclp );
1054     }
1055 }
1056 
1057 /*----------------------------------------------------------------------*\
1058  * int clipline()
1059  *
1060  * Get clipped endpoints
1061 \*----------------------------------------------------------------------*/
1062 
1063 static int
clipline(PLINT * p_x1,PLINT * p_y1,PLINT * p_x2,PLINT * p_y2,PLINT xmin,PLINT xmax,PLINT ymin,PLINT ymax)1064 clipline(PLINT *p_x1, PLINT *p_y1, PLINT *p_x2, PLINT *p_y2,
1065 	 PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax)
1066 {
1067     PLINT t, dx, dy, flipx, flipy;
1068     double dydx = 0, dxdy = 0;
1069 
1070 /* If both points are outside clip region with no hope of intersection,
1071    return with an error */
1072 
1073     if ((*p_x1 <= xmin && *p_x2 <= xmin) ||
1074 	(*p_x1 >= xmax && *p_x2 >= xmax) ||
1075 	(*p_y1 <= ymin && *p_y2 <= ymin) ||
1076 	(*p_y1 >= ymax && *p_y2 >= ymax))
1077 	return 1;
1078 
1079     flipx = 0;
1080     flipy = 0;
1081 
1082     if (*p_x2 < *p_x1) {
1083 	*p_x1 = 2 * xmin - *p_x1;
1084 	*p_x2 = 2 * xmin - *p_x2;
1085 	xmax = 2 * xmin - xmax;
1086 	t = xmax;
1087 	xmax = xmin;
1088 	xmin = t;
1089 	flipx = 1;
1090     }
1091 
1092     if (*p_y2 < *p_y1) {
1093 	*p_y1 = 2 * ymin - *p_y1;
1094 	*p_y2 = 2 * ymin - *p_y2;
1095 	ymax = 2 * ymin - ymax;
1096 	t = ymax;
1097 	ymax = ymin;
1098 	ymin = t;
1099 	flipy = 1;
1100     }
1101 
1102     dx = *p_x2 - *p_x1;
1103     dy = *p_y2 - *p_y1;
1104 
1105     if (dx != 0 && dy != 0) {
1106 	dydx = (double) dy / (double) dx;
1107 	dxdy = 1./ dydx;
1108     }
1109 
1110     if (*p_x1 < xmin) {
1111 	if (dx != 0 && dy != 0)
1112 	    *p_y1 = *p_y1 + ROUND((xmin - *p_x1) * dydx);
1113 	*p_x1 = xmin;
1114     }
1115 
1116     if (*p_y1 < ymin) {
1117 	if (dx != 0 && dy != 0)
1118 	    *p_x1 = *p_x1 + ROUND((ymin - *p_y1) * dxdy);
1119 	*p_y1 = ymin;
1120     }
1121 
1122     if (*p_x1 >= xmax || *p_y1 >= ymax)
1123 	return 1;
1124 
1125     if (*p_y2 > ymax) {
1126 	if (dx != 0 && dy != 0)
1127 	    *p_x2 = *p_x2 - ROUND((*p_y2 - ymax) * dxdy);
1128 	*p_y2 = ymax;
1129     }
1130 
1131     if (*p_x2 > xmax) {
1132 	if (dx != 0 && dy != 0)
1133 	    *p_y2 = *p_y2 - ROUND((*p_x2 - xmax) * dydx);
1134 	*p_x2 = xmax;
1135     }
1136 
1137     if (flipx) {
1138 	*p_x1 = 2 * xmax - *p_x1;
1139 	*p_x2 = 2 * xmax - *p_x2;
1140     }
1141 
1142     if (flipy) {
1143 	*p_y1 = 2 * ymax - *p_y1;
1144 	*p_y2 = 2 * ymax - *p_y2;
1145     }
1146 
1147     return 0;
1148 }
1149 
1150 /*----------------------------------------------------------------------*\
1151  * void genlin()
1152  *
1153  * General line-drawing routine.  Takes line styles into account.
1154  * If only 2 points are in the polyline, it is more efficient to use
1155  * plP_line() rather than plP_polyline().
1156 \*----------------------------------------------------------------------*/
1157 
1158 static void
genlin(short * x,short * y,PLINT npts)1159 genlin(short *x, short *y, PLINT npts)
1160 {
1161 /* Check for solid line */
1162 
1163     if (plsc->nms == 0) {
1164 	if (npts== 2)
1165 	    plP_line(x, y);
1166 	else
1167 	    plP_polyline(x, y, npts);
1168     }
1169 
1170 /* Right now dashed lines don't use polyline capability -- this
1171    should be improved */
1172 
1173     else {
1174 
1175 	PLINT i;
1176 
1177         /* Call escape sequence to draw dashed lines, only for drivers
1178 	   that have this capability */
1179         if (plsc->dev_dash) {
1180 	    plsc->dev_npts = npts;
1181 	    plsc->dev_x = x;
1182 	    plsc->dev_y = y;
1183 	    plP_esc(PLESC_DASH, NULL);
1184             return;
1185         }
1186 
1187 	for (i = 0; i < npts - 1; i++) {
1188 	    grdashline(x+i, y+i);
1189 	}
1190     }
1191 }
1192 
1193 /*----------------------------------------------------------------------*\
1194  * void grdashline()
1195  *
1196  * Draws a dashed line to the specified point from the previous one.
1197 \*----------------------------------------------------------------------*/
1198 
1199 static void
grdashline(short * x,short * y)1200 grdashline(short *x, short *y)
1201 {
1202     PLINT nx, ny, nxp, nyp, incr, temp;
1203     PLINT modulo, dx, dy, i, xtmp, ytmp;
1204     PLINT tstep, pix_distance, j;
1205     int loop_x;
1206     short xl[2], yl[2];
1207     double nxstep, nystep;
1208 
1209 /* Check if pattern needs to be restarted */
1210 
1211     if (x[0] != lastx || y[0] != lasty) {
1212 	plsc->curel = 0;
1213 	plsc->pendn = 1;
1214 	plsc->timecnt = 0;
1215 	plsc->alarm = plsc->mark[0];
1216     }
1217 
1218     lastx = xtmp = x[0];
1219     lasty = ytmp = y[0];
1220 
1221     if (x[0] == x[1] && y[0] == y[1])
1222 	return;
1223 
1224     nx = x[1] - x[0];
1225     dx = (nx > 0) ? 1 : -1;
1226     nxp = ABS(nx);
1227 
1228     ny = y[1] - y[0];
1229     dy = (ny > 0) ? 1 : -1;
1230     nyp = ABS(ny);
1231 
1232     if (nyp > nxp) {
1233 	modulo = nyp;
1234 	incr = nxp;
1235 	loop_x = 0;
1236     }
1237     else {
1238 	modulo = nxp;
1239 	incr = nyp;
1240 	loop_x = 1;
1241     }
1242 
1243     temp = modulo / 2;
1244 
1245 /* Compute the timer step */
1246 
1247     nxstep = nxp * plsc->umx;
1248     nystep = nyp * plsc->umy;
1249     tstep = sqrt( nxstep * nxstep + nystep * nystep ) / modulo;
1250     if (tstep < 1) tstep = 1;
1251 
1252     /* tstep is distance per pixel moved */
1253 
1254     i = 0;
1255     while (i < modulo) {
1256         pix_distance = (plsc->alarm - plsc->timecnt + tstep - 1) / tstep;
1257 	i += pix_distance;
1258 	if (i > modulo)
1259 	    pix_distance -= (i - modulo);
1260 	plsc->timecnt += pix_distance * tstep;
1261 
1262 	temp += pix_distance * incr;
1263 	j = temp / modulo;
1264 	temp = temp % modulo;
1265 
1266 	if (loop_x) {
1267 	    xtmp += pix_distance * dx;
1268 	    ytmp += j * dy;
1269 	}
1270 	else {
1271 	    xtmp += j * dx;
1272 	    ytmp += pix_distance * dy;
1273 	}
1274 	if (plsc->pendn != 0) {
1275 	    xl[0] = lastx;
1276 	    yl[0] = lasty;
1277 	    xl[1] = xtmp;
1278 	    yl[1] = ytmp;
1279 	    plP_line(xl, yl);
1280 	}
1281 
1282 /* Update line style variables when alarm goes off */
1283 
1284 	while (plsc->timecnt >= plsc->alarm) {
1285 	    if (plsc->pendn != 0) {
1286 		plsc->pendn = 0;
1287 		plsc->timecnt -= plsc->alarm;
1288 		plsc->alarm = plsc->space[plsc->curel];
1289 	    }
1290 	    else {
1291 		plsc->pendn = 1;
1292 		plsc->timecnt -= plsc->alarm;
1293 		plsc->curel++;
1294 		if (plsc->curel >= plsc->nms)
1295 		    plsc->curel = 0;
1296 		plsc->alarm = plsc->mark[plsc->curel];
1297 	    }
1298 	}
1299 	lastx = xtmp;
1300 	lasty = ytmp;
1301     }
1302 }
1303 
1304 /*----------------------------------------------------------------------*\
1305  * int pointinpolygon()
1306  *
1307  * Returns 1 if the point is inside the polygon, 0 otherwise
1308  * Note:
1309  * Points on the polygon are considered to be outside
1310 \*----------------------------------------------------------------------*/
1311 
1312 static int
pointinpolygon(int n,PLINT * x,PLINT * y,PLINT xp,PLINT yp)1313 pointinpolygon( int n, PLINT *x, PLINT *y, PLINT xp, PLINT yp )
1314 {
1315     int i;
1316     int count_crossings;
1317     PLFLT xx1, yy1, xx2, yy2, xpp, ypp, xout, yout, xmax;
1318     PLFLT xvp, yvp, xvv, yvv, xv1, yv1, xv2, yv2;
1319     PLFLT inprod1, inprod2;
1320 
1321     xpp = (PLFLT) xp;
1322     ypp = (PLFLT) yp;
1323 
1324     count_crossings = 0;
1325 
1326 
1327     /* Determine a point outside the polygon  */
1328 
1329     xmax = x[0] ;
1330     xout = x[0] ;
1331     yout = y[0] ;
1332     for ( i = 0; i < n ; i ++ ) {
1333         if ( xout > x[i] ) {
1334             xout = x[i] ;
1335         }
1336         if ( xmax < x[i] ) {
1337             xmax = x[i] ;
1338         }
1339     }
1340     xout = xout - (xmax-xout) ;
1341 
1342     /* Determine for each side whether the line segment between
1343        our two points crosses the vertex */
1344 
1345     xpp = (PLFLT) xp;
1346     ypp = (PLFLT) yp;
1347 
1348     xvp = xpp - xout;
1349     yvp = ypp - yout;
1350 
1351     for ( i = 0; i < n; i ++ ) {
1352         xx1 = (PLFLT) x[i] ;
1353         yy1 = (PLFLT) y[i] ;
1354         if ( i < n-1 ) {
1355             xx2 = (PLFLT) x[i+1] ;
1356             yy2 = (PLFLT) y[i+1] ;
1357         } else {
1358             xx2 = (PLFLT) x[0] ;
1359             yy2 = (PLFLT) y[0] ;
1360         }
1361 
1362         /* Skip zero-length segments */
1363         if ( xx1 == xx2 && yy1 == yy2 ) {
1364             continue;
1365         }
1366 
1367         /* Line through the two fixed points:
1368            Are x1 and x2 on either side? */
1369         xv1 = xx1 - xout;
1370         yv1 = yy1 - yout;
1371         xv2 = xx2 - xout;
1372         yv2 = yy2 - yout;
1373         inprod1 = xv1*yvp - yv1*xvp; /* Well, with the normal vector */
1374         inprod2 = xv2*yvp - yv2*xvp;
1375         if ( inprod1 * inprod2 >= 0.0 ) {
1376             /* No crossing possible! */
1377             continue;
1378         }
1379 
1380         /* Line through the two vertices:
1381            Are xout and xpp on either side? */
1382         xvv = xx2  - xx1;
1383         yvv = yy2  - yy1;
1384         xv1 = xpp  - xx1;
1385         yv1 = ypp  - yy1;
1386         xv2 = xout - xx1;
1387         yv2 = yout - yy1;
1388         inprod1 = xv1*yvv - yv1*xvv;
1389         inprod2 = xv2*yvv - yv2*xvv;
1390         if ( inprod1 * inprod2 >= 0.0 ) {
1391             /* No crossing possible! */
1392             continue;
1393         }
1394 
1395         /* We do have a crossing */
1396         count_crossings ++;
1397     }
1398 
1399     /* Return the result: an even number of crossings means the
1400        point is outside the polygon */
1401 
1402     return (count_crossings%2);
1403 }
1404