Mega Code Archive

 
Categories / Java / 3D Graphics
 

ExBluePrint - illustrate use of background images

// //CLASS //ExBluePrint - illustrate use of background images // //LESSON //Add a Background node to place a background image of a blueprint //behind foreground geometry of a mechanical part. // //SEE ALSO //ExBackgroundImage // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.Component; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Light; import javax.media.j3d.LineAttributes; import javax.media.j3d.Material; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Switch; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TriangleStripArray; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Cylinder; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExBluePrint extends Java3DFrame {   //--------------------------------------------------------------   //  SCENE CONTENT   //--------------------------------------------------------------   //   //  Nodes (updated via menu)   //   private Background background = null;   private Switch shadingSwitch = null;   //   //  Build scene   //   public Group buildScene() {     // Get the current image     ImageComponent2D image = imageComponents[currentImage];     // Build the scene root     Group scene = new Group();     // BEGIN EXAMPLE TOPIC     // Create application bounds     BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,         0.0), // Center         1000.0); // Extent     // Set the background color and its application bounds     background = new Background();     background.setColor(White);     background.setImage(image);     background.setCapability(Background.ALLOW_IMAGE_WRITE);     background.setApplicationBounds(worldBounds);     scene.addChild(background);     // END EXAMPLE TOPIC     // Build foreground geometry     scene.addChild(buildGadget());     return scene;   }   //--------------------------------------------------------------   //  FOREGROUND AND ANNOTATION CONTENT   //--------------------------------------------------------------   //   //  Build a mechanical gadget including a few gears and a   //  shaft going through them.   //   private Group buildGadget() {     if (debug)       System.err.println("  gadget...");     //     //  Create two appearances:     //    wireframeApp: draw as blue wireframe     //    shadedApp: draw as metalic shaded polygons     //     //  Wireframe:     //    no Material - defaults to coloring attributes color     //    polygons as lines, with backfaces     //    thick lines     Appearance wireframeApp = new Appearance();     ColoringAttributes wireframeCatt = new ColoringAttributes();     wireframeCatt.setColor(0.0f, 0.2559f, 0.4213f);     wireframeCatt.setShadeModel(ColoringAttributes.SHADE_FLAT);     wireframeApp.setColoringAttributes(wireframeCatt);     PolygonAttributes wireframePatt = new PolygonAttributes();     wireframePatt.setPolygonMode(PolygonAttributes.POLYGON_LINE);     wireframePatt.setCullFace(PolygonAttributes.CULL_NONE);     wireframeApp.setPolygonAttributes(wireframePatt);     LineAttributes wireframeLatt = new LineAttributes();     wireframeLatt.setLineWidth(2.0f);     wireframeApp.setLineAttributes(wireframeLatt);     //  Shaded:     //    silver material     Appearance shadedApp = new Appearance();     Material shadedMat = new Material();     shadedMat.setAmbientColor(0.30f, 0.30f, 0.30f);     shadedMat.setDiffuseColor(0.30f, 0.30f, 0.50f);     shadedMat.setSpecularColor(0.60f, 0.60f, 0.80f);     shadedMat.setShininess(0.10f);     shadedApp.setMaterial(shadedMat);     ColoringAttributes shadedCatt = new ColoringAttributes();     shadedCatt.setShadeModel(ColoringAttributes.SHADE_GOURAUD);     shadedApp.setColoringAttributes(shadedCatt);     //     //  Create a switch group to hold two versions of the     //  shape: one wireframe, and one shaded     //     Transform3D tr = new Transform3D();     tr.set(new Vector3f(-1.0f, 0.2f, 0.0f));     TransformGroup gadget = new TransformGroup(tr);     shadingSwitch = new Switch();     shadingSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);     Group wireframe = new Group();     Group shaded = new Group();     shadingSwitch.addChild(wireframe);     shadingSwitch.addChild(shaded);     shadingSwitch.setWhichChild(1); // shaded     gadget.addChild(shadingSwitch);     //     //  Build a gear (wireframe and shaded)     //     tr = new Transform3D();     tr.rotY(Math.PI / 2.0);     TransformGroup tg = new TransformGroup(tr);     SpurGear gear = new SpurGearThinBody(24, // tooth count         1.6f, // pitch circle radius         0.3f, // shaft radius         0.08f, // addendum         0.05f, // dedendum         0.3f, // gear thickness         0.28f, // tooth tip thickness         wireframeApp);// appearance     tg.addChild(gear);     wireframe.addChild(tg);     tg = new TransformGroup(tr);     gear = new SpurGearThinBody(24, // tooth count         1.6f, // pitch circle radius         0.3f, // shaft radius         0.08f, // addendum         0.05f, // dedendum         0.3f, // gear thickness         0.28f, // tooth tip thickness         shadedApp); // appearance     tg.addChild(gear);     shaded.addChild(tg);     //     //  Build another gear (wireframe and shaded)     //     tr.rotY(Math.PI / 2.0);     Vector3f trans = new Vector3f(-0.5f, 0.0f, 0.0f);     tr.setTranslation(trans);     tg = new TransformGroup(tr);     gear = new SpurGearThinBody(30, // tooth count         2.0f, // pitch circle radius         0.3f, // shaft radius         0.08f, // addendum         0.05f, // dedendum         0.3f, // gear thickness         0.28f, // tooth tip thickness         wireframeApp);// appearance     tg.addChild(gear);     wireframe.addChild(tg);     tg = new TransformGroup(tr);     gear = new SpurGearThinBody(30, // tooth count         2.0f, // pitch circle radius         0.3f, // shaft radius         0.08f, // addendum         0.05f, // dedendum         0.3f, // gear thickness         0.28f, // tooth tip thickness         shadedApp); // appearance     tg.addChild(gear);     shaded.addChild(tg);     //     //  Build a cylindrical shaft (wireframe and shaded)     //     tr.rotZ(-Math.PI / 2.0);     trans = new Vector3f(1.0f, 0.0f, 0.0f);     tr.setTranslation(trans);     tg = new TransformGroup(tr);     Cylinder cyl = new Cylinder(0.3f, // radius         4.0f, // length         Primitive.GENERATE_NORMALS, // format         16, // radial resolution         1, // length-wise resolution         wireframeApp);// appearance     tg.addChild(cyl);     wireframe.addChild(tg);     tg = new TransformGroup(tr);     cyl = new Cylinder(0.3f, // radius         4.0f, // length         Primitive.GENERATE_NORMALS, // format         16, // radial resolution         1, // length-wise resolution         shadedApp); // appearance     tg.addChild(cyl);     shaded.addChild(tg);     //     //  Build shaft teeth (wireframe and shaded)     //     tr.rotY(Math.PI / 2.0);     trans = new Vector3f(2.05f, 0.0f, 0.0f);     tr.setTranslation(trans);     tg = new TransformGroup(tr);     gear = new SpurGear(12, // tooth count         0.5f, // pitch circle radius         0.3f, // shaft radius         0.05f, // addendum         0.05f, // dedendum         1.5f, // gear thickness         0.8f, // tooth tip thickness         wireframeApp);// appearance     tg.addChild(gear);     wireframe.addChild(tg);     tg = new TransformGroup(tr);     gear = new SpurGear(12, // tooth count         0.5f, // pitch circle radius         0.3f, // shaft radius         0.05f, // addendum         0.05f, // dedendum         1.5f, // gear thickness         0.8f, // tooth tip thickness         shadedApp); // appearance     tg.addChild(gear);     shaded.addChild(tg);     return gadget;   }   //--------------------------------------------------------------   //  USER INTERFACE   //--------------------------------------------------------------   //   //  Main   //   public static void main(String[] args) {     ExBluePrint ex = new ExBluePrint();     ex.initialize(args);     ex.buildUniverse();     ex.showFrame();   }   //  Image menu choices   private NameValue[] images = { new NameValue("None", null),       new NameValue("Blueprint", "blueprint.jpg"), };   private int currentImage = 0;   private ImageComponent2D[] imageComponents;   private CheckboxMenuItem[] imageMenu;   private int currentAppearance = 0;   private CheckboxMenuItem[] appearanceMenu;   //   //  Initialize the GUI (application and applet)   //   public void initialize(String[] args) {     // Initialize the window, menubar, etc.     super.initialize(args);     exampleFrame.setTitle("Java 3D Blueprint Example");     //     //  Add a menubar menu to change parameters     //    (images)     //    --------     //    Wireframe     //    Shaded     //     // Add a menu to select among background and shading options     Menu m = new Menu("Options");     imageMenu = new CheckboxMenuItem[images.length];     for (int i = 0; i < images.length; i++) {       imageMenu[i] = new CheckboxMenuItem(images[i].name);       imageMenu[i].addItemListener(this);       imageMenu[i].setState(false);       m.add(imageMenu[i]);     }     imageMenu[currentImage].setState(true);     m.addSeparator();     appearanceMenu = new CheckboxMenuItem[2];     appearanceMenu[0] = new CheckboxMenuItem("Wireframe");     appearanceMenu[0].addItemListener(this);     appearanceMenu[0].setState(false);     m.add(appearanceMenu[0]);     appearanceMenu[1] = new CheckboxMenuItem("Shaded");     appearanceMenu[1].addItemListener(this);     appearanceMenu[1].setState(true);     m.add(appearanceMenu[1]);     exampleMenuBar.add(m);     // Preload background images     TextureLoader texLoader = null;     imageComponents = new ImageComponent2D[images.length];     String value = null;     for (int i = 0; i < images.length; i++) {       value = (String) images[i].value;       if (value == null) {         imageComponents[i] = null;         continue;       }       texLoader = new TextureLoader(value, this);       imageComponents[i] = texLoader.getImage();     }   }   //   //  Handle checkboxes   //   public void itemStateChanged(ItemEvent event) {     Object src = event.getSource();     // Check if it is an image choice     for (int i = 0; i < imageMenu.length; i++) {       if (src == imageMenu[i]) {         // Update the checkboxes         imageMenu[currentImage].setState(false);         currentImage = i;         imageMenu[currentImage].setState(true);         // Set the background image         ImageComponent2D image = imageComponents[currentImage];         background.setImage(image);         return;       }     }     // Check if it is an appearance choice     if (src == appearanceMenu[0]) {       appearanceMenu[1].setState(false);       shadingSwitch.setWhichChild(0);       return;     }     if (src == appearanceMenu[1]) {       appearanceMenu[0].setState(false);       shadingSwitch.setWhichChild(1);       return;     }     // Handle all other checkboxes     super.itemStateChanged(event);   } } /*  * @(#)SpurGearThinBody.java 1.3 98/02/20 14:29:59  *   * Copyright (c) 1996-1998 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.  */ class SpurGearThinBody extends SpurGear {   /**    * Construct a SpurGearThinBody;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    */   public SpurGearThinBody(int toothCount, float pitchCircleRadius,       float shaftRadius, float addendum, float dedendum,       float gearThickness) {     this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,         gearThickness, gearThickness, 0.25f, null);   }   /**    * Construct a SpurGearThinBody;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param look    *            the gear's appearance    */   public SpurGearThinBody(int toothCount, float pitchCircleRadius,       float shaftRadius, float addendum, float dedendum,       float gearThickness, Appearance look) {     this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,         gearThickness, gearThickness, 0.25f, look);   }   /**    * Construct a SpurGearThinBody;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param toothTipThickness    *            thickness of the tip of the tooth    * @param look    *            the gear's appearance    */   public SpurGearThinBody(int toothCount, float pitchCircleRadius,       float shaftRadius, float addendum, float dedendum,       float gearThickness, float toothTipThickness, Appearance look) {     this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,         gearThickness, toothTipThickness, 0.25f, look);   }   /**    * Construct a SpurGearThinBody;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param toothTipThickness    *            thickness of the tip of the tooth    * @param toothToValleyRatio    *            ratio of tooth valley to circular pitch (must be <= .25)    * @param look    *            the gear's appearance object    */   public SpurGearThinBody(int toothCount, float pitchCircleRadius,       float shaftRadius, float addendum, float dedendum,       float gearThickness, float toothTipThickness,       float toothToValleyAngleRatio, Appearance look) {     this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,         gearThickness, toothTipThickness, 0.25f, look,         0.6f * gearThickness, 0.75f * (pitchCircleRadius - shaftRadius));   }   /**    * Construct a SpurGearThinBody;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param toothTipThickness    *            thickness of the tip of the tooth    * @param toothToValleyRatio    *            ratio of tooth valley to circular pitch (must be <= .25)    * @param look    *            the gear's appearance object    * @param bodyThickness    *            the thickness of the gear body    * @param crossSectionWidth    *            the width of the depressed portion of the gear's body    */   public SpurGearThinBody(int toothCount, float pitchCircleRadius,       float shaftRadius, float addendum, float dedendum,       float gearThickness, float toothTipThickness,       float toothToValleyAngleRatio, Appearance look,       float bodyThickness, float crossSectionWidth) {     super(toothCount, pitchCircleRadius, addendum, dedendum,         toothToValleyAngleRatio);     float diskCrossSectionWidth = (rootRadius - shaftRadius - crossSectionWidth) / 2.0f;     float outerShaftRadius = shaftRadius + diskCrossSectionWidth;     float innerToothRadius = rootRadius - diskCrossSectionWidth;     // Generate the gear's body disks, first by the shaft, then in     // the body and, lastly, by the teeth     addBodyDisks(shaftRadius, outerShaftRadius, gearThickness, look);     addBodyDisks(innerToothRadius, rootRadius, gearThickness, look);     addBodyDisks(outerShaftRadius, innerToothRadius, bodyThickness, look);     // Generate the gear's "shaft" equivalents the two at the teeth     // and the two at the shaft     addCylinderSkins(innerToothRadius, gearThickness, InwardNormals, look);     addCylinderSkins(outerShaftRadius, gearThickness, OutwardNormals, look);     // Generate the gear's interior shaft     addCylinderSkins(shaftRadius, gearThickness, InwardNormals, look);     // Generate the gear's teeth     addTeeth(pitchCircleRadius, rootRadius, outsideRadius, gearThickness,         toothTipThickness, toothToValleyAngleRatio, look);   } } /*  * @(#)SpurGear.java 1.12 98/02/20 14:29:58  *   * Copyright (c) 1996-1998 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.  */ class SpurGear extends Gear {   float toothTopAngleIncrement;   float toothDeclineAngleIncrement;   float rootRadius;   float outsideRadius;   //The angle subtended by the ascending or descending portion of a tooth   float circularToothEdgeAngle;   // The angle subtended by a flat (either a tooth top or a valley   // between teeth   float circularToothFlatAngle;   /**    * internal constructor for SpurGear, used by subclasses to establish    * SpurGear's required state    *     * @return a new spur gear that contains sufficient information to continue    *         building    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param toothToValleyAngleRatio    *            the ratio of the angle subtended by the tooth to the angle    *            subtended by the valley (must be <= .25)    */   SpurGear(int toothCount, float pitchCircleRadius, float addendum,       float dedendum, float toothToValleyAngleRatio) {     super(toothCount);     // The angle about Z subtended by one tooth and its associated valley     circularPitchAngle = (float) (2.0 * Math.PI / (double) toothCount);     // The angle subtended by a flat (either a tooth top or a valley     // between teeth     circularToothFlatAngle = circularPitchAngle * toothToValleyAngleRatio;     //The angle subtended by the ascending or descending portion of a tooth     circularToothEdgeAngle = circularPitchAngle / 2.0f         - circularToothFlatAngle;     // Increment angles     toothTopAngleIncrement = circularToothEdgeAngle;     toothDeclineAngleIncrement = toothTopAngleIncrement         + circularToothFlatAngle;     toothValleyAngleIncrement = toothDeclineAngleIncrement         + circularToothEdgeAngle;     // Differential angles for offsetting to the center of tooth's top     // and valley     toothTopCenterAngle = toothTopAngleIncrement + circularToothFlatAngle         / 2.0f;     valleyCenterAngle = toothValleyAngleIncrement + circularToothFlatAngle         / 2.0f;     // Gear start differential angle. All gears are constructed with the     // center of a tooth at Z-axis angle = 0.     gearStartAngle = -1.0 * toothTopCenterAngle;     // The radial distance to the root and top of the teeth, respectively     rootRadius = pitchCircleRadius - dedendum;     outsideRadius = pitchCircleRadius + addendum;     // Allow this object to spin. etc.     this.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);   }   /**    * Construct a SpurGear;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    */   public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,       float addendum, float dedendum, float gearThickness) {     this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,         gearThickness, gearThickness, 0.25f, null);   }   /**    * Construct a SpurGear;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param look    *            the gear's appearance    */   public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,       float addendum, float dedendum, float gearThickness, Appearance look) {     this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,         gearThickness, gearThickness, 0.25f, look);   }   /**    * Construct a SpurGear;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param toothTipThickness    *            thickness of the tip of the tooth    * @param look    *            the gear's appearance    */   public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,       float addendum, float dedendum, float gearThickness,       float toothTipThickness, Appearance look) {     this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,         gearThickness, toothTipThickness, 0.25f, look);   }   /**    * Construct a SpurGear;    *     * @return a new spur gear that conforms to the input paramters    * @param toothCount    *            number of teeth    * @param pitchCircleRadius    *            radius at center of teeth    * @param shaftRadius    *            radius of hole at center    * @param addendum    *            distance from pitch circle to top of teeth    * @param dedendum    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param toothTipThickness    *            thickness of the tip of the tooth    * @param toothToValleyAngleRatio    *            the ratio of the angle subtended by the tooth to the angle    *            subtended by the valley (must be <= .25)    * @param look    *            the gear's appearance object    */   public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,       float addendum, float dedendum, float gearThickness,       float toothTipThickness, float toothToValleyAngleRatio,       Appearance look) {     this(toothCount, pitchCircleRadius, addendum, dedendum,         toothToValleyAngleRatio);     // Generate the gear's body disks     addBodyDisks(shaftRadius, rootRadius, gearThickness, look);     // Generate the gear's interior shaft     addCylinderSkins(shaftRadius, gearThickness, InwardNormals, look);     // Generate the gear's teeth     addTeeth(pitchCircleRadius, rootRadius, outsideRadius, gearThickness,         toothTipThickness, toothToValleyAngleRatio, look);   }   /**    * Construct a SpurGear's teeth by adding the teeth shape nodes    *     * @param pitchCircleRadius    *            radius at center of teeth    * @param rootRadius    *            distance from pitch circle to top of teeth    * @param outsideRadius    *            distance from pitch circle to root of teeth    * @param gearThickness    *            thickness of the gear    * @param toothTipThickness    *            thickness of the tip of the tooth    * @param toothToValleyAngleRatio    *            the ratio of the angle subtended by the tooth to the angle    *            subtended by the valley (must be <= .25)    * @param look    *            the gear's appearance object    */   void addTeeth(float pitchCircleRadius, float rootRadius,       float outsideRadius, float gearThickness, float toothTipThickness,       float toothToValleyAngleRatio, Appearance look) {     int index;     Shape3D newShape;     // Temporaries that store start angle for each portion of tooth facet     double toothStartAngle, toothTopStartAngle, toothDeclineStartAngle, toothValleyStartAngle, nextToothStartAngle;     // The x and y coordinates at each point of a facet and at each     // point on the gear: at the shaft, the root of the teeth, and     // the outer point of the teeth     float xRoot0, yRoot0;     float xOuter1, yOuter1;     float xOuter2, yOuter2;     float xRoot3, yRoot3;     float xRoot4, yRoot4;     // The z coordinates for the gear     final float frontZ = -0.5f * gearThickness;     final float rearZ = 0.5f * gearThickness;     // The z coordinates for the tooth tip of the gear     final float toothTipFrontZ = -0.5f * toothTipThickness;     final float toothTipRearZ = 0.5f * toothTipThickness;     int toothFacetVertexCount; // #(vertices) per tooth facet     int toothFacetCount; // #(facets) per tooth     int toothFaceTotalVertexCount; // #(vertices) in all teeth     int toothFaceStripCount[] = new int[toothCount];     // per tooth vertex count     int topVertexCount; // #(vertices) for teeth tops     int topStripCount[] = new int[1]; // #(vertices) in strip/strip     // Front and rear facing normals for the teeth faces     Vector3f frontToothNormal = new Vector3f(0.0f, 0.0f, -1.0f);     Vector3f rearToothNormal = new Vector3f(0.0f, 0.0f, 1.0f);     // Normals for teeth tops up incline, tooth top, and down incline     Vector3f leftNormal = new Vector3f(-1.0f, 0.0f, 0.0f);     Vector3f rightNormal = new Vector3f(1.0f, 0.0f, 0.0f);     Vector3f outNormal = new Vector3f(1.0f, 0.0f, 0.0f);     Vector3f inNormal = new Vector3f(-1.0f, 0.0f, 0.0f);     // Temporary variables for storing coordinates and vectors     Point3f coordinate = new Point3f(0.0f, 0.0f, 0.0f);     Point3f tempCoordinate1 = new Point3f(0.0f, 0.0f, 0.0f);     Point3f tempCoordinate2 = new Point3f(0.0f, 0.0f, 0.0f);     Point3f tempCoordinate3 = new Point3f(0.0f, 0.0f, 0.0f);     Vector3f tempVector1 = new Vector3f(0.0f, 0.0f, 0.0f);     Vector3f tempVector2 = new Vector3f(0.0f, 0.0f, 0.0f);     /*      * Construct the gear's front facing teeth facets 0______2 / /\ / / \ / / \      * //___________\ 1 3      */     toothFacetVertexCount = 4;     toothFaceTotalVertexCount = toothFacetVertexCount * toothCount;     for (int i = 0; i < toothCount; i++)       toothFaceStripCount[i] = toothFacetVertexCount;     TriangleStripArray frontGearTeeth = new TriangleStripArray(         toothFaceTotalVertexCount, GeometryArray.COORDINATES             | GeometryArray.NORMALS, toothFaceStripCount);     for (int count = 0; count < toothCount; count++) {       index = count * toothFacetVertexCount;       toothStartAngle = gearStartAngle + circularPitchAngle           * (double) count;       toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;       toothDeclineStartAngle = toothStartAngle           + toothDeclineAngleIncrement;       toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;       xRoot0 = rootRadius * (float) Math.cos(toothStartAngle);       yRoot0 = rootRadius * (float) Math.sin(toothStartAngle);       xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);       yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);       xOuter2 = outsideRadius * (float) Math.cos(toothDeclineStartAngle);       yOuter2 = outsideRadius * (float) Math.sin(toothDeclineStartAngle);       xRoot3 = rootRadius * (float) Math.cos(toothValleyStartAngle);       yRoot3 = rootRadius * (float) Math.sin(toothValleyStartAngle);       tempCoordinate1.set(xRoot0, yRoot0, frontZ);       tempCoordinate2.set(xRoot3, yRoot3, frontZ);       tempVector1.sub(tempCoordinate2, tempCoordinate1);       tempCoordinate2.set(xOuter1, yOuter1, toothTipFrontZ);       tempVector2.sub(tempCoordinate2, tempCoordinate1);       frontToothNormal.cross(tempVector1, tempVector2);       frontToothNormal.normalize();       coordinate.set(xOuter1, yOuter1, toothTipFrontZ);       frontGearTeeth.setCoordinate(index, coordinate);       frontGearTeeth.setNormal(index, frontToothNormal);       coordinate.set(xRoot0, yRoot0, frontZ);       frontGearTeeth.setCoordinate(index + 1, coordinate);       frontGearTeeth.setNormal(index + 1, frontToothNormal);       coordinate.set(xOuter2, yOuter2, toothTipFrontZ);       frontGearTeeth.setCoordinate(index + 2, coordinate);       frontGearTeeth.setNormal(index + 2, frontToothNormal);       coordinate.set(xRoot3, yRoot3, frontZ);       frontGearTeeth.setCoordinate(index + 3, coordinate);       frontGearTeeth.setNormal(index + 3, frontToothNormal);     }     newShape = new Shape3D(frontGearTeeth, look);     this.addChild(newShape);     /*      * Construct the gear's rear facing teeth facets (Using Quads) 1______2 / \ / \ / \      * /____________\ 0 3      */     toothFacetVertexCount = 4;     toothFaceTotalVertexCount = toothFacetVertexCount * toothCount;     QuadArray rearGearTeeth = new QuadArray(toothCount         * toothFacetVertexCount, GeometryArray.COORDINATES         | GeometryArray.NORMALS);     for (int count = 0; count < toothCount; count++) {       index = count * toothFacetVertexCount;       toothStartAngle = gearStartAngle + circularPitchAngle           * (double) count;       toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;       toothDeclineStartAngle = toothStartAngle           + toothDeclineAngleIncrement;       toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;       xRoot0 = rootRadius * (float) Math.cos(toothStartAngle);       yRoot0 = rootRadius * (float) Math.sin(toothStartAngle);       xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);       yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);       xOuter2 = outsideRadius * (float) Math.cos(toothDeclineStartAngle);       yOuter2 = outsideRadius * (float) Math.sin(toothDeclineStartAngle);       xRoot3 = rootRadius * (float) Math.cos(toothValleyStartAngle);       yRoot3 = rootRadius * (float) Math.sin(toothValleyStartAngle);       tempCoordinate1.set(xRoot0, yRoot0, rearZ);       tempCoordinate2.set(xRoot3, yRoot3, rearZ);       tempVector1.sub(tempCoordinate2, tempCoordinate1);       tempCoordinate2.set(xOuter1, yOuter1, toothTipRearZ);       tempVector2.sub(tempCoordinate2, tempCoordinate1);       rearToothNormal.cross(tempVector2, tempVector1);       rearToothNormal.normalize();       coordinate.set(xRoot0, yRoot0, rearZ);       rearGearTeeth.setCoordinate(index, coordinate);       rearGearTeeth.setNormal(index, rearToothNormal);       coordinate.set(xOuter1, yOuter1, toothTipRearZ);       rearGearTeeth.setCoordinate(index + 1, coordinate);       rearGearTeeth.setNormal(index + 1, rearToothNormal);       coordinate.set(xOuter2, yOuter2, toothTipRearZ);       rearGearTeeth.setCoordinate(index + 2, coordinate);       rearGearTeeth.setNormal(index + 2, rearToothNormal);       coordinate.set(xRoot3, yRoot3, rearZ);       rearGearTeeth.setCoordinate(index + 3, coordinate);       rearGearTeeth.setNormal(index + 3, rearToothNormal);     }     newShape = new Shape3D(rearGearTeeth, look);     this.addChild(newShape);     /*      * Construct the gear's top teeth faces (As seen from above) Root0      * Outer1 Outer2 Root3 Root4 (RearZ) 0_______3 2_______5 4_______7      * 6_______9 |0 3| |4 7| |8 11| |12 15| | | | | | | | | | | | | | | | |      * |1_____2| |5_____6| |9____10| |13___14| 1 2 3 4 5 6 7 8 Root0 Outer1      * Outer2 Root3 Root4 (FrontZ)      *       * Quad 0123 uses a left normal Quad 2345 uses an out normal Quad 4567      * uses a right normal Quad 6789 uses an out normal      */     topVertexCount = 8 * toothCount + 2;     topStripCount[0] = topVertexCount;     toothFacetVertexCount = 4;     toothFacetCount = 4;     QuadArray topGearTeeth = new QuadArray(toothCount         * toothFacetVertexCount * toothFacetCount,         GeometryArray.COORDINATES | GeometryArray.NORMALS);     for (int count = 0; count < toothCount; count++) {       index = count * toothFacetCount * toothFacetVertexCount;       toothStartAngle = gearStartAngle + circularPitchAngle           * (double) count;       toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;       toothDeclineStartAngle = toothStartAngle           + toothDeclineAngleIncrement;       toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;       nextToothStartAngle = toothStartAngle + circularPitchAngle;       xRoot0 = rootRadius * (float) Math.cos(toothStartAngle);       yRoot0 = rootRadius * (float) Math.sin(toothStartAngle);       xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);       yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);       xOuter2 = outsideRadius * (float) Math.cos(toothDeclineStartAngle);       yOuter2 = outsideRadius * (float) Math.sin(toothDeclineStartAngle);       xRoot3 = rootRadius * (float) Math.cos(toothValleyStartAngle);       yRoot3 = rootRadius * (float) Math.sin(toothValleyStartAngle);       xRoot4 = rootRadius * (float) Math.cos(nextToothStartAngle);       yRoot4 = rootRadius * (float) Math.sin(nextToothStartAngle);       // Compute normal for quad 1       tempCoordinate1.set(xRoot0, yRoot0, frontZ);       tempCoordinate2.set(xOuter1, yOuter1, toothTipFrontZ);       tempVector1.sub(tempCoordinate2, tempCoordinate1);       leftNormal.cross(frontNormal, tempVector1);       leftNormal.normalize();       // Coordinate labeled 0 in the quad       coordinate.set(xRoot0, yRoot0, rearZ);       topGearTeeth.setCoordinate(index, coordinate);       topGearTeeth.setNormal(index, leftNormal);       // Coordinate labeled 1 in the quad       coordinate.set(tempCoordinate1);       topGearTeeth.setCoordinate(index + 1, coordinate);       topGearTeeth.setNormal(index + 1, leftNormal);       // Coordinate labeled 2 in the quad       topGearTeeth.setCoordinate(index + 2, tempCoordinate2);       topGearTeeth.setNormal(index + 2, leftNormal);       topGearTeeth.setCoordinate(index + 5, tempCoordinate2);       // Coordinate labeled 3 in the quad       coordinate.set(xOuter1, yOuter1, toothTipRearZ);       topGearTeeth.setCoordinate(index + 3, coordinate);       topGearTeeth.setNormal(index + 3, leftNormal);       topGearTeeth.setCoordinate(index + 4, coordinate);       // Compute normal for quad 2       tempCoordinate1.set(xOuter1, yOuter1, toothTipFrontZ);       tempCoordinate2.set(xOuter2, yOuter2, toothTipFrontZ);       tempVector1.sub(tempCoordinate2, tempCoordinate1);       outNormal.cross(frontNormal, tempVector1);       outNormal.normalize();       topGearTeeth.setNormal(index + 4, outNormal);       topGearTeeth.setNormal(index + 5, outNormal);       // Coordinate labeled 4 in the quad       topGearTeeth.setCoordinate(index + 6, tempCoordinate2);       topGearTeeth.setNormal(index + 6, outNormal);       topGearTeeth.setCoordinate(index + 9, tempCoordinate2);       // Coordinate labeled 5 in the quad       coordinate.set(xOuter2, yOuter2, toothTipRearZ);       topGearTeeth.setCoordinate(index + 7, coordinate);       topGearTeeth.setNormal(index + 7, outNormal);       topGearTeeth.setCoordinate(index + 8, coordinate);       // Compute normal for quad 3       tempCoordinate1.set(xOuter2, yOuter2, toothTipFrontZ);       tempCoordinate2.set(xRoot3, yRoot3, frontZ);       tempVector1.sub(tempCoordinate2, tempCoordinate1);       rightNormal.cross(frontNormal, tempVector1);       rightNormal.normalize();       topGearTeeth.setNormal(index + 8, rightNormal);       topGearTeeth.setNormal(index + 9, rightNormal);       // Coordinate labeled 7 in the quad       topGearTeeth.setCoordinate(index + 10, tempCoordinate2);       topGearTeeth.setNormal(index + 10, rightNormal);       topGearTeeth.setCoordinate(index + 13, tempCoordinate2);       // Coordinate labeled 6 in the quad       coordinate.set(xRoot3, yRoot3, rearZ);       topGearTeeth.setCoordinate(index + 11, coordinate);       topGearTeeth.setNormal(index + 11, rightNormal);       topGearTeeth.setCoordinate(index + 12, coordinate);       // Compute normal for quad 4       tempCoordinate1.set(xRoot3, yRoot3, frontZ);       tempCoordinate2.set(xRoot4, yRoot4, frontZ);       tempVector1.sub(tempCoordinate2, tempCoordinate1);       outNormal.cross(frontNormal, tempVector1);       outNormal.normalize();       topGearTeeth.setNormal(index + 12, outNormal);       topGearTeeth.setNormal(index + 13, outNormal);       // Coordinate labeled 9 in the quad       topGearTeeth.setCoordinate(index + 14, tempCoordinate2);       topGearTeeth.setNormal(index + 14, outNormal);       // Coordinate labeled 8 in the quad       coordinate.set(xRoot4, yRoot4, rearZ);       topGearTeeth.setCoordinate(index + 15, coordinate);       topGearTeeth.setNormal(index + 15, outNormal);       // Prepare for the loop by computing the new normal       toothTopStartAngle = nextToothStartAngle + toothTopAngleIncrement;       xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);       yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);       tempCoordinate1.set(xRoot4, yRoot4, toothTipFrontZ);       tempCoordinate2.set(xOuter1, yOuter1, toothTipFrontZ);       tempVector1.sub(tempCoordinate2, tempCoordinate1);       leftNormal.cross(frontNormal, tempVector1);       leftNormal.normalize();     }     newShape = new Shape3D(topGearTeeth, look);     this.addChild(newShape);   } } /*  * @(#)Gear.java 1.5 98/02/20 14:29:55  *   * Copyright (c) 1996-1998 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.  */ class Gear extends javax.media.j3d.TransformGroup {   // Specifiers determining whether to generate outward facing normals or   // inward facing normals.   static final int OutwardNormals = 1;   static final int InwardNormals = -1;   // The number of teeth in the gear   int toothCount;   // Gear start differential angle. All gears are constructed with the   // center of a tooth at Z-axis angle = 0.   double gearStartAngle;   // The Z-rotation angle to place the tooth center at theta = 0   float toothTopCenterAngle;   // The Z-rotation angle to place the valley center at theta = 0   float valleyCenterAngle;   // The angle about Z subtended by one tooth and its associated valley   float circularPitchAngle;   // Increment angles   float toothValleyAngleIncrement;   // Front and rear facing normals for the gear's body   final Vector3f frontNormal = new Vector3f(0.0f, 0.0f, -1.0f);   final Vector3f rearNormal = new Vector3f(0.0f, 0.0f, 1.0f);   Gear(int toothCount) {     this.toothCount = toothCount;   }   void addBodyDisks(float shaftRadius, float bodyOuterRadius,       float thickness, Appearance look) {     int gearBodySegmentVertexCount; // #(segments) per tooth-unit     int gearBodyTotalVertexCount; // #(vertices) in a gear face     int gearBodyStripCount[] = new int[1]; // per strip (1) vertex count     // A ray from the gear center, used in normal calculations     float xDirection, yDirection;     // The x and y coordinates at each point of a facet and at each     // point on the gear: at the shaft, the root of the teeth, and     // the outer point of the teeth     float xRoot0, yRoot0, xShaft0, yShaft0;     float xRoot3, yRoot3, xShaft3, yShaft3;     float xRoot4, yRoot4, xShaft4, yShaft4;     // Temporary variables for storing coordinates and vectors     Point3f coordinate = new Point3f(0.0f, 0.0f, 0.0f);     // Gear start differential angle. All gears are constructed with the     // center of a tooth at Z-axis angle = 0.     double gearStartAngle = -1.0 * toothTopCenterAngle;     // Temporaries that store start angle for each portion of tooth facet     double toothStartAngle, toothTopStartAngle, toothDeclineStartAngle, toothValleyStartAngle, nextToothStartAngle;     Shape3D newShape;     int index;     // The z coordinates for the body disks     final float frontZ = -0.5f * thickness;     final float rearZ = 0.5f * thickness;     /*      * Construct the gear's front body (front facing torus disk) __2__ - | -      * 4 - /| /- / / | /| \ 0\ / | / / > \ / | / | > \ / | / / | \ / ____|/ | >      * \-- --__/ | 1 3 5      *        */     gearBodySegmentVertexCount = 4;     gearBodyTotalVertexCount = 2 + gearBodySegmentVertexCount * toothCount;     gearBodyStripCount[0] = gearBodyTotalVertexCount;     TriangleStripArray frontGearBody = new TriangleStripArray(         gearBodyTotalVertexCount, GeometryArray.COORDINATES             | GeometryArray.NORMALS, gearBodyStripCount);     xDirection = (float) Math.cos(gearStartAngle);     yDirection = (float) Math.sin(gearStartAngle);     xShaft0 = shaftRadius * xDirection;     yShaft0 = shaftRadius * yDirection;     xRoot0 = bodyOuterRadius * xDirection;     yRoot0 = bodyOuterRadius * yDirection;     coordinate.set(xRoot0, yRoot0, frontZ);     frontGearBody.setCoordinate(0, coordinate);     frontGearBody.setNormal(0, frontNormal);     coordinate.set(xShaft0, yShaft0, frontZ);     frontGearBody.setCoordinate(1, coordinate);     frontGearBody.setNormal(1, frontNormal);     for (int count = 0; count < toothCount; count++) {       index = 2 + count * 4;       toothStartAngle = gearStartAngle + circularPitchAngle           * (double) count;       toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;       nextToothStartAngle = toothStartAngle + circularPitchAngle;       xDirection = (float) Math.cos(toothValleyStartAngle);       yDirection = (float) Math.sin(toothValleyStartAngle);       xShaft3 = shaftRadius * xDirection;       yShaft3 = shaftRadius * yDirection;       xRoot3 = bodyOuterRadius * xDirection;       yRoot3 = bodyOuterRadius * yDirection;       xDirection = (float) Math.cos(nextToothStartAngle);       yDirection = (float) Math.sin(nextToothStartAngle);       xShaft4 = shaftRadius * xDirection;       yShaft4 = shaftRadius * yDirection;       xRoot4 = bodyOuterRadius * xDirection;       yRoot4 = bodyOuterRadius * yDirection;       coordinate.set(xRoot3, yRoot3, frontZ);       frontGearBody.setCoordinate(index, coordinate);       frontGearBody.setNormal(index, frontNormal);       coordinate.set(xShaft3, yShaft3, frontZ);       frontGearBody.setCoordinate(index + 1, coordinate);       frontGearBody.setNormal(index + 1, frontNormal);       coordinate.set(xRoot4, yRoot4, frontZ);       frontGearBody.setCoordinate(index + 2, coordinate);       frontGearBody.setNormal(index + 2, frontNormal);       coordinate.set(xShaft4, yShaft4, frontZ);       frontGearBody.setCoordinate(index + 3, coordinate);       frontGearBody.setNormal(index + 3, frontNormal);     }     newShape = new Shape3D(frontGearBody, look);     this.addChild(newShape);     // Construct the gear's rear body (rear facing torus disc)     TriangleStripArray rearGearBody = new TriangleStripArray(         gearBodyTotalVertexCount, GeometryArray.COORDINATES             | GeometryArray.NORMALS, gearBodyStripCount);     xDirection = (float) Math.cos(gearStartAngle);     yDirection = (float) Math.sin(gearStartAngle);     xShaft0 = shaftRadius * xDirection;     yShaft0 = shaftRadius * yDirection;     xRoot0 = bodyOuterRadius * xDirection;     yRoot0 = bodyOuterRadius * yDirection;     coordinate.set(xShaft0, yShaft0, rearZ);     rearGearBody.setCoordinate(0, coordinate);     rearGearBody.setNormal(0, rearNormal);     coordinate.set(xRoot0, yRoot0, rearZ);     rearGearBody.setCoordinate(1, coordinate);     rearGearBody.setNormal(1, rearNormal);     for (int count = 0; count < toothCount; count++) {       index = 2 + count * 4;       toothStartAngle = gearStartAngle + circularPitchAngle           * (double) count;       toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;       nextToothStartAngle = toothStartAngle + circularPitchAngle;       xDirection = (float) Math.cos(toothValleyStartAngle);       yDirection = (float) Math.sin(toothValleyStartAngle);       xShaft3 = shaftRadius * xDirection;       yShaft3 = shaftRadius * yDirection;       xRoot3 = bodyOuterRadius * xDirection;       yRoot3 = bodyOuterRadius * yDirection;       xDirection = (float) Math.cos(nextToothStartAngle);       yDirection = (float) Math.sin(nextToothStartAngle);       xShaft4 = shaftRadius * xDirection;       yShaft4 = shaftRadius * yDirection;       xRoot4 = bodyOuterRadius * xDirection;       yRoot4 = bodyOuterRadius * yDirection;       coordinate.set(xShaft3, yShaft3, rearZ);       rearGearBody.setCoordinate(index, coordinate);       rearGearBody.setNormal(index, rearNormal);       coordinate.set(xRoot3, yRoot3, rearZ);       rearGearBody.setCoordinate(index + 1, coordinate);       rearGearBody.setNormal(index + 1, rearNormal);       coordinate.set(xShaft4, yShaft4, rearZ);       rearGearBody.setCoordinate(index + 2, coordinate);       rearGearBody.setNormal(index + 2, rearNormal);       coordinate.set(xRoot4, yRoot4, rearZ);       rearGearBody.setCoordinate(index + 3, coordinate);       rearGearBody.setNormal(index + 3, rearNormal);     }     newShape = new Shape3D(rearGearBody, look);     this.addChild(newShape);   }   void addCylinderSkins(float shaftRadius, float length, int normalDirection,       Appearance look) {     int insideShaftVertexCount; // #(vertices) for shaft     int insideShaftStripCount[] = new int[1]; // #(vertices) in strip/strip     double toothStartAngle, nextToothStartAngle, toothValleyStartAngle;     // A ray from the gear center, used in normal calculations     float xDirection, yDirection;     // The z coordinates for the body disks     final float frontZ = -0.5f * length;     final float rearZ = 0.5f * length;     // Temporary variables for storing coordinates, points, and vectors     float xShaft3, yShaft3, xShaft4, yShaft4;     Point3f coordinate = new Point3f(0.0f, 0.0f, 0.0f);     Vector3f surfaceNormal = new Vector3f();     Shape3D newShape;     int index;     int firstIndex;     int secondIndex;     /*      * Construct gear's inside shaft cylinder First the tooth's up, flat      * outer, and down distances Second the tooth's flat inner distance      *       * Outward facing vertex order: 0_______2____4 | /| /| | / | / | | / | / |      * |/______|/___| 1 3 5      *       * Inward facing vertex order: 1_______3____5 |\ |\ | | \ | \ | | \ | \ |      * |______\|___\| 0 2 4      */     insideShaftVertexCount = 4 * toothCount + 2;     insideShaftStripCount[0] = insideShaftVertexCount;     TriangleStripArray insideShaft = new TriangleStripArray(         insideShaftVertexCount, GeometryArray.COORDINATES             | GeometryArray.NORMALS, insideShaftStripCount);     xShaft3 = shaftRadius * (float) Math.cos(gearStartAngle);     yShaft3 = shaftRadius * (float) Math.sin(gearStartAngle);     if (normalDirection == OutwardNormals) {       surfaceNormal.set(1.0f, 0.0f, 0.0f);       firstIndex = 1;       secondIndex = 0;     } else {       surfaceNormal.set(-1.0f, 0.0f, 0.0f);       firstIndex = 0;       secondIndex = 1;     }     // Coordinate labeled 0 in the strip     coordinate.set(shaftRadius, 0.0f, frontZ);     insideShaft.setCoordinate(firstIndex, coordinate);     insideShaft.setNormal(firstIndex, surfaceNormal);     // Coordinate labeled 1 in the strip     coordinate.set(shaftRadius, 0.0f, rearZ);     insideShaft.setCoordinate(secondIndex, coordinate);     insideShaft.setNormal(secondIndex, surfaceNormal);     for (int count = 0; count < toothCount; count++) {       index = 2 + count * 4;       toothStartAngle = circularPitchAngle * (double) count;       toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;       nextToothStartAngle = toothStartAngle + circularPitchAngle;       xDirection = (float) Math.cos(toothValleyStartAngle);       yDirection = (float) Math.sin(toothValleyStartAngle);       xShaft3 = shaftRadius * xDirection;       yShaft3 = shaftRadius * yDirection;       if (normalDirection == OutwardNormals)         surfaceNormal.set(xDirection, yDirection, 0.0f);       else         surfaceNormal.set(-xDirection, -yDirection, 0.0f);       // Coordinate labeled 2 in the strip       coordinate.set(xShaft3, yShaft3, frontZ);       insideShaft.setCoordinate(index + firstIndex, coordinate);       insideShaft.setNormal(index + firstIndex, surfaceNormal);       // Coordinate labeled 3 in the strip       coordinate.set(xShaft3, yShaft3, rearZ);       insideShaft.setCoordinate(index + secondIndex, coordinate);       insideShaft.setNormal(index + secondIndex, surfaceNormal);       xDirection = (float) Math.cos(nextToothStartAngle);       yDirection = (float) Math.sin(nextToothStartAngle);       xShaft4 = shaftRadius * xDirection;       yShaft4 = shaftRadius * yDirection;       if (normalDirection == OutwardNormals)         surfaceNormal.set(xDirection, yDirection, 0.0f);       else         surfaceNormal.set(-xDirection, -yDirection, 0.0f);       // Coordinate labeled 4 in the strip       coordinate.set(xShaft4, yShaft4, frontZ);       insideShaft.setCoordinate(index + 2 + firstIndex, coordinate);       insideShaft.setNormal(index + 2 + firstIndex, surfaceNormal);       // Coordinate labeled 5 in the strip       coordinate.set(xShaft4, yShaft4, rearZ);       insideShaft.setCoordinate(index + 2 + secondIndex, coordinate);       insideShaft.setNormal(index + 2 + secondIndex, surfaceNormal);     }     newShape = new Shape3D(insideShaft, look);     this.addChild(newShape);   }   public float getToothTopCenterAngle() {     return toothTopCenterAngle;   }   public float getValleyCenterAngle() {     return valleyCenterAngle;   }   public float getCircularPitchAngle() {     return circularPitchAngle;   } } /**  * The Example class is a base class extended by example applications. The class  * provides basic features to create a top-level frame, add a menubar and  * Canvas3D, build the universe, set up "examine" and "walk" style navigation  * behaviors, and provide hooks so that subclasses can add 3D content to the  * example's universe.  * <P>  * Using this Example class simplifies the construction of example applications,  * enabling the author to focus upon 3D content and not the busywork of creating  * windows, menus, and universes.  *   * @version 1.0, 98/04/16  * @author David R. Nadeau, San Diego Supercomputer Center  */ class Java3DFrame extends Applet implements WindowListener, ActionListener,     ItemListener, CheckboxMenuListener {   //  Navigation types   public final static int Walk = 0;   public final static int Examine = 1;   //  Should the scene be compiled?   private boolean shouldCompile = true;   //  GUI objects for our subclasses   protected Java3DFrame example = null;   protected Frame exampleFrame = null;   protected MenuBar exampleMenuBar = null;   protected Canvas3D exampleCanvas = null;   protected TransformGroup exampleViewTransform = null;   protected TransformGroup exampleSceneTransform = null;   protected boolean debug = false;   //  Private GUI objects and state   private boolean headlightOnOff = true;   private int navigationType = Examine;   private CheckboxMenuItem headlightMenuItem = null;   private CheckboxMenuItem walkMenuItem = null;   private CheckboxMenuItem examineMenuItem = null;   private DirectionalLight headlight = null;   private ExamineViewerBehavior examineBehavior = null;   private WalkViewerBehavior walkBehavior = null;   //--------------------------------------------------------------   //  ADMINISTRATION   //--------------------------------------------------------------   /**    * The main program entry point when invoked as an application. Each example    * application that extends this class must define their own main.    *     * @param args    *            a String array of command-line arguments    */   public static void main(String[] args) {     Java3DFrame ex = new Java3DFrame();     ex.initialize(args);     ex.buildUniverse();     ex.showFrame();   }   /**    * Constructs a new Example object.    *     * @return a new Example that draws no 3D content    */   public Java3DFrame() {     // Do nothing   }   /**    * Initializes the application when invoked as an applet.    */   public void init() {     // Collect properties into String array     String[] args = new String[2];     // NOTE: to be done still...     this.initialize(args);     this.buildUniverse();     this.showFrame();     // NOTE: add something to the browser page?   }   /**    * Initializes the Example by parsing command-line arguments, building an    * AWT Frame, constructing a menubar, and creating the 3D canvas.    *     * @param args    *            a String array of command-line arguments    */   protected void initialize(String[] args) {     example = this;     // Parse incoming arguments     parseArgs(args);     // Build the frame     if (debug)       System.err.println("Building GUI...");     exampleFrame = new Frame();     exampleFrame.setSize(640, 480);     exampleFrame.setTitle("Java 3D Example");     exampleFrame.setLayout(new BorderLayout());     // Set up a close behavior     exampleFrame.addWindowListener(this);     // Create a canvas     exampleCanvas = new Canvas3D(null);     exampleCanvas.setSize(630, 460);     exampleFrame.add("Center", exampleCanvas);     // Build the menubar     exampleMenuBar = this.buildMenuBar();     exampleFrame.setMenuBar(exampleMenuBar);     // Pack     exampleFrame.pack();     exampleFrame.validate();     //    exampleFrame.setVisible( true );   }   /**    * Parses incoming command-line arguments. Applications that subclass this    * class may override this method to support their own command-line    * arguments.    *     * @param args    *            a String array of command-line arguments    */   protected void parseArgs(String[] args) {     for (int i = 0; i < args.length; i++) {       if (args[i].equals("-d"))         debug = true;     }   }   //--------------------------------------------------------------   //  SCENE CONTENT   //--------------------------------------------------------------   /**    * Builds the 3D universe by constructing a virtual universe (via    * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via    * SimpleUniverse). A headlight is added and a set of behaviors initialized    * to handle navigation types.    */   protected void buildUniverse() {     //     //  Create a SimpleUniverse object, which builds:     //     //    - a Locale using the given hi-res coordinate origin     //     //    - a ViewingPlatform which in turn builds:     //          - a MultiTransformGroup with which to move the     //            the ViewPlatform about     //     //          - a ViewPlatform to hold the view     //     //          - a BranchGroup to hold avatar geometry (if any)     //     //          - a BranchGroup to hold view platform     //            geometry (if any)     //     //    - a Viewer which in turn builds:     //          - a PhysicalBody which characterizes the user's     //            viewing preferences and abilities     //     //          - a PhysicalEnvironment which characterizes the     //            user's rendering hardware and software     //     //          - a JavaSoundMixer which initializes sound     //            support within the 3D environment     //     //          - a View which renders the scene into a Canvas3D     //     //  All of these actions could be done explicitly, but     //  using the SimpleUniverse utilities simplifies the code.     //     if (debug)       System.err.println("Building scene graph...");     SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate         // for the origin -         // use default         1, // Number of transforms in MultiTransformGroup         exampleCanvas, // Canvas3D into which to draw         null); // URL for user configuration file - use defaults     //     //  Get the viewer and create an audio device so that     //  sound will be enabled in this content.     //     Viewer viewer = universe.getViewer();     viewer.createAudioDevice();     //     //  Get the viewing platform created by SimpleUniverse.     //  From that platform, get the inner-most TransformGroup     //  in the MultiTransformGroup. That inner-most group     //  contains the ViewPlatform. It is this inner-most     //  TransformGroup we need in order to:     //     //    - add a "headlight" that always aims forward from     //       the viewer     //     //    - change the viewing direction in a "walk" style     //     //  The inner-most TransformGroup's transform will be     //  changed by the walk behavior (when enabled).     //     ViewingPlatform viewingPlatform = universe.getViewingPlatform();     exampleViewTransform = viewingPlatform.getViewPlatformTransform();     //     //  Create a "headlight" as a forward-facing directional light.     //  Set the light's bounds to huge. Since we want the light     //  on the viewer's "head", we need the light within the     //  TransformGroup containing the ViewPlatform. The     //  ViewingPlatform class creates a handy hook to do this     //  called "platform geometry". The PlatformGeometry class is     //  subclassed off of BranchGroup, and is intended to contain     //  a description of the 3D platform itself... PLUS a headlight!     //  So, to add the headlight, create a new PlatformGeometry group,     //  add the light to it, then add that platform geometry to the     //  ViewingPlatform.     //     BoundingSphere allBounds = new BoundingSphere(         new Point3d(0.0, 0.0, 0.0), 100000.0);     PlatformGeometry pg = new PlatformGeometry();     headlight = new DirectionalLight();     headlight.setColor(White);     headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));     headlight.setInfluencingBounds(allBounds);     headlight.setCapability(Light.ALLOW_STATE_WRITE);     pg.addChild(headlight);     viewingPlatform.setPlatformGeometry(pg);     //     //  Create the 3D content BranchGroup, containing:     //     //    - a TransformGroup who's transform the examine behavior     //      will change (when enabled).     //     //    - 3D geometry to view     //     // Build the scene root     BranchGroup sceneRoot = new BranchGroup();     // Build a transform that we can modify     exampleSceneTransform = new TransformGroup();     exampleSceneTransform         .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);     exampleSceneTransform         .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);     exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);     //     //  Build the scene, add it to the transform, and add     //  the transform to the scene root     //     if (debug)       System.err.println("  scene...");     Group scene = this.buildScene();     exampleSceneTransform.addChild(scene);     sceneRoot.addChild(exampleSceneTransform);     //     //  Create a pair of behaviors to implement two navigation     //  types:     //     //    - "examine": a style where mouse drags rotate about     //      the scene's origin as if it is an object under     //      examination. This is similar to the "Examine"     //      navigation type used by VRML browsers.     //     //    - "walk": a style where mouse drags rotate about     //      the viewer's center as if the viewer is turning     //      about to look at a scene they are in. This is     //      similar to the "Walk" navigation type used by     //      VRML browsers.     //     //  Aim the examine behavior at the scene's TransformGroup     //  and add the behavior to the scene root.     //     //  Aim the walk behavior at the viewing platform's     //  TransformGroup and add the behavior to the scene root.     //     //  Enable one (and only one!) of the two behaviors     //  depending upon the current navigation type.     //     examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform         // gorup         // to         // modify         exampleFrame); // Parent frame for cusor changes     examineBehavior.setSchedulingBounds(allBounds);     sceneRoot.addChild(examineBehavior);     walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform         // group to         // modify         exampleFrame); // Parent frame for cusor changes     walkBehavior.setSchedulingBounds(allBounds);     sceneRoot.addChild(walkBehavior);     if (navigationType == Walk) {       examineBehavior.setEnable(false);       walkBehavior.setEnable(true);     } else {       examineBehavior.setEnable(true);       walkBehavior.setEnable(false);     }     //     //  Compile the scene branch group and add it to the     //  SimpleUniverse.     //     if (shouldCompile)       sceneRoot.compile();     universe.addBranchGraph(sceneRoot);     reset();   }   /**    * Builds the scene. Example application subclasses should replace this    * method with their own method to build 3D content.    *     * @return a Group containing 3D content to display    */   public Group buildScene() {     // Build the scene group containing nothing     Group scene = new Group();     return scene;   }   //--------------------------------------------------------------   //  SET/GET METHODS   //--------------------------------------------------------------   /**    * Sets the headlight on/off state. The headlight faces forward in the    * direction the viewer is facing. Example applications that add their own    * lights will typically turn the headlight off. A standard menu item    * enables the headlight to be turned on and off via user control.    *     * @param onOff    *            a boolean turning the light on (true) or off (false)    */   public void setHeadlightEnable(boolean onOff) {     headlightOnOff = onOff;     if (headlight != null)       headlight.setEnable(headlightOnOff);     if (headlightMenuItem != null)       headlightMenuItem.setState(headlightOnOff);   }   /**    * Gets the headlight on/off state.    *     * @return a boolean indicating if the headlight is on or off    */   public boolean getHeadlightEnable() {     return headlightOnOff;   }   /**    * Sets the navigation type to be either Examine or Walk. The Examine    * navigation type sets up behaviors that use mouse drags to rotate and    * translate scene content as if it is an object held at arm's length and    * under examination. The Walk navigation type uses mouse drags to rotate    * and translate the viewer as if they are walking through the content. The    * Examine type is the default.    *     * @param nav    *            either Walk or Examine    */   public void setNavigationType(int nav) {     if (nav == Walk) {       navigationType = Walk;       if (walkMenuItem != null)         walkMenuItem.setState(true);       if (examineMenuItem != null)         examineMenuItem.setState(false);       if (walkBehavior != null)         walkBehavior.setEnable(true);       if (examineBehavior != null)         examineBehavior.setEnable(false);     } else {       navigationType = Examine;       if (walkMenuItem != null)         walkMenuItem.setState(false);       if (examineMenuItem != null)         examineMenuItem.setState(true);       if (walkBehavior != null)         walkBehavior.setEnable(false);       if (examineBehavior != null)         examineBehavior.setEnable(true);     }   }   /**    * Gets the current navigation type, returning either Walk or Examine.    *     * @return either Walk or Examine    */   public int getNavigationType() {     return navigationType;   }   /**    * Sets whether the scene graph should be compiled or not. Normally this is    * always a good idea. For some example applications that use this Example    * framework, it is useful to disable compilation - particularly when nodes    * and node components will need to be made un-live in order to make    * changes. Once compiled, such components can be made un-live, but they are    * still unchangable unless appropriate capabilities have been set.    *     * @param onOff    *            a boolean turning compilation on (true) or off (false)    */   public void setCompilable(boolean onOff) {     shouldCompile = onOff;   }   /**    * Gets whether the scene graph will be compiled or not.    *     * @return a boolean indicating if scene graph compilation is on or off    */   public boolean getCompilable() {     return shouldCompile;   }   //These methods will be replaced   //  Set the view position and direction   public void setViewpoint(Point3f position, Vector3f direction) {     Transform3D t = new Transform3D();     t.set(new Vector3f(position));     exampleViewTransform.setTransform(t);     // how to set direction?   }   //  Reset transforms   public void reset() {     Transform3D trans = new Transform3D();     exampleSceneTransform.setTransform(trans);     trans.set(new Vector3f(0.0f, 0.0f, 10.0f));     exampleViewTransform.setTransform(trans);     setNavigationType(navigationType);   }   //   //  Gets the URL (with file: prepended) for the current directory.   //  This is a terrible hack needed in the Alpha release of Java3D   //  in order to build a full path URL for loading sounds with   //  MediaContainer. When MediaContainer is fully implemented,   //  it should handle relative path names, but not yet.   //   public String getCurrentDirectory() {     // Create a bogus file so that we can query it's path     File dummy = new File("dummy.tmp");     String dummyPath = dummy.getAbsolutePath();     // strip "/dummy.tmp" from end of dummyPath and put into 'path'     if (dummyPath.endsWith(File.separator + "dummy.tmp")) {       int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");       if (index >= 0) {         int pathLength = index + 5; // pre-pend 'file:'         char[] charPath = new char[pathLength];         dummyPath.getChars(0, index, charPath, 5);         String path = new String(charPath, 0, pathLength);         path = "file:" + path.substring(5, pathLength);         return path + File.separator;       }     }     return dummyPath + File.separator;   }   //--------------------------------------------------------------   //  USER INTERFACE   //--------------------------------------------------------------   /**    * Builds the example AWT Frame menubar. Standard menus and their options    * are added. Applications that subclass this class should build their    * menubar additions within their initialize method.    *     * @return a MenuBar for the AWT Frame    */   private MenuBar buildMenuBar() {     // Build the menubar     MenuBar menuBar = new MenuBar();     // File menu     Menu m = new Menu("File");     m.addActionListener(this);     m.add("Exit");     menuBar.add(m);     // View menu     m = new Menu("View");     m.addActionListener(this);     m.add("Reset view");     m.addSeparator();     walkMenuItem = new CheckboxMenuItem("Walk");     walkMenuItem.addItemListener(this);     m.add(walkMenuItem);     examineMenuItem = new CheckboxMenuItem("Examine");     examineMenuItem.addItemListener(this);     m.add(examineMenuItem);     if (navigationType == Walk) {       walkMenuItem.setState(true);       examineMenuItem.setState(false);     } else {       walkMenuItem.setState(false);       examineMenuItem.setState(true);     }     m.addSeparator();     headlightMenuItem = new CheckboxMenuItem("Headlight on/off");     headlightMenuItem.addItemListener(this);     headlightMenuItem.setState(headlightOnOff);     m.add(headlightMenuItem);     menuBar.add(m);     return menuBar;   }   /**    * Shows the application's frame, making it and its menubar, 3D canvas, and    * 3D content visible.    */   public void showFrame() {     exampleFrame.show();   }   /**    * Quits the application.    */   public void quit() {     System.exit(0);   }   /**    * Handles menu selections.    *     * @param event    *            an ActionEvent indicating what menu action requires handling    */   public void actionPerformed(ActionEvent event) {     String arg = event.getActionCommand();     if (arg.equals("Reset view"))       reset();     else if (arg.equals("Exit"))       quit();   }   /**    * Handles checkbox items on a CheckboxMenu. The Example class has none of    * its own, but subclasses may have some.    *     * @param menu    *            which CheckboxMenu needs action    * @param check    *            which CheckboxMenu item has changed    */   public void checkboxChanged(CheckboxMenu menu, int check) {     // None for us   }   /**    * Handles on/off checkbox items on a standard menu.    *     * @param event    *            an ItemEvent indicating what requires handling    */   public void itemStateChanged(ItemEvent event) {     Object src = event.getSource();     boolean state;     if (src == headlightMenuItem) {       state = headlightMenuItem.getState();       headlight.setEnable(state);     } else if (src == walkMenuItem)       setNavigationType(Walk);     else if (src == examineMenuItem)       setNavigationType(Examine);   }   /**    * Handles a window closing event notifying the application that the user    * has chosen to close the application without selecting the "Exit" menu    * item.    *     * @param event    *            a WindowEvent indicating the window is closing    */   public void windowClosing(WindowEvent event) {     quit();   }   public void windowClosed(WindowEvent event) {   }   public void windowOpened(WindowEvent event) {   }   public void windowIconified(WindowEvent event) {   }   public void windowDeiconified(WindowEvent event) {   }   public void windowActivated(WindowEvent event) {   }   public void windowDeactivated(WindowEvent event) {   }   //  Well known colors, positions, and directions   public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);   public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);   public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);   public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);   public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);   public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);   public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);   public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);   public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);   public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);   public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);   public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);   public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);   public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);   public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);   public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);   public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);   public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);   public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);   public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);   public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);   public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);   public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);   public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);   public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);   public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);   public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f); } // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {   public void checkboxChanged(CheckboxMenu menu, int check); } /**  * ExamineViewerBehavior  *   * @version 1.0, 98/04/16  */ /**  * Wakeup on mouse button presses, releases, and mouse movements and generate  * transforms in an "examination style" that enables the user to rotate,  * translation, and zoom an object as if it is held at arm's length. Such an  * examination style is similar to the "Examine" navigation type used by VRML  * browsers.  *   * The behavior maps mouse drags to different transforms depending upon the  * mosue button held down:  *   * Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->  * X-axis rotation  *   * Button 2 (middle) Horizontal movement --> nothing Vertical movement -->  * Z-axis translation  *   * Button 3 (right) Horizontal movement --> X-axis translation Vertical movement  * --> Y-axis translation  *   * To support systems with 2 or 1 mouse buttons, the following alternate  * mappings are supported while dragging with any mouse button held down and  * zero or more keyboard modifiers held down:  *   * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3  *   * The behavior automatically modifies a TransformGroup provided to the  * constructor. The TransformGroup's transform can be set at any time by the  * application or other behaviors to cause the examine rotation and translation  * to be reset.  */ // This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // //    (a) encapsulates all three behaviors into one in order to //        enforce a specific "Examine" symantic // //    (b) supports set/get of the rotation and translation factors //        that control the speed of movement. // //    (c) supports the "Control" modifier as an alternative to the //        "Meta" modifier not present on PC, Mac, and most non-Sun //        keyboards. This makes button3 behavior usable on PCs, //        Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {   // Previous cursor location   protected int previousX = 0;   protected int previousY = 0;   // Saved standard cursor   protected Cursor savedCursor = null;   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    */   public ExamineViewerBehavior() {     super();   }   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    *     * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ExamineViewerBehavior(Component parent) {     super(parent);   }   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    */   public ExamineViewerBehavior(TransformGroup transformGroup) {     super();     subjectTransformGroup = transformGroup;   }   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {     super(parent);     subjectTransformGroup = transformGroup;   }   /**    * Respond to a button1 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public void onButton1(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.HAND_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a rotation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute rotation     // angles with the mapping:     //     //   positive X mouse delta --> positive Y-axis rotation     //   positive Y mouse delta --> positive X-axis rotation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = y - previousY;     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xRotationAngle = deltaY * XRotationFactor;     double yRotationAngle = deltaX * YRotationFactor;     //     // Build transforms     //     transform1.rotX(xRotationAngle);     transform2.rotY(yRotationAngle);     // Get and save the current transform matrix     subjectTransformGroup.getTransform(currentTransform);     currentTransform.get(matrix);     translate.set(matrix.m03, matrix.m13, matrix.m23);     // Translate to the origin, rotate, then translate back     currentTransform.setTranslation(origin);     currentTransform.mul(transform1, currentTransform);     currentTransform.mul(transform2, currentTransform);     currentTransform.setTranslation(translate);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Respond to a button2 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public void onButton2(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a translation     //     // Compute the delta in Y from the previous     // position. Use the delta to compute translation     // distances with the mapping:     //     //   positive Y mouse delta --> positive Y-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaY = y - previousY;     if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double zTranslationDistance = deltaY * ZTranslationFactor;     //     // Build transforms     //     translate.set(0.0, 0.0, zTranslationDistance);     transform1.set(translate);     // Get and save the current transform     subjectTransformGroup.getTransform(currentTransform);     // Translate as needed     currentTransform.mul(transform1, currentTransform);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Respond to a button3 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public void onButton3(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a translation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute translation     // distances with the mapping:     //     //   positive X mouse delta --> positive X-axis translation     //   positive Y mouse delta --> negative Y-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = y - previousY;     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xTranslationDistance = deltaX * XTranslationFactor;     double yTranslationDistance = -deltaY * YTranslationFactor;     //     // Build transforms     //     translate.set(xTranslationDistance, yTranslationDistance, 0.0);     transform1.set(translate);     // Get and save the current transform     subjectTransformGroup.getTransform(currentTransform);     // Translate as needed     currentTransform.mul(transform1, currentTransform);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Respond to an elapsed frames event (assuming subclass has set up a wakeup    * criterion for it).    *     * @param time    *            A WakeupOnElapsedFrames criterion to respond to.    */   public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {     // Can't happen   } } /*  *   * Copyright (c) 1998 David R. Nadeau  *    */ /**  * WalkViewerBehavior is a utility class that creates a "walking style"  * navigation symantic.  *   * The behavior wakes up on mouse button presses, releases, and mouse movements  * and generates transforms in a "walk style" that enables the user to walk  * through a scene, translating and turning about as if they are within the  * scene. Such a walk style is similar to the "Walk" navigation type used by  * VRML browsers.  * <P>  * The behavior maps mouse drags to different transforms depending upon the  * mouse button held down:  * <DL>  * <DT>Button 1 (left)  * <DD>Horizontal movement --> Y-axis rotation  * <DD>Vertical movement --> Z-axis translation  *   * <DT>Button 2 (middle)  * <DD>Horizontal movement --> Y-axis rotation  * <DD>Vertical movement --> X-axis rotation  *   * <DT>Button 3 (right)  * <DD>Horizontal movement --> X-axis translation  * <DD>Vertical movement --> Y-axis translation  * </DL>  *   * To support systems with 2 or 1 mouse buttons, the following alternate  * mappings are supported while dragging with any mouse button held down and  * zero or more keyboard modifiers held down:  * <UL>  * <LI>No modifiers = Button 1  * <LI>ALT = Button 2  * <LI>Meta = Button 3  * <LI>Control = Button 3  * </UL>  * The behavior automatically modifies a TransformGroup provided to the  * constructor. The TransformGroup's transform can be set at any time by the  * application or other behaviors to cause the walk rotation and translation to  * be reset.  * <P>  * While a mouse button is down, the behavior automatically changes the cursor  * in a given parent AWT Component. If no parent Component is given, no cursor  * changes are attempted.  *   * @version 1.0, 98/04/16  * @author David R. Nadeau, San Diego Supercomputer Center  */ class WalkViewerBehavior extends ViewerBehavior {   // This class is inspired by the MouseBehavior, MouseRotate,   // MouseTranslate, and MouseZoom utility behaviors provided with   // Java 3D. This class differs from those utilities in that it:   //   //    (a) encapsulates all three behaviors into one in order to   //        enforce a specific "Walk" symantic   //   //    (b) supports set/get of the rotation and translation factors   //        that control the speed of movement.   //   //    (c) supports the "Control" modifier as an alternative to the   //        "Meta" modifier not present on PC, Mac, and most non-Sun   //        keyboards. This makes button3 behavior usable on PCs,   //        Macs, and other systems with fewer than 3 mouse buttons.   // Previous and initial cursor locations   protected int previousX = 0;   protected int previousY = 0;   protected int initialX = 0;   protected int initialY = 0;   // Deadzone size (delta from initial XY for which no   // translate or rotate action is taken   protected static final int DELTAX_DEADZONE = 10;   protected static final int DELTAY_DEADZONE = 10;   // Keep a set of wakeup criterion for animation-generated   // event types.   protected WakeupCriterion[] mouseAndAnimationEvents = null;   protected WakeupOr mouseAndAnimationCriterion = null;   protected WakeupOr savedMouseCriterion = null;   // Saved standard cursor   protected Cursor savedCursor = null;   /**    * Default Rotation and translation scaling factors for animated movements    * (Button 1 press).    */   public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;   public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;   protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;   protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into a    * TransformGroup that must be set using the setTransformGroup method. The    * cursor will be changed during mouse actions if the parent frame is set    * using the setParentComponent method.    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior() {     super();   }   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into a    * TransformGroup that must be set using the setTransformGroup method. The    * cursor will be changed within the given AWT parent Component during mouse    * drags.    *     * @param parent    *            a parent AWT Component within which the cursor will change    *            during mouse drags    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior(Component parent) {     super(parent);   }   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into the given    * TransformGroup. The cursor will be changed during mouse actions if the    * parent frame is set using the setParentComponent method.    *     * @param transformGroup    *            a TransformGroup whos transform is read and written by the    *            behavior    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior(TransformGroup transformGroup) {     super();     subjectTransformGroup = transformGroup;   }   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into the given    * TransformGroup. The cursor will be changed within the given AWT parent    * Component during mouse drags.    *     * @param transformGroup    *            a TransformGroup whos transform is read and written by the    *            behavior    *     * @param parent    *            a parent AWT Component within which the cursor will change    *            during mouse drags    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {     super(parent);     subjectTransformGroup = transformGroup;   }   /**    * Initializes the behavior.    */   public void initialize() {     super.initialize();     savedMouseCriterion = mouseCriterion; // from parent class     mouseAndAnimationEvents = new WakeupCriterion[4];     mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(         MouseEvent.MOUSE_DRAGGED);     mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(         MouseEvent.MOUSE_PRESSED);     mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(         MouseEvent.MOUSE_RELEASED);     mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);     mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);     // Don't use the above criterion until a button 1 down event   }   /**    * Sets the Y rotation animation scaling factor for Y-axis rotations. This    * scaling factor is used to control the speed of Y rotation when button 1    * is pressed and dragged.    *     * @param factor    *            the double Y rotation scaling factor    */   public void setYRotationAnimationFactor(double factor) {     YRotationAnimationFactor = factor;   }   /**    * Gets the current Y animation rotation scaling factor for Y-axis    * rotations.    *     * @return the double Y rotation scaling factor    */   public double getYRotationAnimationFactor() {     return YRotationAnimationFactor;   }   /**    * Sets the Z animation translation scaling factor for Z-axis translations.    * This scaling factor is used to control the speed of Z translation when    * button 1 is pressed and dragged.    *     * @param factor    *            the double Z translation scaling factor    */   public void setZTranslationAnimationFactor(double factor) {     ZTranslationAnimationFactor = factor;   }   /**    * Gets the current Z animation translation scaling factor for Z-axis    * translations.    *     * @return the double Z translation scaling factor    */   public double getZTranslationAnimationFactor() {     return ZTranslationAnimationFactor;   }   /**    * Responds to an elapsed frames event. Such an event is generated on every    * frame while button 1 is held down. On each call, this method computes new    * Y-axis rotation and Z-axis translation values and writes them to the    * behavior's TransformGroup. The translation and rotation amounts are    * computed based upon the distance between the current cursor location and    * the cursor location when button 1 was pressed. As this distance    * increases, the translation or rotation amount increases.    *     * @param time    *            the WakeupOnElapsedFrames criterion to respond to    */   public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {     //     // Time elapsed while button down: create a rotation and     // a translation.     //     // Compute the delta in X and Y from the initial position to     // the previous position. Multiply the delta times a scaling     // factor to compute an offset to add to the current translation     // and rotation. Use the mapping:     //     //   positive X mouse delta --> negative Y-axis rotation     //   positive Y mouse delta --> positive Z-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     if (buttonPressed != BUTTON1)       return;     int deltaX = previousX - initialX;     int deltaY = previousY - initialY;     double yRotationAngle = -deltaX * YRotationAnimationFactor;     double zTranslationDistance = deltaY * ZTranslationAnimationFactor;     //     // Build transforms     //     transform1.rotY(yRotationAngle);     translate.set(0.0, 0.0, zTranslationDistance);     // Get and save the current transform matrix     subjectTransformGroup.getTransform(currentTransform);     currentTransform.get(matrix);     // Translate to the origin, rotate, then translate back     currentTransform.setTranslation(origin);     currentTransform.mul(transform1, currentTransform);     // Translate back from the origin by the original translation     // distance, plus the new walk translation... but force walk     // to travel on a plane by ignoring the Y component of a     // transformed translation vector.     currentTransform.transform(translate);     translate.x += matrix.m03; // add in existing X translation     translate.y = matrix.m13; // use Y translation     translate.z += matrix.m23; // add in existing Z translation     currentTransform.setTranslation(translate);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);   }   /**    * Responds to a button1 event (press, release, or drag). On a press, the    * method adds a wakeup criterion to the behavior's set, callling for the    * behavior to be awoken on each frame. On a button prelease, this criterion    * is removed from the set.    *     * @param mouseEvent    *            the MouseEvent to respond to    */   public void onButton1(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position and change       // the wakeup criterion to include elapsed time wakeups       // so we can animate.       previousX = x;       previousY = y;       initialX = x;       initialY = y;       // Swap criterion... parent class will not reschedule us       mouseCriterion = mouseAndAnimationCriterion;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.HAND_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: restore original wakeup       // criterion which only includes mouse activity, not       // elapsed time       mouseCriterion = savedMouseCriterion;       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     previousX = x;     previousY = y;   }   /**    * Responds to a button2 event (press, release, or drag). On a press, the    * method records the initial cursor location. On a drag, the difference    * between the current and previous cursor location provides a delta that    * controls the amount by which to rotate in X and Y.    *     * @param mouseEvent    *            the MouseEvent to respond to    */   public void onButton2(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       initialX = x;       initialY = y;       // Change to a "rotate" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a rotation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute rotation     // angles with the mapping:     //     //   positive X mouse delta --> negative Y-axis rotation     //   positive Y mouse delta --> negative X-axis rotation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = 0;     if (Math.abs(y - initialY) > DELTAY_DEADZONE) {       // Cursor has moved far enough vertically to consider       // it intentional, so get it's delta.       deltaY = y - previousY;     }     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xRotationAngle = -deltaY * XRotationFactor;     double yRotationAngle = -deltaX * YRotationFactor;     //     // Build transforms     //     transform1.rotX(xRotationAngle);     transform2.rotY(yRotationAngle);     // Get and save the current transform matrix     subjectTransformGroup.getTransform(currentTransform);     currentTransform.get(matrix);     translate.set(matrix.m03, matrix.m13, matrix.m23);     // Translate to the origin, rotate, then translate back     currentTransform.setTranslation(origin);     currentTransform.mul(transform2, currentTransform);     currentTransform.mul(transform1);     currentTransform.setTranslation(translate);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Responds to a button3 event (press, release, or drag). On a drag, the    * difference between the current and previous cursor location provides a    * delta that controls the amount by which to translate in X and Y.    *     * @param mouseEvent    *            the MouseEvent to respond to    */   public void onButton3(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a translation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute translation     // distances with the mapping:     //     //   positive X mouse delta --> positive X-axis translation     //   positive Y mouse delta --> negative Y-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = y - previousY;     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xTranslationDistance = deltaX * XTranslationFactor;     double yTranslationDistance = -deltaY * YTranslationFactor;     //     // Build transforms     //     translate.set(xTranslationDistance, yTranslationDistance, 0.0);     transform1.set(translate);     // Get and save the current transform     subjectTransformGroup.getTransform(currentTransform);     // Translate as needed     currentTransform.mul(transform1);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   } } // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem's //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener's checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {   // State   protected CheckboxMenuItem[] checks = null;   protected int current = 0;   protected CheckboxMenuListener listener = null;   //  Construct   public CheckboxMenu(String name, NameValue[] items,       CheckboxMenuListener listen) {     this(name, items, 0, listen);   }   public CheckboxMenu(String name, NameValue[] items, int cur,       CheckboxMenuListener listen) {     super(name);     current = cur;     listener = listen;     if (items == null)       return;     checks = new CheckboxMenuItem[items.length];     for (int i = 0; i < items.length; i++) {       checks[i] = new CheckboxMenuItem(items[i].name, false);       checks[i].addItemListener(this);       add(checks[i]);     }     checks[cur].setState(true);   }   //  Handle checkbox changed events   public void itemStateChanged(ItemEvent event) {     Object src = event.getSource();     for (int i = 0; i < checks.length; i++) {       if (src == checks[i]) {         // Update the checkboxes         checks[current].setState(false);         current = i;         checks[current].setState(true);         if (listener != null)           listener.checkboxChanged(this, i);         return;       }     }   }   // Methods to get and set state   public int getCurrent() {     return current;   }   public void setCurrent(int cur) {     if (cur < 0 || cur >= checks.length)       return; // ignore out of range choices     if (checks == null)       return;     checks[current].setState(false);     current = cur;     checks[current].setState(true);   }   public CheckboxMenuItem getSelectedCheckbox() {     if (checks == null)       return null;     return checks[current];   }   public void setSelectedCheckbox(CheckboxMenuItem item) {     if (checks == null)       return;     for (int i = 0; i < checks.length; i++) {       if (item == checks[i]) {         checks[i].setState(false);         current = i;         checks[i].setState(true);       }     }   } } /**  * ViewerBehavior  *   * @version 1.0, 98/04/16  */ /**  * Wakeup on mouse button presses, releases, and mouse movements and generate  * transforms for a transform group. Classes that extend this class impose  * specific symantics, such as "Examine" or "Walk" viewing, similar to the  * navigation types used by VRML browsers.  *   * To support systems with 2 or 1 mouse buttons, the following alternate  * mappings are supported while dragging with any mouse button held down and  * zero or more keyboard modifiers held down:  *   * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3  *   * The behavior automatically modifies a TransformGroup provided to the  * constructor. The TransformGroup's transform can be set at any time by the  * application or other behaviors to cause the viewer's rotation and translation  * to be reset.  */ // This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // //    (a) encapsulates all three behaviors into one in order to //        enforce a specific viewing symantic // //    (b) supports set/get of the rotation and translation factors //        that control the speed of movement. // //    (c) supports the "Control" modifier as an alternative to the //        "Meta" modifier not present on PC, Mac, and most non-Sun //        keyboards. This makes button3 behavior usable on PCs, //        Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {   // Keep track of the transform group who's transform we modify   // during mouse motion.   protected TransformGroup subjectTransformGroup = null;   // Keep a set of wakeup criterion for different mouse-generated   // event types.   protected WakeupCriterion[] mouseEvents = null;   protected WakeupOr mouseCriterion = null;   // Track which button was last pressed   protected static final int BUTTONNONE = -1;   protected static final int BUTTON1 = 0;   protected static final int BUTTON2 = 1;   protected static final int BUTTON3 = 2;   protected int buttonPressed = BUTTONNONE;   // Keep a few Transform3Ds for use during event processing. This   // avoids having to allocate new ones on each event.   protected Transform3D currentTransform = new Transform3D();   protected Transform3D transform1 = new Transform3D();   protected Transform3D transform2 = new Transform3D();   protected Matrix4d matrix = new Matrix4d();   protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);   protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);   // Unusual X and Y delta limits.   protected static final int UNUSUAL_XDELTA = 400;   protected static final int UNUSUAL_YDELTA = 400;   protected Component parentComponent = null;   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    */   public ViewerBehavior() {     super();   }   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    *     * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ViewerBehavior(Component parent) {     super();     parentComponent = parent;   }   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    */   public ViewerBehavior(TransformGroup transformGroup) {     super();     subjectTransformGroup = transformGroup;   }   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ViewerBehavior(TransformGroup transformGroup, Component parent) {     super();     subjectTransformGroup = transformGroup;     parentComponent = parent;   }   /**    * Set the transform group modified by the viewer behavior. Setting the    * transform group to null disables the behavior until the transform group    * is again set to an existing group.    *     * @param transformGroup    *            The new transform group to be modified by the behavior.    */   public void setTransformGroup(TransformGroup transformGroup) {     subjectTransformGroup = transformGroup;   }   /**    * Get the transform group modified by the viewer behavior.    */   public TransformGroup getTransformGroup() {     return subjectTransformGroup;   }   /**    * Sets the parent component who's cursor will be changed during mouse    * drags. If no component is given is given to the constructor, or set via    * this method, no cursor changes will be done.    *     * @param parent    *            the AWT Component, such as a Frame, within which cursor    *            changes should take place during mouse drags    */   public void setParentComponent(Component parent) {     parentComponent = parent;   }   /*    * Gets the parent frame within which the cursor changes during mouse drags.    *     * @return the AWT Component, such as a Frame, within which cursor changes    * should take place during mouse drags. Returns null if no parent is set.    */   public Component getParentComponent() {     return parentComponent;   }   /**    * Initialize the behavior.    */   public void initialize() {     // Wakeup when the mouse is dragged or when a mouse button     // is pressed or released.     mouseEvents = new WakeupCriterion[3];     mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);     mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);     mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);     mouseCriterion = new WakeupOr(mouseEvents);     wakeupOn(mouseCriterion);   }   /**    * Process a new wakeup. Interpret mouse button presses, releases, and mouse    * drags.    *     * @param criteria    *            The wakeup criteria causing the behavior wakeup.    */   public void processStimulus(Enumeration criteria) {     WakeupCriterion wakeup = null;     AWTEvent[] event = null;     int whichButton = BUTTONNONE;     // Process all pending wakeups     while (criteria.hasMoreElements()) {       wakeup = (WakeupCriterion) criteria.nextElement();       if (wakeup instanceof WakeupOnAWTEvent) {         event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();         // Process all pending events         for (int i = 0; i < event.length; i++) {           if (event[i].getID() != MouseEvent.MOUSE_PRESSED               && event[i].getID() != MouseEvent.MOUSE_RELEASED               && event[i].getID() != MouseEvent.MOUSE_DRAGGED)             // Ignore uninteresting mouse events             continue;           //           // Regretably, Java event handling (or perhaps           // underlying OS event handling) doesn't always           // catch button bounces (redundant presses and           // releases), or order events so that the last           // drag event is delivered before a release.           // This means we can get stray events that we           // filter out here.           //           if (event[i].getID() == MouseEvent.MOUSE_PRESSED               && buttonPressed != BUTTONNONE)             // Ignore additional button presses until a release             continue;           if (event[i].getID() == MouseEvent.MOUSE_RELEASED               && buttonPressed == BUTTONNONE)             // Ignore additional button releases until a press             continue;           if (event[i].getID() == MouseEvent.MOUSE_DRAGGED               && buttonPressed == BUTTONNONE)             // Ignore drags until a press             continue;           MouseEvent mev = (MouseEvent) event[i];           int modifiers = mev.getModifiers();           //           // Unfortunately, the underlying event handling           // doesn't do a "grab" operation when a mouse button           // is pressed. This means that once a button is           // pressed, if another mouse button or a keyboard           // modifier key is pressed, the delivered mouse event           // will show that a different button is being held           // down. For instance:           //           // Action Event           //  Button 1 press Button 1 press           //  Drag with button 1 down Button 1 drag           //  ALT press -           //  Drag with ALT & button 1 down Button 2 drag           //  Button 1 release Button 2 release           //           // The upshot is that we can get a button press           // without a matching release, and the button           // associated with a drag can change mid-drag.           //           // To fix this, we watch for an initial button           // press, and thenceforth consider that button           // to be the one held down, even if additional           // buttons get pressed, and despite what is           // reported in the event. Only when a button is           // released, do we end such a grab.           //           if (buttonPressed == BUTTONNONE) {             // No button is pressed yet, figure out which             // button is down now and how to direct events             if (((modifiers & InputEvent.BUTTON3_MASK) != 0)                 || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {               // Button 3 activity (META or CTRL down)               whichButton = BUTTON3;             } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {               // Button 2 activity (ALT down)               whichButton = BUTTON2;             } else {               // Button 1 activity (no modifiers down)               whichButton = BUTTON1;             }             // If the event is to press a button, then             // record that that button is now down             if (event[i].getID() == MouseEvent.MOUSE_PRESSED)               buttonPressed = whichButton;           } else {             // Otherwise a button was pressed earlier and             // hasn't been released yet. Assign all further             // events to it, even if ALT, META, CTRL, or             // another button has been pressed as well.             whichButton = buttonPressed;           }           // Distribute the event           switch (whichButton) {           case BUTTON1:             onButton1(mev);             break;           case BUTTON2:             onButton2(mev);             break;           case BUTTON3:             onButton3(mev);             break;           default:             break;           }           // If the event is to release a button, then           // record that that button is now up           if (event[i].getID() == MouseEvent.MOUSE_RELEASED)             buttonPressed = BUTTONNONE;         }         continue;       }       if (wakeup instanceof WakeupOnElapsedFrames) {         onElapsedFrames((WakeupOnElapsedFrames) wakeup);         continue;       }     }     // Reschedule us for another wakeup     wakeupOn(mouseCriterion);   }   /**    * Default X and Y rotation factors, and XYZ translation factors.    */   public static final double DEFAULT_XROTATION_FACTOR = 0.02;   public static final double DEFAULT_YROTATION_FACTOR = 0.005;   public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;   public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;   public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;   protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;   protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;   protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;   protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;   protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;   /**    * Set the X rotation scaling factor for X-axis rotations.    *     * @param factor    *            The new scaling factor.    */   public void setXRotationFactor(double factor) {     XRotationFactor = factor;   }   /**    * Get the current X rotation scaling factor for X-axis rotations.    */   public double getXRotationFactor() {     return XRotationFactor;   }   /**    * Set the Y rotation scaling factor for Y-axis rotations.    *     * @param factor    *            The new scaling factor.    */   public void setYRotationFactor(double factor) {     YRotationFactor = factor;   }   /**    * Get the current Y rotation scaling factor for Y-axis rotations.    */   public double getYRotationFactor() {     return YRotationFactor;   }   /**    * Set the X translation scaling factor for X-axis translations.    *     * @param factor    *            The new scaling factor.    */   public void setXTranslationFactor(double factor) {     XTranslationFactor = factor;   }   /**    * Get the current X translation scaling factor for X-axis translations.    */   public double getXTranslationFactor() {     return XTranslationFactor;   }   /**    * Set the Y translation scaling factor for Y-axis translations.    *     * @param factor    *            The new scaling factor.    */   public void setYTranslationFactor(double factor) {     YTranslationFactor = factor;   }   /**    * Get the current Y translation scaling factor for Y-axis translations.    */   public double getYTranslationFactor() {     return YTranslationFactor;   }   /**    * Set the Z translation scaling factor for Z-axis translations.    *     * @param factor    *            The new scaling factor.    */   public void setZTranslationFactor(double factor) {     ZTranslationFactor = factor;   }   /**    * Get the current Z translation scaling factor for Z-axis translations.    */   public double getZTranslationFactor() {     return ZTranslationFactor;   }   /**    * Respond to a button1 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public abstract void onButton1(MouseEvent mouseEvent);   /**    * Respond to a button2 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public abstract void onButton2(MouseEvent mouseEvent);   /**    * Responed to a button3 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public abstract void onButton3(MouseEvent mouseEvent);   /**    * Respond to an elapsed frames event (assuming subclass has set up a wakeup    * criterion for it).    *     * @param time    *            A WakeupOnElapsedFrames criterion to respond to.    */   public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent); } // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {   public String name;   public Object value;   public NameValue(String n, Object v) {     name = n;     value = v;   } }