/* * * line_clip.c: homogeneous 3D line clipping * * Philippe Bekaert Apr, 29 1995 * * Bascially, it is Paul Heckbert's code (see poly_scan.c) * adapted for lines instead of convex polygons. */ #include #include "line.h" /* * line_clip_to_box; Clip a line to a 3D clipping box using homogeneous * screencoordinates (sx,sy,sz,sw). * * returns: * - LINE_CLIP_OUT: if the line is totally outside the clipping box * - LINE_CLIP_IN: if the line is totally inside * - LINE_CLIP_PARTIAL: if the line is cut by the box. The line is * modified. */ int line_clip_to_box(Line *lin, Poly_box *box) { int x0out = 0, x1out = 0, y0out = 0, y1out = 0, z0out = 0, z1out = 0; Poly_vert *v1 = &lin->v1, *v2 = &lin->v2; float t1, t2, u1, u2, t, *p1, *p2, dp; int mask; /* check first vertex - note how lot's of multiplications could be avoided * since the box is always [-1:1] in each direction */ if (v1->sx < box->x0*v1->sw) x0out++; /* out on left */ if (v1->sx > box->x1*v1->sw) x1out++; /* out on right */ if (v1->sy < box->y0*v1->sw) y0out++; /* out on top */ if (v1->sy > box->y1*v1->sw) y1out++; /* out on bottom */ if (v1->sz < box->z0*v1->sw) z0out++; /* out on near */ if (v1->sz > box->z1*v1->sw) z1out++; /* out on far */ /* check second vertex */ if (v2->sx < box->x0*v2->sw) x0out++; /* out on left */ if (v2->sx > box->x1*v2->sw) x1out++; /* out on right */ if (v2->sy < box->y0*v2->sw) y0out++; /* out on top */ if (v2->sy > box->y1*v2->sw) y1out++; /* out on bottom */ if (v2->sz < box->z0*v2->sw) z0out++; /* out on near */ if (v2->sz > box->z1*v2->sw) z1out++; /* out on far */ /* check if all vertices inside */ if (x0out+x1out+y0out+y1out+z0out+z1out == 0) return LINE_CLIP_IN; /* check if both vertices are "outside" any of the six planes */ if (x0out==2 || x1out==2 || y0out==2 || y1out==2 || z0out==2 || z1out==2) return LINE_CLIP_OUT; /* now find the portion of the line inside the clipping box. It is the * portion given by points p satisfying: p = v1 + t * (v2-v1) with * t in [t1..t2]. To find t1 and t2, we start with the full line and * cut off pieces outside the clipping box. */ t1 = 0.; t2 = 1.; if (x0out) { /* x0out is 0 or 1: either both points are * right of the plane x/w = box->x0 or one * is on the left (outside) and the other on * the right (inside the clipping box */ u1 = v1->sx - box->x0 * v1->sw; u2 = v2->sx - box->x0 * v2->sw; t = u1 / (u1-u2); /* intersection with x/w=box->x0 * plane is found by filling * the points in the equation of * the plane */ if (u1 > 0) { /* v1 is right of the plane, v2 left */ t2 = t; } else { /* v1 is left, v2 must be on the right */ t1 = t; } } if (x1out) { u1 = v1->sx - box->x1 * v1->sw; u2 = v2->sx - box->x1 * v2->sw; t = u1 / (u1-u2); if (u1 <= 0) { /* v1 is left of the plane, v2 right */ if (t < t2) t2 = t; } else { /* v1 is right, v2 must be on the left */ if (t > t1) t1 = t; } } if (y0out) { u1 = v1->sy - box->y0 * v1->sw; u2 = v2->sy - box->y0 * v2->sw; t = u1 / (u1-u2); if (u1 > 0) { if (t < t2) t2 = t; } else { if (t > t1) t1 = t; } } if (y1out) { u1 = v1->sy - box->y1 * v1->sw; u2 = v2->sy - box->y1 * v2->sw; t = u1 / (u1-u2); if (u1 <= 0) { if (t < t2) t2 = t; } else { if (t > t1) t1 = t; } } if (z0out) { u1 = v1->sz - box->z0 * v1->sw; u2 = v2->sz - box->z0 * v2->sw; t = u1 / (u1-u2); if (u1 > 0) { if (t < t2) t2 = t; } else { if (t > t1) t1 = t; } } if (z1out) { u1 = v1->sz - box->z1 * v1->sw; u2 = v2->sz - box->z1 * v2->sw; t = u1 / (u1-u2); if (u1 <= 0) { if (t < t2) t2 = t; } else { if (t > t1) t1 = t; } } /* the whole line is outside the clipping box */ if (t2 <= t1) return LINE_CLIP_OUT; /* find the new endpoints of the line */ t2 = 1. - t2; /* makes things simpler */ p1 = (float *)v1; p2 = (float *)v2; if (t1 > 0. && t2 > 0.) { for (mask = lin->mask; mask; mask >>= 1, p1++, p2++) if (mask&1) { dp = *p2 - *p1; *p1 += t1 * dp; *p2 -= t2 * dp; } } else if (t1 > 0.) { for (mask = lin->mask; mask; mask >>= 1, p1++, p2++) if (mask&1) *p1 += t1 * (*p2 - *p1); } else if (t2 > 0.) { for (mask = lin->mask; mask; mask >>= 1, p1++, p2++) if (mask&1) *p2 += t2 * (*p1 - *p2); } return LINE_CLIP_PARTIAL; }