void draw_round_style_cap_callback (int ncp, double cap[][3], float face_color[3], gleDouble cut[3], gleDouble bi[3], double norms[][3], int frontwards) { double axis[3]; double xycut[3]; double theta; double *last_contour, *next_contour; double *last_norm, *next_norm; double *cap_z; double *tmp; char *malloced_area; int i, j, k; double m[4][4]; if (face_color != NULL) C3F (face_color); /* ------------ start setting up rotation matrix ------------- */ /* if the cut vector is NULL (this should only occur in * a degenerate case), then we can't draw anything. return. */ if (cut == NULL) return; /* make sure that the cut vector points inwards */ if (cut[2] > 0.0) { VEC_SCALE (cut, -1.0, cut); } /* make sure that the bi vector points outwards */ if (bi[2] < 0.0) { VEC_SCALE (bi, -1.0, bi); } /* determine the axis we are to rotate about to get bi-contour. * Note that the axis will always lie in the x-y plane */ VEC_CROSS_PRODUCT (axis, cut, bi); /* reverse the cut vector for the back cap -- * need to do this to get angle right */ if (!frontwards) { VEC_SCALE (cut, -1.0, cut); } /* get angle to rotate by -- arccos of dot product of cut with cut * projected into the x-y plane */ xycut [0] = 0.0; xycut [1] = 0.0; xycut [2] = 1.0; VEC_PERP (xycut, cut, xycut); VEC_NORMALIZE (xycut); VEC_DOT_PRODUCT (theta, xycut, cut); theta = acos (theta); /* we'll tesselate round joins into a number of teeny pieces */ theta /= (double) __ROUND_TESS_PIECES; /* get the matrix */ urot_axis_d (m, theta, axis); /* ------------ done setting up rotation matrix ------------- */ /* This malloc is a fancy version of: * last_contour = (double *) malloc (3*ncp*sizeof(double); * next_contour = (double *) malloc (3*ncp*sizeof(double); */ malloced_area = malloc ((4*3+1) *ncp*sizeof (double)); last_contour = (double *) malloced_area; next_contour = last_contour + 3*ncp; cap_z = next_contour + 3*ncp; last_norm = cap_z + ncp; next_norm = last_norm + 3*ncp; /* make first copy of contour */ if (frontwards) { for (j=0; j<ncp; j++) { last_contour[3*j] = cap[j][0]; last_contour[3*j+1] = cap[j][1]; last_contour[3*j+2] = cap_z[j] = cap[j][2]; } if (norms != NULL) { for (j=0; j<ncp; j++) { VEC_COPY ((&last_norm[3*j]), norms[j]); } } } else { /* in order for backfacing polygon removal to work correctly, have * to have the sense in which the joins are drawn to be reversed * for the back cap. This can be done by reversing the order of * the contour points. Normals are a bit trickier, since the * reversal is off-by-one for facet normals as compared to edge * normals. */ for (j=0; j<ncp; j++) { k = ncp - j - 1; last_contour[3*k] = cap[j][0]; last_contour[3*k+1] = cap[j][1]; last_contour[3*k+2] = cap_z[k] = cap[j][2]; } if (norms != NULL) { if (__TUBE_DRAW_FACET_NORMALS) { for (j=0; j<ncp-1; j++) { k = ncp - j - 2; VEC_COPY ((&last_norm[3*k]), norms[j]); } } else { for (j=0; j<ncp; j++) { k = ncp - j - 1; VEC_COPY ((&last_norm[3*k]), norms[j]); } } } } /* &&&&&&&&&&&&&& start drawing cap &&&&&&&&&&&&& */ for (i=0; i<__ROUND_TESS_PIECES; i++) { for (j=0; j<ncp; j++) { next_contour [3*j+2] -= cap_z[j]; last_contour [3*j+2] -= cap_z[j]; MAT_DOT_VEC_3X3 ( (&next_contour[3*j]), m, (&last_contour[3*j])); next_contour [3*j+2] += cap_z[j]; last_contour [3*j+2] += cap_z[j]; } if (norms != NULL) { for (j=0; j<ncp; j++) { MAT_DOT_VEC_3X3 ( (&next_norm[3*j]), m, (&last_norm[3*j])); } } /* OK, now render it all */ if (norms == NULL) { draw_segment_plain (ncp, (gleVector *) next_contour, (gleVector *) last_contour, 0, 0.0); } else if (__TUBE_DRAW_FACET_NORMALS) { draw_binorm_segment_facet_n (ncp, (gleVector *) next_contour, (gleVector *) last_contour, (gleVector *) next_norm, (gleVector *) last_norm, 0, 0.0); } else { draw_binorm_segment_edge_n (ncp, (gleVector *) next_contour, (gleVector *) last_contour, (gleVector *) next_norm, (gleVector *) last_norm, 0, 0.0); } /* swap contours */ tmp = next_contour; next_contour = last_contour; last_contour = tmp; tmp = next_norm; next_norm = last_norm; last_norm = tmp; } /* &&&&&&&&&&&&&& end drawing cap &&&&&&&&&&&&& */ /* Thou shalt not leak memory */ free (malloced_area); }
/* * The uviewdirection subroutine computes and returns a 4x4 rotation * matrix that puts the negative z axis along the direction v21 and * puts the y axis along the up vector. * * Note that this code is fairly tolerant of "weird" paramters. * It normalizes when necessary, it does nothing when vectors are of * zero length, or are co-linear. This code shouldn't croak, no matter * what the user sends in as arguments. */ void uview_direction (gleDouble m[4][4], /* returned */ gleDouble v21[3], /* input */ gleDouble up[3]) /* input */ { gleDouble amat[4][4]; gleDouble bmat[4][4]; gleDouble cmat[4][4]; gleDouble v_hat_21[3]; gleDouble v_xy[3]; gleDouble sine, cosine; gleDouble len; gleDouble up_proj[3]; gleDouble tmp[3]; /* find the unit vector that points in the v21 direction */ VEC_COPY (v_hat_21, v21); VEC_LENGTH (len, v_hat_21); if (len != 0.0) { len = 1.0 / len; VEC_SCALE (v_hat_21, len, v_hat_21); /* rotate z in the xz-plane until same latitude */ sine = sqrt ( 1.0 - v_hat_21[2] * v_hat_21[2]); ROTY_CS (amat, (-v_hat_21[2]), (-sine)); } else { /* error condition: zero length vecotr passed in -- do nothing */ IDENTIFY_MATRIX_4X4 (amat); } /* project v21 onto the xy plane */ v_xy[0] = v21[0]; v_xy[1] = v21[1]; v_xy[2] = 0.0; VEC_LENGTH (len, v_xy); /* rotate in the x-y plane until v21 lies on z axis --- * but of course, if its already there, do nothing */ if (len != 0.0) { /* want xy projection to be unit vector, so that sines/cosines pop out */ len = 1.0 / len; VEC_SCALE (v_xy, len, v_xy); /* rotate the projection of v21 in the xy-plane over to the x axis */ ROTZ_CS (bmat, v_xy[0], v_xy[1]); /* concatenate these together */ MATRIX_PRODUCT_4X4 (cmat, amat, bmat); } else { /* no-op -- vector is already in correct position */ COPY_MATRIX_4X4 (cmat, amat); } /* up vector really should be perpendicular to the x-form direction -- * Use up a couple of cycles, and make sure it is, * just in case the user blew it. */ VEC_PERP (up_proj, up, v_hat_21); VEC_LENGTH (len, up_proj); if (len != 0.0) { /* normalize the vector */ len = 1.0/len; VEC_SCALE (up_proj, len, up_proj); /* compare the up-vector to the y-axis to get the cosine of the angle */ tmp [0] = cmat [1][0]; tmp [1] = cmat [1][1]; tmp [2] = cmat [1][2]; VEC_DOT_PRODUCT (cosine, tmp, up_proj); /* compare the up-vector to the x-axis to get the sine of the angle */ tmp [0] = cmat [0][0]; tmp [1] = cmat [0][1]; tmp [2] = cmat [0][2]; VEC_DOT_PRODUCT (sine, tmp, up_proj); /* rotate to align the up vector with the y-axis */ ROTZ_CS (amat, cosine, -sine); /* This xform, although computed last, acts first */ MATRIX_PRODUCT_4X4 (m, amat, cmat); } else { /* error condition: up vector is indeterminate (zero length) * -- do nothing */ COPY_MATRIX_4X4 (m, cmat); } }
void extrusion_angle_join (int ncp, /* number of contour points */ gleDouble contour[][2], /* 2D contour */ gleDouble cont_normal[][2], /* 2D normal vecs */ gleDouble up[3], /* up vector for contour */ int npoints, /* numpoints in poly-line */ gleDouble point_array[][3], /* polyline */ float color_array[][3], /* color of polyline */ gleDouble xform_array[][2][3]) /* 2D contour xforms */ { int i, j; int inext, inextnext; gleDouble m[4][4]; gleDouble len; gleDouble len_seg; gleDouble diff[3]; gleDouble bi_0[3], bi_1[3]; /* bisecting plane */ gleDouble bisector_0[3], bisector_1[3]; /* bisecting plane */ gleDouble end_point_0[3], end_point_1[3]; gleDouble origin[3], neg_z[3]; gleDouble yup[3]; /* alternate up vector */ gleDouble *front_loop, *back_loop; /* contours in 3D */ char * mem_anchor; double *norm_loop; double *front_norm, *back_norm, *tmp; /* contour normals in 3D */ int first_time; /* By definition, the contour passed in has its up vector pointing in * the y direction */ if (up == NULL) { yup[0] = 0.0; yup[1] = 1.0; yup[2] = 0.0; } else { VEC_COPY(yup, up); } /* ========== "up" vector sanity check ========== */ (void) up_sanity_check (yup, npoints, point_array); /* the origin is at the origin */ origin [0] = 0.0; origin [1] = 0.0; origin [2] = 0.0; /* and neg_z is at neg z */ neg_z[0] = 0.0; neg_z[1] = 0.0; neg_z[2] = 1.0; /* ignore all segments of zero length */ i = 1; inext = i; FIND_NON_DEGENERATE_POINT (inext, npoints, len, diff, point_array); len_seg = len; /* store for later use */ /* get the bisecting plane */ bisecting_plane (bi_0, point_array[0], point_array[1], point_array[inext]); /* reflect the up vector in the bisecting plane */ VEC_REFLECT (yup, yup, bi_0); /* malloc the storage we'll need for relaying changed contours to the * drawing routines. */ mem_anchor = malloc (2 * 3 * ncp * sizeof(double) + 2 * 3 * ncp * sizeof(gleDouble)); front_loop = (gleDouble *) mem_anchor; back_loop = front_loop + 3 * ncp; front_norm = (double *) (back_loop + 3 * ncp); back_norm = front_norm + 3 * ncp; norm_loop = front_norm; /* may as well get the normals set up now */ if (cont_normal != NULL) { if (xform_array == NULL) { for (j=0; j<ncp; j++) { norm_loop[3*j] = cont_normal[j][0]; norm_loop[3*j+1] = cont_normal[j][1]; norm_loop[3*j+2] = 0.0; } } else { for (j=0; j<ncp; j++) { NORM_XFORM_2X2 ( (&front_norm[3*j]), xform_array[inext-1], cont_normal [j]); front_norm[3*j+2] = 0.0; back_norm[3*j+2] = 0.0; } } } first_time = TRUE; /* draw tubing, not doing the first segment */ while (inext<npoints-1) { inextnext = inext; /* ignore all segments of zero length */ FIND_NON_DEGENERATE_POINT (inextnext, npoints, len, diff, point_array); /* get the next bisecting plane */ bisecting_plane (bi_1, point_array[i], point_array[inext], point_array[inextnext]); /* rotate so that z-axis points down v2-v1 axis, * and so that origen is at v1 */ uviewpoint (m, point_array[i], point_array[inext], yup); PUSHMATRIX (); MULTMATRIX (m); /* rotate the bisecting planes into the local coordinate system */ MAT_DOT_VEC_3X3 (bisector_0, m, bi_0); MAT_DOT_VEC_3X3 (bisector_1, m, bi_1); neg_z[2] = -len_seg; /* draw the tube */ /* --------- START OF TMESH GENERATION -------------- */ for (j=0; j<ncp; j++) { /* if there are normals, and there are either affine xforms, OR * path-edge normals need to be drawn, then compute local * coordinate system normals. */ if (cont_normal != NULL) { /* set up the back normals. (The front normals we inherit * from previous pass through the loop) */ if (xform_array != NULL) { /* do up the normal vectors with the inverse transpose */ NORM_XFORM_2X2 ( (&back_norm[3*j]), xform_array[inext], cont_normal [j]); } /* Note that if the xform array is NULL, then normals are * constant, and are set up outside of the loop. */ /* * if there are normal vectors, and the style calls for it, * then we want to project the normal vectors into the * bisecting plane. (This style is needed to make toroids, etc. * look good: Without this, segmentation artifacts show up * under lighting. */ if (__TUBE_DRAW_PATH_EDGE_NORMALS) { /* Hmm, if no affine xforms, then we haven't yet set * back vector. So do it. */ if (xform_array == NULL) { back_norm[3*j] = cont_normal[j][0]; back_norm[3*j+1] = cont_normal[j][1]; } /* now, start with a fresh normal (z component equal to * zero), project onto bisecting plane (by computing * perpendicular componenet to bisect vector, and renormalize * (since projected vector is not of unit length */ front_norm[3*j+2] = 0.0; VEC_PERP ((&front_norm[3*j]), (&front_norm[3*j]), bisector_0); VEC_NORMALIZE ((&front_norm[3*j])); back_norm[3*j+2] = 0.0; VEC_PERP ((&back_norm[3*j]), (&back_norm[3*j]), bisector_1); VEC_NORMALIZE ((&back_norm[3*j])); } } /* Next, we want to define segements. We find the endpoints of * the segments by intersecting the contour with the bisecting * plane. If there is no local affine transform, this is easy. * * If there is an affine tranform, then we want to remove the * torsional component, so that the intersection points won't * get twisted out of shape. We do this by applying the * local affine transform to the entire coordinate system. */ if (xform_array == NULL) { end_point_0 [0] = contour[j][0]; end_point_0 [1] = contour[j][1]; end_point_1 [0] = contour[j][0]; end_point_1 [1] = contour[j][1]; } else { /* transform the contour points with the local xform */ MAT_DOT_VEC_2X3 (end_point_0, xform_array[inext-1], contour[j]); MAT_DOT_VEC_2X3 (end_point_1, xform_array[inext-1], contour[j]); } end_point_0 [2] = 0.0; end_point_1 [2] = - len_seg; /* The two end-points define a line. Intersect this line * against the clipping plane defined by the PREVIOUS * tube segment. */ INNERSECT ((&front_loop[3*j]), /* intersection point (returned) */ origin, /* point on intersecting plane */ bisector_0, /* normal vector to plane */ end_point_0, /* point on line */ end_point_1); /* another point on the line */ /* The two end-points define a line. Intersect this line * against the clipping plane defined by the NEXT * tube segment. */ /* if there's an affine coordinate change, be sure to use it */ if (xform_array != NULL) { /* transform the contour points with the local xform */ MAT_DOT_VEC_2X3 (end_point_0, xform_array[inext], contour[j]); MAT_DOT_VEC_2X3 (end_point_1, xform_array[inext], contour[j]); } INNERSECT ((&back_loop[3*j]), /* intersection point (returned) */ neg_z, /* point on intersecting plane */ bisector_1, /* normal vector to plane */ end_point_0, /* point on line */ end_point_1); /* another point on the line */ } /* --------- END OF TMESH GENERATION -------------- */ /* v^v^v^v^v^v^v^v^v BEGIN END CAPS v^v^v^v^v^v^v^v^v^v^v^v */ /* if end caps are required, draw them. But don't draw any * but the very first and last caps */ if (__TUBE_DRAW_CAP) { if (first_time) { if (color_array != NULL) C3F (color_array[inext-1]); first_time = FALSE; draw_angle_style_front_cap (ncp, bisector_0, (gleVector *) front_loop); } if (inext == npoints-2) { if (color_array != NULL) C3F (color_array[inext]); draw_angle_style_back_cap (ncp, bisector_1, (gleVector *) back_loop); } } /* v^v^v^v^v^v^v^v^v END END CAPS v^v^v^v^v^v^v^v^v^v^v^v */ /* |||||||||||||||||| START SEGMENT DRAW |||||||||||||||||||| */ /* There are six different cases we can have for presence and/or * absecnce of colors and normals, and for interpretation of * normals. The blechy set of nested if statements below * branch to each of the six cases */ if ((xform_array == NULL) && (!__TUBE_DRAW_PATH_EDGE_NORMALS)) { if (color_array == NULL) { if (cont_normal == NULL) { draw_segment_plain (ncp, (gleVector *) front_loop, (gleVector *) back_loop, inext, len_seg); } else if (__TUBE_DRAW_FACET_NORMALS) { draw_segment_facet_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop, inext, len_seg); } else { draw_segment_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop, inext, len_seg); } } else { if (cont_normal == NULL) { draw_segment_color (ncp, (gleVector *) front_loop, (gleVector *) back_loop, color_array[inext-1], color_array[inext], inext, len_seg); } else if (__TUBE_DRAW_FACET_NORMALS) { draw_segment_c_and_facet_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop, color_array[inext-1], color_array[inext], inext, len_seg); } else { draw_segment_c_and_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop, color_array[inext-1], color_array[inext], inext, len_seg); } } } else { if (color_array == NULL) { if (cont_normal == NULL) { draw_segment_plain (ncp, (gleVector *) front_loop, (gleVector *) back_loop, inext, len_seg); } else if (__TUBE_DRAW_FACET_NORMALS) { draw_binorm_segment_facet_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) front_norm, (gleVector *) back_norm, inext, len_seg); } else { draw_binorm_segment_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) front_norm, (gleVector *) back_norm, inext, len_seg); } } else { if (cont_normal == NULL) { draw_segment_color (ncp, (gleVector *) front_loop, (gleVector *) back_loop, color_array[inext-1], color_array[inext], inext, len_seg); } else if (__TUBE_DRAW_FACET_NORMALS) { draw_binorm_segment_c_and_facet_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) front_norm, (gleVector *) back_norm, color_array[inext-1], color_array[inext], inext, len_seg); } else { draw_binorm_segment_c_and_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) front_norm, (gleVector *) back_norm, color_array[inext-1], color_array[inext], inext, len_seg); } } } /* |||||||||||||||||| END SEGMENT DRAW |||||||||||||||||||| */ /* pop this matrix, do the next set */ POPMATRIX (); /* bump everything to the next vertex */ len_seg = len; i = inext; inext = inextnext; VEC_COPY (bi_0, bi_1); /* trade norm loops */ tmp = front_norm; front_norm = back_norm; back_norm = tmp; /* reflect the up vector in the bisecting plane */ VEC_REFLECT (yup, yup, bi_0); } /* be sure to free it all up */ free (mem_anchor); }
/* * The uview_dire subroutine computes and returns a 4x4 rotation * matrix that puts the negative z axis along the direction v21 and * puts the y axis along the up vector. * * It computes exactly the same matrix as the code above * (uview_direction), but with an entirely different (and slower) * algorithm. * * Note that the code below is slightly less robust than that above -- * it may croak if the supplied vectors are of zero length, or are * parallel to each other ... */ void uview_dire (float m[4][4], /* returned */ float v21[3], /* input */ float up[3]) /* input */ { gleDouble theta; float v_hat_21 [3]; float z_hat [3]; float v_cross_z [3]; float u[3]; float y_hat [3]; float u_cross_y [3]; gleDouble cosine; float zmat [4][4]; float upmat[4][4]; float dot; /* perform rotation to z-axis only if not already * pointing down z */ if ((v21[0] != 0.0 ) || (v21[1] != 0.0)) { /* find the unit vector that points in the v21 direction */ VEC_COPY (v_hat_21, v21); VEC_NORMALIZE (v_hat_21); /* cosine theta equals v_hat dot z_hat */ cosine = - v_hat_21 [2]; theta = - acos (cosine); /* Take cros product with z -- we need this, because we will rotate * about this axis */ z_hat[0] = 0.0; z_hat[1] = 0.0; z_hat[2] = -1.0; VEC_CROSS_PRODUCT (v_cross_z, v_hat_21, z_hat); VEC_NORMALIZE (v_cross_z); /* compute rotation matrix that takes -z axis to the v21 axis */ urot_axis (zmat, (float) theta, v_cross_z); } else { IDENTIFY_MATRIX_4X4 (zmat); if (v21[2] > 0.0) { /* if its pointing down the positive z-axis, flip it, so that * we point down negative z-axis. We flip x so that the partiy * isn't destroyed (looks like a rotation) */ zmat[0][0] = -1.0; zmat[2][2] = -1.0; } } /* --------------------- */ /* OK, now compute the part that takes the y-axis to the up vector */ VEC_COPY (u, up); /* the rotation blows up, if the up vector is not perpendicular to * the v21 vector. Let us make sure that this is so. */ VEC_PERP (u, u, v_hat_21); /* need to run the y axis through above x-form, to see where it went */ y_hat[0] = zmat [1][0]; y_hat[1] = zmat [1][1]; y_hat[2] = zmat [1][2]; /* perform rotation to up-axis only if not already * pointing along y axis */ VEC_DOT_PRODUCT (dot, y_hat, u); if ((-1.0 < dot) && (dot < 1.0)) { /* make sure that up really is a unit vector */ VEC_NORMALIZE (u); /* cosine phi equals y_hat dot up_vec */ VEC_DOT_PRODUCT (cosine, u, y_hat); theta = - acos (cosine); /* Take cross product with y */ VEC_CROSS_PRODUCT (u_cross_y, u, y_hat); VEC_NORMALIZE (u_cross_y); /* As a matter of fact, u_cross_y points either in the v21 direction, * or in the minus v21 direction. In either case, we needed to compute * it, because the the arccosine function returns values only for * 0 to 180 degree, not 0 to 360, which is what we need. The * cross-product helps us make up for this. */ /* rotate about the NEW z axis (i.e. v21) by the cosine */ urot_axis (upmat, (float) theta, u_cross_y); } else { IDENTIFY_MATRIX_4X4 (upmat); if (dot == -1.0) { /* if its pointing along the negative y-axis, flip it, so that * we point along the positive y-axis. We flip x so that the partiy * isn't destroyed (looks like a rotation) */ upmat[0][0] = -1.0; upmat[1][1] = -1.0; } } MATRIX_PRODUCT_4X4 (m, zmat, upmat); }