Annotation of OpenXM_contrib/gnuplot/util3d.c, Revision 1.1.1.2
1.1 maekawa 1: #ifndef lint
1.1.1.2 ! maekawa 2: static char *RCSid = "$Id: util3d.c,v 1.7 1998/09/21 21:04:42 lhecking Exp $";
1.1 maekawa 3: #endif
4:
5:
6: /* GNUPLOT - util3d.c */
7:
8: /*[
9: * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
10: *
11: * Permission to use, copy, and distribute this software and its
12: * documentation for any purpose with or without fee is hereby granted,
13: * provided that the above copyright notice appear in all copies and
14: * that both that copyright notice and this permission notice appear
15: * in supporting documentation.
16: *
17: * Permission to modify the software is granted, but not the right to
18: * distribute the complete modified source code. Modifications are to
19: * be distributed as patches to the released version. Permission to
20: * distribute binaries produced by compiling modified sources is granted,
21: * provided you
22: * 1. distribute the corresponding source modifications from the
23: * released version in the form of a patch file along with the binaries,
24: * 2. add special version identification to distinguish your version
25: * in addition to the base release version number,
26: * 3. provide your name and address as the primary contact for the
27: * support of your modified version, and
28: * 4. retain our contact information in regard to use of the base
29: * software.
30: * Permission to distribute the released version of the source code along
31: * with corresponding source modifications in the form of a patch file is
32: * granted with same provisions 2 through 4 for binary distributions.
33: *
34: * This software is provided "as is" without express or implied warranty
35: * to the extent permitted by applicable law.
36: ]*/
37:
38:
39: /*
40: * 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu)
41: * Added user-specified bases for log scaling.
42: *
43: * 3.6 - split graph3d.c into graph3d.c (graph),
44: * util3d.c (intersections, etc)
45: * hidden3d.c (hidden-line removal code)
46: *
47: */
48:
49: #include "plot.h"
50: #include "setshow.h"
51:
52: extern int xleft,xright,ybot,ytop;
53: extern int hidden_active; /* HBB 980324: hidden_no_update was unused here */
54:
55: /* ACCESS THINGS THAT OUGHT TO BE HIDDEN IN hidden3d.c - perhaps we
56: * can move the relevant code into hidden3d.c sometime
57: */
58:
59: /* Bitmap of the screen. The array for each x value is malloc-ed as needed */
60:
61: extern int suppressMove;
62:
63: extern double min_array[], max_array[];
64: extern int auto_array[], log_array[];
65: extern double base_array[], log_base_array[];
66:
67: /* for convenience while converting to use these arrays */
68: #define x_min3d min_array[FIRST_X_AXIS]
69: #define x_max3d max_array[FIRST_X_AXIS]
70: #define y_min3d min_array[FIRST_Y_AXIS]
71: #define y_max3d max_array[FIRST_Y_AXIS]
72: #define z_min3d min_array[FIRST_Z_AXIS]
73: #define z_max3d max_array[FIRST_Z_AXIS]
74: #define min3d_z min_array[FIRST_Z_AXIS]
75: #define max3d_z max_array[FIRST_Z_AXIS]
76:
77: typedef double transform_matrix[4][4];
78:
79: void mat_unit(mat)
80: transform_matrix mat;
81: {
82: int i, j;
83:
84: for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
85: if (i == j)
86: mat[i][j] = 1.0;
87: else
88: mat[i][j] = 0.0;
89: }
90:
91: void mat_trans(tx, ty, tz, mat)
92: double tx, ty, tz;
93: transform_matrix mat;
94: {
95: mat_unit(mat); /* Make it unit matrix. */
96: mat[3][0] = tx;
97: mat[3][1] = ty;
98: mat[3][2] = tz;
99: }
100:
101: void mat_scale(sx, sy, sz, mat)
102: double sx, sy, sz;
103: transform_matrix mat;
104: {
105: mat_unit(mat); /* Make it unit matrix. */
106: mat[0][0] = sx;
107: mat[1][1] = sy;
108: mat[2][2] = sz;
109: }
110:
111: void mat_rot_x(teta, mat)
112: double teta;
113: transform_matrix mat;
114: {
115: double cos_teta, sin_teta;
116:
117: teta *= Pi / 180.0;
118: cos_teta = cos(teta);
119: sin_teta = sin(teta);
120:
121: mat_unit(mat); /* Make it unit matrix. */
122: mat[1][1] = cos_teta;
123: mat[1][2] = -sin_teta;
124: mat[2][1] = sin_teta;
125: mat[2][2] = cos_teta;
126: }
127:
128: void mat_rot_y(teta, mat)
129: double teta;
130: transform_matrix mat;
131: {
132: double cos_teta, sin_teta;
133:
134: teta *= Pi / 180.0;
135: cos_teta = cos(teta);
136: sin_teta = sin(teta);
137:
138: mat_unit(mat); /* Make it unit matrix. */
139: mat[0][0] = cos_teta;
140: mat[0][2] = -sin_teta;
141: mat[2][0] = sin_teta;
142: mat[2][2] = cos_teta;
143: }
144:
145: void mat_rot_z(teta, mat)
146: double teta;
147: transform_matrix mat;
148: {
149: double cos_teta, sin_teta;
150:
151: teta *= Pi / 180.0;
152: cos_teta = cos(teta);
153: sin_teta = sin(teta);
154:
155: mat_unit(mat); /* Make it unit matrix. */
156: mat[0][0] = cos_teta;
157: mat[0][1] = -sin_teta;
158: mat[1][0] = sin_teta;
159: mat[1][1] = cos_teta;
160: }
161:
162: /* Multiply two transform_matrix. Result can be one of two operands. */
163: void mat_mult(mat_res, mat1, mat2)
164: transform_matrix mat_res, mat1, mat2;
165: {
166: int i, j, k;
167: transform_matrix mat_res_temp;
168:
169: for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) {
170: mat_res_temp[i][j] = 0;
171: for (k = 0; k < 4; k++) mat_res_temp[i][j] += mat1[i][k] * mat2[k][j];
172: }
173: for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
174: mat_res[i][j] = mat_res_temp[i][j];
175: }
176:
177:
178: /* Test a single point to be within the xleft,xright,ybot,ytop bbox.
179: * Sets the returned integers 4 l.s.b. as follows:
180: * bit 0 if to the left of xleft.
181: * bit 1 if to the right of xright.
182: * bit 2 if above of ytop.
183: * bit 3 if below of ybot.
184: * 0 is returned if inside.
185: */
186: int clip_point(x, y)
187: unsigned int x, y;
188: {
189: int ret_val = 0;
190:
191: if (x < xleft) ret_val |= 0x01;
192: if (x > xright) ret_val |= 0x02;
193: if (y < ybot) ret_val |= 0x04;
194: if (y > ytop) ret_val |= 0x08;
195:
196: return ret_val;
197: }
198:
199: /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
200: * This routine uses the cohen & sutherland bit mapping for fast clipping -
201: * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
202: */
203: void draw_clip_line(x1, y1, x2, y2)
204: unsigned int x1, y1, x2, y2;
205: {
206: int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
207: register struct termentry *t = term;
208:
209: #if defined(ATARI) || defined(MTOS)
210: if(x1<0||x2<0||y1<0||y2<0) return; /* temp bug fix */
211: #endif
212:
213: pos1 = clip_point(x1, y1);
214: pos2 = clip_point(x2, y2);
215: if (pos1 || pos2) {
216: if (pos1 & pos2) return; /* segment is totally out. */
217:
218: /* Here part of the segment MAY be inside. test the intersection
219: * of this segment with the 4 boundaries for hopefully 2 intersections
220: * in. If none are found segment is totaly out.
221: * Under rare circumstances there may be up to 4 intersections (e.g.
222: * when the line passes directly through at least one corner). In
223: * this case it is sufficient to take any 2 intersections (e.g. the
224: * first two found).
225: */
226: count = 0;
227: dx = x2 - x1;
228: dy = y2 - y1;
229:
230: /* Find intersections with the x parallel bbox lines: */
231: if (dy != 0) {
232: x = (ybot - y2) * dx / dy + x2; /* Test for ybot boundary. */
233: if (x >= xleft && x <= xright) {
234: x_intr[count] = x;
235: y_intr[count++] = ybot;
236: }
237: x = (ytop - y2) * dx / dy + x2; /* Test for ytop boundary. */
238: if (x >= xleft && x <= xright) {
239: x_intr[count] = x;
240: y_intr[count++] = ytop;
241: }
242: }
243:
244: /* Find intersections with the y parallel bbox lines: */
245: if (dx != 0) {
246: y = (xleft - x2) * dy / dx + y2; /* Test for xleft boundary. */
247: if (y >= ybot && y <= ytop) {
248: x_intr[count] = xleft;
249: y_intr[count++] = y;
250: }
251: y = (xright - x2) * dy / dx + y2; /* Test for xright boundary. */
252: if (y >= ybot && y <= ytop) {
253: x_intr[count] = xright;
254: y_intr[count++] = y;
255: }
256: }
257:
258: if (count >= 2) {
259: int x_max, x_min, y_max, y_min;
260:
261: x_min = GPMIN(x1, x2);
262: x_max = GPMAX(x1, x2);
263: y_min = GPMIN(y1, y2);
264: y_max = GPMAX(y1, y2);
265:
266: if (pos1 && pos2) { /* Both were out - update both */
267: x1 = x_intr[0];
268: y1 = y_intr[0];
269: x2 = x_intr[1];
270: y2 = y_intr[1];
271: }
272: else if (pos1) { /* Only x1/y1 was out - update only it */
273: if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) {
274: x1 = x_intr[0];
275: y1 = y_intr[0];
276: }
277: else {
278: x1 = x_intr[1];
279: y1 = y_intr[1];
280: }
281: }
282: else { /* Only x2/y2 was out - update only it */
283: if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - y1) > 0) {
284: x2 = x_intr[0];
285: y2 = y_intr[0];
286: }
287: else {
288: x2 = x_intr[1];
289: y2 = y_intr[1];
290: }
291: }
292:
293: if (x1 < x_min || x1 > x_max ||
294: x2 < x_min || x2 > x_max ||
295: y1 < y_min || y1 > y_max ||
296: y2 < y_min || y2 > y_max) return;
297: }
298: else
299: return;
300: }
301:
302:
303: #ifndef LITE
304: if(hidden3d && hidden_active && draw_surface)
305: {
306: draw_line_hidden(x1, y1, x2, y2);
307: return;
308: };
309: #endif /* not LITE */
310: if(!suppressMove) (*t->move)(x1,y1);
311: (*t->vector)(x2,y2);
312: }
313:
314:
315:
316: /* And text clipping routine. */
317: void clip_put_text(x, y, str)
318: unsigned int x,y;
319: char *str;
320: {
321: register struct termentry *t = term;
322:
323: if (clip_point(x, y)) return;
324:
325: (*t->put_text)(x,y,str);
326: }
327:
328: /* seems sensible to put the justification in here too..? */
329: void clip_put_text_just(x,y,str,just)
330: unsigned int x,y;
331: char *str;
332: enum JUSTIFY just;
333: {
334: register struct termentry *t = term;
335: if (clip_point(x,y)) return;
336: if (!(*t->justify_text)(just)) {
337: assert(CENTRE == 1 && RIGHT == 2);
338: x -= (t->h_char * strlen(str) * just)/2;
339: }
340: (*t->put_text)(x,y,str);
341: }
342:
343:
344:
345: /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
346: * This routine uses the cohen & sutherland bit mapping for fast clipping -
347: * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
348: */
349:
350: int clip_line(x1, y1, x2, y2)
351: int *x1, *y1, *x2, *y2;
352: {
353: int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
354: int x_max, x_min, y_max, y_min;
355: pos1 = clip_point(*x1, *y1);
356: pos2 = clip_point(*x2, *y2);
357: if (!pos1 && !pos2) return 1; /* segment is totally in */
358: if (pos1 & pos2) return 0; /* segment is totally out. */
359: /* Here part of the segment MAY be inside. test the intersection
360: * of this segment with the 4 boundaries for hopefully 2 intersections
361: * in. If non found segment is totaly out.
362: */
363: count = 0;
364: dx = *x2 - *x1;
365: dy = *y2 - *y1;
366: /* Find intersections with the x parallel bbox lines: */
367: if (dy != 0) {
368: x = (ybot - *y2) * dx / dy + *x2; /* Test for ybot boundary. */
369: if (x >= xleft && x <= xright) {
370: x_intr[count] = x;
371: y_intr[count++] = ybot;
372: }
373: x = (ytop - *y2) * dx / dy + *x2; /* Test for ytop boundary. */
374: if (x >= xleft && x <= xright) {
375: x_intr[count] = x;
376: y_intr[count++] = ytop;
377: }
378: }
379: /* Find intersections with the y parallel bbox lines: */
380: if (dx != 0) {
381: y = (xleft - *x2) * dy / dx + *y2; /* Test for xleft boundary. */
382: if (y >= ybot && y <= ytop) {
383: x_intr[count] = xleft;
384: y_intr[count++] = y;
385: }
386: y = (xright - *x2) * dy / dx + *y2; /* Test for xright boundary. */
387: if (y >= ybot && y <= ytop) {
388: x_intr[count] = xright;
389: y_intr[count++] = y;
390: }
391: }
392: if (count < 2) return 0;
393: if (*x1 < *x2)
394: x_min = *x1, x_max = *x2;
395: else
396: x_min = *x2, x_max = *x1;
397: if (*y1 < *y2)
398: y_min = *y1, y_max = *y2;
399: else
400: y_min = *y2, y_max = *y1;
401: if (pos1 && pos2) { /* Both were out - update both */
402: *x1 = x_intr[0];
403: *y1 = y_intr[0];
404: *x2 = x_intr[1];
405: *y2 = y_intr[1];
406: }
407: else if (pos1)
408: { /* Only x1/y1 was out - update only it */
409: if (dx * (*x2 - x_intr[0]) + dy * (*y2 - y_intr[0]) >= 0)
410: {
411: *x1 = x_intr[0];
412: *y1 = y_intr[0];
413: }
414: else
415: {
416: *x1 = x_intr[1];
417: *y1 = y_intr[1];
418: }
419: }
420: else
421: { /* Only x2/y2 was out - update only it */
422: if (dx * (x_intr[0] - *x1) + dy * (y_intr[0] - *y1) >= 0)
423: {
424: *x2 = x_intr[0];
425: *y2 = y_intr[0];
426: }
427: else
428: {
429: *x2 = x_intr[1];
430: *y2 = y_intr[1];
431: }
432: }
433:
434: if (*x1 < x_min || *x1 > x_max ||
435: *x2 < x_min || *x2 > x_max ||
436: *y1 < y_min || *y1 > y_max ||
437: *y2 < y_min || *y2 > y_max)
438: return 0;
439: return 1;
440: }
441:
442:
443: /* single edge intersection algorithm */
444: /* Given two points, one inside and one outside the plot, return
445: * the point where an edge of the plot intersects the line segment defined
446: * by the two points.
447: */
448: void edge3d_intersect(points, i, ex, ey, ez)
449: struct coordinate GPHUGE *points; /* the points array */
450: int i; /* line segment from point i-1 to point i */
451: double *ex, *ey, *ez; /* the point where it crosses an edge */
452: {
453: /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
454: int count;
455: double ix = points[i-1].x;
456: double iy = points[i-1].y;
457: double iz = points[i-1].z;
458: double ox = points[i].x;
459: double oy = points[i].y;
460: double oz = points[i].z;
461: double x, y, z; /* possible intersection point */
462:
463: if(points[i].type == INRANGE)
464: {
465: /* swap points around so that ix/ix/iz are INRANGE and ox/oy/oz are OUTRANGE */
466: x = ix;ix = ox;ox = x;
467: y = iy;iy = oy;oy = y;
468: z = iz;iz = oz;oz = z;
469: }
470:
471: /* nasty degenerate cases, effectively drawing to an infinity point (?)
472: cope with them here, so don't process them as a "real" OUTRANGE point
473:
474: If more than one coord is -VERYLARGE, then can't ratio the "infinities"
475: so drop out by returning the INRANGE point.
476:
477: Obviously, only need to test the OUTRANGE point (coordinates) */
478:
479: /* nasty degenerate cases, effectively drawing to an infinity point (?)
480: cope with them here, so don't process them as a "real" OUTRANGE point
481:
482: If more than one coord is -VERYLARGE, then can't ratio the "infinities"
483: so drop out by returning FALSE */
484:
485: count = 0;
486: if(ox == -VERYLARGE) count++;
487: if(oy == -VERYLARGE) count++;
488: if(oz == -VERYLARGE) count++;
489:
490: /* either doesn't pass through 3D volume *or*
491: can't ratio infinities to get a direction to draw line, so return the INRANGE point */
492: if(count > 1){
493: *ex = ix;
494: *ey = iy;
495: *ez = iz;
496:
497: return;
498: }
499:
500: if(count == 1)
501: {
502: *ex = ix;
503: *ey = iy;
504: *ez = iz;
505:
506: if(ox == -VERYLARGE)
507: {
508: *ex = x_min3d;
509: return;
510: }
511:
512: if(oy == -VERYLARGE)
513: {
514: *ey = y_min3d;
515: return;
516: }
517:
518: /* obviously oz is -VERYLARGE and (ox != -VERYLARGE && oy != -VERYLARGE) */
519: *ez = min3d_z;
520: return;
521: }
522:
523: /*
524: * Can't have case (ix == ox && iy == oy && iz == oz) as one point
525: * is INRANGE and one point is OUTRANGE.
526: */
527: if(ix == ox) {
528: if(iy == oy) {
529: /* line parallel to z axis */
530:
531: /* assume inrange(iy, y_min3d, y_max3d) && inrange(ix, x_min3d, x_max3d) */
532: *ex = ix; /* == ox */
533: *ey = iy; /* == oy */
534:
535: if (inrange(max3d_z, iz, oz))
536: *ez = max3d_z;
537: else if (inrange(min3d_z, iz, oz))
538: *ez = min3d_z;
539: else {
540: graph_error("error in edge3d_intersect");
541: }
542:
543: return;
544: }
545:
546: if(iz == oz) {
547: /* line parallel to y axis */
548:
549: /* assume inrange(iz, min3d_z, max3d_z) && inrange(ix, x_min3d, x_max3d) */
550: *ex = ix; /* == ox */
551: *ez = iz; /* == oz */
552:
553: if (inrange(y_max3d, iy, oy))
554: *ey = y_max3d;
555: else if (inrange(y_min3d, iy, oy))
556: *ey = y_min3d;
557: else {
558: graph_error("error in edge3d_intersect");
559: }
560:
561: return;
562: }
563:
564: /* nasty 2D slanted line in a yz plane */
565:
566: /* does it intersect y_min3d edge */
567: if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
568: z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
569: if (inrange(z, min3d_z, max3d_z)) {
570: *ex = ix;
571: *ey = y_min3d;
572: *ez = z;
573: return;
574: }
575: }
576:
577: /* does it intersect y_max3d edge */
578: if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
579: z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
580: if (inrange(z, min3d_z, max3d_z)) {
581: *ex = ix;
582: *ey = y_max3d;
583: *ez = z;
584: return;
585: }
586: }
587:
588: /* does it intersect min3d_z edge */
589: if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
590: y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
591: if (inrange(y, y_min3d, y_max3d)) {
592: *ex = ix;
593: *ey = y;
594: *ez = min3d_z;
595: return;
596: }
597: }
598:
599: /* does it intersect max3d_z edge */
600: if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
601: y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
602: if (inrange(y, y_min3d, y_max3d)) {
603: *ex = ix;
604: *ey = y;
605: *ez = max3d_z;
606: return;
607: }
608: }
609: }
610:
611: if(iy == oy) {
612: /* already checked case (ix == ox && iy == oy) */
613: if(oz == iz) {
614: /* line parallel to x axis */
615:
616: /* assume inrange(iz, min3d_z, max3d_z) && inrange(iy, y_min3d, y_max3d) */
617: *ey = iy; /* == oy */
618: *ez = iz; /* == oz */
619:
620: if (inrange(x_max3d, ix, ox))
621: *ex = x_max3d;
622: else if (inrange(x_min3d, ix, ox))
623: *ex = x_min3d;
624: else {
625: graph_error("error in edge3d_intersect");
626: }
627:
628: return;
629: }
630:
631: /* nasty 2D slanted line in an xz plane */
632:
633: /* does it intersect x_min3d edge */
634: if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
635: z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
636: if (inrange(z, min3d_z, max3d_z)) {
637: *ex = x_min3d;
638: *ey = iy;
639: *ez = z;
640: return;
641: }
642: }
643:
644: /* does it intersect x_max3d edge */
645: if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
646: z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
647: if (inrange(z, min3d_z, max3d_z)) {
648: *ex = x_max3d;
649: *ey = iy;
650: *ez = z;
651: return;
652: }
653: }
654:
655: /* does it intersect min3d_z edge */
656: if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
657: x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
658: if (inrange(x, x_min3d, x_max3d)) {
659: *ex = x;
660: *ey = iy;
661: *ez = min3d_z;
662: return;
663: }
664: }
665:
666: /* does it intersect max3d_z edge */
667: if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
668: x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
669: if (inrange(x, x_min3d, x_max3d)) {
670: *ex = x;
671: *ey = iy;
672: *ez = max3d_z;
673: return;
674: }
675: }
676: }
677:
678: if(iz == oz) {
679: /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
680:
681: /* nasty 2D slanted line in an xy plane */
682:
683: /* assume inrange(oz, min3d_z, max3d_z) */
684:
685: /* does it intersect x_min3d edge */
686: if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
687: y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
688: if (inrange(y, y_min3d, y_max3d)) {
689: *ex = x_min3d;
690: *ey = y;
691: *ez = iz;
692: return;
693: }
694: }
695:
696: /* does it intersect x_max3d edge */
697: if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
698: y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
699: if (inrange(y, y_min3d, y_max3d)) {
700: *ex = x_max3d;
701: *ey = y;
702: *ez = iz;
703: return;
704: }
705: }
706:
707: /* does it intersect y_min3d edge */
708: if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
709: x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
710: if (inrange(x, x_min3d, x_max3d)) {
711: *ex = x;
712: *ey = y_min3d;
713: *ez = iz;
714: return;
715: }
716: }
717:
718: /* does it intersect y_max3d edge */
719: if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
720: x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
721: if (inrange(x, x_min3d, x_max3d)) {
722: *ex = x;
723: *ey = y_max3d;
724: *ez = iz;
725: return;
726: }
727: }
728: }
729:
730: /* really nasty general slanted 3D case */
731:
732: /* does it intersect x_min3d edge */
733: if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
734: y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
735: z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
736: if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
737: *ex = x_min3d;
738: *ey = y;
739: *ez = z;
740: return;
741: }
742: }
743:
744: /* does it intersect x_max3d edge */
745: if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
746: y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
747: z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
748: if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
749: *ex = x_max3d;
750: *ey = y;
751: *ez = z;
752: return;
753: }
754: }
755:
756: /* does it intersect y_min3d edge */
757: if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
758: x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
759: z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
760: if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
761: *ex = x;
762: *ey = y_min3d;
763: *ez = z;
764: return;
765: }
766: }
767:
768: /* does it intersect y_max3d edge */
769: if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
770: x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
771: z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
772: if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
773: *ex = x;
774: *ey = y_max3d;
775: *ez = z;
776: return;
777: }
778: }
779:
780: /* does it intersect min3d_z edge */
781: if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
782: x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
783: y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
784: if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
785: *ex = x;
786: *ey = y;
787: *ez = min3d_z;
788: return;
789: }
790: }
791:
792: /* does it intersect max3d_z edge */
793: if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
794: x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
795: y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
796: if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
797: *ex = x;
798: *ey = y;
799: *ez = max3d_z;
800: return;
801: }
802: }
803:
804: /* If we reach here, the inrange point is on the edge, and
805: * the line segment from the outrange point does not cross any
806: * other edges to get there. In this case, we return the inrange
807: * point as the 'edge' intersection point. This will basically draw
808: * line.
809: */
810: *ex = ix;
811: *ey = iy;
812: *ez = iz;
813: return;
814: }
815:
816: /* double edge intersection algorithm */
817: /* Given two points, both outside the plot, return
818: * the points where an edge of the plot intersects the line segment defined
819: * by the two points. There may be zero, one, two, or an infinite number
820: * of intersection points. (One means an intersection at a corner, infinite
821: * means overlaying the edge itself). We return FALSE when there is nothing
822: * to draw (zero intersections), and TRUE when there is something to
823: * draw (the one-point case is a degenerate of the two-point case and we do
824: * not distinguish it - we draw it anyway).
825: */
826: TBOOLEAN /* any intersection? */
827: two_edge3d_intersect(points, i, lx, ly, lz)
828: struct coordinate GPHUGE *points; /* the points array */
829: int i; /* line segment from point i-1 to point i */
830: double *lx, *ly, *lz; /* lx[2], ly[2], lz[2]: points where it crosses edges */
831: {
832: int count;
833: /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
834: double ix = points[i-1].x;
835: double iy = points[i-1].y;
836: double iz = points[i-1].z;
837: double ox = points[i].x;
838: double oy = points[i].y;
839: double oz = points[i].z;
840: double t[6];
841: double swap;
842: double x, y, z; /* possible intersection point */
843: double t_min, t_max;
844:
845: /* nasty degenerate cases, effectively drawing to an infinity point (?)
846: cope with them here, so don't process them as a "real" OUTRANGE point
847:
848: If more than one coord is -VERYLARGE, then can't ratio the "infinities"
849: so drop out by returning FALSE */
850:
851: count = 0;
852: if(ix == -VERYLARGE) count++;
853: if(ox == -VERYLARGE) count++;
854: if(iy == -VERYLARGE) count++;
855: if(oy == -VERYLARGE) count++;
856: if(iz == -VERYLARGE) count++;
857: if(oz == -VERYLARGE) count++;
858:
859: /* either doesn't pass through 3D volume *or*
860: can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
861: if(count > 1){
862: return(FALSE);
863: }
864:
865: if(ox == -VERYLARGE || ix == -VERYLARGE)
866: {
867: if(ix == -VERYLARGE)
868: {
869: /* swap points so ix/iy/iz don't have a -VERYLARGE component */
870: x = ix;ix = ox;ox = x;
871: y = iy;iy = oy;oy = y;
872: z = iz;iz = oz;oz = z;
873: }
874:
875: /* check actually passes through the 3D graph volume */
876: if(ix > x_max3d && inrange(iy, y_min3d, y_max3d) && inrange(iz, min3d_z, max3d_z))
877: {
878: lx[0] = x_min3d;
879: ly[0] = iy;
880: lz[0] = iz;
881:
882: lx[1] = x_max3d;
883: ly[1] = iy;
884: lz[1] = iz;
885:
886: return(TRUE);
887: }
888: else {
889: return(FALSE);
890: }
891: }
892:
893: if(oy == -VERYLARGE || iy == -VERYLARGE)
894: {
895: if(iy == -VERYLARGE)
896: {
897: /* swap points so ix/iy/iz don't have a -VERYLARGE component */
898: x = ix; ix = ox; ox = x;
899: y = iy; iy = oy; oy = y;
900: z = iz; iz = oz; oz = z;
901: }
902:
903: /* check actually passes through the 3D graph volume */
904: if(iy > y_max3d && inrange(ix, x_min3d, x_max3d) && inrange(iz, min3d_z, max3d_z))
905: {
906: lx[0] = ix;
907: ly[0] = y_min3d;
908: lz[0] = iz;
909:
910: lx[1] = ix;
911: ly[1] = y_max3d;
912: lz[1] = iz;
913:
914: return(TRUE);
915: }
916: else {
917: return(FALSE);
918: }
919: }
920:
921: if(oz == -VERYLARGE || iz == -VERYLARGE)
922: {
923: if(iz == -VERYLARGE)
924: {
925: /* swap points so ix/iy/iz don't have a -VERYLARGE component */
926: x = ix; ix = ox; ox = x;
927: y = iy; iy = oy; oy = y;
928: z = iz; iz = oz; oz = z;
929: }
930:
931: /* check actually passes through the 3D graph volume */
932: if(iz > max3d_z && inrange(ix, x_min3d, x_max3d) && inrange(iy, y_min3d, y_max3d))
933: {
934: lx[0] = ix;
935: ly[0] = iy;
936: lz[0] = min3d_z;
937:
938: lx[1] = ix;
939: ly[1] = iy;
940: lz[1] = max3d_z;
941:
942: return(TRUE);
943: }
944: else {
945: return(FALSE);
946: }
947: }
948:
949: /*
950: * Quick outcode tests on the 3d graph volume
951: */
952:
953: /*
954: * test z coord first --- most surface OUTRANGE points generated between
955: * min3d_z and z_min3d (i.e. when ticslevel is non-zero)
956: */
957: if(GPMAX(iz,oz) < min3d_z || GPMIN(iz,oz) > max3d_z)
958: return(FALSE);
959:
960: if(GPMAX(ix,ox) < x_min3d || GPMIN(ix,ox) > x_max3d)
961: return(FALSE);
962:
963: if(GPMAX(iy,oy) < y_min3d || GPMIN(iy,oy) > y_max3d)
964: return(FALSE);
965:
966: /*
967: * Special horizontal/vertical, etc. cases are checked and remaining
968: * slant lines are checked separately.
969: *
970: * The slant line intersections are solved using the parametric form
971: * of the equation for a line, since if we test x/y/z min/max planes explicitly
972: * then e.g. a line passing through a corner point (x_min,y_min,z_min)
973: * actually intersects all 3 planes and hence further tests would be required
974: * to anticipate this and similar situations.
975: */
976:
977: /*
978: * Can have case (ix == ox && iy == oy && iz == oz) as both points OUTRANGE
979: */
980: if(ix == ox && iy == oy && iz == oz)
981: {
982: /* but as only define single outrange point, can't intersect 3D graph volume */
983: return(FALSE);
984: }
985:
986: if(ix == ox) {
987: if(iy == oy) {
988: /* line parallel to z axis */
989:
990: /* x and y coords must be in range, and line must span both min3d_z and max3d_z */
991: /* note that spanning min3d_z implies spanning max3d_z as both points OUTRANGE */
992: if(!inrange(ix, x_min3d, x_max3d) || !inrange(iy, y_min3d, y_max3d))
993: {
994: return(FALSE);
995: }
996:
997: if (inrange(min3d_z, iz, oz)) {
998: lx[0] = ix;
999: ly[0] = iy;
1000: lz[0] = min3d_z;
1001:
1002: lx[1] = ix;
1003: ly[1] = iy;
1004: lz[1] = max3d_z;
1005:
1006: return(TRUE);
1007: } else
1008: return(FALSE);
1009: }
1010:
1011: if(iz == oz) {
1012: /* line parallel to y axis */
1013:
1014: /* x and z coords must be in range, and line must span both y_min3d and y_max3d */
1015: /* note that spanning y_min3d implies spanning y_max3d, as both points OUTRANGE */
1016: if(!inrange(ix, x_min3d, x_max3d) || !inrange(iz, min3d_z, max3d_z))
1017: {
1018: return(FALSE);
1019: }
1020:
1021: if (inrange(y_min3d, iy, oy)) {
1022: lx[0] = ix;
1023: ly[0] = y_min3d;
1024: lz[0] = iz;
1025:
1026: lx[1] = ix;
1027: ly[1] = y_max3d;
1028: lz[1] = iz;
1029:
1030: return(TRUE);
1031: } else
1032: return(FALSE);
1033: }
1034:
1035: /* nasty 2D slanted line in a yz plane */
1036:
1037: if(!inrange(ox, x_min3d, x_max3d))
1038: return(FALSE);
1039:
1040: t[0] = (y_min3d - iy)/(oy - iy);
1041: t[1] = (y_max3d - iy)/(oy - iy);
1042:
1043: if(t[0] > t[1]) {
1044: swap = t[0];t[0] = t[1];t[1] = swap;
1045: }
1046:
1047: t[2] = (min3d_z - iz)/(oz - iz);
1048: t[3] = (max3d_z - iz)/(oz - iz);
1049:
1050: if(t[2] > t[3]) {
1051: swap = t[2];t[2] = t[3];t[3] = swap;
1052: }
1053:
1054: t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
1055: t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
1056:
1057: if(t_min > t_max)
1058: return(FALSE);
1059:
1060: lx[0] = ix;
1061: ly[0] = iy + t_min * (oy - iy);
1062: lz[0] = iz + t_min * (oz - iz);
1063:
1064: lx[1] = ix;
1065: ly[1] = iy + t_max * (oy - iy);
1066: lz[1] = iz + t_max * (oz - iz);
1067:
1068: /*
1069: * Can only have 0 or 2 intersection points -- only need test one coord
1070: */
1071: if(inrange(ly[0], y_min3d, y_max3d) &&
1072: inrange(lz[0], min3d_z, max3d_z))
1073: {
1074: return(TRUE);
1075: }
1076:
1077: return(FALSE);
1078: }
1079:
1080: if(iy == oy) {
1081: /* already checked case (ix == ox && iy == oy) */
1082: if(oz == iz) {
1083: /* line parallel to x axis */
1084:
1085: /* y and z coords must be in range, and line must span both x_min3d and x_max3d */
1086: /* note that spanning x_min3d implies spanning x_max3d, as both points OUTRANGE */
1087: if(!inrange(iy, y_min3d, y_max3d) || !inrange(iz, min3d_z, max3d_z))
1088: {
1089: return(FALSE);
1090: }
1091:
1092: if (inrange(x_min3d, ix, ox)) {
1093: lx[0] = x_min3d;
1094: ly[0] = iy;
1095: lz[0] = iz;
1096:
1097: lx[1] = x_max3d;
1098: ly[1] = iy;
1099: lz[1] = iz;
1100:
1101: return(TRUE);
1102: } else
1103: return(FALSE);
1104: }
1105:
1106: /* nasty 2D slanted line in an xz plane */
1107:
1108: if(!inrange(oy, y_min3d, y_max3d))
1109: return(FALSE);
1110:
1111: t[0] = (x_min3d - ix)/(ox - ix);
1112: t[1] = (x_max3d - ix)/(ox - ix);
1113:
1114: if(t[0] > t[1]) {
1115: swap = t[0];t[0] = t[1];t[1] = swap;
1116: }
1117:
1118: t[2] = (min3d_z - iz)/(oz - iz);
1119: t[3] = (max3d_z - iz)/(oz - iz);
1120:
1121: if(t[2] > t[3]) {
1122: swap = t[2];t[2] = t[3];t[3] = swap;
1123: }
1124:
1125: t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
1126: t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
1127:
1128: if(t_min > t_max)
1129: return(FALSE);
1130:
1131: lx[0] = ix + t_min * (ox - ix);
1132: ly[0] = iy;
1133: lz[0] = iz + t_min * (oz - iz);
1134:
1135: lx[1] = ix + t_max * (ox - ix);
1136: ly[1] = iy;
1137: lz[1] = iz + t_max * (oz - iz);
1138:
1139: /*
1140: * Can only have 0 or 2 intersection points -- only need test one coord
1141: */
1142: if(inrange(lx[0], x_min3d, x_max3d) &&
1143: inrange(lz[0], min3d_z, max3d_z))
1144: {
1145: return(TRUE);
1146: }
1147:
1148: return(FALSE);
1149: }
1150:
1151: if(iz == oz) {
1152: /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
1153:
1154: /* nasty 2D slanted line in an xy plane */
1155:
1156: if(!inrange(oz, min3d_z, max3d_z))
1157: return(FALSE);
1158:
1159: t[0] = (x_min3d - ix)/(ox - ix);
1160: t[1] = (x_max3d - ix)/(ox - ix);
1161:
1162: if(t[0] > t[1]) {
1163: swap = t[0];t[0] = t[1];t[1] = swap;
1164: }
1165:
1166: t[2] = (y_min3d - iy)/(oy - iy);
1167: t[3] = (y_max3d - iy)/(oy - iy);
1168:
1169: if(t[2] > t[3]) {
1170: swap = t[2];t[2] = t[3];t[3] = swap;
1171: }
1172:
1173: t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
1174: t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
1175:
1176: if(t_min > t_max)
1177: return(FALSE);
1178:
1179: lx[0] = ix + t_min * (ox - ix);
1180: ly[0] = iy + t_min * (oy - iy);
1181: lz[0] = iz;
1182:
1183: lx[1] = ix + t_max * (ox - ix);
1184: ly[1] = iy + t_max * (oy - iy);
1185: lz[1] = iz;
1186:
1187: /*
1188: * Can only have 0 or 2 intersection points -- only need test one coord
1189: */
1190: if(inrange(lx[0], x_min3d, x_max3d) &&
1191: inrange(ly[0], y_min3d, y_max3d))
1192: {
1193: return(TRUE);
1194: }
1195:
1196: return(FALSE);
1197: }
1198:
1199: /* really nasty general slanted 3D case */
1200:
1201: /*
1202: Solve parametric equation
1203:
1204: (ix, iy, iz) + t (diff_x, diff_y, diff_z)
1205:
1206: where 0.0 <= t <= 1.0 and
1207:
1208: diff_x = (ox - ix);
1209: diff_y = (oy - iy);
1210: diff_z = (oz - iz);
1211: */
1212:
1213: t[0] = (x_min3d - ix)/(ox - ix);
1214: t[1] = (x_max3d - ix)/(ox - ix);
1215:
1216: if(t[0] > t[1]) {
1217: swap = t[0];t[0] = t[1];t[1] = swap;
1218: }
1219:
1220: t[2] = (y_min3d - iy)/(oy - iy);
1221: t[3] = (y_max3d - iy)/(oy - iy);
1222:
1223: if(t[2] > t[3]) {
1224: swap = t[2];t[2] = t[3];t[3] = swap;
1225: }
1226:
1227: t[4] = (iz == oz) ? 0.0 : (min3d_z - iz)/(oz - iz);
1228: t[5] = (iz == oz) ? 1.0 : (max3d_z - iz)/(oz - iz);
1229:
1230: if(t[4] > t[5]) {
1231: swap = t[4];t[4] = t[5];t[5] = swap;
1232: }
1233:
1234: t_min = GPMAX(GPMAX(t[0],t[2]),GPMAX(t[4],0.0));
1235: t_max = GPMIN(GPMIN(t[1],t[3]),GPMIN(t[5],1.0));
1236:
1237: if(t_min > t_max)
1238: return(FALSE);
1239:
1240: lx[0] = ix + t_min * (ox - ix);
1241: ly[0] = iy + t_min * (oy - iy);
1242: lz[0] = iz + t_min * (oz - iz);
1243:
1244: lx[1] = ix + t_max * (ox - ix);
1245: ly[1] = iy + t_max * (oy - iy);
1246: lz[1] = iz + t_max * (oz - iz);
1247:
1248: /*
1249: * Can only have 0 or 2 intersection points -- only need test one coord
1250: */
1251: if(inrange(lx[0], x_min3d, x_max3d) &&
1252: inrange(ly[0], y_min3d, y_max3d) &&
1253: inrange(lz[0], min3d_z, max3d_z))
1254: {
1255: return(TRUE);
1256: }
1257:
1258: return(FALSE);
1259: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>