/* * @(#)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 (float)Math.PI ) { ca -= (float)Math.PI; } NormalGenerator ng = new NormalGenerator(ca); ng.generateNormals(gi); impl = gi.getGeometryArray(); implObject = impl; implReady = true; } 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 <> 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 <> // Modified by Pasi Paasiala (Pasi.Paasiala@solibri.com) 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]); <> //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 <> // 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; il-k; i-- ) coordIndex[indE++] = i; } else { for ( int i = k-1; i>0; i-- ) coordIndex[indB++] = i; for ( int i = 0; 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); il-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; } }