#ifndef lint static char *RCSid = "$Id: util3d.c,v 1.7 1998/09/21 21:04:42 lhecking Exp $"; #endif /* GNUPLOT - util3d.c */ /*[ * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley * * Permission to use, copy, and distribute this software and its * documentation for any purpose with or without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. * * Permission to modify the software is granted, but not the right to * distribute the complete modified source code. Modifications are to * be distributed as patches to the released version. Permission to * distribute binaries produced by compiling modified sources is granted, * provided you * 1. distribute the corresponding source modifications from the * released version in the form of a patch file along with the binaries, * 2. add special version identification to distinguish your version * in addition to the base release version number, * 3. provide your name and address as the primary contact for the * support of your modified version, and * 4. retain our contact information in regard to use of the base * software. * Permission to distribute the released version of the source code along * with corresponding source modifications in the form of a patch file is * granted with same provisions 2 through 4 for binary distributions. * * This software is provided "as is" without express or implied warranty * to the extent permitted by applicable law. ]*/ /* * 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu) * Added user-specified bases for log scaling. * * 3.6 - split graph3d.c into graph3d.c (graph), * util3d.c (intersections, etc) * hidden3d.c (hidden-line removal code) * */ #include "plot.h" #include "setshow.h" extern int xleft,xright,ybot,ytop; extern int hidden_active; /* HBB 980324: hidden_no_update was unused here */ /* ACCESS THINGS THAT OUGHT TO BE HIDDEN IN hidden3d.c - perhaps we * can move the relevant code into hidden3d.c sometime */ /* Bitmap of the screen. The array for each x value is malloc-ed as needed */ extern int suppressMove; extern double min_array[], max_array[]; extern int auto_array[], log_array[]; extern double base_array[], log_base_array[]; /* for convenience while converting to use these arrays */ #define x_min3d min_array[FIRST_X_AXIS] #define x_max3d max_array[FIRST_X_AXIS] #define y_min3d min_array[FIRST_Y_AXIS] #define y_max3d max_array[FIRST_Y_AXIS] #define z_min3d min_array[FIRST_Z_AXIS] #define z_max3d max_array[FIRST_Z_AXIS] #define min3d_z min_array[FIRST_Z_AXIS] #define max3d_z max_array[FIRST_Z_AXIS] typedef double transform_matrix[4][4]; void mat_unit(mat) transform_matrix mat; { int i, j; for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) if (i == j) mat[i][j] = 1.0; else mat[i][j] = 0.0; } void mat_trans(tx, ty, tz, mat) double tx, ty, tz; transform_matrix mat; { mat_unit(mat); /* Make it unit matrix. */ mat[3][0] = tx; mat[3][1] = ty; mat[3][2] = tz; } void mat_scale(sx, sy, sz, mat) double sx, sy, sz; transform_matrix mat; { mat_unit(mat); /* Make it unit matrix. */ mat[0][0] = sx; mat[1][1] = sy; mat[2][2] = sz; } void mat_rot_x(teta, mat) double teta; transform_matrix mat; { double cos_teta, sin_teta; teta *= Pi / 180.0; cos_teta = cos(teta); sin_teta = sin(teta); mat_unit(mat); /* Make it unit matrix. */ mat[1][1] = cos_teta; mat[1][2] = -sin_teta; mat[2][1] = sin_teta; mat[2][2] = cos_teta; } void mat_rot_y(teta, mat) double teta; transform_matrix mat; { double cos_teta, sin_teta; teta *= Pi / 180.0; cos_teta = cos(teta); sin_teta = sin(teta); mat_unit(mat); /* Make it unit matrix. */ mat[0][0] = cos_teta; mat[0][2] = -sin_teta; mat[2][0] = sin_teta; mat[2][2] = cos_teta; } void mat_rot_z(teta, mat) double teta; transform_matrix mat; { double cos_teta, sin_teta; teta *= Pi / 180.0; cos_teta = cos(teta); sin_teta = sin(teta); mat_unit(mat); /* Make it unit matrix. */ mat[0][0] = cos_teta; mat[0][1] = -sin_teta; mat[1][0] = sin_teta; mat[1][1] = cos_teta; } /* Multiply two transform_matrix. Result can be one of two operands. */ void mat_mult(mat_res, mat1, mat2) transform_matrix mat_res, mat1, mat2; { int i, j, k; transform_matrix mat_res_temp; for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) { mat_res_temp[i][j] = 0; for (k = 0; k < 4; k++) mat_res_temp[i][j] += mat1[i][k] * mat2[k][j]; } for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) mat_res[i][j] = mat_res_temp[i][j]; } /* Test a single point to be within the xleft,xright,ybot,ytop bbox. * Sets the returned integers 4 l.s.b. as follows: * bit 0 if to the left of xleft. * bit 1 if to the right of xright. * bit 2 if above of ytop. * bit 3 if below of ybot. * 0 is returned if inside. */ int clip_point(x, y) unsigned int x, y; { int ret_val = 0; if (x < xleft) ret_val |= 0x01; if (x > xright) ret_val |= 0x02; if (y < ybot) ret_val |= 0x04; if (y > ytop) ret_val |= 0x08; return ret_val; } /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop. * This routine uses the cohen & sutherland bit mapping for fast clipping - * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65. */ void draw_clip_line(x1, y1, x2, y2) unsigned int x1, y1, x2, y2; { int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2; register struct termentry *t = term; #if defined(ATARI) || defined(MTOS) if(x1<0||x2<0||y1<0||y2<0) return; /* temp bug fix */ #endif pos1 = clip_point(x1, y1); pos2 = clip_point(x2, y2); if (pos1 || pos2) { if (pos1 & pos2) return; /* segment is totally out. */ /* Here part of the segment MAY be inside. test the intersection * of this segment with the 4 boundaries for hopefully 2 intersections * in. If none are found segment is totaly out. * Under rare circumstances there may be up to 4 intersections (e.g. * when the line passes directly through at least one corner). In * this case it is sufficient to take any 2 intersections (e.g. the * first two found). */ count = 0; dx = x2 - x1; dy = y2 - y1; /* Find intersections with the x parallel bbox lines: */ if (dy != 0) { x = (ybot - y2) * dx / dy + x2; /* Test for ybot boundary. */ if (x >= xleft && x <= xright) { x_intr[count] = x; y_intr[count++] = ybot; } x = (ytop - y2) * dx / dy + x2; /* Test for ytop boundary. */ if (x >= xleft && x <= xright) { x_intr[count] = x; y_intr[count++] = ytop; } } /* Find intersections with the y parallel bbox lines: */ if (dx != 0) { y = (xleft - x2) * dy / dx + y2; /* Test for xleft boundary. */ if (y >= ybot && y <= ytop) { x_intr[count] = xleft; y_intr[count++] = y; } y = (xright - x2) * dy / dx + y2; /* Test for xright boundary. */ if (y >= ybot && y <= ytop) { x_intr[count] = xright; y_intr[count++] = y; } } if (count >= 2) { int x_max, x_min, y_max, y_min; x_min = GPMIN(x1, x2); x_max = GPMAX(x1, x2); y_min = GPMIN(y1, y2); y_max = GPMAX(y1, y2); if (pos1 && pos2) { /* Both were out - update both */ x1 = x_intr[0]; y1 = y_intr[0]; x2 = x_intr[1]; y2 = y_intr[1]; } else if (pos1) { /* Only x1/y1 was out - update only it */ if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) { x1 = x_intr[0]; y1 = y_intr[0]; } else { x1 = x_intr[1]; y1 = y_intr[1]; } } else { /* Only x2/y2 was out - update only it */ if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - y1) > 0) { x2 = x_intr[0]; y2 = y_intr[0]; } else { x2 = x_intr[1]; y2 = y_intr[1]; } } if (x1 < x_min || x1 > x_max || x2 < x_min || x2 > x_max || y1 < y_min || y1 > y_max || y2 < y_min || y2 > y_max) return; } else return; } #ifndef LITE if(hidden3d && hidden_active && draw_surface) { draw_line_hidden(x1, y1, x2, y2); return; }; #endif /* not LITE */ if(!suppressMove) (*t->move)(x1,y1); (*t->vector)(x2,y2); } /* And text clipping routine. */ void clip_put_text(x, y, str) unsigned int x,y; char *str; { register struct termentry *t = term; if (clip_point(x, y)) return; (*t->put_text)(x,y,str); } /* seems sensible to put the justification in here too..? */ void clip_put_text_just(x,y,str,just) unsigned int x,y; char *str; enum JUSTIFY just; { register struct termentry *t = term; if (clip_point(x,y)) return; if (!(*t->justify_text)(just)) { assert(CENTRE == 1 && RIGHT == 2); x -= (t->h_char * strlen(str) * just)/2; } (*t->put_text)(x,y,str); } /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop. * This routine uses the cohen & sutherland bit mapping for fast clipping - * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65. */ int clip_line(x1, y1, x2, y2) int *x1, *y1, *x2, *y2; { int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2; int x_max, x_min, y_max, y_min; pos1 = clip_point(*x1, *y1); pos2 = clip_point(*x2, *y2); if (!pos1 && !pos2) return 1; /* segment is totally in */ if (pos1 & pos2) return 0; /* segment is totally out. */ /* Here part of the segment MAY be inside. test the intersection * of this segment with the 4 boundaries for hopefully 2 intersections * in. If non found segment is totaly out. */ count = 0; dx = *x2 - *x1; dy = *y2 - *y1; /* Find intersections with the x parallel bbox lines: */ if (dy != 0) { x = (ybot - *y2) * dx / dy + *x2; /* Test for ybot boundary. */ if (x >= xleft && x <= xright) { x_intr[count] = x; y_intr[count++] = ybot; } x = (ytop - *y2) * dx / dy + *x2; /* Test for ytop boundary. */ if (x >= xleft && x <= xright) { x_intr[count] = x; y_intr[count++] = ytop; } } /* Find intersections with the y parallel bbox lines: */ if (dx != 0) { y = (xleft - *x2) * dy / dx + *y2; /* Test for xleft boundary. */ if (y >= ybot && y <= ytop) { x_intr[count] = xleft; y_intr[count++] = y; } y = (xright - *x2) * dy / dx + *y2; /* Test for xright boundary. */ if (y >= ybot && y <= ytop) { x_intr[count] = xright; y_intr[count++] = y; } } if (count < 2) return 0; if (*x1 < *x2) x_min = *x1, x_max = *x2; else x_min = *x2, x_max = *x1; if (*y1 < *y2) y_min = *y1, y_max = *y2; else y_min = *y2, y_max = *y1; if (pos1 && pos2) { /* Both were out - update both */ *x1 = x_intr[0]; *y1 = y_intr[0]; *x2 = x_intr[1]; *y2 = y_intr[1]; } else if (pos1) { /* Only x1/y1 was out - update only it */ if (dx * (*x2 - x_intr[0]) + dy * (*y2 - y_intr[0]) >= 0) { *x1 = x_intr[0]; *y1 = y_intr[0]; } else { *x1 = x_intr[1]; *y1 = y_intr[1]; } } else { /* Only x2/y2 was out - update only it */ if (dx * (x_intr[0] - *x1) + dy * (y_intr[0] - *y1) >= 0) { *x2 = x_intr[0]; *y2 = y_intr[0]; } else { *x2 = x_intr[1]; *y2 = y_intr[1]; } } if (*x1 < x_min || *x1 > x_max || *x2 < x_min || *x2 > x_max || *y1 < y_min || *y1 > y_max || *y2 < y_min || *y2 > y_max) return 0; return 1; } /* single edge intersection algorithm */ /* Given two points, one inside and one outside the plot, return * the point where an edge of the plot intersects the line segment defined * by the two points. */ void edge3d_intersect(points, i, ex, ey, ez) struct coordinate GPHUGE *points; /* the points array */ int i; /* line segment from point i-1 to point i */ double *ex, *ey, *ez; /* the point where it crosses an edge */ { /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */ int count; double ix = points[i-1].x; double iy = points[i-1].y; double iz = points[i-1].z; double ox = points[i].x; double oy = points[i].y; double oz = points[i].z; double x, y, z; /* possible intersection point */ if(points[i].type == INRANGE) { /* swap points around so that ix/ix/iz are INRANGE and ox/oy/oz are OUTRANGE */ x = ix;ix = ox;ox = x; y = iy;iy = oy;oy = y; z = iz;iz = oz;oz = z; } /* nasty degenerate cases, effectively drawing to an infinity point (?) cope with them here, so don't process them as a "real" OUTRANGE point If more than one coord is -VERYLARGE, then can't ratio the "infinities" so drop out by returning the INRANGE point. Obviously, only need to test the OUTRANGE point (coordinates) */ /* nasty degenerate cases, effectively drawing to an infinity point (?) cope with them here, so don't process them as a "real" OUTRANGE point If more than one coord is -VERYLARGE, then can't ratio the "infinities" so drop out by returning FALSE */ count = 0; if(ox == -VERYLARGE) count++; if(oy == -VERYLARGE) count++; if(oz == -VERYLARGE) count++; /* either doesn't pass through 3D volume *or* can't ratio infinities to get a direction to draw line, so return the INRANGE point */ if(count > 1){ *ex = ix; *ey = iy; *ez = iz; return; } if(count == 1) { *ex = ix; *ey = iy; *ez = iz; if(ox == -VERYLARGE) { *ex = x_min3d; return; } if(oy == -VERYLARGE) { *ey = y_min3d; return; } /* obviously oz is -VERYLARGE and (ox != -VERYLARGE && oy != -VERYLARGE) */ *ez = min3d_z; return; } /* * Can't have case (ix == ox && iy == oy && iz == oz) as one point * is INRANGE and one point is OUTRANGE. */ if(ix == ox) { if(iy == oy) { /* line parallel to z axis */ /* assume inrange(iy, y_min3d, y_max3d) && inrange(ix, x_min3d, x_max3d) */ *ex = ix; /* == ox */ *ey = iy; /* == oy */ if (inrange(max3d_z, iz, oz)) *ez = max3d_z; else if (inrange(min3d_z, iz, oz)) *ez = min3d_z; else { graph_error("error in edge3d_intersect"); } return; } if(iz == oz) { /* line parallel to y axis */ /* assume inrange(iz, min3d_z, max3d_z) && inrange(ix, x_min3d, x_max3d) */ *ex = ix; /* == ox */ *ez = iz; /* == oz */ if (inrange(y_max3d, iy, oy)) *ey = y_max3d; else if (inrange(y_min3d, iy, oy)) *ey = y_min3d; else { graph_error("error in edge3d_intersect"); } return; } /* nasty 2D slanted line in a yz plane */ /* does it intersect y_min3d edge */ if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) { z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy)); if (inrange(z, min3d_z, max3d_z)) { *ex = ix; *ey = y_min3d; *ez = z; return; } } /* does it intersect y_max3d edge */ if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) { z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy)); if (inrange(z, min3d_z, max3d_z)) { *ex = ix; *ey = y_max3d; *ez = z; return; } } /* does it intersect min3d_z edge */ if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) { y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz)); if (inrange(y, y_min3d, y_max3d)) { *ex = ix; *ey = y; *ez = min3d_z; return; } } /* does it intersect max3d_z edge */ if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) { y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz)); if (inrange(y, y_min3d, y_max3d)) { *ex = ix; *ey = y; *ez = max3d_z; return; } } } if(iy == oy) { /* already checked case (ix == ox && iy == oy) */ if(oz == iz) { /* line parallel to x axis */ /* assume inrange(iz, min3d_z, max3d_z) && inrange(iy, y_min3d, y_max3d) */ *ey = iy; /* == oy */ *ez = iz; /* == oz */ if (inrange(x_max3d, ix, ox)) *ex = x_max3d; else if (inrange(x_min3d, ix, ox)) *ex = x_min3d; else { graph_error("error in edge3d_intersect"); } return; } /* nasty 2D slanted line in an xz plane */ /* does it intersect x_min3d edge */ if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) { z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix)); if (inrange(z, min3d_z, max3d_z)) { *ex = x_min3d; *ey = iy; *ez = z; return; } } /* does it intersect x_max3d edge */ if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) { z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix)); if (inrange(z, min3d_z, max3d_z)) { *ex = x_max3d; *ey = iy; *ez = z; return; } } /* does it intersect min3d_z edge */ if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) { x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz)); if (inrange(x, x_min3d, x_max3d)) { *ex = x; *ey = iy; *ez = min3d_z; return; } } /* does it intersect max3d_z edge */ if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) { x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz)); if (inrange(x, x_min3d, x_max3d)) { *ex = x; *ey = iy; *ez = max3d_z; return; } } } if(iz == oz) { /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */ /* nasty 2D slanted line in an xy plane */ /* assume inrange(oz, min3d_z, max3d_z) */ /* does it intersect x_min3d edge */ if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) { y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix)); if (inrange(y, y_min3d, y_max3d)) { *ex = x_min3d; *ey = y; *ez = iz; return; } } /* does it intersect x_max3d edge */ if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) { y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix)); if (inrange(y, y_min3d, y_max3d)) { *ex = x_max3d; *ey = y; *ez = iz; return; } } /* does it intersect y_min3d edge */ if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) { x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy)); if (inrange(x, x_min3d, x_max3d)) { *ex = x; *ey = y_min3d; *ez = iz; return; } } /* does it intersect y_max3d edge */ if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) { x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy)); if (inrange(x, x_min3d, x_max3d)) { *ex = x; *ey = y_max3d; *ez = iz; return; } } } /* really nasty general slanted 3D case */ /* does it intersect x_min3d edge */ if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) { y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix)); z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix)); if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) { *ex = x_min3d; *ey = y; *ez = z; return; } } /* does it intersect x_max3d edge */ if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) { y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix)); z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix)); if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) { *ex = x_max3d; *ey = y; *ez = z; return; } } /* does it intersect y_min3d edge */ if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) { x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy)); z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy)); if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) { *ex = x; *ey = y_min3d; *ez = z; return; } } /* does it intersect y_max3d edge */ if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) { x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy)); z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy)); if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) { *ex = x; *ey = y_max3d; *ez = z; return; } } /* does it intersect min3d_z edge */ if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) { x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz)); y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz)); if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) { *ex = x; *ey = y; *ez = min3d_z; return; } } /* does it intersect max3d_z edge */ if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) { x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz)); y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz)); if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) { *ex = x; *ey = y; *ez = max3d_z; return; } } /* If we reach here, the inrange point is on the edge, and * the line segment from the outrange point does not cross any * other edges to get there. In this case, we return the inrange * point as the 'edge' intersection point. This will basically draw * line. */ *ex = ix; *ey = iy; *ez = iz; return; } /* double edge intersection algorithm */ /* Given two points, both outside the plot, return * the points where an edge of the plot intersects the line segment defined * by the two points. There may be zero, one, two, or an infinite number * of intersection points. (One means an intersection at a corner, infinite * means overlaying the edge itself). We return FALSE when there is nothing * to draw (zero intersections), and TRUE when there is something to * draw (the one-point case is a degenerate of the two-point case and we do * not distinguish it - we draw it anyway). */ TBOOLEAN /* any intersection? */ two_edge3d_intersect(points, i, lx, ly, lz) struct coordinate GPHUGE *points; /* the points array */ int i; /* line segment from point i-1 to point i */ double *lx, *ly, *lz; /* lx[2], ly[2], lz[2]: points where it crosses edges */ { int count; /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */ double ix = points[i-1].x; double iy = points[i-1].y; double iz = points[i-1].z; double ox = points[i].x; double oy = points[i].y; double oz = points[i].z; double t[6]; double swap; double x, y, z; /* possible intersection point */ double t_min, t_max; /* nasty degenerate cases, effectively drawing to an infinity point (?) cope with them here, so don't process them as a "real" OUTRANGE point If more than one coord is -VERYLARGE, then can't ratio the "infinities" so drop out by returning FALSE */ count = 0; if(ix == -VERYLARGE) count++; if(ox == -VERYLARGE) count++; if(iy == -VERYLARGE) count++; if(oy == -VERYLARGE) count++; if(iz == -VERYLARGE) count++; if(oz == -VERYLARGE) count++; /* either doesn't pass through 3D volume *or* can't ratio infinities to get a direction to draw line, so simply return(FALSE) */ if(count > 1){ return(FALSE); } if(ox == -VERYLARGE || ix == -VERYLARGE) { if(ix == -VERYLARGE) { /* swap points so ix/iy/iz don't have a -VERYLARGE component */ x = ix;ix = ox;ox = x; y = iy;iy = oy;oy = y; z = iz;iz = oz;oz = z; } /* check actually passes through the 3D graph volume */ if(ix > x_max3d && inrange(iy, y_min3d, y_max3d) && inrange(iz, min3d_z, max3d_z)) { lx[0] = x_min3d; ly[0] = iy; lz[0] = iz; lx[1] = x_max3d; ly[1] = iy; lz[1] = iz; return(TRUE); } else { return(FALSE); } } if(oy == -VERYLARGE || iy == -VERYLARGE) { if(iy == -VERYLARGE) { /* swap points so ix/iy/iz don't have a -VERYLARGE component */ x = ix; ix = ox; ox = x; y = iy; iy = oy; oy = y; z = iz; iz = oz; oz = z; } /* check actually passes through the 3D graph volume */ if(iy > y_max3d && inrange(ix, x_min3d, x_max3d) && inrange(iz, min3d_z, max3d_z)) { lx[0] = ix; ly[0] = y_min3d; lz[0] = iz; lx[1] = ix; ly[1] = y_max3d; lz[1] = iz; return(TRUE); } else { return(FALSE); } } if(oz == -VERYLARGE || iz == -VERYLARGE) { if(iz == -VERYLARGE) { /* swap points so ix/iy/iz don't have a -VERYLARGE component */ x = ix; ix = ox; ox = x; y = iy; iy = oy; oy = y; z = iz; iz = oz; oz = z; } /* check actually passes through the 3D graph volume */ if(iz > max3d_z && inrange(ix, x_min3d, x_max3d) && inrange(iy, y_min3d, y_max3d)) { lx[0] = ix; ly[0] = iy; lz[0] = min3d_z; lx[1] = ix; ly[1] = iy; lz[1] = max3d_z; return(TRUE); } else { return(FALSE); } } /* * Quick outcode tests on the 3d graph volume */ /* * test z coord first --- most surface OUTRANGE points generated between * min3d_z and z_min3d (i.e. when ticslevel is non-zero) */ if(GPMAX(iz,oz) < min3d_z || GPMIN(iz,oz) > max3d_z) return(FALSE); if(GPMAX(ix,ox) < x_min3d || GPMIN(ix,ox) > x_max3d) return(FALSE); if(GPMAX(iy,oy) < y_min3d || GPMIN(iy,oy) > y_max3d) return(FALSE); /* * Special horizontal/vertical, etc. cases are checked and remaining * slant lines are checked separately. * * The slant line intersections are solved using the parametric form * of the equation for a line, since if we test x/y/z min/max planes explicitly * then e.g. a line passing through a corner point (x_min,y_min,z_min) * actually intersects all 3 planes and hence further tests would be required * to anticipate this and similar situations. */ /* * Can have case (ix == ox && iy == oy && iz == oz) as both points OUTRANGE */ if(ix == ox && iy == oy && iz == oz) { /* but as only define single outrange point, can't intersect 3D graph volume */ return(FALSE); } if(ix == ox) { if(iy == oy) { /* line parallel to z axis */ /* x and y coords must be in range, and line must span both min3d_z and max3d_z */ /* note that spanning min3d_z implies spanning max3d_z as both points OUTRANGE */ if(!inrange(ix, x_min3d, x_max3d) || !inrange(iy, y_min3d, y_max3d)) { return(FALSE); } if (inrange(min3d_z, iz, oz)) { lx[0] = ix; ly[0] = iy; lz[0] = min3d_z; lx[1] = ix; ly[1] = iy; lz[1] = max3d_z; return(TRUE); } else return(FALSE); } if(iz == oz) { /* line parallel to y axis */ /* x and z coords must be in range, and line must span both y_min3d and y_max3d */ /* note that spanning y_min3d implies spanning y_max3d, as both points OUTRANGE */ if(!inrange(ix, x_min3d, x_max3d) || !inrange(iz, min3d_z, max3d_z)) { return(FALSE); } if (inrange(y_min3d, iy, oy)) { lx[0] = ix; ly[0] = y_min3d; lz[0] = iz; lx[1] = ix; ly[1] = y_max3d; lz[1] = iz; return(TRUE); } else return(FALSE); } /* nasty 2D slanted line in a yz plane */ if(!inrange(ox, x_min3d, x_max3d)) return(FALSE); t[0] = (y_min3d - iy)/(oy - iy); t[1] = (y_max3d - iy)/(oy - iy); if(t[0] > t[1]) { swap = t[0];t[0] = t[1];t[1] = swap; } t[2] = (min3d_z - iz)/(oz - iz); t[3] = (max3d_z - iz)/(oz - iz); if(t[2] > t[3]) { swap = t[2];t[2] = t[3];t[3] = swap; } t_min = GPMAX(GPMAX(t[0],t[2]),0.0); t_max = GPMIN(GPMIN(t[1],t[3]),1.0); if(t_min > t_max) return(FALSE); lx[0] = ix; ly[0] = iy + t_min * (oy - iy); lz[0] = iz + t_min * (oz - iz); lx[1] = ix; ly[1] = iy + t_max * (oy - iy); lz[1] = iz + t_max * (oz - iz); /* * Can only have 0 or 2 intersection points -- only need test one coord */ if(inrange(ly[0], y_min3d, y_max3d) && inrange(lz[0], min3d_z, max3d_z)) { return(TRUE); } return(FALSE); } if(iy == oy) { /* already checked case (ix == ox && iy == oy) */ if(oz == iz) { /* line parallel to x axis */ /* y and z coords must be in range, and line must span both x_min3d and x_max3d */ /* note that spanning x_min3d implies spanning x_max3d, as both points OUTRANGE */ if(!inrange(iy, y_min3d, y_max3d) || !inrange(iz, min3d_z, max3d_z)) { return(FALSE); } if (inrange(x_min3d, ix, ox)) { lx[0] = x_min3d; ly[0] = iy; lz[0] = iz; lx[1] = x_max3d; ly[1] = iy; lz[1] = iz; return(TRUE); } else return(FALSE); } /* nasty 2D slanted line in an xz plane */ if(!inrange(oy, y_min3d, y_max3d)) return(FALSE); t[0] = (x_min3d - ix)/(ox - ix); t[1] = (x_max3d - ix)/(ox - ix); if(t[0] > t[1]) { swap = t[0];t[0] = t[1];t[1] = swap; } t[2] = (min3d_z - iz)/(oz - iz); t[3] = (max3d_z - iz)/(oz - iz); if(t[2] > t[3]) { swap = t[2];t[2] = t[3];t[3] = swap; } t_min = GPMAX(GPMAX(t[0],t[2]),0.0); t_max = GPMIN(GPMIN(t[1],t[3]),1.0); if(t_min > t_max) return(FALSE); lx[0] = ix + t_min * (ox - ix); ly[0] = iy; lz[0] = iz + t_min * (oz - iz); lx[1] = ix + t_max * (ox - ix); ly[1] = iy; lz[1] = iz + t_max * (oz - iz); /* * Can only have 0 or 2 intersection points -- only need test one coord */ if(inrange(lx[0], x_min3d, x_max3d) && inrange(lz[0], min3d_z, max3d_z)) { return(TRUE); } return(FALSE); } if(iz == oz) { /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */ /* nasty 2D slanted line in an xy plane */ if(!inrange(oz, min3d_z, max3d_z)) return(FALSE); t[0] = (x_min3d - ix)/(ox - ix); t[1] = (x_max3d - ix)/(ox - ix); if(t[0] > t[1]) { swap = t[0];t[0] = t[1];t[1] = swap; } t[2] = (y_min3d - iy)/(oy - iy); t[3] = (y_max3d - iy)/(oy - iy); if(t[2] > t[3]) { swap = t[2];t[2] = t[3];t[3] = swap; } t_min = GPMAX(GPMAX(t[0],t[2]),0.0); t_max = GPMIN(GPMIN(t[1],t[3]),1.0); if(t_min > t_max) return(FALSE); lx[0] = ix + t_min * (ox - ix); ly[0] = iy + t_min * (oy - iy); lz[0] = iz; lx[1] = ix + t_max * (ox - ix); ly[1] = iy + t_max * (oy - iy); lz[1] = iz; /* * Can only have 0 or 2 intersection points -- only need test one coord */ if(inrange(lx[0], x_min3d, x_max3d) && inrange(ly[0], y_min3d, y_max3d)) { return(TRUE); } return(FALSE); } /* really nasty general slanted 3D case */ /* Solve parametric equation (ix, iy, iz) + t (diff_x, diff_y, diff_z) where 0.0 <= t <= 1.0 and diff_x = (ox - ix); diff_y = (oy - iy); diff_z = (oz - iz); */ t[0] = (x_min3d - ix)/(ox - ix); t[1] = (x_max3d - ix)/(ox - ix); if(t[0] > t[1]) { swap = t[0];t[0] = t[1];t[1] = swap; } t[2] = (y_min3d - iy)/(oy - iy); t[3] = (y_max3d - iy)/(oy - iy); if(t[2] > t[3]) { swap = t[2];t[2] = t[3];t[3] = swap; } t[4] = (iz == oz) ? 0.0 : (min3d_z - iz)/(oz - iz); t[5] = (iz == oz) ? 1.0 : (max3d_z - iz)/(oz - iz); if(t[4] > t[5]) { swap = t[4];t[4] = t[5];t[5] = swap; } t_min = GPMAX(GPMAX(t[0],t[2]),GPMAX(t[4],0.0)); t_max = GPMIN(GPMIN(t[1],t[3]),GPMIN(t[5],1.0)); if(t_min > t_max) return(FALSE); lx[0] = ix + t_min * (ox - ix); ly[0] = iy + t_min * (oy - iy); lz[0] = iz + t_min * (oz - iz); lx[1] = ix + t_max * (ox - ix); ly[1] = iy + t_max * (oy - iy); lz[1] = iz + t_max * (oz - iz); /* * Can only have 0 or 2 intersection points -- only need test one coord */ if(inrange(lx[0], x_min3d, x_max3d) && inrange(ly[0], y_min3d, y_max3d) && inrange(lz[0], min3d_z, max3d_z)) { return(TRUE); } return(FALSE); }