Great find. I did find some switching of handed rules from the
specification and noted that, so probably the best thing to do
is report this as a fix to the document.

Regards,
______________________________________
Rick Goldberg - Member Technical Staff
Sun Microsystems
Graphics Products - Computer Systems
901 San Antonio Road, MS UMPK27-302
Palo Alto, CA 94043-4900
650 786-0108 Direct
650 856-2114 Fax
[EMAIL PROTECTED]

> MIME-Version: 1.0
> X-Priority: 3
> X-MSMail-Priority: Normal
> X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3
> Date: Tue, 1 Feb 2000 12:49:13 +0200
> From: Pasi Paasiala <[EMAIL PROTECTED]>
> Subject: [JAVA3D] com.sun.j3d.loaders.vrml97.impl.Extrusion improved (a lot)
> Comments: To: vrml-java3d <[EMAIL PROTECTED]>
> To: [EMAIL PROTECTED]
>
> Hello all,
>
> I have spent some time (actually quite some time) with
> com.sun.j3d.loaders.vrml97.impl.Extrusion.
>
> The original version created strange results. These were mainly caused by
> setting up the SCP's incorrectly. In short I have made the following
> corrections:
>
>     z[i].cross(u,v); // was previously: z[i].cross(v,u);
>     x[i].cross(y[i],z[i]); // was previously: x[i].cross(z[i],y[i])
>
> The original SCP was not right handed. The new one is. It seems also that
> there is an error in the VRML specification regarding the calculation of Z
> in
> (http://www.vrml.org/Specifications/VRML97/part1/nodesRef.html#Extrusion). I
> think this was noticed also by Rick Goldberg as seen in the original
> comments.
>
> I have also made some improvements:
>
> if spine is collinear a real SCP is calculated. Previously it was constant
>
> added createCorrectionRotations -method that calculates per spine rotations
> that are needed to prevent the extusion from twisting around the spine when
> the z coordinates of SCP's vary. I'm not sure, if this is in the spirit of
> VRML, but I think it is easier to create smooth extrusions this way. You can
> compare results given by the original Extrusion, CosmoPlayer, and this
> Extrusion with the attached pipe2.wrl file.
>
> Attached is also the new version of Extrusion.
>
> Pasi
>
> P.S. In a previous mail I was wondering if it should also be possible to
> programmatically populate a VRML-Java3D scene and output VRML from it. By
> definition the overridden toStringBody() methods of BaseNode should do this,
> but they seem not implemented. Are there any plans to do this?
/*
 *      @(#)Extrusion.java 1.23 99/03/24 15:56:40
 *
 * Copyright (c) 1996-1999 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

/*
 * @Author: Rick Goldberg
 *
 */
package com.sun.j3d.loaders.vrml97.impl;

import com.sun.j3d.loaders.vrml97.impl.*;
import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import com.sun.j3d.utils.geometry.Stripifier;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.IndexedQuadArray;
import javax.media.j3d.BoundingBox;
import javax.media.j3d.Transform3D;
import javax.media.j3d.Shape3D;
import javax.vecmath.*;


public class Extrusion extends Geometry implements Ownable{

    SFBool beginCap;
    SFBool ccw;
    SFBool convex;
    SFFloat creaseAngle;
    MFVec2f crossSection;
    SFBool endCap;
    MFRotation orientation;
    MFVec2f scale;
    SFBool solid;
    MFVec3f spine;

    GeometryArray impl;
    BoundingBox bounds;
    GeometryInfo gi;
    Shape owner;

    /** Shape to be displayed */
        Shape3D shape = new Shape3D();

    Point3f[] spines;
    Vector3f[] scales;
    AxisAngle4f[] orientations;
    Transform3D[] spineTransforms;
    Point3f[] crossSectionPts;
    // will contain the per spine
    // transform composed with orientation
    Matrix3f[] rotations;
    Transform3D[] transforms;

    // data -> gi
    Point3f[] coords;
    int[] coordIndex;
    int[] stripCounts;

    boolean collinear=false;
    boolean closed=false; // spines

    float[] a2 = new float[2];
    float[] a3 = new float[3];
    float[] a4 = new float[4];

    int numTris = 0;

    public static boolean hardDebug = false;



    public Extrusion(Loader loader) {
      super(loader);
      beginCap = new SFBool(true);
      endCap = new SFBool(true);
      ccw = new SFBool(true);
      convex = new SFBool(true);
      solid = new SFBool(true);
      if (loader.autoSmooth) {
          creaseAngle = new SFFloat(.9f);
      } else {
          creaseAngle = new SFFloat(0);
      }

      crossSection = new MFVec2f(5, new float[10]);
      crossSection.set1Value(0,1.0f,1.0f);
      crossSection.set1Value(1,1.0f,-1.0f);
      crossSection.set1Value(2,-1.0f,-1.0f);
      crossSection.set1Value(3,-1.0f,1.0f);
      crossSection.set1Value(4,1.0f,1.0f);
      spine = new MFVec3f(2, new float[6]);
      spine.set1Value(0,0.0f,0.0f,0.0f);
      spine.set1Value(1,0.0f,1.0f,0.0f);
      orientation = new MFRotation(1,new float[4]);
      orientation.set1Value(0,0.0f,0.0f,1.0f,0.0f);
      scale = new MFVec2f(new float[2]);
      scale.set1Value(0,1.0f,1.0f);
      initFields();
    }

    Extrusion(Loader loader, SFBool beginCap, SFBool ccw,
        SFBool convex, SFFloat creaseAngle, MFVec2f crossSection,
        SFBool endCap, MFRotation orientation, MFVec2f scale,
        SFBool solid, MFVec3f spine) {

      super(loader);
      this.beginCap = beginCap;
      this.ccw = ccw;
      this.convex = convex;
      this.creaseAngle = creaseAngle;
      this.crossSection = crossSection;
      this.endCap = endCap;
      this.orientation = orientation;
      this.scale = scale;
      this.solid = solid;
      this.spine = spine;
      initFields();
    }


        public Extrusion(Loader loader, boolean beginCap, boolean ccw,
                boolean convex, float creaseAngle, Tuple2d[] crossSection,
                boolean endCap, AxisAngle4d[] orientations, Vector2d[] scales,
                boolean solid, Tuple3d[] spine) {

                super(loader);
    this.beginCap = new SFBool(beginCap);
    this.ccw = new SFBool(ccw);
                this.convex = new SFBool(convex);
                this.creaseAngle = new SFFloat(creaseAngle);

    if (hardDebug)
    {
        System.out.println("Crossection = ");
      for (int i = 0; i < crossSection.length; i++)
      {
        System.out.println(crossSection[i]);
      }
        System.out.println("Spine = ");
      for (int i = 0; i < spine.length; i++)
      {
        System.out.println(spine[i]);
      }
    }

                float[] aCrossSection = new float[2*crossSection.length];
                for (int i = 0; i < crossSection.length; i++)
    {
      aCrossSection[i*2 + 0] = (float)crossSection[i].x;
      aCrossSection[i*2 + 1] = (float)crossSection[i].y;
    }
    this.crossSection = new MFVec2f(aCrossSection);


                this.endCap = new SFBool(endCap);

                float[][] aOrientations = new float[orientations.length][4];
    for (int i = 0; i < orientations.length; i++)
    {
      aOrientations[i][0] = (float)orientations[i].x;
      aOrientations[i][1] = (float)orientations[i].y;
      aOrientations[i][2] = (float)orientations[i].z;
      aOrientations[i][3] = (float)orientations[i].angle;
    }
                this.orientation = new MFRotation(aOrientations);

    float[] aScales = new float[2*scales.length];
                for (int i = 0; i < scales.length; i++)
    {
      aScales[i*2 + 0] = (float)scales[i].x;
      aScales[i*2 + 1] = (float)scales[i].y;
    }
                this.scale = new MFVec2f(aScales);

                this.solid = new SFBool(solid);

                float[] aSpine = new float[spine.length*3];
    for (int i = 0; i < spine.length; i++)
    {
        aSpine[i*3 + 0] = (float)spine[i].x;
      aSpine[i*3 + 1] = (float)spine[i].y;
      aSpine[i*3 + 2] = (float)spine[i].z;
    }
    this.spine = new MFVec3f(aSpine);
    initImpl();
  }
  public boolean haveTexture() { return false; }

  public int getNumTris() { return numTris; }

  public Shape3D getShape()
  {
                shape.setGeometry(impl);
    return shape;
  }

  public javax.media.j3d.Geometry getImplGeom() {
                return impl;
  }

  BoundingBox getBoundingBox() {

        // create a tiny bounding box about a coordinate
        // to seed the placement
      Point3d epsilon = new Point3d(.000001,.000001,.000001);
      Point3d lower = new Point3d(coords[0]);
      Point3d upper = new Point3d(coords[0]);
      lower.sub(epsilon);
      upper.add(epsilon);

      bounds=new javax.media.j3d.BoundingBox(lower,upper);

      for ( int c = 1; c<coords.length; c++ ) {
          bounds.combine(new Point3d(coords[c]));
      }
      return bounds;

    }

    void initImpl() {

        // endcaps may need special handling
        gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);

        // convert vrml data to intermediate form
        initSetup();

        // calculate per spine SCP transforms
        // results in transforms[] being filled withs SCP info
        // complete with scale, translation and orientation from
        // fields
        calculateSCP();

        // transform the crossSections to coordinates
        createExtrusion();

        // indexify, including endcaps if needed
        // leaves coordIndex with index and stripCounts with counts
        // per facet.
        createIndices();
        if ( hardDebug ) {
            System.out.println("coords");
            for ( int i = 0; i < coords.length; i ++ ) System.out.println(coords[i]);
            System.out.println("coordIndex");
            for ( int i = 0; i < coordIndex.length; i ++ ) 
System.out.println(coordIndex[i]);
            System.out.println("stripCounts");
            for ( int i = 0; i < stripCounts.length; i ++ ) 
System.out.println(stripCounts[i]);
        }
        gi.setCoordinates(coords);
        gi.setCoordinateIndices(coordIndex);
        gi.setStripCounts(stripCounts);

        //Stripifier st = new Stripifier();
        //st.stripify(gi);

        float ca = creaseAngle.getValue();
        if ( ca < 0.0f ) {
            ca = 0.0f;
        }
        if ( ca > (float)Math.PI ) {
            ca -= (float)Math.PI;
        }

        NormalGenerator ng = new NormalGenerator(ca);
        ng.generateNormals(gi);
        
        impl = gi.getGeometryArray();


    }

    void initSetup() {
        // load the crossSectionPts data
        crossSectionPts = new Point3f[ crossSection.getSize() ];

        if ( hardDebug ) System.out.println(crossSection.getSize());
        for ( int i = 0; i < crossSectionPts.length; i++ ) {
            crossSection.get1Value(i,a2);
    crossSectionPts[i] = new Point3f(a2[0],0.0f,a2[1]);
        }

        // load the scales
        // scales size may not match spine size, if so
        // use previously set scale
        scales = new Vector3f[ spine.getSize() ];
        for ( int i = 0; i < scales.length; i++ ) {
            if ( i < scale.getSize() )
                scale.get1Value(i,a2);
            scales[i] = new Vector3f(a2[0],1.0f,a2[1]);
        }

        // load the spines
        spines = new Point3f[ spine.getSize() ];
        for ( int i = 0; i < spines.length; i++ ) {
            spine.get1Value(i,a3);
            spines[i] = new Point3f(a3);
        }

        // load the per spine orientation modifiers
        orientations = new AxisAngle4f[ spine.getSize() ];
        for ( int i = 0; i < orientations.length; i++ ) {
            if ( i < orientation.getSize() )
                orientation.get1Value(i,a4);
            orientations[i] = new AxisAngle4f(a4);
        }

        rotations = new Matrix3f[ spines.length ];

        // if the tail meets the head
        if (spines[0].equals(spines[spines.length - 1])) {
            closed = true;
        }

        // if entirely collinear
        Vector3d v2 = new Vector3d();
        Vector3d v1 = new Vector3d();
        Vector3d v0 = new Vector3d();
        double d=0.0;
        for ( int i = 1; i < spines.length - 1; i++ ) {
            v2.set(spines[i+1]);        
            v1.set(spines[i]);
            v0.set(spines[i-1]);  
            v2.sub(v1);
            v1.sub(v0);
            v0.cross(v2,v1);
            d += v0.dot(v0);
        }
        collinear=(d==0.0);
        if ( hardDebug && collinear ) System.out.println("spine is straight");

    }

    void calculateSCP() {
        // find an orthonormal basis and construct rotation matrix
        // for each spine. handle special cases in second pass
        Vector3f[] x,y,z;
        Vector3f u,v;
        Vector3f zero = new Vector3f(0.0f,0.0f,0.0f);
        int last = spines.length-1;
        
        x = new Vector3f[ spines.length ];
        y = new Vector3f[ spines.length ];
        z = new Vector3f[ spines.length ];
        
        if ( collinear ) {
            if (closed) { 
                throw new vrml.InvalidVRMLSyntaxException(
                    "invalid Extrusion data; looks like a solid of revolution"
                );
            }
    // Direction is the first spine point that does not equal to
    // spines[0]
    Vector3f direction = null;
    for (int i = 0; i < spines.length; i++)
    {
        if (!spines[0].equals(spines[i]))
      {
        direction = new Vector3f(spines[i]);
      }
    }

                y[0] = new Vector3f();
                y[0].sub( direction, spines[0] );
                try {
                    norm(y[0]);
                } catch ( ArithmeticException ae ) {
        ae.printStackTrace();
                }

                // Create an initial x[0]
                if (y[0].x == 1.0f)
    {
        x[0] = new Vector3f(0.0f,-1.0f,0.0f);
    }
                else if (y[0].x == -1.0f)
    {
        x[0] = new Vector3f(0.0f,1.0f,0.0f);
    }
    else
    {
        x[0] = new Vector3f(1.0f,0.0f,0.0f);
    }

                // Create z[0]
                z[0] = new Vector3f();
    z[0].cross(x[0],y[0]);

    // Create final x[0]
    x[0].cross(y[0],z[0]);

    for ( int i = 1; i < spines.length; i++ ) {
      // redo, this should take the direction of y
      // redone by Pasi Paasiala <<check this>>
      x[i] = new Vector3f(x[0]);
      y[i] = new Vector3f(y[0]);
      z[i] = new Vector3f(z[0]);
    }
        } else {
            // find y[i] for all but first and last
            // most times the exception cases are bad data and hopefully
            // wont happen. It is free to try catch you later, so hopes
            // 99% cases will be one if faster by not checking the if
            for ( int i = 1; i < last; i++ ) {
                y[i] = new Vector3f();
                y[i].sub( spines[i+1], spines[i-1] );
                try {
                    norm(y[i]);
                } catch ( ArithmeticException ae ) {
                    if ( hardDebug ) System.out.println(ae+" "+y[i]);
                    // spines[i+1] equals spines[i-1]
                    try {
                        y[i].sub( spines[i+1], spines[i] );
                        norm(y[i]);
                    } catch ( ArithmeticException ae1 ) {
                        if ( hardDebug ) System.out.println(ae1+" "+y[i]);
                        // spines[i+1] equaled spines[i]
                        try {
                            y[i].sub( spines[i], spines[i-1] );
                            norm(y[i]);
                        } catch ( ArithmeticException ae2 ) {
                            if ( hardDebug ) System.out.println(ae2+" "+y[i]);
                            // spines[i] equaled spines[i-1]
                            // real bad case, do something
                            int w=i+2;
                            while (( w < last+1 ) && (spines[i-1].equals(spines[w]))) 
                                w++;
                            if ( w < last+1 ) {
                                y[i].sub(spines[w],spines[i-1]);
                                if ( hardDebug ) System.out.println("did something 
"+y[i]);
                                norm(y[i]); // should never divide by zero here
                            } else { // worst worst case
                                if ( hardDebug ) System.out.println("worst worst y 
"+y[i]);
                                y[i] = new Vector3f(0.0f,1.0f,0.0f);
                            }
                        }
                    }
                }
            }
            // y for ends
            if ( closed ) {
                // closed and not collinear -> not all one point
                y[0] = new Vector3f();
                y[0].sub(spines[1],spines[last-1]);
                try {
                    norm(y[0]);
                } catch ( ArithmeticException ae ) {
                    // bad case that the spine[n-2] == spine[1]
                    int w=last-2; 
                    while((w > 1) && (spines[1].equals(spines[w]))) 
                        w--;
                    if (w > 1) {
                        y[0].sub(spines[1],spines[w]); 
                        norm(y[0]); // should never divide by zero here
                    } else
                        // how did this happen?
                        y[0].set(0.0f,0.0f,1.0f);
                }
                y[last] = new Vector3f(y[0]);
            } else {
                y[0] = new Vector3f();
                y[last] = new Vector3f();
                y[0].sub(spines[1],spines[0]);
                try {
                    norm(y[0]);
                } catch ( ArithmeticException ae ) {
                    int w=2;
                    while ((w < last) && (spines[0].equals(spines[w]))) 
                        w++;
                    if (w < last) {
                        y[0].sub(spines[w],spines[0]);
                        norm(y[0]); // should not divide by zero here
                    } else 
                        y[0].set(0.0f,0.0f,1.0f);
                }
                y[last] = new Vector3f();
                y[last].sub(spines[last],spines[last-1]);
                try {
                    norm(y[last]);
                } catch ( ArithmeticException ae ) {
                    int w=last-2;
                    while ((w > -1) && (spines[last].equals(spines[w]))) 
                        w--;
                    if (w > -1)  {
                        y[last].sub(spines[last],spines[w]);
                        norm(y[last]);
                    } else
                        y[last].set(0.0f,0.0f,1.0f);
                }
            }
            // now z axis for each spine
            // first all except first and last  
                
            boolean recheck = false;
            for ( int i = 1; i < last; i++ ) {
                u = new Vector3f();
                v = new Vector3f();
                z[i] = new Vector3f();
                u.sub(spines[i-1],spines[i]);
                v.sub(spines[i+1],spines[i]);
                // spec seems backwards on u and v
                // shouldn't it be z[i].cross(u,v)???
                //z[i].cross(v,u);
    //--> z[i].cross(u,v); is correct <<check this>>
    // Modified by Pasi Paasiala
                z[i].cross(u,v);
                try {
                    norm(z[i]);
                } catch ( ArithmeticException ae ) {    
                    recheck=true;
                }
            }
            if ( closed ) {
                z[0] = z[last] = new Vector3f();
                u = new Vector3f();
                v = new Vector3f();
                u.sub(spines[last-1],spines[0]);
                v.sub(spines[1],spines[0]);
                try {
                    z[0].cross(u,v);
                } catch ( ArithmeticException ae ) {
                    recheck=true;
                }
            } else { // not closed
                z[0] = new Vector3f(z[1]);
                z[last] = new Vector3f(z[last-1]);
            }
            if ( recheck ) { // found adjacent collinear spines 
                // first z has no length ?
                if ( hardDebug ) 
                    System.out.println("rechecking, found adjacent collinear spines");
                if ( z[0].dot(z[0]) == 0.0f ) {
                    for ( int i = 1; i < spines.length; i++ ) {
                        if ( z[i].dot(z[i]) > 0.0f ) 
                            z[0] = new Vector3f(z[i]);
                    } 
                    // test again could be most degenerate of cases
                    if ( z[0].dot(z[0]) == 0.0f ) 
                        z[0] = new Vector3f(0.0f,0.0f,1.0f);
                }

                // check rest of z's
                for ( int i = 1; i < last+1; i++ ) {
                    if ( z[i].dot(z[i]) == 0.0f )
                        z[i] = new Vector3f(z[i-1]);
                }
            }
            // finally, do a neighbor comparison
            // and evaluate the x's
            for ( int i = 0; i < spines.length; i++ ) {
                if ( i > 0 ) 
                    if ( z[i].dot(z[i-1]) < 0.0f ) 
                       z[i].negate();

                // at this point, y and z should be nice
                x[i] = new Vector3f();
    //Original was: x[i].cross(z[i],y[i]); <<check this>>
                //but it doesn't result in right handed coordinates
    // Modified by Pasi Paasiala
                x[i].cross(y[i],z[i]);
                try {
                    norm(x[i]);
                } catch ( ArithmeticException ae ) {
                    // this should not happen
                    ae.printStackTrace();
                }
                if( hardDebug ) System.out.println("x["+i+"] "+x[i]);
            }
        }


        // should now have orthonormal vectors for each
        // spine. create the rotation matrix with scale for
        // each spine. spec is unclear whether a twist imparted
        // at one of the spines is inherited by its "children"
        // so assume not.

        // also, the order looks like SxTxRscpxRo , ie ,
        // the spec doc looks suspect, double check

        Matrix3f m = new Matrix3f();
        transforms = new Transform3D[spines.length];

        for ( int i = 0; i < spines.length; i++ ) {
            rotations[i] = new Matrix3f();
            if ( hardDebug )
      {
                                Vector3f xd = new Vector3f(spines[i]);
        xd.add(x[i]);
                                Vector3f yd = new Vector3f(spines[i]);
        yd.add(y[i]);
                                Vector3f zd = new Vector3f(spines[i]);
        zd.add(z[i]);
        System.out.println("\northos (ABS) "+
                i+" "+xd+" "+yd+" "+zd+" "+orientations[i]);
        System.out.println("orthos "+
                i+" "+x[i]+" "+y[i]+" "+z[i]+" "+orientations[i]);
        }
                        // Original had setRow. This is correct <<check this>>
      // Modified by Pasi Paasiala
            rotations[i].setColumn(0,x[i]);
            rotations[i].setColumn(1,y[i]);
            rotations[i].setColumn(2,z[i]);
  }

        Matrix3f[] correctionRotations = createCorrectionRotations(z);

        // Create the transforms
        for ( int i = 0; i < spines.length; i++ ) {
        rotations[i].mul(correctionRotations[i]);
            m.set(orientations[i]);
            rotations[i].mul(m);
            transforms[i]=new Transform3D();
            transforms[i].setScale(new Vector3d(scales[i]));
            transforms[i].setTranslation(new Vector3d(spines[i]));
            transforms[i].setRotation(rotations[i]);
        }

    }

  /**
  * Creates a rotation for each spine point to avoid twisting of the profile
  * when the orientation of SCP changes.
  * @author Pasi Paasiala
  * @param z the vector containing the z unit vectors for each spine point
  */
        private Matrix3f[] createCorrectionRotations(Vector3f[] z)
  {
    Matrix3f[] correctionRotations = new Matrix3f[spines.length];
    correctionRotations[0] = new Matrix3f();
    correctionRotations[0].setIdentity();
    AxisAngle4f checkAngle = new AxisAngle4f();
    // testPoint is used to find the angle that gives the smallest distance
    // between the previous and current rotation. Find a point that is not
    // in the origin.
    Point3f testPoint = crossSectionPts[0];
    for (int i = 0; i < crossSectionPts.length; i++)
    {
      if (crossSectionPts[i].x != 0 || crossSectionPts[i].z != 0)
      {
        testPoint = crossSectionPts[i];
        break;
      }
    }

    // Fix the orientations by using the angle between previous z and current z
    for (int i = 1; i < spines.length; i++)
    {
      float angle = z[i].angle(z[i-1]);
      correctionRotations[i] = correctionRotations[i-1];
      if (angle != 0)
      {
        correctionRotations[i] = new Matrix3f(correctionRotations[i-1]);
        Point3f previous = new Point3f();
  //                    Point3f previous = testPoint;
        // Test with negative angle:
        Matrix3f previousRotation = new Matrix3f(rotations[i-1]);
        previousRotation.mul(correctionRotations[i-1]);
        previousRotation.transform(testPoint, previous);
        Matrix3f delta = new Matrix3f();
        delta.setIdentity();
        delta.rotY(-angle);
        correctionRotations[i].mul(delta);
        Matrix3f negativeRotation = new Matrix3f(rotations[i]);
        negativeRotation.mul(correctionRotations[i]);
        Point3f pointNegative = new Point3f();
        negativeRotation.transform(testPoint,pointNegative);
        float distNegative = pointNegative.distance(previous);

        // Test with positive angle
        delta.rotY(angle*2);
        correctionRotations[i].mul(delta);
        Matrix3f positiveRotation = new Matrix3f(rotations[i]);
        positiveRotation.mul(correctionRotations[i]);
        Point3f pointPositive = new Point3f();
        positiveRotation.transform(pointPositive);
        float distPositive = pointPositive.distance(previous);

        if (distPositive > distNegative)
        {
          // Reset correctionRotations to negative angle
          delta.rotY(-angle*2);
          correctionRotations[i].mul(delta);
        }

        if (hardDebug)
        {
          System.out.println("i = " + i + " Angle is " +
          (distPositive > distNegative ? "negative " : "positive ") +
          "\n previous = " + previous +
          "\npointNegative =" + pointNegative + "\npointPositive =" + pointPositive +
          angle + " dist+ = " + distPositive + " dist- = " + distNegative + "\n");
        }

        // Check that the angle is not more than PI.
        // If it is subtract PI from angle
        checkAngle.set(correctionRotations[i]);
        if (((float)Math.PI - checkAngle.angle) < 0.001)
        {
          correctionRotations[i].rotY((float)(checkAngle.angle - Math.PI));
        }
      }
    }
    return correctionRotations;
  }

    // create a list of unique coords ( of Point3f )
    // by applying the transforms to the crossSectionPts
    void createExtrusion() {

        coords = new Point3f[ spines.length * crossSectionPts.length ];
  if (hardDebug)
  {
                System.out.println("Transformations");
        for (int i = 0; i < transforms.length; i++)
    {
                        Matrix3d rotation = new Matrix3d();
      Vector3d location = new Vector3d();
        transforms[i].get(rotation, location);
      System.out.println(rotation.toString() + "\n" + location.toString() + "\n");
    }
  }

        for ( int i = 0; i < spines.length; i++ ) {
            for ( int j = 0; j < crossSectionPts.length; j++ ) {
                int ind = i*(crossSectionPts.length) + j;
                coords[ind] = new Point3f(crossSectionPts[j]);
    if (hardDebug)
    {
        System.out.print("Transforming " + j +" "+ crossSectionPts[j] + " i =" + i);
    }
                transforms[i].transform(coords[ind]);
    if (hardDebug)
    {
        System.out.println("Result = " + coords[ind]);
    }
            }
        }
    }

    // wind the coords with indexed connectivity and create
    // stripCounts see page 47 of small bluebook
    void createIndices() {
        int m = 0; // coordIndex length
        int k = crossSectionPts.length;
        int l = coords.length;
        int s = 0;
        int n = 0; // coordIndex count
        if ( endCap.getValue() ) {
           m += k-1;
           s++;
        }
        if ( beginCap.getValue() ) {
           m += k-1;
           s++;
        }
        m += (spines.length-1)*(4*(k-1));
        coordIndex = new int[m];
        if ( hardDebug ) System.out.println("coordIndexSize"+m);
        stripCounts = new int[ s+(spines.length-1)*(k-1) ];
        s=0;
        if ( hardDebug ) System.out.println("stripCounts.length"+stripCounts.length);
        if ( hardDebug ) System.out.println("spines.length"+spines.length);
        // start with extrusion body from bottom
        for ( int i = 0; i < spines.length-1; i++ ) {
            if ( hardDebug ) System.out.println(" i " + i);
            for ( int j = 0; j < k-1; j++ ) {
                if ( hardDebug ) System.out.println(" j " + j);
                // create a quad
                if ( ccw.getValue() ) {
                    if ( hardDebug ) System.out.println("i "+i+" j "+j+" k "+k);
                    coordIndex[n++] = (i*k) + j;
                    if ( hardDebug ) System.out.println((n-1)+" "+((i*k)+(j)));
                    coordIndex[n++] = (i*k) + j + 1;
                    if ( hardDebug ) System.out.println((n-1)+" "+((i*k)+(j+1)));
                    coordIndex[n++] = ((i+1)*k) + j + 1;
                    if ( hardDebug ) System.out.println((n-1)+" "+(((i+1)*k)+(j+1)));
                    coordIndex[n++] = ((i+1)*k) + j;
                    if ( hardDebug ) System.out.println((n-1)+" "+(((i+1)*k)+(j)));
                } else {
                    coordIndex[n++] = (i*k) + j;
                    coordIndex[n++] = ((i+1)*k) + j;
                    coordIndex[n++] = ((i+1)*k) + j + 1;
                    coordIndex[n++] = (i*k) + j + 1;
                }
                stripCounts[s++]=4;
                numTris+=2;
            }
        }
        // add top and bottom
        // note: when switching cw from ccw notice that 
        // the index is off by one, this is ok since there
        // is one extra point in the cross-section, each
        // cap has 2 ways to be drawn
        // also note top and bottom caps are reverse oriented to 
        // each other
        if ( beginCap.getValue() && endCap.getValue() ) {
            int indB = m-(2*(k-1));
            int indE = m-(k-1);
            if ( !ccw.getValue() ) {
                for ( int i = 0; i<k-1; i++ ) 
                    coordIndex[indB++] = i;

                for ( int i = l-1; i>l-k; i-- ) 
                    coordIndex[indE++] = i;
                
            } else {
                for ( int i = k-1; i>0; i-- )
                    coordIndex[indB++] = i;
                for ( int i = 0; i<k-1; i++ )
                    coordIndex[indE++] = l-(k-1)+i;
            }
            stripCounts[s++]=k-1;
            stripCounts[s++]=k-1;
            numTris += k-1; // best guess what gi did?
        } else if ( beginCap.getValue() ) {
            int ind = m-(k-1);
            if ( !ccw.getValue() ) {
                for ( int i = 0; i < k-1; i++ ) 
                    coordIndex[ind++] = i;
            } else { // this is ok since extra x-sectpt give off by one
                for ( int i = k-1; i > 0; i-- ) 
                    coordIndex[ind++] = i;
            }
            stripCounts[s++]=k-1;
            numTris += k-1;
        } else if ( endCap.getValue() ) {
            int ind = m-(k-1);
            if ( ccw.getValue() ) {
                for ( int i = l-(k-1); i<l; i++ )
                    coordIndex[ind++] = i;
            } else {
                for ( int i = l-1; i>l-k; i-- )
                    coordIndex[ind++] = i;
            }
            stripCounts[s++]=k-1;
            numTris += k-1;
        }       
    }

    // the vecmath package was not throwing ArithmeticExceptions as
    // expected from the normalize() method.
    void norm(Vector3f n) {
                        float norml = (float)Math.sqrt(n.x*n.x + n.y*n.y + n.z*n.z);
                        if ( norml == 0.0f ) throw new ArithmeticException();

                        n.x /= norml;
                        n.y /= norml;
                        n.z /= norml;
    }

    public void notifyMethod(String eventInName, double time) {
        if (!eventInName.startsWith("route_")) {
            initImpl(); 
            ((Shape3D)(owner.implNode)).setGeometry(impl);
        }
        else { 
            ((Shape3D)(owner.implNode)).setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
        }
    }   

    void initFields() {

        beginCap.init( this, FieldSpec, Field.FIELD, "beginCap");
        ccw.init( this, FieldSpec, Field.FIELD, "ccw");
        convex.init( this, FieldSpec, Field.FIELD, "convex");
        creaseAngle.init( this, FieldSpec, Field.FIELD, "creaseAngle");
        crossSection.init( this, FieldSpec, Field.EVENT_IN, "crossSection");
        endCap.init( this, FieldSpec, Field.FIELD, "endCap");
        orientation.init( this, FieldSpec, Field.EVENT_IN, "orientation");
        scale.init( this, FieldSpec, Field.EVENT_IN, "scale");
        solid.init( this, FieldSpec, Field.FIELD, "solid");
        spine.init( this, FieldSpec, Field.EVENT_IN, "spine");
    }

    public Object clone() {
        return new Extrusion( loader, (SFBool) beginCap.clone(), (SFBool) ccw.clone(),
            (SFBool)convex.clone(), (SFFloat)creaseAngle.clone(), 
(MFVec2f)crossSection.clone(),
            (SFBool)endCap.clone(), (MFRotation)orientation.clone(), 
(MFVec2f)scale.clone(),
            (SFBool)solid.clone(), (MFVec3f)spine.clone() );
    }

    public String getType() {
        return "Extrusion";
    }

    public boolean getSolid() { return solid.getValue(); }
    public void setOwner(Shape owner) { this.owner = owner; }
}

Reply via email to