Exemple #1
0
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);
}
Exemple #2
0
/*
 * 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);

}
Exemple #4
0
/*
 * 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);

}