1 /*
2  * Grace - Graphics for Exploratory Data Analysis
3  *
4  * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5  *
6  * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7  * Copyright (c) 1996-2001 Grace Development Team
8  *
9  * Maintained by Evgeny Stambulchik
10  *
11  *
12  *                           All Rights Reserved
13  *
14  *    This program is free software; you can redistribute it and/or modify
15  *    it under the terms of the GNU General Public License as published by
16  *    the Free Software Foundation; either version 2 of the License, or
17  *    (at your option) any later version.
18  *
19  *    This program is distributed in the hope that it will be useful,
20  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *    GNU General Public License for more details.
23  *
24  *    You should have received a copy of the GNU General Public License
25  *    along with this program; if not, write to the Free Software
26  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  */
28 
29 /*
30  *
31  * routines to allocate, manipulate, and return
32  * information about regions.
33  *
34  */
35 
36 #include <config.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 
41 #include "globals.h"
42 
43 #include "draw.h"
44 #include "graphs.h"
45 #include "utils.h"
46 #include "protos.h"
47 
48 int regiontype = 0;
49 
50 /*
51  * see if (x,y) lies inside the plot
52  */
inbounds(int gno,double x,double y)53 int inbounds(int gno, double x, double y)
54 {
55     WPoint wp;
56 
57     wp.x = x;
58     wp.y = y;
59     return is_validWPoint(wp);
60 }
61 
isactive_region(int regno)62 int isactive_region(int regno)
63 {
64     return (regno == MAXREGION || regno == MAXREGION + 1 || rg[regno].active == TRUE);
65 }
66 
region_types(int it,int which)67 char *region_types(int it, int which)
68 {
69     char *s;
70 
71     s = "UNDEFINED";
72     switch (it) {
73     case REGION_TOLEFT:
74 	s = "REGION_TOLEFT";
75 	break;
76     case REGION_TORIGHT:
77 	s = "REGION_TORIGHT";
78 	break;
79     case REGION_ABOVE:
80 	s = "REGION_ABOVE";
81 	break;
82     case REGION_BELOW:
83 	s = "REGION_BELOW";
84 	break;
85     case REGION_POLYI:
86 	if (which) {
87 	    s = "REGION_POLYI";
88 	} else {
89 	    s = "INSIDE POLY";
90 	}
91 	break;
92     case REGION_POLYO:
93 	if (which) {
94 	    s = "REGION_POLYO";
95 	} else {
96 	    s = "OUTSIDE POLY";
97 	}
98 	break;
99     case REGION_HORIZI:
100       s ="REGION_HORIZI";
101       break;
102     case REGION_VERTI:
103       s ="REGION_VERTI";
104       break;
105     case REGION_HORIZO:
106        s ="REGION_HORIZO";
107       break;
108     case REGION_VERTO:
109       s ="REGION_VERTO";
110       break;
111 
112     }
113     return s;
114 }
115 
kill_region(int r)116 void kill_region(int r)
117 {
118     if (rg[r].active) {
119 	XCFREE(rg[r].x);
120 	XCFREE(rg[r].y);
121         rg[r].active = FALSE;
122         rg[r].n = 0;
123         set_dirtystate();
124     }
125 }
126 
kill_all_regions(void)127 void kill_all_regions(void)
128 {
129     int r;
130     for (r = 0; r < MAXREGION; r++) {
131         kill_region(r);
132     }
133 }
134 
activate_region(int r,int type,int linkto)135 void activate_region(int r, int type, int linkto)
136 {
137     kill_region(r);
138     rg[r].active = TRUE;
139     rg[r].type = type;
140     rg[r].linkto = linkto;
141     set_dirtystate();
142 }
143 
144 
145 /*
146  * report on sets in a region
147  */
reporton_region(int gno,int rno,int type)148 void reporton_region(int gno, int rno, int type)
149 {
150     char buf[256];
151     int i, j, first, contained;
152     double *x, *y;
153     sprintf(buf, "\nRegion R%1d contains:\n", rno);
154     stufftext(buf);
155     for (j = 0; j < number_of_sets(gno); j++) {
156 	if (is_set_active(gno, j)) {
157 	    x = getx(gno, j);
158 	    y = gety(gno, j);
159 	    first = 1;
160 	    contained = 0;
161 	    for (i = 0; i < getsetlength(gno, j); i++) {
162 		if (inregion(rno, x[i], y[i])) {
163 		    contained = 1;
164 		    switch (type) {
165 		    case 0:	/* report on sets */
166 			if (first) {
167 			    first = 0;
168 			    sprintf(buf, "  Set S%1d\n", j);
169 			    stufftext(buf);
170 			}
171 			break;
172 		    case 1:	/* points */
173 			if (first) {
174 			    first = 0;
175 			    sprintf(buf, "  Set S%1d\n", j);
176 			    stufftext(buf);
177 			}
178 			sprintf(buf, "    %d %f %f\n", i + 1, x[i], y[i]);
179 			stufftext(buf);
180 			break;
181 		    }
182 		} else {
183 		    contained = 0;
184 		}
185 	    }
186 	}
187     }
188     stufftext("\n");
189 }
190 
load_poly_region(int r,int gno,int n,WPoint * wps)191 void load_poly_region(int r, int gno, int n, WPoint *wps)
192 {
193     int i;
194 
195     if (n > 2) {
196 	activate_region(r, regiontype, gno);
197 	rg[r].n = n;
198 	rg[r].x = xcalloc(n, SIZEOF_DOUBLE);
199 	rg[r].y = xcalloc(n, SIZEOF_DOUBLE);
200 	for (i = 0; i < n; i++) {
201 	    rg[r].x[i] = wps[i].x;
202 	    rg[r].y[i] = wps[i].y;
203 	}
204     }
205 }
206 
207 /*
208  * routines to determine if a point lies in a polygon
209 */
intersect_to_left(double x,double y,double x1,double y1,double x2,double y2)210 int intersect_to_left(double x, double y, double x1, double y1, double x2, double y2)
211 {
212     double xtmp, m, b;
213 
214     /* ignore horizontal lines */
215     if (y1 == y2) {
216 	return 0;
217     }
218     /* not contained vertically */
219     if (((y < y1) && (y < y2)) || ((y > y1) && (y > y2))) {
220 	return 0;
221     }
222     /* none of the above, compute the intersection */
223     if ((xtmp = x2 - x1) != 0.0) {
224 	m = (y2 - y1) / xtmp;
225 	b = y1 - m * x1;
226 	xtmp = (y - b) / m;
227     } else {
228 	xtmp = x1;
229     }
230     if (xtmp <= x) {
231 	/* check for intersections at a vertex */
232 	/* if this is the max ordinate then accept */
233 	if (y == y1) {
234 	    if (y1 > y2) {
235 		return 1;
236 	    } else {
237 		return 0;
238 	    }
239 	}
240 	/* check for intersections at a vertex */
241 	if (y == y2) {
242 	    if (y2 > y1) {
243 		return 1;
244 	    } else {
245 		return 0;
246 	    }
247 	}
248 	/* no vertices intersected */
249 	return 1;
250     }
251     return 0;
252 }
253 
254 /*
255  * determine if (x,y) is in the polygon xlist[], ylist[]
256  */
inbound(double x,double y,double * xlist,double * ylist,int n)257 int inbound(double x, double y, double *xlist, double *ylist, int n)
258 {
259     int i, l = 0;
260 
261     for (i = 0; i < n; i++) {
262 	l += intersect_to_left(x, y, xlist[i], ylist[i], xlist[(i + 1) % n], ylist[(i + 1) % n]);
263     }
264     return l % 2;
265 }
266 
267 /*
268  * routines to determine if a point lies to the left of an infinite line
269 */
isleft(double x,double y,double x1,double y1,double x2,double y2)270 int isleft(double x, double y, double x1, double y1, double x2, double y2)
271 {
272     double xtmp, m, b;
273 
274     /* horizontal lines */
275     if (y1 == y2) {
276 	return 0;
277     }
278     /* none of the above, compute the intersection */
279     if ((xtmp = x2 - x1) != 0.0) {
280 	m = (y2 - y1) / xtmp;
281 	b = y1 - m * x1;
282 	xtmp = (y - b) / m;
283     } else {
284 	xtmp = x1;
285     }
286     if (xtmp >= x) {
287 	return 1;
288     }
289     return 0;
290 }
291 
292 
293 /*
294  * routines to determine if a point lies to the left of an infinite line
295 */
isright(double x,double y,double x1,double y1,double x2,double y2)296 int isright(double x, double y, double x1, double y1, double x2, double y2)
297 {
298     double xtmp, m, b;
299 
300     /* horizontal lines */
301     if (y1 == y2) {
302 	return 0;
303     }
304     if ((xtmp = x2 - x1) != 0.0) {
305 	m = (y2 - y1) / xtmp;
306 	b = y1 - m * x1;
307 	xtmp = (y - b) / m;
308     } else {
309 	xtmp = x1;
310     }
311     if (xtmp <= x) {
312 	return 1;
313     }
314     return 0;
315 }
316 
317 /*
318  * routines to determine if a point lies above an infinite line
319 */
isabove(double x,double y,double x1,double y1,double x2,double y2)320 int isabove(double x, double y, double x1, double y1, double x2, double y2)
321 {
322     double ytmp, m, b;
323 
324     /* vertical lines */
325     if (x1 == x2) {
326 	return 0;
327     }
328     if ((ytmp = y2 - y1) != 0.0) {
329 	m = ytmp / (x2 - x1);
330 	b = y1 - m * x1;
331 	ytmp = m * x + b;
332     } else {
333 	ytmp = y1;
334     }
335     if (ytmp <= y) {
336 	return 1;
337     }
338     return 0;
339 }
340 
341 /*
342  * routines to determine if a point lies below an infinite line
343 */
isbelow(double x,double y,double x1,double y1,double x2,double y2)344 int isbelow(double x, double y, double x1, double y1, double x2, double y2)
345 {
346     double ytmp, m, b;
347 
348     /* vertical lines */
349     if (x1 == x2) {
350 	return 0;
351     }
352     if ((ytmp = y2 - y1) != 0.0) {
353 	m = ytmp / (x2 - x1);
354 	b = y1 - m * x1;
355 	ytmp = m * x + b;
356     } else {
357 	ytmp = y1;
358     }
359     if (ytmp >= y) {
360 	return 1;
361     }
362     return 0;
363 }
364 
inregion(int regno,double x,double y)365 int inregion(int regno, double x, double y)
366 {
367     if (regno == MAXREGION) {
368 	return (inbounds(get_cg() , x, y));
369     }
370     if (regno == MAXREGION + 1) {
371 	return (!inbounds(get_cg() , x, y));
372     }
373     if (rg[regno].active == TRUE) {
374 	switch (rg[regno].type) {
375 	case REGION_POLYI:
376 	    if (inbound(x, y, rg[regno].x, rg[regno].y, rg[regno].n)) {
377 		return 1;
378 	    }
379 	    break;
380 	case REGION_POLYO:
381 	    if (!inbound(x, y, rg[regno].x, rg[regno].y, rg[regno].n)) {
382 		return 1;
383 	    }
384 	    break;
385 	case REGION_TORIGHT:
386 	    if (isright(x, y, rg[regno].x1, rg[regno].y1, rg[regno].x2, rg[regno].y2)) {
387 		return 1;
388 	    }
389 	    break;
390 	case REGION_TOLEFT:
391 	    if (isleft(x, y, rg[regno].x1, rg[regno].y1, rg[regno].x2, rg[regno].y2)) {
392 		return 1;
393 	    }
394 	    break;
395 	case REGION_ABOVE:
396 	    if (isabove(x, y, rg[regno].x1, rg[regno].y1, rg[regno].x2, rg[regno].y2)) {
397 		return 1;
398 	    }
399 	    break;
400 	case REGION_BELOW:
401 	    if (isbelow(x, y, rg[regno].x1, rg[regno].y1, rg[regno].x2, rg[regno].y2)) {
402 		return 1;
403 	    }
404 	    break;
405 	case REGION_HORIZI:
406 	  return (x >= rg[regno].x1) && ( x <= rg[regno].x2);
407 	  break;
408 	case REGION_VERTI:
409 	  return (y >= rg[regno].y1) && ( y <= rg[regno].y2);
410 	  break;
411 	case REGION_HORIZO:
412 	  return !( (x >= rg[regno].x1) && ( x <= rg[regno].x2) );
413 	  break;
414 	case REGION_VERTO:
415 	  return !( (y >= rg[regno].y1) && ( y <= rg[regno].y2) );
416 	  break;
417 
418 	}
419     }
420     return 0;
421 }
422