Chris,

Okay, you've probably finished your entire project and/or gotten a few PhDs
by now, but here's my third installment on this, hopefully not half-assing
it like I did before (I guess that means I'm full-assing it?).  That is, if
you are still continuing with Java 3D after today's news.  As for me, I just
have to rectify what I said before - and hopefully I got it right this time.

About the two points yielding the same vertex, I apologize because what I
said didn't make any sense; and the part about the open loop was only
partially correct.  Now that I've looked at it in depth, the yielded
vertices should indeed be different, and they are in my code when I ran the
algorithm.  To give you something concrete to look at (and to criticize if
you like), I've attached my own segment of Java 3D code that I converted
from Walser's code.  I've added stuff to handle the closed loop which wasn't
there before.

One note: I had problems with the surface not displaying correctly for only
a couple of segments, I think it was the first and last.  I stopped all
culling, which solved the problem, but I never took the time to find out
what the problem was.

Any feedback is appreciated.  Well, *almost* any feedback.

Good luck - in whatever 3D technology you're now using!

    Chet Urata



--- Chet Urata <[EMAIL PROTECTED]> wrote:
> Sorry, Chris, I failed to provide anything useful except agreeing
> with what
> you said.
>
> I imagine there are other ways to do this, but I just added a check
> to see
> if the cross product went to zero, then chose another "up" vector.
> It is
> not very elegant in my estimation, but I think it is okay since the
> idea
> appears to be to create a set of orthogonal vectors to define a frame
> that
> has the forward axis pointing along the path between two points of
> the line.
> There are other ways to do this, maybe using AxisAngle with the
> translation,
> but I haven't looked into what the trade-offs are.
>
> As for the two points situation, I'm not sure what the vertex you are
> printing is, but, as I mentioned last time, the segments being
> calculated
> will be the same for only two points.  I haven't tried to actually
> run it,
> but it appears from the code that the tube should still be created
> (although, with only two points, it won't be much of a torus, if that
> was
> what you were looking for).
>
> And for my change that I added for the "if" statement, it would
> actually
> seem to me that the final segment is not needed for an open loop.
>
>         Thoughts?
>
>         Chet Urata
>
>
>
> -----Original Message-----
> From: Discussion list for Java 3D API
> [mailto:[EMAIL PROTECTED] Behalf Of Chet Urata
> Sent: Friday, July 25, 2003 3:00 PM
> To: [EMAIL PROTECTED]
> Subject: Re: [JAVA3D] 3D line to 3D tube
>
>
> Greetings, Chris,
>
>         Although the points (1,1,0) and (2,2,0) are not parallel to
> the
> z-axis,
> your point is correct.  Good catch!  The code does fail when two
> points are
> parallel to the z-axis because of the cross product of the direction
> vector
> with the vector (0, 0, 1).  We need to find a work-around.
>
>         And it is indeed problematic for the algorithm when using
> only two
> points.
> If you follow the code, you'll notice something:
>
> Consider the two values for i, 0 and 1:
>
> i == 0: forward.sub(path[1],path[0])
> i == 1: forward.sub(path[1],path[0]) // because (i == (segs-1)) for
> either
> closed or open loops
>
> They look mighty similar.
>
> I also had a problem with the code
>
> >                         if closed  : forward.sub(path[i],path[i-1])
> >                         else       : forward.sub(path[1],path[0])
>
> changing it to something like
>
> >                         if closed  : forward.sub(path[0],path[i])
> >                         else       : forward.sub(path[i],path[i-1])
>
>         Chet Urata
>
>
>
>
> --- Chris Fenton <[EMAIL PROTECTED]> wrote:
> > Some notes on code.
> > Code fails when consective path points are parallel to Z axis
> (1,1,0)
> > (2,2,0).
> > But more mysteriously the translational part of the transform does
> > not seem to be working look at the following.
> >
> > def CreateTube(path,radius,steps,closed=0):
> >      ---create circle ---
> >      for i in range(segs):
> >                 forward, right = Vector3d(), Vector3d()
> >                 if i != segs-1: forward.sub(path[i+1],path[i])
> >                 else               :
> >                         if closed  : forward.sub(path[i],path[i-1])
> >                         else       : forward.sub(path[1],path[0])
> >                 forward.normalize()
> >                 up           = Vector3d(0.0,0.0,1.0)
> >                 right.cross(forward, up)
> >                 up.cross(forward, right)
> >                 frenetmatrix = Matrix3d(right,up, forward)
> >                 trans        = Transform3D(frenetmatrix,
> Vector3d(),
> > 1)
> >                 trans.setTranslation(path[i])
> >                 for k in range(steps):
> >                         tempvert = Vector3d()
> >                         trans.transform(circle[k], tempvert)
> >      ---print ---
> >
> >
> > Input :
> >         path   = ((1,1,1),(2,2,2))
> >         total  = CreateTube(path, 0.5, 3, 1)
> >
> > Notice:
> >
> > Path = (1,1,1), circle(0.5,0,0), vertex(0.288-, 0.166-, 0.288-)
> > Path = (2,2,2), circle(0.5,0,0), vertex(0.288-, 0.166-, 0.288-)
> >
> > HOW IS THIS POSSIBLE WITH DIFFERENT TRANSLATIONAL COMPONENTS ?
> >
> > Output :
> >
> >
> > trans ------------------------------
> > 0.5773502691896258, -0.5773502691896258, 0.0, 1.0
> > 0.3333333333333334, 0.3333333333333334, -0.6666666666666669, 1.0
> > 0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 1.0
> > 0.0, 0.0, 0.0, 1.0
> >
> > point (1.0, 1.0, 1.0)
> > circle (0.5, 0.0, 0.0)
> > vertex (0.2886751345948129, 0.1666666666666667, 0.2886751345948129)
> >
> >
> > trans ------------------------------
> > 0.5773502691896258, -0.5773502691896258, 0.0, 2.0
> > 0.3333333333333334, 0.3333333333333334, -0.6666666666666669, 2.0
> > 0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 2.0
> > 0.0, 0.0, 0.0, 1.0
> >
> > point (2.0, 2.0, 2.0)
> > circle (0.5, 0.0, 0.0)
> > vertex (0.2886751345948129, 0.1666666666666667, 0.2886751345948129)
> >
> >
>
===========================================================================
> > To unsubscribe, send email to [EMAIL PROTECTED] and include in
> > the body
> > of the message "signoff JAVA3D-INTEREST".  For general help, send
> > email to
> > [EMAIL PROTECTED] and include in the body of the message
> "help".
>
>
===========================================================================
> To unsubscribe, send email to [EMAIL PROTECTED] and include in
> the body
> of the message "signoff JAVA3D-INTEREST".  For general help, send
> email to
> [EMAIL PROTECTED] and include in the body of the message "help".
>
>
===========================================================================
> To unsubscribe, send email to [EMAIL PROTECTED] and include in
> the body
> of the message "signoff JAVA3D-INTEREST".  For general help, send
> email to
> [EMAIL PROTECTED] and include in the body of the message "help".
    /**
     * Creates the surface mesh for a tube with the indicated radius, traveling
     * along the indicated path.
     *
     * Adapted from idx3d code developed by Peter Walser,
     * http://www2.active.ch/~proxima
     *
     * @param path       points of the path that the tube is to run along.
     *                   The path will actually run along the center of the tube.
     * @param tubeRadius the radius of the tube itself
     * @param steps      number of triangles to make in the mesh
     * @param isClosed   indicates if the path is a closed loop or not
     * @param vertexList list of unique vertices
     * @param normalList list of normals corresponding to the vertex list
     * @param indexList  list of indices into the vertex list (and the other
     *                   lists, as well) that has an entry per triangle vertex.
     */
    protected void createTubeGeometry( Point3d[] path,
                                       double tubeRadius,
                                       int steps,
                                       boolean isClosed,
                                       double[] vertexList,
                                       float[] normalList,
                                       int[] indexList ) {


        Point3d[] crossSection = createCrossSection( tubeRadius, steps );

        /*
         * Creation of all objects that will be used in the upcoming "for."
         * This could be split up to reduce the size of this method, but
         * there should be consideration for performance if the split
         * requires the creation of a significant number of new objects.
         * In the following implementation, no new objects are created within
         * the "for."
         */
        Vector3d forward = new Vector3d();          // vector pointing forward
        Vector3d up = new Vector3d();               // vector pointing up
        Vector3d right = new Vector3d();            // vector pointing right
        Matrix4d transformMatrix = new Matrix4d();  // transformation matrix
        Point3d tempVertex = new Point3d();         // transformed vertex
        Matrix3d rotationMatrix = new Matrix3d();   // rot. matrix for normals
        Point3d tempNormal = new Point3d();         // transformed normal
        Vector3f normalVector = new Vector3f();     // current normal vector

        int numSegments = path.length - 1;
        int vertexIndex = 0;
        for (int currPoint = 0; currPoint < path.length; currPoint++) {

            // Calculate Frenet frame matrix.
            
            // Get the path between this point and the next.
            if (currPoint < (path.length - 1)) {
                forward.sub( path[ currPoint + 1 ], path[ currPoint ] );
            } else {

                /*
                 * If the path is a closed loop, point towards the head vertex.
                 * Otherwise, the point towards the vertex.
                 */
                if (isClosed) {
                    forward.sub( path[ 0 ], path[ currPoint ] );
                } else {
                    forward.sub( path[ currPoint ], path[ currPoint - 1 ] );
                }
            }

            /* 
             * Create a vector to help create a coordinate frame using the 
             * forward vector as one axis.
             */
            forward.normalize();
            up.set( 0, 0, 1.0 );
            right.cross( forward, up );
            
            /*
             * If the up vector is parallel with the forward vector, choose
             * another vector or we won't be able to create a coordinate frame.
             */
            if (right.equals( new Vector3d( 0, 0, 0 ))) {
                up.set( -1.0, 0, 0 );
                right.cross( forward, up );
            }
            right.normalize();
            up.cross( forward, right );
            up.normalize();

            transformMatrix.setIdentity(); // reinit the matrix.
            transformMatrix.setColumn( 0, right.x,
                                          right.y,
                                          right.z,
                                          0d );
            transformMatrix.setColumn( 1, up.x,
                                          up.y,
                                          up.z,
                                          0d );
            transformMatrix.setColumn( 2, forward.x,
                                          forward.y,
                                          forward.z,
                                          0d );
            transformMatrix.setColumn( 3, path[ currPoint ].x,
                                          path[ currPoint ].y,
                                          path[ currPoint ].z,
                                          1d );

            // Add vertex nodes
            for (int k = 0; k < steps; k++) {

                // Transform the vertices of the original cross-section.
                transformMatrix.transform( crossSection[ k ], tempVertex );

                // Create the normal for this vertex.
                transformMatrix.get( rotationMatrix );
                rotationMatrix.transform( crossSection[ k ], tempNormal );
                normalVector.set( tempNormal );
                normalVector.normalize();
                vertexList[ vertexIndex ]   = tempVertex.x;
                normalList[ vertexIndex++ ] = normalVector.x;
                vertexList[ vertexIndex ]   = tempVertex.y;
                normalList[ vertexIndex++ ] = normalVector.y;
                vertexList[ vertexIndex ]   = tempVertex.z;
                normalList[ vertexIndex++ ] = normalVector.z;

            } // for (int k = 0; k < steps; k++)
        } // for (int currPoint = 0;...

        createIndexList( steps, numSegments, isClosed, indexList );
    }

    /**
     * Creates evenly spaced points along the surface of a cross-section of
     * the tube.
     * This particular version creates a circle cross-section.  Subclasses
     * should override this method to create cross-sections of different
     * shapes.
     *
     * Adapted from idx3d code developed by Peter Walser,
     * http://www2.active.ch/~proxima
     *
     * @param tubeRadius the radius of the tube itself.
     * @param steps number of triangles to make in the mesh.
     *
     * @return points along the surface of the cross-section.
     */
    protected Point3d[] createCrossSection( double tubeRadius,
                                            int steps ) {

        /*
         * If this is a new cross-section, find equidistant points on the
         * circle that will serve to define the tube's cross-section.
         * Otherwise, if this is not a new cross-section, return the current
         * one.
         * We will be creating a new cross-section if there hasn't been one
         * before (null), or if the number of steps are the same and the radius
         * is the same (note that the origin of the circle is at (0, 0, 0).
         * Note: the points are ordered so that they proceed in a 
         * counter-clockwise progression.  This may be important for determining
         * normals and other surface vectors.
         */
        if ((crossSection == null) ||
            ((crossSection.length != steps) &&
             (crossSection[ 0 ].distance( new Point3d() ) != tubeRadius))) {

            crossSection = new Point3d[ steps ];
            double angle;
            for (int i = 0; i < steps; i++) {

                angle = (2 * Math.PI) * ((double) i / (double) steps);
                crossSection[ i ] = new Point3d( tubeRadius * Math.cos( angle ),
                                                 tubeRadius * Math.sin( angle ),
                                                 0 );
            } // for
        } // if ((crossSection == null) || ...

        return crossSection;
    }

    /**
     * Creates the list of indices into the vertex and other lists.
     * 
     * The crazy way the indexes are set is actually very organized.  The
     * calculations for the index numbers are done so that the groups
     * of three vertices (the triangles) are non-overlapping, adjacaent, and
     * proceed around the outside of the tube in a counter-clockwise fashion.
     * This is provided that the points for each cross-section described in 
     * the vertex list are sequentially ordered, going around the circle in a
     * counter-clockwise fashion (see createCrossSection()).  
     *
     * Adapted from idx3d code developed by Peter Walser,
     * http://www2.active.ch/~proxima
     *
     * @param steps       number of points that the cross-section is split into.
     * @param numSegments number of segments in the tube's path.
     *                    This is equivalent to one less than the number of
     *                    points in the path.
     * @param isClosed    indicates if the path is a closed loop or not
     * @param indexList   list of indices for the triangles of the mesh.
     *                    The indices are grouped in triplets, each triplet
     *                    representing one triangle (of course).
     */
    protected void createIndexList( int steps,
                                    int numSegments,
                                    boolean isClosed,
                                    int[] indexList ) {

        // Create the vertex index list.
        int vertexA;
        int vertexB;
        int listIndex = 0;
        for (int i = 0; i < numSegments; i++) {

            for (int k = 0; k < (steps - 1); k++) {

                vertexA = (i * steps) + k;
                vertexB = vertexA + 1;
                listIndex = setIndexList( vertexA,
                                          vertexB,
                                          steps,
                                          indexList, 
                                          listIndex );

            } // for (int k = 0; k < (steps - 1); k++)

            vertexA = ((i + 1) * steps) - 1;
            vertexB = vertexA + 1 - steps;
            listIndex = setIndexList( vertexA,
                                      vertexB,
                                      steps,
                                      indexList, 
                                      listIndex );

        } // for (int i = 0; i < numSegments; i++)

        // If the loop is closed, connect the end of the loop with the head.
        int lastGroupStartIndex = numSegments * steps;
        if (isClosed) {

            for (int n = 0; n < (steps - 1); n++) {

                vertexA = n;
                vertexB = vertexA + 1;
                listIndex = setIndexList( vertexA,
                                          vertexB,
                                          lastGroupStartIndex,
                                          indexList, 
                                          listIndex );

            } // for (int n = 0; n < (steps - 1); n++)

            vertexA = steps - 1;
            vertexB = 0;
            listIndex = setIndexList( vertexA,
                                      vertexB,
                                      lastGroupStartIndex,
                                      indexList, 
                                      listIndex );
        } // if (isClosed)
    }

    /**
     * Sets the indicated indices into the indexList.
     * This sets the vertices for two triangles.  The order of the vertices 
     * is counter-clockwise, in relation to the outward normal (at least it's
     * supposed to be!).
     * 
     * Adapted from idx3d code developed by Peter Walser,
     * http://www2.active.ch/~proxima
     *
     * @see #createIndexList()
     * 
     * @param vertexA     index into vertex list.  Indicates a point on the
     *                    edge of a cross-section of the tube.
     * @param vertexB     like vertexA, on the same cross-section.  This 
     *                    point must be found counter-clockwise from A, in 
     *                    relation to the outward normal.
     * @param nextSection offset to next cross-section's indices
     * @param indexList   list of indices for the triangles of the mesh.
     *                    The indices are grouped in triplets, each triplet
     *                    representing one triangle (of course).  The new
     *                    vertex indices are added to this list.
     * @param listIndex   index into indexList, pointing at next location to 
     *                    be filled
     * 
     * @return updated index into indexList, pointing at next location to be 
     *         filled
     */
    protected int setIndexList( int vertexA,
                                int vertexB,
                                int nextSection,
                                int[] indexList,
                                int listIndex ) {

        int vertexC = vertexA + nextSection;
        int vertexD = vertexB + nextSection;

        indexList[ listIndex++ ] = vertexA;
        indexList[ listIndex++ ] = vertexC;
        indexList[ listIndex++ ] = vertexB;

        indexList[ listIndex++ ] = vertexB;
        indexList[ listIndex++ ] = vertexC;
        indexList[ listIndex++ ] = vertexD;

        return (listIndex);
    }

Reply via email to