ExBluePrint - illustrate use of background images : Background « 3D « Java

Java
1. 2D Graphics GUI
2. 3D
3. Advanced Graphics
4. Ant
5. Apache Common
6. Chart
7. Class
8. Collections Data Structure
9. Data Type
10. Database SQL JDBC
11. Design Pattern
12. Development Class
13. EJB3
14. Email
15. Event
16. File Input Output
17. Game
18. Generics
19. GWT
20. Hibernate
21. I18N
22. J2EE
23. J2ME
24. JDK 6
25. JNDI LDAP
26. JPA
27. JSP
28. JSTL
29. Language Basics
30. Network Protocol
31. PDF RTF
32. Reflection
33. Regular Expressions
34. Scripting
35. Security
36. Servlets
37. Spring
38. Swing Components
39. Swing JFC
40. SWT JFace Eclipse
41. Threads
42. Tiny Application
43. Velocity
44. Web Services SOA
45. XML
Java Tutorial
Java Source Code / Java Documentation
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java » 3D » BackgroundScreenshots 
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.00.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.0f0.2559f0.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.30f0.30f0.30f);
    shadedMat.setDiffuseColor(0.30f0.30f0.50f);
    shadedMat.setSpecularColor(0.60f0.60f0.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.0f0.2f0.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.5f0.0f0.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.0f0.0f0.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.05f0.0f0.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[inew 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[0new CheckboxMenuItem("Wireframe");
    appearanceMenu[0].addItemListener(this);
    appearanceMenu[0].setState(false);
    m.add(appearanceMenu[0]);

    appearanceMenu[1new 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 = (Stringimages[i].value;
      if (value == null) {
        imageComponents[inull;
        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.25fnull);
  }

  /**
   * 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 - crossSectionWidth2.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 / (doubletoothCount);

    // 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.25fnull);
  }

  /**
   * 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.0f0.0f, -1.0f);
    Vector3f rearToothNormal = new Vector3f(0.0f0.0f1.0f);

    // Normals for teeth tops up incline, tooth top, and down incline
    Vector3f leftNormal = new Vector3f(-1.0f0.0f0.0f);
    Vector3f rightNormal = new Vector3f(1.0f0.0f0.0f);
    Vector3f outNormal = new Vector3f(1.0f0.0f0.0f);
    Vector3f inNormal = new Vector3f(-1.0f0.0f0.0f);

    // Temporary variables for storing coordinates and vectors
    Point3f coordinate = new Point3f(0.0f0.0f0.0f);
    Point3f tempCoordinate1 = new Point3f(0.0f0.0f0.0f);
    Point3f tempCoordinate2 = new Point3f(0.0f0.0f0.0f);
    Point3f tempCoordinate3 = new Point3f(0.0f0.0f0.0f);
    Vector3f tempVector1 = new Vector3f(0.0f0.0f0.0f);
    Vector3f tempVector2 = new Vector3f(0.0f0.0f0.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
          (doublecount;
      toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;
      toothDeclineStartAngle = toothStartAngle
          + toothDeclineAngleIncrement;
      toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;

      xRoot0 = rootRadius * (floatMath.cos(toothStartAngle);
      yRoot0 = rootRadius * (floatMath.sin(toothStartAngle);
      xOuter1 = outsideRadius * (floatMath.cos(toothTopStartAngle);
      yOuter1 = outsideRadius * (floatMath.sin(toothTopStartAngle);
      xOuter2 = outsideRadius * (floatMath.cos(toothDeclineStartAngle);
      yOuter2 = outsideRadius * (floatMath.sin(toothDeclineStartAngle);
      xRoot3 = rootRadius * (floatMath.cos(toothValleyStartAngle);
      yRoot3 = rootRadius * (floatMath.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
          (doublecount;
      toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;
      toothDeclineStartAngle = toothStartAngle
          + toothDeclineAngleIncrement;
      toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;

      xRoot0 = rootRadius * (floatMath.cos(toothStartAngle);
      yRoot0 = rootRadius * (floatMath.sin(toothStartAngle);
      xOuter1 = outsideRadius * (floatMath.cos(toothTopStartAngle);
      yOuter1 = outsideRadius * (floatMath.sin(toothTopStartAngle);
      xOuter2 = outsideRadius * (floatMath.cos(toothDeclineStartAngle);
      yOuter2 = outsideRadius * (floatMath.sin(toothDeclineStartAngle);
      xRoot3 = rootRadius * (floatMath.cos(toothValleyStartAngle);
      yRoot3 = rootRadius * (floatMath.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 = * 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
          (doublecount;
      toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;
      toothDeclineStartAngle = toothStartAngle
          + toothDeclineAngleIncrement;
      toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
      nextToothStartAngle = toothStartAngle + circularPitchAngle;

      xRoot0 = rootRadius * (floatMath.cos(toothStartAngle);
      yRoot0 = rootRadius * (floatMath.sin(toothStartAngle);
      xOuter1 = outsideRadius * (floatMath.cos(toothTopStartAngle);
      yOuter1 = outsideRadius * (floatMath.sin(toothTopStartAngle);
      xOuter2 = outsideRadius * (floatMath.cos(toothDeclineStartAngle);
      yOuter2 = outsideRadius * (floatMath.sin(toothDeclineStartAngle);
      xRoot3 = rootRadius * (floatMath.cos(toothValleyStartAngle);
      yRoot3 = rootRadius * (floatMath.sin(toothValleyStartAngle);
      xRoot4 = rootRadius * (floatMath.cos(nextToothStartAngle);
      yRoot4 = rootRadius * (floatMath.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 * (floatMath.cos(toothTopStartAngle);
      yOuter1 = outsideRadius * (floatMath.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.0f0.0f, -1.0f);

  final Vector3f rearNormal = new Vector3f(0.0f0.0f1.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.0f0.0f0.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 = + gearBodySegmentVertexCount * toothCount;
    gearBodyStripCount[0= gearBodyTotalVertexCount;

    TriangleStripArray frontGearBody = new TriangleStripArray(
        gearBodyTotalVertexCount, GeometryArray.COORDINATES
            | GeometryArray.NORMALS, gearBodyStripCount);

    xDirection = (floatMath.cos(gearStartAngle);
    yDirection = (floatMath.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 = + count * 4;
      toothStartAngle = gearStartAngle + circularPitchAngle
          (doublecount;
      toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
      nextToothStartAngle = toothStartAngle + circularPitchAngle;

      xDirection = (floatMath.cos(toothValleyStartAngle);
      yDirection = (floatMath.sin(toothValleyStartAngle);
      xShaft3 = shaftRadius * xDirection;
      yShaft3 = shaftRadius * yDirection;
      xRoot3 = bodyOuterRadius * xDirection;
      yRoot3 = bodyOuterRadius * yDirection;

      xDirection = (floatMath.cos(nextToothStartAngle);
      yDirection = (floatMath.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 = (floatMath.cos(gearStartAngle);
    yDirection = (floatMath.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 = + count * 4;
      toothStartAngle = gearStartAngle + circularPitchAngle
          (doublecount;
      toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
      nextToothStartAngle = toothStartAngle + circularPitchAngle;

      xDirection = (floatMath.cos(toothValleyStartAngle);
      yDirection = (floatMath.sin(toothValleyStartAngle);
      xShaft3 = shaftRadius * xDirection;
      yShaft3 = shaftRadius * yDirection;
      xRoot3 = bodyOuterRadius * xDirection;
      yRoot3 = bodyOuterRadius * yDirection;

      xDirection = (floatMath.cos(nextToothStartAngle);
      yDirection = (floatMath.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.0f0.0f0.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 = * toothCount + 2;
    insideShaftStripCount[0= insideShaftVertexCount;

    TriangleStripArray insideShaft = new TriangleStripArray(
        insideShaftVertexCount, GeometryArray.COORDINATES
            | GeometryArray.NORMALS, insideShaftStripCount);
    xShaft3 = shaftRadius * (floatMath.cos(gearStartAngle);
    yShaft3 = shaftRadius * (floatMath.sin(gearStartAngle);

    if (normalDirection == OutwardNormals) {
      surfaceNormal.set(1.0f0.0f0.0f);
      firstIndex = 1;
      secondIndex = 0;
    else {
      surfaceNormal.set(-1.0f0.0f0.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 = + count * 4;

      toothStartAngle = circularPitchAngle * (doublecount;
      toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
      nextToothStartAngle = toothStartAngle + circularPitchAngle;

      xDirection = (floatMath.cos(toothValleyStartAngle);
      yDirection = (floatMath.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 = (floatMath.cos(nextToothStartAngle);
      yDirection = (floatMath.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 + + firstIndex, coordinate);
      insideShaft.setNormal(index + + firstIndex, surfaceNormal);

      // Coordinate labeled 5 in the strip
      coordinate.set(xShaft4, yShaft4, rearZ);
      insideShaft.setCoordinate(index + + secondIndex, coordinate);
      insideShaft.setNormal(index + + 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(640480);
    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(630460);
    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.00.00.0)100000.0);

    PlatformGeometry pg = new PlatformGeometry();
    headlight = new DirectionalLight();
    headlight.setColor(White);
    headlight.setDirection(new Vector3f(0.0f0.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.0f0.0f10.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.0f1.0f1.0f);

  public final static Color3f Gray = new Color3f(0.7f0.7f0.7f);

  public final static Color3f DarkGray = new Color3f(0.2f0.2f0.2f);

  public final static Color3f Black = new Color3f(0.0f0.0f0.0f);

  public final static Color3f Red = new Color3f(1.0f0.0f0.0f);

  public final static Color3f DarkRed = new Color3f(0.3f0.0f0.0f);

  public final static Color3f Yellow = new Color3f(1.0f1.0f0.0f);

  public final static Color3f DarkYellow = new Color3f(0.3f0.3f0.0f);

  public final static Color3f Green = new Color3f(0.0f1.0f0.0f);

  public final static Color3f DarkGreen = new Color3f(0.0f0.3f0.0f);

  public final static Color3f Cyan = new Color3f(0.0f1.0f1.0f);

  public final static Color3f Blue = new Color3f(0.0f0.0f1.0f);

  public final static Color3f DarkBlue = new Color3f(0.0f0.0f0.3f);

  public final static Color3f Magenta = new Color3f(1.0f0.0f1.0f);

  public final static Vector3f PosX = new Vector3f(1.0f0.0f0.0f);

  public final static Vector3f NegX = new Vector3f(-1.0f0.0f0.0f);

  public final static Vector3f PosY = new Vector3f(0.0f1.0f0.0f);

  public final static Vector3f NegY = new Vector3f(0.0f, -1.0f0.0f);

  public final static Vector3f PosZ = new Vector3f(0.0f0.0f1.0f);

  public final static Vector3f NegZ = new Vector3f(0.0f0.0f, -1.0f);

  public final static Point3f Origin = new Point3f(0.0f0.0f0.0f);

  public final static Point3f PlusX = new Point3f(0.75f0.0f0.0f);

  public final static Point3f MinusX = new Point3f(-0.75f0.0f0.0f);

  public final static Point3f PlusY = new Point3f(0.0f0.75f0.0f);

  public final static Point3f MinusY = new Point3f(0.0f, -0.75f0.0f);

  public final static Point3f PlusZ = new Point3f(0.0f0.0f0.75f);

  public final static Point3f MinusZ = new Point3f(0.0f0.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.00.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[0new WakeupOnAWTEvent(
        MouseEvent.MOUSE_DRAGGED);
    mouseAndAnimationEvents[1new WakeupOnAWTEvent(
        MouseEvent.MOUSE_PRESSED);
    mouseAndAnimationEvents[2new WakeupOnAWTEvent(
        MouseEvent.MOUSE_RELEASED);
    mouseAndAnimationEvents[3new 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.00.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[inew 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 < || 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.00.00.0);

  protected Vector3d translate = new Vector3d(0.00.00.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[0new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
    mouseEvents[1new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
    mouseEvents[2new 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 = (WakeupCriterioncriteria.nextElement();
      if (wakeup instanceof WakeupOnAWTEvent) {
        event = ((WakeupOnAWTEventwakeup).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 = (MouseEventevent[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((WakeupOnElapsedFrameswakeup);
        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;
  }
}


           
       
Related examples in the same category
1. ExBackgroundImage - illustrate use of background images
2. ExBackgroundColor - illustrate use of colored Backgrounds ExBackgroundColor - illustrate use of colored Backgrounds
3. Creating a geometric background in a Java 3DCreating a geometric background in a Java 3D
4. Background GeometryBackground Geometry
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.