Java/3D/Background

Материал из Java эксперт
Перейти к: навигация, поиск

Background Geometry

   <source lang="java">

/*

* @(#)BackgroundGeometry.java 1.11 02/10/21 13:37:48
* 
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*  - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*  - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* 
* 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.
* 
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility.
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Material; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.mouse.MouseRotate; import com.sun.j3d.utils.behaviors.mouse.MouseTranslate; import com.sun.j3d.utils.behaviors.mouse.MouseZoom; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; public class BackgroundGeometry extends Applet {

 BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
     100.0);
 private java.net.URL bgImage = null;
 private SimpleUniverse u = null;
 public BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Create a Transformgroup to scale all objects so they
   // appear in the scene.
   TransformGroup objScale = new TransformGroup();
   Transform3D t3d = new Transform3D();
   t3d.setScale(0.4);
   objScale.setTransform(t3d);
   objRoot.addChild(objScale);
   // Create the transform group node and initialize it to the
   // identity. Enable the TRANSFORM_WRITE capability so that
   // our behavior code can modify it at runtime.
   TransformGroup objTrans = new TransformGroup();
   objScale.addChild(objTrans);
   Background bg = new Background();
   bg.setApplicationBounds(bounds);
   BranchGroup backGeoBranch = new BranchGroup();
   Sphere sphereObj = new Sphere(1.0f, Sphere.GENERATE_NORMALS
       | Sphere.GENERATE_NORMALS_INWARD
       | Sphere.GENERATE_TEXTURE_COORDS, 45);
   Appearance backgroundApp = sphereObj.getAppearance();
   backGeoBranch.addChild(sphereObj);
   bg.setGeometry(backGeoBranch);
   objTrans.addChild(bg);
   TextureLoader tex = new TextureLoader(bgImage, new String("RGB"), this);
   if (tex != null)
     backgroundApp.setTexture(tex.getTexture());
   Vector3f tranlation = new Vector3f(2.0f, 0.0f, 0.0f);
   Transform3D modelTransform = new Transform3D();
   Transform3D tmpTransform = new Transform3D();
   double angleInc = Math.PI / 8.0;
   double angle = 0.0;
   int numBoxes = 16;
   float scaleX[] = { 0.1f, 0.2f, 0.2f, 0.3f, 0.2f, 0.1f, 0.2f, 0.3f,
       0.1f, 0.3f, 0.2f, 0.3f, 0.1f, 0.3f, 0.2f, 0.3f };
   float scaleY[] = { 0.3f, 0.4f, 0.3f, 0.4f, 0.3f, 0.4f, 0.3f, 0.4f,
       0.3f, 0.3f, 0.3f, 0.3f, 0.3f, 0.3f, 0.3f, 0.4f };
   float scaleZ[] = { 0.3f, 0.2f, 0.1f, 0.1f, 0.3f, 0.2f, 0.1f, 0.3f,
       0.3f, 0.2f, 0.1f, 0.3f, 0.3f, 0.2f, 0.1f, 0.2f };
   Appearance a1 = new Appearance();
   Color3f eColor = new Color3f(0.0f, 0.0f, 0.0f);
   Color3f sColor = new Color3f(0.5f, 0.5f, 1.0f);
   Color3f oColor = new Color3f(0.5f, 0.5f, 0.3f);
   Material m = new Material(oColor, eColor, oColor, sColor, 100.0f);
   m.setLightingEnable(true);
   a1.setMaterial(m);
   for (int i = 0; i < numBoxes; i++, angle += angleInc) {
     modelTransform.rotY(angle);
     tmpTransform.set(tranlation);
     modelTransform.mul(tmpTransform);
     TransformGroup tgroup = new TransformGroup(modelTransform);
     objTrans.addChild(tgroup);
     tgroup.addChild(new Box(scaleX[i], scaleY[i], scaleZ[i],
         Box.GENERATE_NORMALS, a1));
   }
   // Shine it with two lights.
   Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f);
   Color3f lColor2 = new Color3f(0.2f, 0.2f, 0.1f);
   Vector3f lDir1 = new Vector3f(-1.0f, -1.0f, -1.0f);
   Vector3f lDir2 = new Vector3f(0.0f, 0.0f, -1.0f);
   DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1);
   DirectionalLight lgt2 = new DirectionalLight(lColor2, lDir2);
   lgt1.setInfluencingBounds(bounds);
   lgt2.setInfluencingBounds(bounds);
   objScale.addChild(lgt1);
   objScale.addChild(lgt2);
   return objRoot;
 }
 public BackgroundGeometry() {
 }
 public BackgroundGeometry(java.net.URL bgurl) {
   bgImage = bgurl;
 }
 public void init() {
   if (bgImage == null) {
     // the path to the image for an applet
     try {
       bgImage = new java.net.URL(getCodeBase().toString()
           + "/bg.jpg");
     } catch (java.net.MalformedURLException ex) {
       System.out.println(ex.getMessage());
       System.exit(1);
     }
   }
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   Canvas3D c = new Canvas3D(config);
   add("Center", c);
   BranchGroup scene = createSceneGraph();
   u = new SimpleUniverse(c);
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   u.getViewingPlatform().setNominalViewingTransform();
   TransformGroup viewTrans = u.getViewingPlatform()
       .getViewPlatformTransform();
   // Create the rotate behavior node
   MouseRotate behavior1 = new MouseRotate(viewTrans);
   scene.addChild(behavior1);
   behavior1.setSchedulingBounds(bounds);
   // Create the zoom behavior node
   MouseZoom behavior2 = new MouseZoom(viewTrans);
   scene.addChild(behavior2);
   behavior2.setSchedulingBounds(bounds);
   // Create the translate behavior node
   MouseTranslate behavior3 = new MouseTranslate(viewTrans);
   scene.addChild(behavior3);
   behavior3.setSchedulingBounds(bounds);
   // Let Java 3D perform optimizations on this scene graph.
   scene.rupile();
   u.addBranchGraph(scene);
 }
 public void destroy() {
   u.cleanup();
 }
 public static void main(String argv[]) {
   System.out
       .println("Usage: mouse buttons to rotate, zoom or translate the view platform transform");
   System.out
       .println("       Note that the background geometry only changes with rotation");
   // the path to the image file for an application
   java.net.URL bgurl = null;
   try {
     bgurl = new java.net.URL("file:bg.jpg");
   } catch (java.net.MalformedURLException ex) {
     System.out.println(ex.getMessage());
     System.exit(1);
   }
   new MainFrame(new BackgroundGeometry(bgurl), 750, 750);
 }

}


      </source>
   
  
 
  



Creating a geometric background in a Java 3D

   <source lang="java">

/*

* @(#)BackgroundApp.java 1.1 00/09/22 14:03
* 
* Copyright (c) 1996-2000 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.
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.Window; import java.awt.event.*; import java.awt.GraphicsConfiguration; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.*; import com.sun.j3d.utils.geometry.*; import javax.media.j3d.*; import javax.vecmath.*; import java.awt.event.*; import java.awt.AWTEvent; import java.util.Enumeration; import com.sun.j3d.utils.behaviors.keyboard.*; /*

* BackgroundApp is an example of creating a geometric background in a Java 3D
* program.
* 
* This program creates a geometric background of stars and one constellation.
* Also created in the scene is a grid of green lines. These represent a
* horizontal surface (the ground) in in the local area. It is provided for a
* reference for movement in the scene.
* 
* The viewer can move in this virtual world using the arrow keys. The movement
* is provided by the KeyNavigator class.
* 
* This example also uses a BoundingLeaf object for the application bounds for
* the KeyNavigatorBehavior class. There is a bug kludge in this code. See below
* for more details.
* 
* This program was created as a companion to the Java 3D API tutorial available
* from the Java 3D API homepage.
* 
* Background and BoundingLeaf classes are documented in Chapter 3. Interaction,
* in general, and KeyNavigator class, specifically, are documented in Chapter
* 4.
* 
* The first 200 lines (or so) of code simply create the geometry for the
* background and the rest of the view.
* 
* There is no guarantee that any of the stars nor the constellation is an
* accurate representation of the the view from earth.
*/

public class BackgroundApp extends Applet {

 Shape3D createLand() {
   LineArray landGeom = new LineArray(44, GeometryArray.COORDINATES
       | GeometryArray.COLOR_3);
   float l = -50.0f;
   for (int c = 0; c < 44; c += 4) {
     landGeom.setCoordinate(c + 0, new Point3f(-50.0f, 0.0f, l));
     landGeom.setCoordinate(c + 1, new Point3f(50.0f, 0.0f, l));
     landGeom.setCoordinate(c + 2, new Point3f(l, 0.0f, -50.0f));
     landGeom.setCoordinate(c + 3, new Point3f(l, 0.0f, 50.0f));
     l += 10.0f;
   }
   Color3f c = new Color3f(0.1f, 0.8f, 0.1f);
   for (int i = 0; i < 44; i++)
     landGeom.setColor(i, c);
   return new Shape3D(landGeom);
 }
 public BranchGroup createSceneGraph(SimpleUniverse su) {
   // Create the root of the branch graph
   BranchGroup objRootBG = new BranchGroup();
   Vector3f translate = new Vector3f();
   Transform3D T3D = new Transform3D();
   translate.set(0.0f, -0.3f, 0.0f);
   T3D.setTranslation(translate);
   TransformGroup objRoot = new TransformGroup(T3D);
   objRootBG.addChild(objRoot);
   objRoot.addChild(createLand());
   BoundingLeaf boundingLeaf = new BoundingLeaf(new BoundingSphere());
   PlatformGeometry platformGeom = new PlatformGeometry();
   platformGeom.addChild(boundingLeaf);
   platformGeom.rupile();
   su.getViewingPlatform().setPlatformGeometry(platformGeom);
   KeyNavigatorBehavior keyNavBeh = new KeyNavigatorBehavior(su
       .getViewingPlatform().getViewPlatformTransform());
   keyNavBeh.setSchedulingBoundingLeaf(boundingLeaf);
   objRootBG.addChild(keyNavBeh);
   Background background = new Background();
   background.setApplicationBounds(new BoundingSphere(new Point3d(),
       1000.0));
   background.setGeometry(createBackGraph());
   objRoot.addChild(background);
   AmbientLight ambientLight = new AmbientLight();
   ambientLight.setInfluencingBounds(new BoundingSphere());
   objRootBG.addChild(ambientLight);
   return objRootBG;
 } // end of CreateSceneGraph method
 /////////////////////////////////////////////////////////
 public BranchGroup createBackGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   PointArray starGeom1 = new PointArray(7, PointArray.COORDINATES);
   Appearance starAppear1 = new Appearance();
   starGeom1.setCoordinate(0, new Point3f(0.79483311f, -0.58810995f,
       0.14955615f));
   starGeom1.setCoordinate(1, new Point3f(0.44430932f, -0.55736839f,
       -0.70137505f));
   starGeom1.setCoordinate(2, new Point3f(0.94901367f, -0.30404968f,
       0.08322775f));
   starGeom1.setCoordinate(3, new Point3f(0.68060123f, -0.43044807f,
       0.59287173f));
   starGeom1.setCoordinate(4, new Point3f(-0.11641672f, 0.47273532f,
       0.87348049f));
   starGeom1.setCoordinate(5, new Point3f(-0.10399289f, -0.98059412f,
       0.16619437f));
   starGeom1.setCoordinate(6, new Point3f(0.08024400f, -0.96944100f,
       -0.23182900f));
   PointAttributes point1 = new PointAttributes(4.0f, false);
   starAppear1.setPointAttributes(point1);
   objRoot.addChild(new Shape3D(starGeom1, starAppear1));
   PointArray starGeom2 = new PointArray(18, PointArray.COORDINATES);
   starGeom2.setCoordinate(0,
       new Point3f(0.050844f, -0.992329f, 0.112678f));
   starGeom2.setCoordinate(1, new Point3f(-0.063091f, -0.997672f,
       0.025869f));
   starGeom2.setCoordinate(2,
       new Point3f(0.096706f, -0.980384f, 0.171736f));
   starGeom2.setCoordinate(3,
       new Point3f(-0.562384f, 0.073568f, 0.823595f));
   starGeom2.setCoordinate(4,
       new Point3f(-0.863904f, 0.059045f, 0.500180f));
   starGeom2.setCoordinate(5,
       new Point3f(-0.727033f, 0.304149f, 0.615559f));
   starGeom2.setCoordinate(6,
       new Point3f(-0.724850f, 0.535590f, 0.433281f));
   starGeom2.setCoordinate(7, new Point3f(0.185904f, -0.976907f,
       -0.105311f));
   starGeom2.setCoordinate(8, new Point3f(0.738028f, -0.531886f,
       -0.415221f));
   starGeom2.setCoordinate(9, new Point3f(-0.402152f, 0.392690f,
       -0.827085f));
   starGeom2.setCoordinate(10, new Point3f(-0.020020f, -0.999468f,
       -0.025724f));
   starGeom2.setCoordinate(11, new Point3f(-0.384103f, -0.887075f,
       0.256050f));
   starGeom2.setCoordinate(12, new Point3f(-0.224464f, -0.968946f,
       -0.103720f));
   starGeom2.setCoordinate(13, new Point3f(-0.828880f, -0.397932f,
       -0.393203f));
   starGeom2.setCoordinate(14, new Point3f(-0.010557f, -0.998653f,
       0.050797f));
   starGeom2.setCoordinate(15, new Point3f(-0.282122f, 0.258380f,
       -0.923930f));
   starGeom2.setCoordinate(16, new Point3f(-0.941342f, -0.030364f,
       0.336082f));
   starGeom2
       .setCoordinate(17, new Point3f(0.00057f, -0.99651f, -0.08344f));
   Appearance starAppear2 = new Appearance();
   PointAttributes point2 = new PointAttributes(2.0f, false);
   starAppear2.setPointAttributes(point2);
   objRoot.addChild(new Shape3D(starGeom2, starAppear2));
   PointArray starGeom3 = new PointArray(20, PointArray.COORDINATES);
   starGeom3.setCoordinate(0, new Point3f(0.07292f, -0.98862f, -0.13153f));
   starGeom3.setCoordinate(1, new Point3f(0.23133f, -0.87605f, -0.42309f));
   starGeom3.setCoordinate(2, new Point3f(-0.08215f, -0.64657f, 0.75840f));
   starGeom3.setCoordinate(3, new Point3f(-0.84545f, 0.53398f, 0.00691f));
   starGeom3
       .setCoordinate(4, new Point3f(-0.49365f, -0.83645f, -0.23795f));
   starGeom3.setCoordinate(5, new Point3f(0.06883f, -0.99319f, -0.09396f));
   starGeom3.setCoordinate(6, new Point3f(0.87582f, -0.40662f, 0.25997f));
   starGeom3.setCoordinate(7, new Point3f(-0.09095f, -0.99555f, 0.02467f));
   starGeom3.setCoordinate(8, new Point3f(0.45306f, -0.81575f, -0.35955f));
   starGeom3.setCoordinate(9, new Point3f(0.17669f, -0.97939f, 0.09776f));
   starGeom3.setCoordinate(10, new Point3f(0.27421f, -0.83963f, 0.46884f));
   starGeom3
       .setCoordinate(11, new Point3f(0.32703f, -0.94013f, -0.09584f));
   starGeom3.setCoordinate(12,
       new Point3f(-0.01615f, -0.99798f, -0.06132f));
   starGeom3
       .setCoordinate(13, new Point3f(-0.76665f, 0.45998f, -0.44791f));
   starGeom3
       .setCoordinate(14, new Point3f(-0.91025f, -0.07102f, 0.40791f));
   starGeom3.setCoordinate(15,
       new Point3f(-0.00240f, -0.97104f, -0.23887f));
   starGeom3.setCoordinate(16, new Point3f(0.91936f, -0.39244f, 0.02740f));
   starGeom3.setCoordinate(17, new Point3f(0.18290f, -0.97993f, 0.07920f));
   starGeom3.setCoordinate(18, new Point3f(-0.48755f, 0.61592f, 0.61884f));
   starGeom3
       .setCoordinate(19, new Point3f(-0.89375f, 0.36087f, -0.26626f));
   objRoot.addChild(new Shape3D(starGeom3));
   int[] stripCount = { 10 };
   LineStripArray orion = new LineStripArray(10,
       LineStripArray.COORDINATES, stripCount);
   orion.setCoordinate(0, new Point3f(0.978330f, -0.033900f, 0.204426f));
   orion.setCoordinate(1, new Point3f(0.968007f, -0.167860f, 0.186506f));
   orion.setCoordinate(2, new Point3f(0.981477f, -0.142660f, 0.127873f));
   orion.setCoordinate(3, new Point3f(0.983764f, -0.005220f, 0.179391f));
   orion.setCoordinate(4, new Point3f(0.981112f, 0.110597f, 0.158705f));
   orion.setCoordinate(5, new Point3f(0.967377f, 0.172516f, 0.185523f));
   orion.setCoordinate(6, new Point3f(0.961385f, 0.128845f, 0.243183f));
   orion.setCoordinate(7, new Point3f(0.978330f, -0.033900f, 0.204426f));
   orion.setCoordinate(8, new Point3f(0.981293f, -0.020980f, 0.191375f));
   orion.setCoordinate(9, new Point3f(0.983764f, -0.005220f, 0.179391f));
   objRoot.addChild(new Shape3D(orion));
   objRoot.rupile();
   return objRoot;
 } // end of CreateBackGraph method
 /////////////////////BackgroundApp//////////////////////
 public BackgroundApp() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   Canvas3D canvas3D = new Canvas3D(config);
   canvas3D.setStereoEnable(false);
   add("Center", canvas3D);
   // SimpleUniverse is a Convenience Utility class
   SimpleUniverse simpleU = new SimpleUniverse(canvas3D);
   BranchGroup scene = createSceneGraph(simpleU);
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   simpleU.getViewingPlatform().setNominalViewingTransform();
   simpleU.addBranchGraph(scene);
   new OtherView(simpleU.getLocale()); /* see note below */
 } // end of BackgroundApp (constructor)
 /*
  * This class was created to make the boundingleaf work for this example
  * program. One OtherView object is created just a couple of lines above.
  * Inserting a second frame in the scene makes the BoundingLeaf object work
  * as desired.
  */
 public class OtherView extends Object {
   public TransformGroup vpTrans;
   public OtherView(Locale locale) {
     GraphicsConfiguration config = SimpleUniverse
         .getPreferredConfiguration();
     Canvas3D canvas3D = new Canvas3D(config);
     canvas3D.setStereoEnable(false);
     PhysicalBody body = new PhysicalBody();
     PhysicalEnvironment environment = new PhysicalEnvironment();
     View view = new View();
     view.setPhysicalBody(body);
     view.setPhysicalEnvironment(environment);
     BranchGroup vpRoot = new BranchGroup();
     Transform3D viewT3D = new Transform3D();
     viewT3D.set(new Vector3f(0.0f, 0.0f, 2.0f));
     ViewPlatform vp = new ViewPlatform();
     vpTrans = new TransformGroup(viewT3D);
     vpTrans.addChild(vp);
     vpRoot.addChild(vpTrans);
     view.attachViewPlatform(vp);
     locale.addBranchGraph(vpRoot);
   }
 } // end of OtherView class
 //  The following allows this to be run as an application
 //  as well as an applet
 public static void main(String[] args) {
   System.out
       .println("BackgroundApp.java - a demonstration of placing geometry");
   System.out.println("in the background of a Java 3D scene.");
   System.out
       .println("When the app loads, you can use the arrow keys to move.");
   System.out.println("The Java 3D Tutorial is available on the web at:");
   System.out
       .println("http://java.sun.ru/products/java-media/3D/collateral");
   Frame frame = new MainFrame(new BackgroundApp(), 256, 256);
 } // end of main (method of BackgroundApp)

} // end of class BackgroundApp


      </source>
   
  
 
  



ExBackgroundColor - illustrate use of colored Backgrounds

   <source lang="java">

// //CLASS //ExBackgroundColor - illustrate use of colored Backgrounds // //LESSON //Add a Background node to set the background color. // //SEE ALSO //ExBackgroundImage //ExBackgroundGeometry // //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.ruponent; 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.AmbientLight; 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.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.IndexedTriangleStripArray; import javax.media.j3d.Light; import javax.media.j3d.Link; import javax.media.j3d.Material; import javax.media.j3d.Shape3D; import javax.media.j3d.SharedGroup; import javax.media.j3d.Texture; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; 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.Matrix3f; 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.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 ExBackgroundColor extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private Background background = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current color
   Color3f color = (Color3f) colors[currentColor].value;
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Default to walk navigation
   setNavigationType(Walk);
   // Create the scene group
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create application bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Set the background color and its application bounds
   background = new Background();
   background.setColor(color);
   background.setCapability(Background.ALLOW_COLOR_WRITE);
   background.setApplicationBounds(worldBounds);
   scene.addChild(background);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(new TowerScene(this));
   return scene;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExBackgroundColor ex = new ExBackgroundColor();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  Color menu choices
 private NameValue[] colors = { new NameValue("White", White),
     new NameValue("Dark Gray", DarkGray),
     new NameValue("Black", Black), new NameValue("Dark Red", DarkRed),
     new NameValue("Dark Green", DarkGreen),
     new NameValue("Dark Blue", DarkBlue), };
 private int currentColor = 2;
 private CheckboxMenu colorMenu = null;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Background Color Example");
   //
   //  Add a menubar menu to change node parameters
   //    Color -->
   //
   Menu m = new Menu("Background");
   colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
   m.add(colorMenu);
   exampleMenuBar.add(m);
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void checkboxChanged(CheckboxMenu menu, int check) {
   if (menu == colorMenu) {
     // Change the light color
     currentColor = check;
     Color3f color = (Color3f) colors[check].value;
     background.setColor(color);
     return;
   }
   // Handle all other checkboxes
   super.checkboxChanged(menu, check);
 }

} // //CLASS //TowerScene - shapes and lights for a scene with towers // //DESCRIPTION //This class builds a scene containing a cratered surface, a set of //stone towers, plus appropriate lighting. The scene is used in several //of the examples to provide content to affect with lights, background //colors and images, and so forth. // //SEE ALSO //ExBackgroundColor //ExBackgroundImage //ExBackgroundGeometry // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class TowerScene extends Group {

 private static final double[][] craters = {
 // x,z,radius are in a normalized -1.0 to 1.0 space
     // x z radius depth
     { 0.0, 0.0, 0.7, 0.20 }, { 0.3, 0.3, 0.5, 0.20 },
     { -0.3, 0.1, 0.6, 0.20 }, { -0.2, 0.4, 0.4, 0.20 },
     { -0.9, -0.9, 0.5, 0.20 }, { 0.4, 0.5, 0.3, 0.10 },
     { 0.9, -0.2, 0.4, 0.10 }, { -0.8, 0.1, 0.2, 0.10 },
     { 0.2, 0.7, 0.3, 0.20 }, { 0.5, -0.5, 0.21, 0.20 },
     { 0.8, -0.8, 0.16, 0.10 }, { -0.3, 0.7, 0.23, 0.20 },
     { 0.5, 0.5, 0.22, 0.10 }, { -0.7, 0.8, 0.15, 0.10 },
     { -0.5, -0.3, 0.22, 0.10 }, { 0.2, 0.2, 0.15, 0.10 },
     { 0.1, 0.8, 0.25, 0.20 }, { 0.4, 0.9, 0.28, 0.09 },
     { 0.9, -0.1, 0.23, 0.10 }, { 0.1, -0.0, 0.33, 0.08 },
     { 0.1, -0.9, 0.23, 0.20 }, { -1.0, 0.8, 0.13, 0.15 },
     { -0.9, 0.7, 0.10, 0.15 }, { -0.2, 0.1, 0.10, 0.16 },
     { 1.1, 1.0, 0.12, 0.15 }, { 0.9, 0.5, 0.13, 0.14 },
     { -0.1, -0.1, 0.14, 0.15 }, { -0.5, -0.5, 0.10, 0.13 },
     { 0.1, -0.4, 0.10, 0.15 }, { -0.4, -1.0, 0.25, 0.15 },
     { 0.4, 1.0, 0.25, 0.15 }, };
 public TowerScene(Component observer) {
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Add a few lights
   AmbientLight ambient = new AmbientLight();
   ambient.setEnable(true);
   ambient.setColor(new Color3f(0.2f, 0.2f, 0.2f));
   ambient.setInfluencingBounds(worldBounds);
   addChild(ambient);
   DirectionalLight dir1 = new DirectionalLight();
   dir1.setEnable(true);
   dir1.setColor(new Color3f(1.0f, 0.15f, 0.15f));
   dir1.setDirection(new Vector3f(0.8f, -0.35f, -0.5f));
   dir1.setInfluencingBounds(worldBounds);
   addChild(dir1);
   DirectionalLight dir2 = new DirectionalLight();
   dir2.setEnable(true);
   dir2.setColor(new Color3f(0.15f, 0.15f, 1.0f));
   dir2.setDirection(new Vector3f(-0.7f, -0.35f, 0.5f));
   dir2.setInfluencingBounds(worldBounds);
   addChild(dir2);
   // Load textures
   TextureLoader texLoader = new TextureLoader("moon5.jpg", observer);
   Texture moon = texLoader.getTexture();
   if (moon == null)
     System.err.println("Cannot load moon5.jpg texture");
   else {
     moon.setBoundaryModeS(Texture.WRAP);
     moon.setBoundaryModeT(Texture.WRAP);
     moon.setMinFilter(Texture.NICEST);
     moon.setMagFilter(Texture.NICEST);
     moon.setMipMapMode(Texture.BASE_LEVEL);
     moon.setEnable(true);
   }
   texLoader = new TextureLoader("stonebrk2.jpg", observer);
   Texture stone = texLoader.getTexture();
   if (stone == null)
     System.err.println("Cannot load stonebrk2.jpg texture");
   else {
     stone.setBoundaryModeS(Texture.WRAP);
     stone.setBoundaryModeT(Texture.WRAP);
     stone.setMinFilter(Texture.NICEST);
     stone.setMagFilter(Texture.NICEST);
     stone.setMipMapMode(Texture.BASE_LEVEL);
     stone.setEnable(true);
   }
   //
   //  Build a rough terrain
   //
   Appearance moonApp = new Appearance();
   Material moonMat = new Material();
   moonMat.setAmbientColor(0.5f, 0.5f, 0.5f);
   moonMat.setDiffuseColor(1.0f, 1.0f, 1.0f);
   moonMat.setSpecularColor(0.0f, 0.0f, 0.0f);
   moonApp.setMaterial(moonMat);
   TextureAttributes moonTexAtt = new TextureAttributes();
   moonTexAtt.setTextureMode(TextureAttributes.MODULATE);
   moonTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
   moonApp.setTextureAttributes(moonTexAtt);
   if (moon != null)
     moonApp.setTexture(moon);
   CraterGrid grid = new CraterGrid(50, 50, // grid dimensions
       1.0, 1.0, // grid spacing
       4.0, // height exageration factor
       craters, // grid elevations
       moonApp); // grid appearance
   addChild(grid);
   //
   // Build several towers on the terrain
   //
   SharedGroup tower = new SharedGroup();
   Appearance towerApp = new Appearance();
   Material towerMat = new Material();
   towerMat.setAmbientColor(0.6f, 0.6f, 0.6f);
   towerMat.setDiffuseColor(1.0f, 1.0f, 1.0f);
   towerMat.setSpecularColor(0.0f, 0.0f, 0.0f);
   towerApp.setMaterial(towerMat);
   Transform3D tr = new Transform3D();
   tr.setScale(new Vector3d(4.0, 4.0, 1.0));
   TextureAttributes towerTexAtt = new TextureAttributes();
   towerTexAtt.setTextureMode(TextureAttributes.MODULATE);
   towerTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
   towerTexAtt.setTextureTransform(tr);
   towerApp.setTextureAttributes(towerTexAtt);
   if (stone != null)
     towerApp.setTexture(stone);
   Arch towerShape = new Arch(0.0, // start Phi
       1.571, // end Phi
       2, // nPhi
       0.0, // start Theta
       Math.PI * 2.0, // end Theta
       5, // nTheta
       3.0, // start radius
       8.0, // end radius
       0.0, // start phi thickness
       0.0, // end phi thickness
       towerApp); // appearance
   tower.addChild(towerShape);
   // Place towers
   Matrix3f rot = new Matrix3f();
   rot.setIdentity();
   TransformGroup tg = new TransformGroup(new Transform3D(rot,
       new Vector3d(2.0, -3.0, -8.0), 1.0));
   tg.addChild(new Link(tower));
   addChild(tg);
   tg = new TransformGroup(new Transform3D(rot, new Vector3d(-1.0, -3.0,
       -6.0), 0.5));
   tg.addChild(new Link(tower));
   addChild(tg);
   tg = new TransformGroup(new Transform3D(rot, new Vector3d(5.0, -3.0,
       -6.0), 0.75));
   tg.addChild(new Link(tower));
   addChild(tg);
   tg = new TransformGroup(new Transform3D(rot, new Vector3d(1.0, -3.0,
       -3.0), 0.35));
   tg.addChild(new Link(tower));
   addChild(tg);
 }

} // //CLASS //Arch - generalized arch // //DESCRIPTION //This class builds a generalized arch where incoming parameters //specify the angle range in theta (around the equator of a sphere), //the angle range in phi (north-south), the number of subdivisions //in theta and phi, and optionally radii and outer-to-inner wall //thickness variations as phi varies from its starting value to //its ending value. If the thicknesses are 0.0, then only an outer //surface is created. // //Using this class, you can create spheres with or without inner //surfaces, hemisphers, quarter spheres, and arches stretched or //compressed vertically. // //This is probably not as general as it could be, but it was enough //for the purposes at hand. // //SEE ALSO //ModernFire // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class Arch extends Group {

 // The shape
 private Shape3D arch = null;
 // Construct an arch
 public Arch() {
   // Default to a sphere
   this(0.0, Math.PI / 2.0, 9, 0.0, Math.PI, 17, 1.0, 1.0, 0.0, 0.0,
       new Appearance());
 }
 public Arch(Appearance app) {
   // Default to a sphere
   this(0.0, Math.PI / 2.0, 9, 0.0, Math.PI, 17, 1.0, 1.0, 0.0, 0.0, app);
 }
 public Arch(double startPhi, double endPhi, int nPhi, double startTheta,
     double endTheta, int nTheta, Appearance app) {
   // Default to constant radius, no thickness
   this(startPhi, endPhi, nPhi, startTheta, endTheta, nTheta, 1.0, 1.0,
       0.0, 0.0, app);
 }
 public Arch(double startPhi, double endPhi, int nPhi, double startTheta,
     double endTheta, int nTheta, double startPhiRadius,
     double endPhiRadius, double startPhiThickness,
     double endPhiThickness, Appearance app) {
   double theta, phi, radius, radius2, thickness;
   double x, y, z;
   double[] xyz = new double[3];
   float[] norm = new float[3];
   float[] tex = new float[3];
   // Compute some values for our looping
   double deltaTheta = (endTheta - startTheta) / (double) (nTheta - 1);
   double deltaPhi = (endPhi - startPhi) / (double) (nPhi - 1);
   double deltaTexX = 1.0 / (double) (nTheta - 1);
   double deltaTexY = 1.0 / (double) (nPhi - 1);
   double deltaPhiRadius = (endPhiRadius - startPhiRadius)
       / (double) (nPhi - 1);
   double deltaPhiThickness = (endPhiThickness - startPhiThickness)
       / (double) (nPhi - 1);
   boolean doThickness = true;
   if (startPhiThickness == 0.0 && endPhiThickness == 0.0)
     doThickness = false;
   //  Create geometry
   int vertexCount = nTheta * nPhi;
   if (doThickness)
     vertexCount *= 2;
   int indexCount = (nTheta - 1) * (nPhi - 1) * 4; // Outer surface
   if (doThickness) {
     indexCount *= 2; // plus inner surface
     indexCount += (nPhi - 1) * 4 * 2; // plus left & right edges
   }
   IndexedQuadArray polys = new IndexedQuadArray(vertexCount,
       GeometryArray.COORDINATES | GeometryArray.NORMALS
           | GeometryArray.TEXTURE_COORDINATE_2, indexCount);
   //
   //  Compute coordinates, normals, and texture coordinates
   //
   theta = startTheta;
   tex[0] = 0.0f;
   int index = 0;
   for (int i = 0; i < nTheta; i++) {
     phi = startPhi;
     radius = startPhiRadius;
     thickness = startPhiThickness;
     tex[1] = 0.0f;
     for (int j = 0; j < nPhi; j++) {
       norm[0] = (float) (Math.cos(phi) * Math.cos(theta));
       norm[1] = (float) (Math.sin(phi));
       norm[2] = (float) (-Math.cos(phi) * Math.sin(theta));
       xyz[0] = radius * norm[0];
       xyz[1] = radius * norm[1];
       xyz[2] = radius * norm[2];
       polys.setCoordinate(index, xyz);
       polys.setNormal(index, norm);
       polys.setTextureCoordinate(index, tex);
       index++;
       if (doThickness) {
         radius2 = radius - thickness;
         xyz[0] = radius2 * norm[0];
         xyz[1] = radius2 * norm[1];
         xyz[2] = radius2 * norm[2];
         norm[0] *= -1.0f;
         norm[1] *= -1.0f;
         norm[2] *= -1.0f;
         polys.setCoordinate(index, xyz);
         polys.setNormal(index, norm);
         polys.setTextureCoordinate(index, tex);
         index++;
       }
       phi += deltaPhi;
       radius += deltaPhiRadius;
       thickness += deltaPhiThickness;
       tex[1] += deltaTexY;
     }
     theta += deltaTheta;
     tex[0] += deltaTexX;
   }
   //
   //  Compute coordinate indexes
   //  (also used as normal and texture indexes)
   //
   index = 0;
   int phiRow = nPhi;
   int phiCol = 1;
   if (doThickness) {
     phiRow += nPhi;
     phiCol += 1;
   }
   int[] indices = new int[indexCount];
   // Outer surface
   int n;
   for (int i = 0; i < nTheta - 1; i++) {
     for (int j = 0; j < nPhi - 1; j++) {
       n = i * phiRow + j * phiCol;
       indices[index + 0] = n;
       indices[index + 1] = n + phiRow;
       indices[index + 2] = n + phiRow + phiCol;
       indices[index + 3] = n + phiCol;
       index += 4;
     }
   }
   // Inner surface
   if (doThickness) {
     for (int i = 0; i < nTheta - 1; i++) {
       for (int j = 0; j < nPhi - 1; j++) {
         n = i * phiRow + j * phiCol;
         indices[index + 0] = n + 1;
         indices[index + 1] = n + phiCol + 1;
         indices[index + 2] = n + phiRow + phiCol + 1;
         indices[index + 3] = n + phiRow + 1;
         index += 4;
       }
     }
   }
   // Edges
   if (doThickness) {
     for (int j = 0; j < nPhi - 1; j++) {
       n = j * phiCol;
       indices[index + 0] = n;
       indices[index + 1] = n + phiCol;
       indices[index + 2] = n + phiCol + 1;
       indices[index + 3] = n + 1;
       index += 4;
     }
     for (int j = 0; j < nPhi - 1; j++) {
       n = (nTheta - 1) * phiRow + j * phiCol;
       indices[index + 0] = n;
       indices[index + 1] = n + 1;
       indices[index + 2] = n + phiCol + 1;
       indices[index + 3] = n + phiCol;
       index += 4;
     }
   }
   polys.setCoordinateIndices(0, indices);
   polys.setNormalIndices(0, indices);
   polys.setTextureCoordinateIndices(0, indices);
   //
   //  Build a shape
   //
   arch = new Shape3D();
   arch.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
   arch.setGeometry(polys);
   arch.setAppearance(app);
   addChild(arch);
 }
 public void setAppearance(Appearance app) {
   if (arch != null)
     arch.setAppearance(app);
 }

} // //CLASS //ElevationGrid - a 3D terrain grid built from a list of heights // //DESCRIPTION //This class creates a 3D terrain on a grid whose X and Z dimensions, //and row/column spacing are parameters, along with a list of heights //(elevations), one per grid row/column pair. // class ElevationGrid extends Primitive {

 // Parameters
 protected int xDimension = 0, zDimension = 0;
 protected double xSpacing = 0.0, zSpacing = 0.0;
 protected double[] heights = null;
 // 3D nodes
 private Appearance mainAppearance = null;
 private Shape3D shape = null;
 private IndexedTriangleStripArray tristrip = null;
 //
 //  Construct an elevation grid
 //
 public ElevationGrid() {
   xDimension = 2;
   zDimension = 2;
   xSpacing = 1.0;
   zSpacing = 1.0;
   mainAppearance = null;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = 1.0;
   zSpacing = 1.0;
   mainAppearance = null;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, Appearance app) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = 1.0;
   zSpacing = 1.0;
   mainAppearance = app;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = xSpace;
   zSpacing = zSpace;
   mainAppearance = null;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace,
     Appearance app) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = xSpace;
   zSpacing = zSpace;
   mainAppearance = app;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, double[] h) {
   this(xDim, zDim, 1.0, 1.0, h, null);
 }
 public ElevationGrid(int xDim, int zDim, double[] h, Appearance app) {
   this(xDim, zDim, 1.0, 1.0, h, app);
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace,
     double[] h) {
   this(xDim, zDim, xSpace, zSpace, h, null);
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace,
     double[] h, Appearance app) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = xSpace;
   zSpacing = zSpace;
   mainAppearance = app;
   if (h == null)
     zeroHeights();
   else {
     heights = new double[h.length];
     for (int i = 0; i < h.length; i++)
       heights[i] = h[i];
   }
   rebuild();
 }
 private void zeroHeights() {
   int n = xDimension * zDimension;
   heights = new double[n];
   for (int i = 0; i < n; i++)
     heights[i] = 0.0;
 }
 private void rebuild() {
   // Build a shape
   if (shape == null) {
     shape = new Shape3D();
     shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
     shape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
     shape.setAppearance(mainAppearance);
     addChild(shape);
   } else {
     shape.setAppearance(mainAppearance);
   }
   if (xDimension < 2 || zDimension < 2 || heights == null
       || heights.length < 4) {
     tristrip = null;
     shape.setGeometry(null);
     return;
   }
   // Create a list of coordinates, one per grid row/column
   double[] coordinates = new double[xDimension * zDimension * 3];
   double x, z;
   int n = 0, k = 0;
   z = ((double) (zDimension - 1)) * zSpacing / 2.0; // start at front edge
   for (int i = 0; i < zDimension; i++) {
     x = -((double) (xDimension - 1)) * xSpacing / 2.0;// start at left
     // edge
     for (int j = 0; j < xDimension; j++) {
       coordinates[n++] = x;
       coordinates[n++] = heights[k++];
       coordinates[n++] = z;
       x += xSpacing;
     }
     z -= zSpacing;
   }
   // Create a list of normals, one per grid row/column
   float[] normals = new float[xDimension * zDimension * 3];
   Vector3f one = new Vector3f(0.0f, 0.0f, 0.0f);
   Vector3f two = new Vector3f(0.0f, 0.0f, 0.0f);
   Vector3f norm = new Vector3f(0.0f, 0.0f, 0.0f);
   n = 0;
   k = 0;
   for (int i = 0; i < zDimension - 1; i++) {
     for (int j = 0; j < xDimension - 1; j++) {
       // Vector to right in X
       one.set((float) xSpacing,
           (float) (heights[k + 1] - heights[k]), 0.0f);
       // Vector back in Z
       two.set(0.0f, (float) (heights[k + xDimension] - heights[k]),
           (float) -zSpacing);
       // Cross them to get the normal
       norm.cross(one, two);
       normals[n++] = norm.x;
       normals[n++] = norm.y;
       normals[n++] = norm.z;
       k++;
     }
     // Last normal in row is a copy of the previous one
     normals[n] = normals[n - 3]; // X
     normals[n + 1] = normals[n - 2]; // Y
     normals[n + 2] = normals[n - 1]; // Z
     n += 3;
     k++;
   }
   // Last row of normals is a copy of the previous row
   for (int j = 0; j < xDimension; j++) {
     normals[n] = normals[n - xDimension * 3]; // X
     normals[n + 1] = normals[n - xDimension * 3 + 1]; // Y
     normals[n + 2] = normals[n - xDimension * 3 + 2]; // Z
     n += 3;
   }
   // Create a list of texture coordinates, one per grid row/column
   float[] texcoordinates = new float[xDimension * zDimension * 2];
   float deltaS = 1.0f / (float) (xDimension - 1);
   float deltaT = 1.0f / (float) (zDimension - 1);
   float s = 0.0f;
   float t = 0.0f;
   n = 0;
   for (int i = 0; i < zDimension; i++) {
     s = 0.0f;
     for (int j = 0; j < xDimension; j++) {
       texcoordinates[n++] = s;
       texcoordinates[n++] = t;
       s += deltaS;
     }
     t += deltaT;
   }
   // Create a list of triangle strip indexes. Each strip goes
   // down one row (X direction) of the elevation grid.
   int[] indexes = new int[xDimension * (zDimension - 1) * 2];
   int[] stripCounts = new int[zDimension - 1];
   n = 0;
   k = 0;
   for (int i = 0; i < zDimension - 1; i++) {
     stripCounts[i] = xDimension * 2;
     for (int j = 0; j < xDimension; j++) {
       indexes[n++] = k + xDimension;
       indexes[n++] = k;
       k++;
     }
   }
   // Create geometry for collection of triangle strips, one
   // strip per row of the elevation grid
   tristrip = new IndexedTriangleStripArray(coordinates.length,
       GeometryArray.COORDINATES | GeometryArray.NORMALS
           | GeometryArray.TEXTURE_COORDINATE_2, indexes.length,
       stripCounts);
   tristrip.setCoordinates(0, coordinates);
   tristrip.setNormals(0, normals);
   tristrip.setTextureCoordinates(0, texcoordinates);
   tristrip.setCoordinateIndices(0, indexes);
   tristrip.setNormalIndices(0, indexes);
   tristrip.setTextureCoordinateIndices(0, indexes);
   // Set the geometry for the shape
   shape.setGeometry(tristrip);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   mainAppearance = app;
   if (shape != null)
     shape.setAppearance(mainAppearance);
 }
 //
 //  Control grid parameters
 //
 public void setHeights(double[] h) {
   if (h == null)
     zeroHeights();
   else {
     heights = new double[h.length];
     for (int i = 0; i < h.length; i++)
       heights[i] = h[i];
   }
   rebuild();
 }
 public double[] getHeights() {
   return heights;
 }
 public void setXDimension(int xDim) {
   xDimension = xDim;
   rebuild();
 }
 public int getXDimension() {
   return xDimension;
 }
 public void setZDimension(int zDim) {
   zDimension = zDim;
   rebuild();
 }
 public int getZDimension() {
   return zDimension;
 }
 public void setXSpacing(double xSpace) {
   xSpacing = xSpace;
   rebuild();
 }
 public double getXSpacing() {
   return xSpacing;
 }
 public void setZSpacing(double zSpace) {
   zSpacing = zSpace;
   rebuild();
 }
 public double getZSpacing() {
   return zSpacing;
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   return shape;
 }
 public int getNumTriangles() {
   return xDimension * zDimension * 2;
 }
 public int getNumVertices() {
   return xDimension * zDimension;
 }
 /*
  * (non-Javadoc)
  * 
  * @see com.sun.j3d.utils.geometry.Primitive#getAppearance(int)
  */
 public Appearance getAppearance(int arg0) {
   // TODO Auto-generated method stub
   return null;
 }

} // //CLASS //CraterGrid - a 3D terrain grid built from a list of heights // //DESCRIPTION //This class creates a 3D terrain on a grid whose X and Z dimensions, //and row/column spacing are parameters, along with a list of heights //(elevations), one per grid row/column pair. // class CraterGrid extends ElevationGrid {

 // Parameters
 double[][] craters = null;
 double exagerationFactor = 1.0;
 // 3D nodes
 private Shape3D shape = null;
 private IndexedTriangleStripArray tristrip = null;
 //
 //  Construct a crater grid
 //
 public CraterGrid() {
   super();
   craters = null;
 }
 public CraterGrid(int xDim, int zDim, double[][] craters, Appearance app) {
   this(xDim, zDim, 1.0, 1.0, 1.0, craters, app);
 }
 public CraterGrid(int xDim, int zDim, double xSpace, double zSpace,
     double exagerate, double[][] crat, Appearance app) {
   super(xDim, zDim, xSpace, zSpace, null, app);
   exagerationFactor = exagerate;
   if (crat == null)
     craters = null;
   else {
     craters = new double[crat.length][4];
     for (int i = 0; i < crat.length; i++) {
       craters[i][0] = crat[i][0];
       craters[i][1] = crat[i][1];
       craters[i][2] = crat[i][2];
       craters[i][3] = crat[i][3];
     }
   }
   computeHeights();
 }
 private void computeHeights() {
   if (craters == null)
     return;
   double[] high = new double[xDimension * zDimension];
   int n = 0;
   double xdelta = 2.0 / (double) xDimension;
   double zdelta = 2.0 / (double) zDimension;
   double x, z, dx, dz;
   double angle, distance;
   // Compute heights
   z = 1.0;
   for (int i = 0; i < zDimension; i++) {
     x = -1.0;
     for (int j = 0; j < xDimension; j++) {
       // Compute a distance to the center of each crater.
       // If that distance is <= the radius, drop the height
       // by the crater"s depth. Sum across all craters
       // within range to get the height at this grid point.
       high[n] = 0.0;
       for (int k = 0; k < craters.length; k++) {
         dx = craters[k][0] - x;
         dz = craters[k][1] - z;
         distance = Math.sqrt(dx * dx + dz * dz);
         if (distance > craters[k][2])
           continue;
         high[n] -= exagerationFactor * craters[k][3];
       }
       n++;
       x += xdelta;
     }
     z -= zdelta;
   }
   setHeights(high);
 }
 //
 //  Control grid parameters
 //
 public void setCraters(double[][] crat) {
   craters = new double[crat.length][4];
   for (int i = 0; i < crat.length; i++) {
     craters[i][0] = crat[i][0];
     craters[i][1] = crat[i][1];
     craters[i][2] = crat[i][2];
     craters[i][3] = crat[i][3];
   }
   computeHeights();
 }
 public void getCraters(double[][] crat) {
   for (int i = 0; i < craters.length; i++) {
     crat[i][0] = craters[i][0];
     crat[i][1] = craters[i][1];
     crat[i][2] = craters[i][2];
     crat[i][3] = craters[i][3];
   }
 }
 public void setExageration(double exagerate) {
   exagerationFactor = exagerate;
   computeHeights();
 }
 public double getExageration() {
   return exagerationFactor;
 }

} /**

* 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.
*

* Using this Example class simplifies the construction of example applications, * enabling the author to focus upon 3D content and not the busywork of creating * windows, menus, and universes. * * @version 1.0, 98/04/16 * @author David R. Nadeau, San Diego Supercomputer Center */ class Java3DFrame extends Applet implements WindowListener, ActionListener, ItemListener, CheckboxMenuListener { // Navigation types public final static int Walk = 0; public final static int Examine = 1; // Should the scene be compiled? private boolean shouldCompile = true; // GUI objects for our subclasses protected Java3DFrame example = null; protected Frame exampleFrame = null; protected MenuBar exampleMenuBar = null; protected Canvas3D exampleCanvas = null; protected TransformGroup exampleViewTransform = null; protected TransformGroup exampleSceneTransform = null; protected boolean debug = false; // Private GUI objects and state private boolean headlightOnOff = true; private int navigationType = Examine; private CheckboxMenuItem headlightMenuItem = null; private CheckboxMenuItem walkMenuItem = null; private CheckboxMenuItem examineMenuItem = null; private DirectionalLight headlight = null; private ExamineViewerBehavior examineBehavior = null; private WalkViewerBehavior walkBehavior = null; //-------------------------------------------------------------- // ADMINISTRATION //-------------------------------------------------------------- /** * The main program entry point when invoked as an application. Each example * application that extends this class must define their own main. * * @param args * a String array of command-line arguments */ public static void main(String[] args) { Java3DFrame ex = new Java3DFrame(); ex.initialize(args); ex.buildUniverse(); ex.showFrame(); } /** * Constructs a new Example object. * * @return a new Example that draws no 3D content */ public Java3DFrame() { // Do nothing } /** * Initializes the application when invoked as an applet. */ public void init() { // Collect properties into String array String[] args = new String[2]; // NOTE: to be done still... this.initialize(args); this.buildUniverse(); this.showFrame(); // NOTE: add something to the browser page? } /** * Initializes the Example by parsing command-line arguments, building an * AWT Frame, constructing a menubar, and creating the 3D canvas. * * @param args * a String array of command-line arguments */ protected void initialize(String[] args) { example = this; // Parse incoming arguments parseArgs(args); // Build the frame if (debug) System.err.println("Building GUI..."); exampleFrame = new Frame(); exampleFrame.setSize(640, 480); exampleFrame.setTitle("Java 3D Example"); exampleFrame.setLayout(new BorderLayout()); // Set up a close behavior exampleFrame.addWindowListener(this); // Create a canvas exampleCanvas = new Canvas3D(null); exampleCanvas.setSize(630, 460); exampleFrame.add("Center", exampleCanvas); // Build the menubar exampleMenuBar = this.buildMenuBar(); exampleFrame.setMenuBar(exampleMenuBar); // Pack exampleFrame.pack(); exampleFrame.validate(); // exampleFrame.setVisible( true ); } /** * Parses incoming command-line arguments. Applications that subclass this * class may override this method to support their own command-line * arguments. * * @param args * a String array of command-line arguments */ protected void parseArgs(String[] args) { for (int i = 0; i < args.length; i++) { if (args[i].equals("-d")) debug = true; } } //-------------------------------------------------------------- // SCENE CONTENT //-------------------------------------------------------------- /** * Builds the 3D universe by constructing a virtual universe (via * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via * SimpleUniverse). A headlight is added and a set of behaviors initialized * to handle navigation types. */ protected void buildUniverse() { // // Create a SimpleUniverse object, which builds: // // - a Locale using the given hi-res coordinate origin // // - a ViewingPlatform which in turn builds: // - a MultiTransformGroup with which to move the // the ViewPlatform about // // - a ViewPlatform to hold the view // // - a BranchGroup to hold avatar geometry (if any) // // - a BranchGroup to hold view platform // geometry (if any) // // - a Viewer which in turn builds: // - a PhysicalBody which characterizes the user"s // viewing preferences and abilities // // - a PhysicalEnvironment which characterizes the // user"s rendering hardware and software // // - a JavaSoundMixer which initializes sound // support within the 3D environment // // - a View which renders the scene into a Canvas3D // // All of these actions could be done explicitly, but // using the SimpleUniverse utilities simplifies the code. // if (debug) System.err.println("Building scene graph..."); SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate // for the origin - // use default 1, // Number of transforms in MultiTransformGroup exampleCanvas, // Canvas3D into which to draw null); // URL for user configuration file - use defaults // // Get the viewer and create an audio device so that // sound will be enabled in this content. // Viewer viewer = universe.getViewer(); viewer.createAudioDevice(); // // Get the viewing platform created by SimpleUniverse. // From that platform, get the inner-most TransformGroup // in the MultiTransformGroup. That inner-most group // contains the ViewPlatform. It is this inner-most // TransformGroup we need in order to: // // - add a "headlight" that always aims forward from // the viewer // // - change the viewing direction in a "walk" style // // The inner-most TransformGroup"s transform will be // changed by the walk behavior (when enabled). // ViewingPlatform viewingPlatform = universe.getViewingPlatform(); exampleViewTransform = viewingPlatform.getViewPlatformTransform(); // // Create a "headlight" as a forward-facing directional light. // Set the light"s bounds to huge. Since we want the light // on the viewer"s "head", we need the light within the // TransformGroup containing the ViewPlatform. The // ViewingPlatform class creates a handy hook to do this // called "platform geometry". The PlatformGeometry class is // subclassed off of BranchGroup, and is intended to contain // a description of the 3D platform itself... PLUS a headlight! // So, to add the headlight, create a new PlatformGeometry group, // add the light to it, then add that platform geometry to the // ViewingPlatform. // BoundingSphere allBounds = new BoundingSphere( new Point3d(0.0, 0.0, 0.0), 100000.0); PlatformGeometry pg = new PlatformGeometry(); headlight = new DirectionalLight(); headlight.setColor(White); headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f)); headlight.setInfluencingBounds(allBounds); headlight.setCapability(Light.ALLOW_STATE_WRITE); pg.addChild(headlight); viewingPlatform.setPlatformGeometry(pg); // // Create the 3D content BranchGroup, containing: // // - a TransformGroup who"s transform the examine behavior // will change (when enabled). // // - 3D geometry to view // // Build the scene root BranchGroup sceneRoot = new BranchGroup(); // Build a transform that we can modify exampleSceneTransform = new TransformGroup(); exampleSceneTransform .setCapability(TransformGroup.ALLOW_TRANSFORM_READ); exampleSceneTransform .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND); // // Build the scene, add it to the transform, and add // the transform to the scene root // if (debug) System.err.println(" scene..."); Group scene = this.buildScene(); exampleSceneTransform.addChild(scene); sceneRoot.addChild(exampleSceneTransform); // // Create a pair of behaviors to implement two navigation // types: // // - "examine": a style where mouse drags rotate about // the scene"s origin as if it is an object under // examination. This is similar to the "Examine" // navigation type used by VRML browsers. // // - "walk": a style where mouse drags rotate about // the viewer"s center as if the viewer is turning // about to look at a scene they are in. This is // similar to the "Walk" navigation type used by // VRML browsers. // // Aim the examine behavior at the scene"s TransformGroup // and add the behavior to the scene root. // // Aim the walk behavior at the viewing platform"s // TransformGroup and add the behavior to the scene root. // // Enable one (and only one!) of the two behaviors // depending upon the current navigation type. // examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform // gorup // to // modify exampleFrame); // Parent frame for cusor changes examineBehavior.setSchedulingBounds(allBounds); sceneRoot.addChild(examineBehavior); walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform // group to // modify exampleFrame); // Parent frame for cusor changes walkBehavior.setSchedulingBounds(allBounds); sceneRoot.addChild(walkBehavior); if (navigationType == Walk) { examineBehavior.setEnable(false); walkBehavior.setEnable(true); } else { examineBehavior.setEnable(true); walkBehavior.setEnable(false); } // // Compile the scene branch group and add it to the // SimpleUniverse. // if (shouldCompile) sceneRoot.rupile(); universe.addBranchGraph(sceneRoot); reset(); } /** * Builds the scene. Example application subclasses should replace this * method with their own method to build 3D content. * * @return a Group containing 3D content to display */ public Group buildScene() { // Build the scene group containing nothing Group scene = new Group(); return scene; } //-------------------------------------------------------------- // SET/GET METHODS //-------------------------------------------------------------- /** * Sets the headlight on/off state. The headlight faces forward in the * direction the viewer is facing. Example applications that add their own * lights will typically turn the headlight off. A standard menu item * enables the headlight to be turned on and off via user control. * * @param onOff * a boolean turning the light on (true) or off (false) */ public void setHeadlightEnable(boolean onOff) { headlightOnOff = onOff; if (headlight != null) headlight.setEnable(headlightOnOff); if (headlightMenuItem != null) headlightMenuItem.setState(headlightOnOff); } /** * Gets the headlight on/off state. * * @return a boolean indicating if the headlight is on or off */ public boolean getHeadlightEnable() { return headlightOnOff; } /** * Sets the navigation type to be either Examine or Walk. The Examine * navigation type sets up behaviors that use mouse drags to rotate and * translate scene content as if it is an object held at arm"s length and * under examination. The Walk navigation type uses mouse drags to rotate * and translate the viewer as if they are walking through the content. The * Examine type is the default. * * @param nav * either Walk or Examine */ public void setNavigationType(int nav) { if (nav == Walk) { navigationType = Walk; if (walkMenuItem != null) walkMenuItem.setState(true); if (examineMenuItem != null) examineMenuItem.setState(false); if (walkBehavior != null) walkBehavior.setEnable(true); if (examineBehavior != null) examineBehavior.setEnable(false); } else { navigationType = Examine; if (walkMenuItem != null) walkMenuItem.setState(false); if (examineMenuItem != null) examineMenuItem.setState(true); if (walkBehavior != null) walkBehavior.setEnable(false); if (examineBehavior != null) examineBehavior.setEnable(true); } } /** * Gets the current navigation type, returning either Walk or Examine. * * @return either Walk or Examine */ public int getNavigationType() { return navigationType; } /** * Sets whether the scene graph should be compiled or not. Normally this is * always a good idea. For some example applications that use this Example * framework, it is useful to disable compilation - particularly when nodes * and node components will need to be made un-live in order to make * changes. Once compiled, such components can be made un-live, but they are * still unchangable unless appropriate capabilities have been set. * * @param onOff * a boolean turning compilation on (true) or off (false) */ public void setCompilable(boolean onOff) { shouldCompile = onOff; } /** * Gets whether the scene graph will be compiled or not. * * @return a boolean indicating if scene graph compilation is on or off */ public boolean getCompilable() { return shouldCompile; } //These methods will be replaced // Set the view position and direction public void setViewpoint(Point3f position, Vector3f direction) { Transform3D t = new Transform3D(); t.set(new Vector3f(position)); exampleViewTransform.setTransform(t); // how to set direction? } // Reset transforms public void reset() { Transform3D trans = new Transform3D(); exampleSceneTransform.setTransform(trans); trans.set(new Vector3f(0.0f, 0.0f, 10.0f)); exampleViewTransform.setTransform(trans); setNavigationType(navigationType); } // // Gets the URL (with file: prepended) for the current directory. // This is a terrible hack needed in the Alpha release of Java3D // in order to build a full path URL for loading sounds with // MediaContainer. When MediaContainer is fully implemented, // it should handle relative path names, but not yet. // public String getCurrentDirectory() { // Create a bogus file so that we can query it"s path File dummy = new File("dummy.tmp"); String dummyPath = dummy.getAbsolutePath(); // strip "/dummy.tmp" from end of dummyPath and put into "path" if (dummyPath.endsWith(File.separator + "dummy.tmp")) { int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp"); if (index >= 0) { int pathLength = index + 5; // pre-pend "file:" char[] charPath = new char[pathLength]; dummyPath.getChars(0, index, charPath, 5); String path = new String(charPath, 0, pathLength); path = "file:" + path.substring(5, pathLength); return path + File.separator; } } return dummyPath + File.separator; } //-------------------------------------------------------------- // USER INTERFACE //-------------------------------------------------------------- /** * Builds the example AWT Frame menubar. Standard menus and their options * are added. Applications that subclass this class should build their * menubar additions within their initialize method. * * @return a MenuBar for the AWT Frame */ private MenuBar buildMenuBar() { // Build the menubar MenuBar menuBar = new MenuBar(); // File menu Menu m = new Menu("File"); m.addActionListener(this); m.add("Exit"); menuBar.add(m); // View menu m = new Menu("View"); m.addActionListener(this); m.add("Reset view"); m.addSeparator(); walkMenuItem = new CheckboxMenuItem("Walk"); walkMenuItem.addItemListener(this); m.add(walkMenuItem); examineMenuItem = new CheckboxMenuItem("Examine"); examineMenuItem.addItemListener(this); m.add(examineMenuItem); if (navigationType == Walk) { walkMenuItem.setState(true); examineMenuItem.setState(false); } else { walkMenuItem.setState(false); examineMenuItem.setState(true); } m.addSeparator(); headlightMenuItem = new CheckboxMenuItem("Headlight on/off"); headlightMenuItem.addItemListener(this); headlightMenuItem.setState(headlightOnOff); m.add(headlightMenuItem); menuBar.add(m); return menuBar; } /** * Shows the application"s frame, making it and its menubar, 3D canvas, and * 3D content visible. */ public void showFrame() { exampleFrame.show(); } /** * Quits the application. */ public void quit() { System.exit(0); } /** * Handles menu selections. * * @param event * an ActionEvent indicating what menu action requires handling */ public void actionPerformed(ActionEvent event) { String arg = event.getActionCommand(); if (arg.equals("Reset view")) reset(); else if (arg.equals("Exit")) quit(); } /** * Handles checkbox items on a CheckboxMenu. The Example class has none of * its own, but subclasses may have some. * * @param menu * which CheckboxMenu needs action * @param check * which CheckboxMenu item has changed */ public void checkboxChanged(CheckboxMenu menu, int check) { // None for us } /** * Handles on/off checkbox items on a standard menu. * * @param event * an ItemEvent indicating what requires handling */ public void itemStateChanged(ItemEvent event) { Object src = event.getSource(); boolean state; if (src == headlightMenuItem) { state = headlightMenuItem.getState(); headlight.setEnable(state); } else if (src == walkMenuItem) setNavigationType(Walk); else if (src == examineMenuItem) setNavigationType(Examine); } /** * Handles a window closing event notifying the application that the user * has chosen to close the application without selecting the "Exit" menu * item. * * @param event * a WindowEvent indicating the window is closing */ public void windowClosing(WindowEvent event) { quit(); } public void windowClosed(WindowEvent event) { } public void windowOpened(WindowEvent event) { } public void windowIconified(WindowEvent event) { } public void windowDeiconified(WindowEvent event) { } public void windowActivated(WindowEvent event) { } public void windowDeactivated(WindowEvent event) { } // Well known colors, positions, and directions public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f); public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f); public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f); public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f); public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f); public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f); public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f); public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f); public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f); public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f); public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f); public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f); public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f); public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f); public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f); public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f); public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f); public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f); public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f); public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f); public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f); public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f); public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f); public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f); public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f); public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f); public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f); } // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener { public void checkboxChanged(CheckboxMenu menu, int check); } /** * ExamineViewerBehavior * * @version 1.0, 98/04/16 */ /** * Wakeup on mouse button presses, releases, and mouse movements and generate * transforms in an "examination style" that enables the user to rotate, * translation, and zoom an object as if it is held at arm"s length. Such an * examination style is similar to the "Examine" navigation type used by VRML * browsers. * * The behavior maps mouse drags to different transforms depending upon the * mosue button held down: * * Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement --> * X-axis rotation * * Button 2 (middle) Horizontal movement --> nothing Vertical movement --> * Z-axis translation * * Button 3 (right) Horizontal movement --> X-axis translation Vertical movement * --> Y-axis translation * * To support systems with 2 or 1 mouse buttons, the following alternate * mappings are supported while dragging with any mouse button held down and * zero or more keyboard modifiers held down: * * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3 * * The behavior automatically modifies a TransformGroup provided to the * constructor. The TransformGroup"s transform can be set at any time by the * application or other behaviors to cause the examine rotation and translation * to be reset. */ // This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior { // Previous cursor location protected int previousX = 0; protected int previousY = 0; // Saved standard cursor protected Cursor savedCursor = null; /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. */ public ExamineViewerBehavior() { super(); } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. * * @param parent * The AWT Component that contains the area generating mouse * events. */ public ExamineViewerBehavior(Component parent) { super(parent); } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. */ public ExamineViewerBehavior(TransformGroup transformGroup) { super(); subjectTransformGroup = transformGroup; } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. * @param parent * The AWT Component that contains the area generating mouse * events. */ public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) { super(parent); subjectTransformGroup = transformGroup; } /** * Respond to a button1 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton1(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.HAND_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a rotation // // Compute the delta in X and Y from the previous // position. Use the delta to compute rotation // angles with the mapping: // // positive X mouse delta --> positive Y-axis rotation // positive Y mouse delta --> positive X-axis rotation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = y - previousY; if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xRotationAngle = deltaY * XRotationFactor; double yRotationAngle = deltaX * YRotationFactor; // // Build transforms // transform1.rotX(xRotationAngle); transform2.rotY(yRotationAngle); // Get and save the current transform matrix subjectTransformGroup.getTransform(currentTransform); currentTransform.get(matrix); translate.set(matrix.m03, matrix.m13, matrix.m23); // Translate to the origin, rotate, then translate back currentTransform.setTranslation(origin); currentTransform.mul(transform1, currentTransform); currentTransform.mul(transform2, currentTransform); currentTransform.setTranslation(translate); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to a button2 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton2(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a translation // // Compute the delta in Y from the previous // position. Use the delta to compute translation // distances with the mapping: // // positive Y mouse delta --> positive Y-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaY = y - previousY; if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double zTranslationDistance = deltaY * ZTranslationFactor; // // Build transforms // translate.set(0.0, 0.0, zTranslationDistance); transform1.set(translate); // Get and save the current transform subjectTransformGroup.getTransform(currentTransform); // Translate as needed currentTransform.mul(transform1, currentTransform); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to a button3 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton3(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a translation // // Compute the delta in X and Y from the previous // position. Use the delta to compute translation // distances with the mapping: // // positive X mouse delta --> positive X-axis translation // positive Y mouse delta --> negative Y-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = y - previousY; if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xTranslationDistance = deltaX * XTranslationFactor; double yTranslationDistance = -deltaY * YTranslationFactor; // // Build transforms // translate.set(xTranslationDistance, yTranslationDistance, 0.0); transform1.set(translate); // Get and save the current transform subjectTransformGroup.getTransform(currentTransform); // Translate as needed currentTransform.mul(transform1, currentTransform); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to an elapsed frames event (assuming subclass has set up a wakeup * criterion for it). * * @param time * A WakeupOnElapsedFrames criterion to respond to. */ public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) { // Can"t happen } } /* * * Copyright (c) 1998 David R. Nadeau * */ /** * WalkViewerBehavior is a utility class that creates a "walking style" * navigation symantic. * * The behavior wakes up on mouse button presses, releases, and mouse movements * and generates transforms in a "walk style" that enables the user to walk * through a scene, translating and turning about as if they are within the * scene. Such a walk style is similar to the "Walk" navigation type used by * VRML browsers. * <P> * The behavior maps mouse drags to different transforms depending upon the * mouse button held down: *

*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
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 walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}

      </source>
   
  
 
  



ExBackgroundImage - illustrate use of background images

   <source lang="java">

// //CLASS //ExBackgroundImage - illustrate use of background images // //LESSON //Add a Background node to place an background image behind geometry. // //SEE ALSO //ExBackgroundColor //ExBackgroundGeometry // //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.ruponent; 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.AmbientLight; 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.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.IndexedTriangleStripArray; import javax.media.j3d.Light; import javax.media.j3d.Link; import javax.media.j3d.Material; import javax.media.j3d.Shape3D; import javax.media.j3d.SharedGroup; import javax.media.j3d.Texture; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; 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.Matrix3f; 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.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 ExBackgroundImage extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private Background background = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current image
   ImageComponent2D image = imageComponents[currentImage];
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Default to walk navigation
   setNavigationType(Walk);
   // Build the scene root
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create application bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Set the background color and its application bounds
   background = new Background();
   background.setImage(image);
   background.setCapability(Background.ALLOW_IMAGE_WRITE);
   background.setApplicationBounds(worldBounds);
   scene.addChild(background);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(new TowerScene(this));
   return scene;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExBackgroundImage ex = new ExBackgroundImage();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  Image menu choices
 private NameValue[] images = { new NameValue("None", null),
     new NameValue("Stars", "stars2.jpg"),
     new NameValue("Red Clouds", "oddclouds.jpg"),
     new NameValue("White Clouds", "clouds.jpg"), };
 private int currentImage = 0;
 private ImageComponent2D[] imageComponents = null;
 private CheckboxMenu imageMenu = null;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Background Image Example");
   //
   //  Add a menubar menu to change node parameters
   //    Image -->
   //
   Menu m = new Menu("Background");
   imageMenu = new CheckboxMenu("Image", images, currentImage, this);
   m.add(imageMenu);
   exampleMenuBar.add(m);
   // Preload the background images
   //   Use the texture loading utility to read in the image
   //   files and process them into an ImageComponent2D
   //   for use in the Background node.
   if (debug)
     System.err.println("  background images...");
   TextureLoader texLoader = null;
   String value = null;
   imageComponents = new ImageComponent2D[images.length];
   for (int i = 0; i < images.length; i++) {
     value = (String) images[i].value;
     if (value == null) {
       imageComponents[i] = null;
       continue;
     }
     texLoader = new TextureLoader(value, this);
     imageComponents[i] = texLoader.getImage();
     // Component could be null if image couldn"t be loaded
   }
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void checkboxChanged(CheckboxMenu menu, int check) {
   if (menu == imageMenu) {
     // Change the background image
     currentImage = check;
     ImageComponent2D image = imageComponents[check];
     background.setImage(image);
     return;
   }
   // Handle all other checkboxes
   super.checkboxChanged(menu, check);
 }

} // //CLASS //TowerScene - shapes and lights for a scene with towers // //DESCRIPTION //This class builds a scene containing a cratered surface, a set of //stone towers, plus appropriate lighting. The scene is used in several //of the examples to provide content to affect with lights, background //colors and images, and so forth. // //SEE ALSO //ExBackgroundColor //ExBackgroundImage //ExBackgroundGeometry // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class TowerScene extends Group {

 private static final double[][] craters = {
 // x,z,radius are in a normalized -1.0 to 1.0 space
     // x z radius depth
     { 0.0, 0.0, 0.7, 0.20 }, { 0.3, 0.3, 0.5, 0.20 },
     { -0.3, 0.1, 0.6, 0.20 }, { -0.2, 0.4, 0.4, 0.20 },
     { -0.9, -0.9, 0.5, 0.20 }, { 0.4, 0.5, 0.3, 0.10 },
     { 0.9, -0.2, 0.4, 0.10 }, { -0.8, 0.1, 0.2, 0.10 },
     { 0.2, 0.7, 0.3, 0.20 }, { 0.5, -0.5, 0.21, 0.20 },
     { 0.8, -0.8, 0.16, 0.10 }, { -0.3, 0.7, 0.23, 0.20 },
     { 0.5, 0.5, 0.22, 0.10 }, { -0.7, 0.8, 0.15, 0.10 },
     { -0.5, -0.3, 0.22, 0.10 }, { 0.2, 0.2, 0.15, 0.10 },
     { 0.1, 0.8, 0.25, 0.20 }, { 0.4, 0.9, 0.28, 0.09 },
     { 0.9, -0.1, 0.23, 0.10 }, { 0.1, -0.0, 0.33, 0.08 },
     { 0.1, -0.9, 0.23, 0.20 }, { -1.0, 0.8, 0.13, 0.15 },
     { -0.9, 0.7, 0.10, 0.15 }, { -0.2, 0.1, 0.10, 0.16 },
     { 1.1, 1.0, 0.12, 0.15 }, { 0.9, 0.5, 0.13, 0.14 },
     { -0.1, -0.1, 0.14, 0.15 }, { -0.5, -0.5, 0.10, 0.13 },
     { 0.1, -0.4, 0.10, 0.15 }, { -0.4, -1.0, 0.25, 0.15 },
     { 0.4, 1.0, 0.25, 0.15 }, };
 public TowerScene(Component observer) {
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Add a few lights
   AmbientLight ambient = new AmbientLight();
   ambient.setEnable(true);
   ambient.setColor(new Color3f(0.2f, 0.2f, 0.2f));
   ambient.setInfluencingBounds(worldBounds);
   addChild(ambient);
   DirectionalLight dir1 = new DirectionalLight();
   dir1.setEnable(true);
   dir1.setColor(new Color3f(1.0f, 0.15f, 0.15f));
   dir1.setDirection(new Vector3f(0.8f, -0.35f, -0.5f));
   dir1.setInfluencingBounds(worldBounds);
   addChild(dir1);
   DirectionalLight dir2 = new DirectionalLight();
   dir2.setEnable(true);
   dir2.setColor(new Color3f(0.15f, 0.15f, 1.0f));
   dir2.setDirection(new Vector3f(-0.7f, -0.35f, 0.5f));
   dir2.setInfluencingBounds(worldBounds);
   addChild(dir2);
   // Load textures
   TextureLoader texLoader = new TextureLoader("moon5.jpg", observer);
   Texture moon = texLoader.getTexture();
   if (moon == null)
     System.err.println("Cannot load moon5.jpg texture");
   else {
     moon.setBoundaryModeS(Texture.WRAP);
     moon.setBoundaryModeT(Texture.WRAP);
     moon.setMinFilter(Texture.NICEST);
     moon.setMagFilter(Texture.NICEST);
     moon.setMipMapMode(Texture.BASE_LEVEL);
     moon.setEnable(true);
   }
   texLoader = new TextureLoader("stonebrk2.jpg", observer);
   Texture stone = texLoader.getTexture();
   if (stone == null)
     System.err.println("Cannot load stonebrk2.jpg texture");
   else {
     stone.setBoundaryModeS(Texture.WRAP);
     stone.setBoundaryModeT(Texture.WRAP);
     stone.setMinFilter(Texture.NICEST);
     stone.setMagFilter(Texture.NICEST);
     stone.setMipMapMode(Texture.BASE_LEVEL);
     stone.setEnable(true);
   }
   //
   //  Build a rough terrain
   //
   Appearance moonApp = new Appearance();
   Material moonMat = new Material();
   moonMat.setAmbientColor(0.5f, 0.5f, 0.5f);
   moonMat.setDiffuseColor(1.0f, 1.0f, 1.0f);
   moonMat.setSpecularColor(0.0f, 0.0f, 0.0f);
   moonApp.setMaterial(moonMat);
   TextureAttributes moonTexAtt = new TextureAttributes();
   moonTexAtt.setTextureMode(TextureAttributes.MODULATE);
   moonTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
   moonApp.setTextureAttributes(moonTexAtt);
   if (moon != null)
     moonApp.setTexture(moon);
   CraterGrid grid = new CraterGrid(50, 50, // grid dimensions
       1.0, 1.0, // grid spacing
       4.0, // height exageration factor
       craters, // grid elevations
       moonApp); // grid appearance
   addChild(grid);
   //
   // Build several towers on the terrain
   //
   SharedGroup tower = new SharedGroup();
   Appearance towerApp = new Appearance();
   Material towerMat = new Material();
   towerMat.setAmbientColor(0.6f, 0.6f, 0.6f);
   towerMat.setDiffuseColor(1.0f, 1.0f, 1.0f);
   towerMat.setSpecularColor(0.0f, 0.0f, 0.0f);
   towerApp.setMaterial(towerMat);
   Transform3D tr = new Transform3D();
   tr.setScale(new Vector3d(4.0, 4.0, 1.0));
   TextureAttributes towerTexAtt = new TextureAttributes();
   towerTexAtt.setTextureMode(TextureAttributes.MODULATE);
   towerTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
   towerTexAtt.setTextureTransform(tr);
   towerApp.setTextureAttributes(towerTexAtt);
   if (stone != null)
     towerApp.setTexture(stone);
   Arch towerShape = new Arch(0.0, // start Phi
       1.571, // end Phi
       2, // nPhi
       0.0, // start Theta
       Math.PI * 2.0, // end Theta
       5, // nTheta
       3.0, // start radius
       8.0, // end radius
       0.0, // start phi thickness
       0.0, // end phi thickness
       towerApp); // appearance
   tower.addChild(towerShape);
   // Place towers
   Matrix3f rot = new Matrix3f();
   rot.setIdentity();
   TransformGroup tg = new TransformGroup(new Transform3D(rot,
       new Vector3d(2.0, -3.0, -8.0), 1.0));
   tg.addChild(new Link(tower));
   addChild(tg);
   tg = new TransformGroup(new Transform3D(rot, new Vector3d(-1.0, -3.0,
       -6.0), 0.5));
   tg.addChild(new Link(tower));
   addChild(tg);
   tg = new TransformGroup(new Transform3D(rot, new Vector3d(5.0, -3.0,
       -6.0), 0.75));
   tg.addChild(new Link(tower));
   addChild(tg);
   tg = new TransformGroup(new Transform3D(rot, new Vector3d(1.0, -3.0,
       -3.0), 0.35));
   tg.addChild(new Link(tower));
   addChild(tg);
 }

} // //CLASS //Arch - generalized arch // //DESCRIPTION //This class builds a generalized arch where incoming parameters //specify the angle range in theta (around the equator of a sphere), //the angle range in phi (north-south), the number of subdivisions //in theta and phi, and optionally radii and outer-to-inner wall //thickness variations as phi varies from its starting value to //its ending value. If the thicknesses are 0.0, then only an outer //surface is created. // //Using this class, you can create spheres with or without inner //surfaces, hemisphers, quarter spheres, and arches stretched or //compressed vertically. // //This is probably not as general as it could be, but it was enough //for the purposes at hand. // //SEE ALSO //ModernFire // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class Arch extends Group {

 // The shape
 private Shape3D arch = null;
 // Construct an arch
 public Arch() {
   // Default to a sphere
   this(0.0, Math.PI / 2.0, 9, 0.0, Math.PI, 17, 1.0, 1.0, 0.0, 0.0,
       new Appearance());
 }
 public Arch(Appearance app) {
   // Default to a sphere
   this(0.0, Math.PI / 2.0, 9, 0.0, Math.PI, 17, 1.0, 1.0, 0.0, 0.0, app);
 }
 public Arch(double startPhi, double endPhi, int nPhi, double startTheta,
     double endTheta, int nTheta, Appearance app) {
   // Default to constant radius, no thickness
   this(startPhi, endPhi, nPhi, startTheta, endTheta, nTheta, 1.0, 1.0,
       0.0, 0.0, app);
 }
 public Arch(double startPhi, double endPhi, int nPhi, double startTheta,
     double endTheta, int nTheta, double startPhiRadius,
     double endPhiRadius, double startPhiThickness,
     double endPhiThickness, Appearance app) {
   double theta, phi, radius, radius2, thickness;
   double x, y, z;
   double[] xyz = new double[3];
   float[] norm = new float[3];
   float[] tex = new float[3];
   // Compute some values for our looping
   double deltaTheta = (endTheta - startTheta) / (double) (nTheta - 1);
   double deltaPhi = (endPhi - startPhi) / (double) (nPhi - 1);
   double deltaTexX = 1.0 / (double) (nTheta - 1);
   double deltaTexY = 1.0 / (double) (nPhi - 1);
   double deltaPhiRadius = (endPhiRadius - startPhiRadius)
       / (double) (nPhi - 1);
   double deltaPhiThickness = (endPhiThickness - startPhiThickness)
       / (double) (nPhi - 1);
   boolean doThickness = true;
   if (startPhiThickness == 0.0 && endPhiThickness == 0.0)
     doThickness = false;
   //  Create geometry
   int vertexCount = nTheta * nPhi;
   if (doThickness)
     vertexCount *= 2;
   int indexCount = (nTheta - 1) * (nPhi - 1) * 4; // Outer surface
   if (doThickness) {
     indexCount *= 2; // plus inner surface
     indexCount += (nPhi - 1) * 4 * 2; // plus left & right edges
   }
   IndexedQuadArray polys = new IndexedQuadArray(vertexCount,
       GeometryArray.COORDINATES | GeometryArray.NORMALS
           | GeometryArray.TEXTURE_COORDINATE_2, indexCount);
   //
   //  Compute coordinates, normals, and texture coordinates
   //
   theta = startTheta;
   tex[0] = 0.0f;
   int index = 0;
   for (int i = 0; i < nTheta; i++) {
     phi = startPhi;
     radius = startPhiRadius;
     thickness = startPhiThickness;
     tex[1] = 0.0f;
     for (int j = 0; j < nPhi; j++) {
       norm[0] = (float) (Math.cos(phi) * Math.cos(theta));
       norm[1] = (float) (Math.sin(phi));
       norm[2] = (float) (-Math.cos(phi) * Math.sin(theta));
       xyz[0] = radius * norm[0];
       xyz[1] = radius * norm[1];
       xyz[2] = radius * norm[2];
       polys.setCoordinate(index, xyz);
       polys.setNormal(index, norm);
       polys.setTextureCoordinate(index, tex);
       index++;
       if (doThickness) {
         radius2 = radius - thickness;
         xyz[0] = radius2 * norm[0];
         xyz[1] = radius2 * norm[1];
         xyz[2] = radius2 * norm[2];
         norm[0] *= -1.0f;
         norm[1] *= -1.0f;
         norm[2] *= -1.0f;
         polys.setCoordinate(index, xyz);
         polys.setNormal(index, norm);
         polys.setTextureCoordinate(index, tex);
         index++;
       }
       phi += deltaPhi;
       radius += deltaPhiRadius;
       thickness += deltaPhiThickness;
       tex[1] += deltaTexY;
     }
     theta += deltaTheta;
     tex[0] += deltaTexX;
   }
   //
   //  Compute coordinate indexes
   //  (also used as normal and texture indexes)
   //
   index = 0;
   int phiRow = nPhi;
   int phiCol = 1;
   if (doThickness) {
     phiRow += nPhi;
     phiCol += 1;
   }
   int[] indices = new int[indexCount];
   // Outer surface
   int n;
   for (int i = 0; i < nTheta - 1; i++) {
     for (int j = 0; j < nPhi - 1; j++) {
       n = i * phiRow + j * phiCol;
       indices[index + 0] = n;
       indices[index + 1] = n + phiRow;
       indices[index + 2] = n + phiRow + phiCol;
       indices[index + 3] = n + phiCol;
       index += 4;
     }
   }
   // Inner surface
   if (doThickness) {
     for (int i = 0; i < nTheta - 1; i++) {
       for (int j = 0; j < nPhi - 1; j++) {
         n = i * phiRow + j * phiCol;
         indices[index + 0] = n + 1;
         indices[index + 1] = n + phiCol + 1;
         indices[index + 2] = n + phiRow + phiCol + 1;
         indices[index + 3] = n + phiRow + 1;
         index += 4;
       }
     }
   }
   // Edges
   if (doThickness) {
     for (int j = 0; j < nPhi - 1; j++) {
       n = j * phiCol;
       indices[index + 0] = n;
       indices[index + 1] = n + phiCol;
       indices[index + 2] = n + phiCol + 1;
       indices[index + 3] = n + 1;
       index += 4;
     }
     for (int j = 0; j < nPhi - 1; j++) {
       n = (nTheta - 1) * phiRow + j * phiCol;
       indices[index + 0] = n;
       indices[index + 1] = n + 1;
       indices[index + 2] = n + phiCol + 1;
       indices[index + 3] = n + phiCol;
       index += 4;
     }
   }
   polys.setCoordinateIndices(0, indices);
   polys.setNormalIndices(0, indices);
   polys.setTextureCoordinateIndices(0, indices);
   //
   //  Build a shape
   //
   arch = new Shape3D();
   arch.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
   arch.setGeometry(polys);
   arch.setAppearance(app);
   addChild(arch);
 }
 public void setAppearance(Appearance app) {
   if (arch != null)
     arch.setAppearance(app);
 }

} // //CLASS //CraterGrid - a 3D terrain grid built from a list of heights // //DESCRIPTION //This class creates a 3D terrain on a grid whose X and Z dimensions, //and row/column spacing are parameters, along with a list of heights //(elevations), one per grid row/column pair. // class CraterGrid extends ElevationGrid {

 // Parameters
 double[][] craters = null;
 double exagerationFactor = 1.0;
 // 3D nodes
 private Shape3D shape = null;
 private IndexedTriangleStripArray tristrip = null;
 //
 //  Construct a crater grid
 //
 public CraterGrid() {
   super();
   craters = null;
 }
 public CraterGrid(int xDim, int zDim, double[][] craters, Appearance app) {
   this(xDim, zDim, 1.0, 1.0, 1.0, craters, app);
 }
 public CraterGrid(int xDim, int zDim, double xSpace, double zSpace,
     double exagerate, double[][] crat, Appearance app) {
   super(xDim, zDim, xSpace, zSpace, null, app);
   exagerationFactor = exagerate;
   if (crat == null)
     craters = null;
   else {
     craters = new double[crat.length][4];
     for (int i = 0; i < crat.length; i++) {
       craters[i][0] = crat[i][0];
       craters[i][1] = crat[i][1];
       craters[i][2] = crat[i][2];
       craters[i][3] = crat[i][3];
     }
   }
   computeHeights();
 }
 private void computeHeights() {
   if (craters == null)
     return;
   double[] high = new double[xDimension * zDimension];
   int n = 0;
   double xdelta = 2.0 / (double) xDimension;
   double zdelta = 2.0 / (double) zDimension;
   double x, z, dx, dz;
   double angle, distance;
   // Compute heights
   z = 1.0;
   for (int i = 0; i < zDimension; i++) {
     x = -1.0;
     for (int j = 0; j < xDimension; j++) {
       // Compute a distance to the center of each crater.
       // If that distance is <= the radius, drop the height
       // by the crater"s depth. Sum across all craters
       // within range to get the height at this grid point.
       high[n] = 0.0;
       for (int k = 0; k < craters.length; k++) {
         dx = craters[k][0] - x;
         dz = craters[k][1] - z;
         distance = Math.sqrt(dx * dx + dz * dz);
         if (distance > craters[k][2])
           continue;
         high[n] -= exagerationFactor * craters[k][3];
       }
       n++;
       x += xdelta;
     }
     z -= zdelta;
   }
   setHeights(high);
 }
 //
 //  Control grid parameters
 //
 public void setCraters(double[][] crat) {
   craters = new double[crat.length][4];
   for (int i = 0; i < crat.length; i++) {
     craters[i][0] = crat[i][0];
     craters[i][1] = crat[i][1];
     craters[i][2] = crat[i][2];
     craters[i][3] = crat[i][3];
   }
   computeHeights();
 }
 public void getCraters(double[][] crat) {
   for (int i = 0; i < craters.length; i++) {
     crat[i][0] = craters[i][0];
     crat[i][1] = craters[i][1];
     crat[i][2] = craters[i][2];
     crat[i][3] = craters[i][3];
   }
 }
 public void setExageration(double exagerate) {
   exagerationFactor = exagerate;
   computeHeights();
 }
 public double getExageration() {
   return exagerationFactor;
 }

} // //CLASS //ElevationGrid - a 3D terrain grid built from a list of heights // //DESCRIPTION //This class creates a 3D terrain on a grid whose X and Z dimensions, //and row/column spacing are parameters, along with a list of heights //(elevations), one per grid row/column pair. // class ElevationGrid extends Primitive {

 // Parameters
 protected int xDimension = 0, zDimension = 0;
 protected double xSpacing = 0.0, zSpacing = 0.0;
 protected double[] heights = null;
 // 3D nodes
 private Appearance mainAppearance = null;
 private Shape3D shape = null;
 private IndexedTriangleStripArray tristrip = null;
 //
 //  Construct an elevation grid
 //
 public ElevationGrid() {
   xDimension = 2;
   zDimension = 2;
   xSpacing = 1.0;
   zSpacing = 1.0;
   mainAppearance = null;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = 1.0;
   zSpacing = 1.0;
   mainAppearance = null;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, Appearance app) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = 1.0;
   zSpacing = 1.0;
   mainAppearance = app;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = xSpace;
   zSpacing = zSpace;
   mainAppearance = null;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace,
     Appearance app) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = xSpace;
   zSpacing = zSpace;
   mainAppearance = app;
   zeroHeights();
   rebuild();
 }
 public ElevationGrid(int xDim, int zDim, double[] h) {
   this(xDim, zDim, 1.0, 1.0, h, null);
 }
 public ElevationGrid(int xDim, int zDim, double[] h, Appearance app) {
   this(xDim, zDim, 1.0, 1.0, h, app);
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace,
     double[] h) {
   this(xDim, zDim, xSpace, zSpace, h, null);
 }
 public ElevationGrid(int xDim, int zDim, double xSpace, double zSpace,
     double[] h, Appearance app) {
   xDimension = xDim;
   zDimension = zDim;
   xSpacing = xSpace;
   zSpacing = zSpace;
   mainAppearance = app;
   if (h == null)
     zeroHeights();
   else {
     heights = new double[h.length];
     for (int i = 0; i < h.length; i++)
       heights[i] = h[i];
   }
   rebuild();
 }
 private void zeroHeights() {
   int n = xDimension * zDimension;
   heights = new double[n];
   for (int i = 0; i < n; i++)
     heights[i] = 0.0;
 }
 private void rebuild() {
   // Build a shape
   if (shape == null) {
     shape = new Shape3D();
     shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
     shape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
     shape.setAppearance(mainAppearance);
     addChild(shape);
   } else {
     shape.setAppearance(mainAppearance);
   }
   if (xDimension < 2 || zDimension < 2 || heights == null
       || heights.length < 4) {
     tristrip = null;
     shape.setGeometry(null);
     return;
   }
   // Create a list of coordinates, one per grid row/column
   double[] coordinates = new double[xDimension * zDimension * 3];
   double x, z;
   int n = 0, k = 0;
   z = ((double) (zDimension - 1)) * zSpacing / 2.0; // start at front edge
   for (int i = 0; i < zDimension; i++) {
     x = -((double) (xDimension - 1)) * xSpacing / 2.0;// start at left
     // edge
     for (int j = 0; j < xDimension; j++) {
       coordinates[n++] = x;
       coordinates[n++] = heights[k++];
       coordinates[n++] = z;
       x += xSpacing;
     }
     z -= zSpacing;
   }
   // Create a list of normals, one per grid row/column
   float[] normals = new float[xDimension * zDimension * 3];
   Vector3f one = new Vector3f(0.0f, 0.0f, 0.0f);
   Vector3f two = new Vector3f(0.0f, 0.0f, 0.0f);
   Vector3f norm = new Vector3f(0.0f, 0.0f, 0.0f);
   n = 0;
   k = 0;
   for (int i = 0; i < zDimension - 1; i++) {
     for (int j = 0; j < xDimension - 1; j++) {
       // Vector to right in X
       one.set((float) xSpacing,
           (float) (heights[k + 1] - heights[k]), 0.0f);
       // Vector back in Z
       two.set(0.0f, (float) (heights[k + xDimension] - heights[k]),
           (float) -zSpacing);
       // Cross them to get the normal
       norm.cross(one, two);
       normals[n++] = norm.x;
       normals[n++] = norm.y;
       normals[n++] = norm.z;
       k++;
     }
     // Last normal in row is a copy of the previous one
     normals[n] = normals[n - 3]; // X
     normals[n + 1] = normals[n - 2]; // Y
     normals[n + 2] = normals[n - 1]; // Z
     n += 3;
     k++;
   }
   // Last row of normals is a copy of the previous row
   for (int j = 0; j < xDimension; j++) {
     normals[n] = normals[n - xDimension * 3]; // X
     normals[n + 1] = normals[n - xDimension * 3 + 1]; // Y
     normals[n + 2] = normals[n - xDimension * 3 + 2]; // Z
     n += 3;
   }
   // Create a list of texture coordinates, one per grid row/column
   float[] texcoordinates = new float[xDimension * zDimension * 2];
   float deltaS = 1.0f / (float) (xDimension - 1);
   float deltaT = 1.0f / (float) (zDimension - 1);
   float s = 0.0f;
   float t = 0.0f;
   n = 0;
   for (int i = 0; i < zDimension; i++) {
     s = 0.0f;
     for (int j = 0; j < xDimension; j++) {
       texcoordinates[n++] = s;
       texcoordinates[n++] = t;
       s += deltaS;
     }
     t += deltaT;
   }
   // Create a list of triangle strip indexes. Each strip goes
   // down one row (X direction) of the elevation grid.
   int[] indexes = new int[xDimension * (zDimension - 1) * 2];
   int[] stripCounts = new int[zDimension - 1];
   n = 0;
   k = 0;
   for (int i = 0; i < zDimension - 1; i++) {
     stripCounts[i] = xDimension * 2;
     for (int j = 0; j < xDimension; j++) {
       indexes[n++] = k + xDimension;
       indexes[n++] = k;
       k++;
     }
   }
   // Create geometry for collection of triangle strips, one
   // strip per row of the elevation grid
   tristrip = new IndexedTriangleStripArray(coordinates.length,
       GeometryArray.COORDINATES | GeometryArray.NORMALS
           | GeometryArray.TEXTURE_COORDINATE_2, indexes.length,
       stripCounts);
   tristrip.setCoordinates(0, coordinates);
   tristrip.setNormals(0, normals);
   tristrip.setTextureCoordinates(0, texcoordinates);
   tristrip.setCoordinateIndices(0, indexes);
   tristrip.setNormalIndices(0, indexes);
   tristrip.setTextureCoordinateIndices(0, indexes);
   // Set the geometry for the shape
   shape.setGeometry(tristrip);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   mainAppearance = app;
   if (shape != null)
     shape.setAppearance(mainAppearance);
 }
 //
 //  Control grid parameters
 //
 public void setHeights(double[] h) {
   if (h == null)
     zeroHeights();
   else {
     heights = new double[h.length];
     for (int i = 0; i < h.length; i++)
       heights[i] = h[i];
   }
   rebuild();
 }
 public double[] getHeights() {
   return heights;
 }
 public void setXDimension(int xDim) {
   xDimension = xDim;
   rebuild();
 }
 public int getXDimension() {
   return xDimension;
 }
 public void setZDimension(int zDim) {
   zDimension = zDim;
   rebuild();
 }
 public int getZDimension() {
   return zDimension;
 }
 public void setXSpacing(double xSpace) {
   xSpacing = xSpace;
   rebuild();
 }
 public double getXSpacing() {
   return xSpacing;
 }
 public void setZSpacing(double zSpace) {
   zSpacing = zSpace;
   rebuild();
 }
 public double getZSpacing() {
   return zSpacing;
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   return shape;
 }
 public int getNumTriangles() {
   return xDimension * zDimension * 2;
 }
 public int getNumVertices() {
   return xDimension * zDimension;
 }
 /*
  * (non-Javadoc)
  * 
  * @see com.sun.j3d.utils.geometry.Primitive#getAppearance(int)
  */
 public Appearance getAppearance(int arg0) {
   // TODO Auto-generated method stub
   return null;
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class Java3DFrame extends Applet implements WindowListener, ActionListener,

   ItemListener, CheckboxMenuListener {
 //  Navigation types
 public final static int Walk = 0;
 public final static int Examine = 1;
 //  Should the scene be compiled?
 private boolean shouldCompile = true;
 //  GUI objects for our subclasses
 protected Java3DFrame example = null;
 protected Frame exampleFrame = null;
 protected MenuBar exampleMenuBar = null;
 protected Canvas3D exampleCanvas = null;
 protected TransformGroup exampleViewTransform = null;
 protected TransformGroup exampleSceneTransform = null;
 protected boolean debug = false;
 //  Private GUI objects and state
 private boolean headlightOnOff = true;
 private int navigationType = Examine;
 private CheckboxMenuItem headlightMenuItem = null;
 private CheckboxMenuItem walkMenuItem = null;
 private CheckboxMenuItem examineMenuItem = null;
 private DirectionalLight headlight = null;
 private ExamineViewerBehavior examineBehavior = null;
 private WalkViewerBehavior walkBehavior = null;
 //--------------------------------------------------------------
 //  ADMINISTRATION
 //--------------------------------------------------------------
 /**
  * The main program entry point when invoked as an application. Each example
  * application that extends this class must define their own main.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 public static void main(String[] args) {
   Java3DFrame ex = new Java3DFrame();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 /**
  * Constructs a new Example object.
  * 
  * @return a new Example that draws no 3D content
  */
 public Java3DFrame() {
   // Do nothing
 }
 /**
  * Initializes the application when invoked as an applet.
  */
 public void init() {
   // Collect properties into String array
   String[] args = new String[2];
   // NOTE: to be done still...
   this.initialize(args);
   this.buildUniverse();
   this.showFrame();
   // NOTE: add something to the browser page?
 }
 /**
  * Initializes the Example by parsing command-line arguments, building an
  * AWT Frame, constructing a menubar, and creating the 3D canvas.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void initialize(String[] args) {
   example = this;
   // Parse incoming arguments
   parseArgs(args);
   // Build the frame
   if (debug)
     System.err.println("Building GUI...");
   exampleFrame = new Frame();
   exampleFrame.setSize(640, 480);
   exampleFrame.setTitle("Java 3D Example");
   exampleFrame.setLayout(new BorderLayout());
   // Set up a close behavior
   exampleFrame.addWindowListener(this);
   // Create a canvas
   exampleCanvas = new Canvas3D(null);
   exampleCanvas.setSize(630, 460);
   exampleFrame.add("Center", exampleCanvas);
   // Build the menubar
   exampleMenuBar = this.buildMenuBar();
   exampleFrame.setMenuBar(exampleMenuBar);
   // Pack
   exampleFrame.pack();
   exampleFrame.validate();
   //    exampleFrame.setVisible( true );
 }
 /**
  * Parses incoming command-line arguments. Applications that subclass this
  * class may override this method to support their own command-line
  * arguments.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void parseArgs(String[] args) {
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-d"))
       debug = true;
   }
 }
 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 /**
  * Builds the 3D universe by constructing a virtual universe (via
  * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
  * SimpleUniverse). A headlight is added and a set of behaviors initialized
  * to handle navigation types.
  */
 protected void buildUniverse() {
   //
   //  Create a SimpleUniverse object, which builds:
   //
   //    - a Locale using the given hi-res coordinate origin
   //
   //    - a ViewingPlatform which in turn builds:
   //          - a MultiTransformGroup with which to move the
   //            the ViewPlatform about
   //
   //          - a ViewPlatform to hold the view
   //
   //          - a BranchGroup to hold avatar geometry (if any)
   //
   //          - a BranchGroup to hold view platform
   //            geometry (if any)
   //
   //    - a Viewer which in turn builds:
   //          - a PhysicalBody which characterizes the user"s
   //            viewing preferences and abilities
   //
   //          - a PhysicalEnvironment which characterizes the
   //            user"s rendering hardware and software
   //
   //          - a JavaSoundMixer which initializes sound
   //            support within the 3D environment
   //
   //          - a View which renders the scene into a Canvas3D
   //
   //  All of these actions could be done explicitly, but
   //  using the SimpleUniverse utilities simplifies the code.
   //
   if (debug)
     System.err.println("Building scene graph...");
   SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
       // for the origin -
       // use default
       1, // Number of transforms in MultiTransformGroup
       exampleCanvas, // Canvas3D into which to draw
       null); // URL for user configuration file - use defaults
   //
   //  Get the viewer and create an audio device so that
   //  sound will be enabled in this content.
   //
   Viewer viewer = universe.getViewer();
   viewer.createAudioDevice();
   //
   //  Get the viewing platform created by SimpleUniverse.
   //  From that platform, get the inner-most TransformGroup
   //  in the MultiTransformGroup. That inner-most group
   //  contains the ViewPlatform. It is this inner-most
   //  TransformGroup we need in order to:
   //
   //    - add a "headlight" that always aims forward from
   //       the viewer
   //
   //    - change the viewing direction in a "walk" style
   //
   //  The inner-most TransformGroup"s transform will be
   //  changed by the walk behavior (when enabled).
   //
   ViewingPlatform viewingPlatform = universe.getViewingPlatform();
   exampleViewTransform = viewingPlatform.getViewPlatformTransform();
   //
   //  Create a "headlight" as a forward-facing directional light.
   //  Set the light"s bounds to huge. Since we want the light
   //  on the viewer"s "head", we need the light within the
   //  TransformGroup containing the ViewPlatform. The
   //  ViewingPlatform class creates a handy hook to do this
   //  called "platform geometry". The PlatformGeometry class is
   //  subclassed off of BranchGroup, and is intended to contain
   //  a description of the 3D platform itself... PLUS a headlight!
   //  So, to add the headlight, create a new PlatformGeometry group,
   //  add the light to it, then add that platform geometry to the
   //  ViewingPlatform.
   //
   BoundingSphere allBounds = new BoundingSphere(
       new Point3d(0.0, 0.0, 0.0), 100000.0);
   PlatformGeometry pg = new PlatformGeometry();
   headlight = new DirectionalLight();
   headlight.setColor(White);
   headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
   headlight.setInfluencingBounds(allBounds);
   headlight.setCapability(Light.ALLOW_STATE_WRITE);
   pg.addChild(headlight);
   viewingPlatform.setPlatformGeometry(pg);
   //
   //  Create the 3D content BranchGroup, containing:
   //
   //    - a TransformGroup who"s transform the examine behavior
   //      will change (when enabled).
   //
   //    - 3D geometry to view
   //
   // Build the scene root
   BranchGroup sceneRoot = new BranchGroup();
   // Build a transform that we can modify
   exampleSceneTransform = new TransformGroup();
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
   //
   //  Build the scene, add it to the transform, and add
   //  the transform to the scene root
   //
   if (debug)
     System.err.println("  scene...");
   Group scene = this.buildScene();
   exampleSceneTransform.addChild(scene);
   sceneRoot.addChild(exampleSceneTransform);
   //
   //  Create a pair of behaviors to implement two navigation
   //  types:
   //
   //    - "examine": a style where mouse drags rotate about
   //      the scene"s origin as if it is an object under
   //      examination. This is similar to the "Examine"
   //      navigation type used by VRML browsers.
   //
   //    - "walk": a style where mouse drags rotate about
   //      the viewer"s center as if the viewer is turning
   //      about to look at a scene they are in. This is
   //      similar to the "Walk" navigation type used by
   //      VRML browsers.
   //
   //  Aim the examine behavior at the scene"s TransformGroup
   //  and add the behavior to the scene root.
   //
   //  Aim the walk behavior at the viewing platform"s
   //  TransformGroup and add the behavior to the scene root.
   //
   //  Enable one (and only one!) of the two behaviors
   //  depending upon the current navigation type.
   //
   examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
       // gorup
       // to
       // modify
       exampleFrame); // Parent frame for cusor changes
   examineBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(examineBehavior);
   walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
       // group to
       // modify
       exampleFrame); // Parent frame for cusor changes
   walkBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(walkBehavior);
   if (navigationType == Walk) {
     examineBehavior.setEnable(false);
     walkBehavior.setEnable(true);
   } else {
     examineBehavior.setEnable(true);
     walkBehavior.setEnable(false);
   }
   //
   //  Compile the scene branch group and add it to the
   //  SimpleUniverse.
   //
   if (shouldCompile)
     sceneRoot.rupile();
   universe.addBranchGraph(sceneRoot);
   reset();
 }
 /**
  * Builds the scene. Example application subclasses should replace this
  * method with their own method to build 3D content.
  * 
  * @return a Group containing 3D content to display
  */
 public Group buildScene() {
   // Build the scene group containing nothing
   Group scene = new Group();
   return scene;
 }
 //--------------------------------------------------------------
 //  SET/GET METHODS
 //--------------------------------------------------------------
 /**
  * Sets the headlight on/off state. The headlight faces forward in the
  * direction the viewer is facing. Example applications that add their own
  * lights will typically turn the headlight off. A standard menu item
  * enables the headlight to be turned on and off via user control.
  * 
  * @param onOff
  *            a boolean turning the light on (true) or off (false)
  */
 public void setHeadlightEnable(boolean onOff) {
   headlightOnOff = onOff;
   if (headlight != null)
     headlight.setEnable(headlightOnOff);
   if (headlightMenuItem != null)
     headlightMenuItem.setState(headlightOnOff);
 }
 /**
  * Gets the headlight on/off state.
  * 
  * @return a boolean indicating if the headlight is on or off
  */
 public boolean getHeadlightEnable() {
   return headlightOnOff;
 }
 /**
  * Sets the navigation type to be either Examine or Walk. The Examine
  * navigation type sets up behaviors that use mouse drags to rotate and
  * translate scene content as if it is an object held at arm"s length and
  * under examination. The Walk navigation type uses mouse drags to rotate
  * and translate the viewer as if they are walking through the content. The
  * Examine type is the default.
  * 
  * @param nav
  *            either Walk or Examine
  */
 public void setNavigationType(int nav) {
   if (nav == Walk) {
     navigationType = Walk;
     if (walkMenuItem != null)
       walkMenuItem.setState(true);
     if (examineMenuItem != null)
       examineMenuItem.setState(false);
     if (walkBehavior != null)
       walkBehavior.setEnable(true);
     if (examineBehavior != null)
       examineBehavior.setEnable(false);
   } else {
     navigationType = Examine;
     if (walkMenuItem != null)
       walkMenuItem.setState(false);
     if (examineMenuItem != null)
       examineMenuItem.setState(true);
     if (walkBehavior != null)
       walkBehavior.setEnable(false);
     if (examineBehavior != null)
       examineBehavior.setEnable(true);
   }
 }
 /**
  * Gets the current navigation type, returning either Walk or Examine.
  * 
  * @return either Walk or Examine
  */
 public int getNavigationType() {
   return navigationType;
 }
 /**
  * Sets whether the scene graph should be compiled or not. Normally this is
  * always a good idea. For some example applications that use this Example
  * framework, it is useful to disable compilation - particularly when nodes
  * and node components will need to be made un-live in order to make
  * changes. Once compiled, such components can be made un-live, but they are
  * still unchangable unless appropriate capabilities have been set.
  * 
  * @param onOff
  *            a boolean turning compilation on (true) or off (false)
  */
 public void setCompilable(boolean onOff) {
   shouldCompile = onOff;
 }
 /**
  * Gets whether the scene graph will be compiled or not.
  * 
  * @return a boolean indicating if scene graph compilation is on or off
  */
 public boolean getCompilable() {
   return shouldCompile;
 }
 //These methods will be replaced
 //  Set the view position and direction
 public void setViewpoint(Point3f position, Vector3f direction) {
   Transform3D t = new Transform3D();
   t.set(new Vector3f(position));
   exampleViewTransform.setTransform(t);
   // how to set direction?
 }
 //  Reset transforms
 public void reset() {
   Transform3D trans = new Transform3D();
   exampleSceneTransform.setTransform(trans);
   trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
   exampleViewTransform.setTransform(trans);
   setNavigationType(navigationType);
 }
 //
 //  Gets the URL (with file: prepended) for the current directory.
 //  This is a terrible hack needed in the Alpha release of Java3D
 //  in order to build a full path URL for loading sounds with
 //  MediaContainer. When MediaContainer is fully implemented,
 //  it should handle relative path names, but not yet.
 //
 public String getCurrentDirectory() {
   // Create a bogus file so that we can query it"s path
   File dummy = new File("dummy.tmp");
   String dummyPath = dummy.getAbsolutePath();
   // strip "/dummy.tmp" from end of dummyPath and put into "path"
   if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
     int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
     if (index >= 0) {
       int pathLength = index + 5; // pre-pend "file:"
       char[] charPath = new char[pathLength];
       dummyPath.getChars(0, index, charPath, 5);
       String path = new String(charPath, 0, pathLength);
       path = "file:" + path.substring(5, pathLength);
       return path + File.separator;
     }
   }
   return dummyPath + File.separator;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 /**
  * Builds the example AWT Frame menubar. Standard menus and their options
  * are added. Applications that subclass this class should build their
  * menubar additions within their initialize method.
  * 
  * @return a MenuBar for the AWT Frame
  */
 private MenuBar buildMenuBar() {
   // Build the menubar
   MenuBar menuBar = new MenuBar();
   // File menu
   Menu m = new Menu("File");
   m.addActionListener(this);
   m.add("Exit");
   menuBar.add(m);
   // View menu
   m = new Menu("View");
   m.addActionListener(this);
   m.add("Reset view");
   m.addSeparator();
   walkMenuItem = new CheckboxMenuItem("Walk");
   walkMenuItem.addItemListener(this);
   m.add(walkMenuItem);
   examineMenuItem = new CheckboxMenuItem("Examine");
   examineMenuItem.addItemListener(this);
   m.add(examineMenuItem);
   if (navigationType == Walk) {
     walkMenuItem.setState(true);
     examineMenuItem.setState(false);
   } else {
     walkMenuItem.setState(false);
     examineMenuItem.setState(true);
   }
   m.addSeparator();
   headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
   headlightMenuItem.addItemListener(this);
   headlightMenuItem.setState(headlightOnOff);
   m.add(headlightMenuItem);
   menuBar.add(m);
   return menuBar;
 }
 /**
  * Shows the application"s frame, making it and its menubar, 3D canvas, and
  * 3D content visible.
  */
 public void showFrame() {
   exampleFrame.show();
 }
 /**
  * Quits the application.
  */
 public void quit() {
   System.exit(0);
 }
 /**
  * Handles menu selections.
  * 
  * @param event
  *            an ActionEvent indicating what menu action requires handling
  */
 public void actionPerformed(ActionEvent event) {
   String arg = event.getActionCommand();
   if (arg.equals("Reset view"))
     reset();
   else if (arg.equals("Exit"))
     quit();
 }
 /**
  * Handles checkbox items on a CheckboxMenu. The Example class has none of
  * its own, but subclasses may have some.
  * 
  * @param menu
  *            which CheckboxMenu needs action
  * @param check
  *            which CheckboxMenu item has changed
  */
 public void checkboxChanged(CheckboxMenu menu, int check) {
   // None for us
 }
 /**
  * Handles on/off checkbox items on a standard menu.
  * 
  * @param event
  *            an ItemEvent indicating what requires handling
  */
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   boolean state;
   if (src == headlightMenuItem) {
     state = headlightMenuItem.getState();
     headlight.setEnable(state);
   } else if (src == walkMenuItem)
     setNavigationType(Walk);
   else if (src == examineMenuItem)
     setNavigationType(Examine);
 }
 /**
  * Handles a window closing event notifying the application that the user
  * has chosen to close the application without selecting the "Exit" menu
  * item.
  * 
  * @param event
  *            a WindowEvent indicating the window is closing
  */
 public void windowClosing(WindowEvent event) {
   quit();
 }
 public void windowClosed(WindowEvent event) {
 }
 public void windowOpened(WindowEvent event) {
 }
 public void windowIconified(WindowEvent event) {
 }
 public void windowDeiconified(WindowEvent event) {
 }
 public void windowActivated(WindowEvent event) {
 }
 public void windowDeactivated(WindowEvent event) {
 }
 //  Well known colors, positions, and directions
 public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
 public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
 public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
 public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
 public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
 public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
 public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
 public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
 public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
 public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
 public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
 public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
 public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
 public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
 public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
 public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
 public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
 public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
 public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
 public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
 public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
 public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
 public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
 public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
 public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
 public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
 public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);

} // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {

 public void checkboxChanged(CheckboxMenu menu, int check);

} /**

* ExamineViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm"s length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
* 
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
* 
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
* 
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
* 
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {

 // Previous cursor location
 protected int previousX = 0;
 protected int previousY = 0;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ExamineViewerBehavior() {
   super();
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> positive Y-axis rotation
   //   positive Y mouse delta --> positive X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = deltaY * XRotationFactor;
   double yRotationAngle = deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive Y mouse delta --> positive Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaY = y - previousY;
   if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double zTranslationDistance = deltaY * ZTranslationFactor;
   //
   // Build transforms
   //
   translate.set(0.0, 0.0, zTranslationDistance);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   // Can"t happen
 }

} /*

* 
* Copyright (c) 1998 David R. Nadeau
*  
*/

/**

* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
* 
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
*
*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
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 walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}


      </source>
   
  
 
  



ExBluePrint - illustrate use of background images

   <source lang="java">

// //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.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Light; import javax.media.j3d.LineAttributes; import javax.media.j3d.Material; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Switch; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TriangleStripArray; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Cylinder; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExBluePrint extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private Background background = null;
 private Switch shadingSwitch = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current image
   ImageComponent2D image = imageComponents[currentImage];
   // Build the scene root
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create application bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Set the background color and its application bounds
   background = new Background();
   background.setColor(White);
   background.setImage(image);
   background.setCapability(Background.ALLOW_IMAGE_WRITE);
   background.setApplicationBounds(worldBounds);
   scene.addChild(background);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(buildGadget());
   return scene;
 }
 //--------------------------------------------------------------
 //  FOREGROUND AND ANNOTATION CONTENT
 //--------------------------------------------------------------
 //
 //  Build a mechanical gadget including a few gears and a
 //  shaft going through them.
 //
 private Group buildGadget() {
   if (debug)
     System.err.println("  gadget...");
   //
   //  Create two appearances:
   //    wireframeApp: draw as blue wireframe
   //    shadedApp: draw as metalic shaded polygons
   //
   //  Wireframe:
   //    no Material - defaults to coloring attributes color
   //    polygons as lines, with backfaces
   //    thick lines
   Appearance wireframeApp = new Appearance();
   ColoringAttributes wireframeCatt = new ColoringAttributes();
   wireframeCatt.setColor(0.0f, 0.2559f, 0.4213f);
   wireframeCatt.setShadeModel(ColoringAttributes.SHADE_FLAT);
   wireframeApp.setColoringAttributes(wireframeCatt);
   PolygonAttributes wireframePatt = new PolygonAttributes();
   wireframePatt.setPolygonMode(PolygonAttributes.POLYGON_LINE);
   wireframePatt.setCullFace(PolygonAttributes.CULL_NONE);
   wireframeApp.setPolygonAttributes(wireframePatt);
   LineAttributes wireframeLatt = new LineAttributes();
   wireframeLatt.setLineWidth(2.0f);
   wireframeApp.setLineAttributes(wireframeLatt);
   //  Shaded:
   //    silver material
   Appearance shadedApp = new Appearance();
   Material shadedMat = new Material();
   shadedMat.setAmbientColor(0.30f, 0.30f, 0.30f);
   shadedMat.setDiffuseColor(0.30f, 0.30f, 0.50f);
   shadedMat.setSpecularColor(0.60f, 0.60f, 0.80f);
   shadedMat.setShininess(0.10f);
   shadedApp.setMaterial(shadedMat);
   ColoringAttributes shadedCatt = new ColoringAttributes();
   shadedCatt.setShadeModel(ColoringAttributes.SHADE_GOURAUD);
   shadedApp.setColoringAttributes(shadedCatt);
   //
   //  Create a switch group to hold two versions of the
   //  shape: one wireframe, and one shaded
   //
   Transform3D tr = new Transform3D();
   tr.set(new Vector3f(-1.0f, 0.2f, 0.0f));
   TransformGroup gadget = new TransformGroup(tr);
   shadingSwitch = new Switch();
   shadingSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
   Group wireframe = new Group();
   Group shaded = new Group();
   shadingSwitch.addChild(wireframe);
   shadingSwitch.addChild(shaded);
   shadingSwitch.setWhichChild(1); // shaded
   gadget.addChild(shadingSwitch);
   //
   //  Build a gear (wireframe and shaded)
   //
   tr = new Transform3D();
   tr.rotY(Math.PI / 2.0);
   TransformGroup tg = new TransformGroup(tr);
   SpurGear gear = new SpurGearThinBody(24, // tooth count
       1.6f, // pitch circle radius
       0.3f, // shaft radius
       0.08f, // addendum
       0.05f, // dedendum
       0.3f, // gear thickness
       0.28f, // tooth tip thickness
       wireframeApp);// appearance
   tg.addChild(gear);
   wireframe.addChild(tg);
   tg = new TransformGroup(tr);
   gear = new SpurGearThinBody(24, // tooth count
       1.6f, // pitch circle radius
       0.3f, // shaft radius
       0.08f, // addendum
       0.05f, // dedendum
       0.3f, // gear thickness
       0.28f, // tooth tip thickness
       shadedApp); // appearance
   tg.addChild(gear);
   shaded.addChild(tg);
   //
   //  Build another gear (wireframe and shaded)
   //
   tr.rotY(Math.PI / 2.0);
   Vector3f trans = new Vector3f(-0.5f, 0.0f, 0.0f);
   tr.setTranslation(trans);
   tg = new TransformGroup(tr);
   gear = new SpurGearThinBody(30, // tooth count
       2.0f, // pitch circle radius
       0.3f, // shaft radius
       0.08f, // addendum
       0.05f, // dedendum
       0.3f, // gear thickness
       0.28f, // tooth tip thickness
       wireframeApp);// appearance
   tg.addChild(gear);
   wireframe.addChild(tg);
   tg = new TransformGroup(tr);
   gear = new SpurGearThinBody(30, // tooth count
       2.0f, // pitch circle radius
       0.3f, // shaft radius
       0.08f, // addendum
       0.05f, // dedendum
       0.3f, // gear thickness
       0.28f, // tooth tip thickness
       shadedApp); // appearance
   tg.addChild(gear);
   shaded.addChild(tg);
   //
   //  Build a cylindrical shaft (wireframe and shaded)
   //
   tr.rotZ(-Math.PI / 2.0);
   trans = new Vector3f(1.0f, 0.0f, 0.0f);
   tr.setTranslation(trans);
   tg = new TransformGroup(tr);
   Cylinder cyl = new Cylinder(0.3f, // radius
       4.0f, // length
       Primitive.GENERATE_NORMALS, // format
       16, // radial resolution
       1, // length-wise resolution
       wireframeApp);// appearance
   tg.addChild(cyl);
   wireframe.addChild(tg);
   tg = new TransformGroup(tr);
   cyl = new Cylinder(0.3f, // radius
       4.0f, // length
       Primitive.GENERATE_NORMALS, // format
       16, // radial resolution
       1, // length-wise resolution
       shadedApp); // appearance
   tg.addChild(cyl);
   shaded.addChild(tg);
   //
   //  Build shaft teeth (wireframe and shaded)
   //
   tr.rotY(Math.PI / 2.0);
   trans = new Vector3f(2.05f, 0.0f, 0.0f);
   tr.setTranslation(trans);
   tg = new TransformGroup(tr);
   gear = new SpurGear(12, // tooth count
       0.5f, // pitch circle radius
       0.3f, // shaft radius
       0.05f, // addendum
       0.05f, // dedendum
       1.5f, // gear thickness
       0.8f, // tooth tip thickness
       wireframeApp);// appearance
   tg.addChild(gear);
   wireframe.addChild(tg);
   tg = new TransformGroup(tr);
   gear = new SpurGear(12, // tooth count
       0.5f, // pitch circle radius
       0.3f, // shaft radius
       0.05f, // addendum
       0.05f, // dedendum
       1.5f, // gear thickness
       0.8f, // tooth tip thickness
       shadedApp); // appearance
   tg.addChild(gear);
   shaded.addChild(tg);
   return gadget;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExBluePrint ex = new ExBluePrint();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  Image menu choices
 private NameValue[] images = { new NameValue("None", null),
     new NameValue("Blueprint", "blueprint.jpg"), };
 private int currentImage = 0;
 private ImageComponent2D[] imageComponents;
 private CheckboxMenuItem[] imageMenu;
 private int currentAppearance = 0;
 private CheckboxMenuItem[] appearanceMenu;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Blueprint Example");
   //
   //  Add a menubar menu to change parameters
   //    (images)
   //    --------
   //    Wireframe
   //    Shaded
   //
   // Add a menu to select among background and shading options
   Menu m = new Menu("Options");
   imageMenu = new CheckboxMenuItem[images.length];
   for (int i = 0; i < images.length; i++) {
     imageMenu[i] = new CheckboxMenuItem(images[i].name);
     imageMenu[i].addItemListener(this);
     imageMenu[i].setState(false);
     m.add(imageMenu[i]);
   }
   imageMenu[currentImage].setState(true);
   m.addSeparator();
   appearanceMenu = new CheckboxMenuItem[2];
   appearanceMenu[0] = new CheckboxMenuItem("Wireframe");
   appearanceMenu[0].addItemListener(this);
   appearanceMenu[0].setState(false);
   m.add(appearanceMenu[0]);
   appearanceMenu[1] = new CheckboxMenuItem("Shaded");
   appearanceMenu[1].addItemListener(this);
   appearanceMenu[1].setState(true);
   m.add(appearanceMenu[1]);
   exampleMenuBar.add(m);
   // Preload background images
   TextureLoader texLoader = null;
   imageComponents = new ImageComponent2D[images.length];
   String value = null;
   for (int i = 0; i < images.length; i++) {
     value = (String) images[i].value;
     if (value == null) {
       imageComponents[i] = null;
       continue;
     }
     texLoader = new TextureLoader(value, this);
     imageComponents[i] = texLoader.getImage();
   }
 }
 //
 //  Handle checkboxes
 //
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   // Check if it is an image choice
   for (int i = 0; i < imageMenu.length; i++) {
     if (src == imageMenu[i]) {
       // Update the checkboxes
       imageMenu[currentImage].setState(false);
       currentImage = i;
       imageMenu[currentImage].setState(true);
       // Set the background image
       ImageComponent2D image = imageComponents[currentImage];
       background.setImage(image);
       return;
     }
   }
   // Check if it is an appearance choice
   if (src == appearanceMenu[0]) {
     appearanceMenu[1].setState(false);
     shadingSwitch.setWhichChild(0);
     return;
   }
   if (src == appearanceMenu[1]) {
     appearanceMenu[0].setState(false);
     shadingSwitch.setWhichChild(1);
     return;
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }

} /*

* @(#)SpurGearThinBody.java 1.3 98/02/20 14:29:59
* 
* Copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
* 
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear facility.
* Licensee represents and warrants that it will not use or redistribute the
* Software for such purposes.
*/

class SpurGearThinBody extends SpurGear {

 /**
  * Construct a SpurGearThinBody;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  */
 public SpurGearThinBody(int toothCount, float pitchCircleRadius,
     float shaftRadius, float addendum, float dedendum,
     float gearThickness) {
   this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,
       gearThickness, gearThickness, 0.25f, null);
 }
 /**
  * Construct a SpurGearThinBody;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param look
  *            the gear"s appearance
  */
 public SpurGearThinBody(int toothCount, float pitchCircleRadius,
     float shaftRadius, float addendum, float dedendum,
     float gearThickness, Appearance look) {
   this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,
       gearThickness, gearThickness, 0.25f, look);
 }
 /**
  * Construct a SpurGearThinBody;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param toothTipThickness
  *            thickness of the tip of the tooth
  * @param look
  *            the gear"s appearance
  */
 public SpurGearThinBody(int toothCount, float pitchCircleRadius,
     float shaftRadius, float addendum, float dedendum,
     float gearThickness, float toothTipThickness, Appearance look) {
   this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,
       gearThickness, toothTipThickness, 0.25f, look);
 }
 /**
  * Construct a SpurGearThinBody;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param toothTipThickness
  *            thickness of the tip of the tooth
  * @param toothToValleyRatio
  *            ratio of tooth valley to circular pitch (must be <= .25)
  * @param look
  *            the gear"s appearance object
  */
 public SpurGearThinBody(int toothCount, float pitchCircleRadius,
     float shaftRadius, float addendum, float dedendum,
     float gearThickness, float toothTipThickness,
     float toothToValleyAngleRatio, Appearance look) {
   this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,
       gearThickness, toothTipThickness, 0.25f, look,
       0.6f * gearThickness, 0.75f * (pitchCircleRadius - shaftRadius));
 }
 /**
  * Construct a SpurGearThinBody;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param toothTipThickness
  *            thickness of the tip of the tooth
  * @param toothToValleyRatio
  *            ratio of tooth valley to circular pitch (must be <= .25)
  * @param look
  *            the gear"s appearance object
  * @param bodyThickness
  *            the thickness of the gear body
  * @param crossSectionWidth
  *            the width of the depressed portion of the gear"s body
  */
 public SpurGearThinBody(int toothCount, float pitchCircleRadius,
     float shaftRadius, float addendum, float dedendum,
     float gearThickness, float toothTipThickness,
     float toothToValleyAngleRatio, Appearance look,
     float bodyThickness, float crossSectionWidth) {
   super(toothCount, pitchCircleRadius, addendum, dedendum,
       toothToValleyAngleRatio);
   float diskCrossSectionWidth = (rootRadius - shaftRadius - crossSectionWidth) / 2.0f;
   float outerShaftRadius = shaftRadius + diskCrossSectionWidth;
   float innerToothRadius = rootRadius - diskCrossSectionWidth;
   // Generate the gear"s body disks, first by the shaft, then in
   // the body and, lastly, by the teeth
   addBodyDisks(shaftRadius, outerShaftRadius, gearThickness, look);
   addBodyDisks(innerToothRadius, rootRadius, gearThickness, look);
   addBodyDisks(outerShaftRadius, innerToothRadius, bodyThickness, look);
   // Generate the gear"s "shaft" equivalents the two at the teeth
   // and the two at the shaft
   addCylinderSkins(innerToothRadius, gearThickness, InwardNormals, look);
   addCylinderSkins(outerShaftRadius, gearThickness, OutwardNormals, look);
   // Generate the gear"s interior shaft
   addCylinderSkins(shaftRadius, gearThickness, InwardNormals, look);
   // Generate the gear"s teeth
   addTeeth(pitchCircleRadius, rootRadius, outsideRadius, gearThickness,
       toothTipThickness, toothToValleyAngleRatio, look);
 }

} /*

* @(#)SpurGear.java 1.12 98/02/20 14:29:58
* 
* Copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
* 
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear facility.
* Licensee represents and warrants that it will not use or redistribute the
* Software for such purposes.
*/

class SpurGear extends Gear {

 float toothTopAngleIncrement;
 float toothDeclineAngleIncrement;
 float rootRadius;
 float outsideRadius;
 //The angle subtended by the ascending or descending portion of a tooth
 float circularToothEdgeAngle;
 // The angle subtended by a flat (either a tooth top or a valley
 // between teeth
 float circularToothFlatAngle;
 /**
  * internal constructor for SpurGear, used by subclasses to establish
  * SpurGear"s required state
  * 
  * @return a new spur gear that contains sufficient information to continue
  *         building
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param toothToValleyAngleRatio
  *            the ratio of the angle subtended by the tooth to the angle
  *            subtended by the valley (must be <= .25)
  */
 SpurGear(int toothCount, float pitchCircleRadius, float addendum,
     float dedendum, float toothToValleyAngleRatio) {
   super(toothCount);
   // The angle about Z subtended by one tooth and its associated valley
   circularPitchAngle = (float) (2.0 * Math.PI / (double) toothCount);
   // The angle subtended by a flat (either a tooth top or a valley
   // between teeth
   circularToothFlatAngle = circularPitchAngle * toothToValleyAngleRatio;
   //The angle subtended by the ascending or descending portion of a tooth
   circularToothEdgeAngle = circularPitchAngle / 2.0f
       - circularToothFlatAngle;
   // Increment angles
   toothTopAngleIncrement = circularToothEdgeAngle;
   toothDeclineAngleIncrement = toothTopAngleIncrement
       + circularToothFlatAngle;
   toothValleyAngleIncrement = toothDeclineAngleIncrement
       + circularToothEdgeAngle;
   // Differential angles for offsetting to the center of tooth"s top
   // and valley
   toothTopCenterAngle = toothTopAngleIncrement + circularToothFlatAngle
       / 2.0f;
   valleyCenterAngle = toothValleyAngleIncrement + circularToothFlatAngle
       / 2.0f;
   // Gear start differential angle. All gears are constructed with the
   // center of a tooth at Z-axis angle = 0.
   gearStartAngle = -1.0 * toothTopCenterAngle;
   // The radial distance to the root and top of the teeth, respectively
   rootRadius = pitchCircleRadius - dedendum;
   outsideRadius = pitchCircleRadius + addendum;
   // Allow this object to spin. etc.
   this.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
 }
 /**
  * Construct a SpurGear;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  */
 public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,
     float addendum, float dedendum, float gearThickness) {
   this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,
       gearThickness, gearThickness, 0.25f, null);
 }
 /**
  * Construct a SpurGear;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param look
  *            the gear"s appearance
  */
 public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,
     float addendum, float dedendum, float gearThickness, Appearance look) {
   this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,
       gearThickness, gearThickness, 0.25f, look);
 }
 /**
  * Construct a SpurGear;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param toothTipThickness
  *            thickness of the tip of the tooth
  * @param look
  *            the gear"s appearance
  */
 public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,
     float addendum, float dedendum, float gearThickness,
     float toothTipThickness, Appearance look) {
   this(toothCount, pitchCircleRadius, shaftRadius, addendum, dedendum,
       gearThickness, toothTipThickness, 0.25f, look);
 }
 /**
  * Construct a SpurGear;
  * 
  * @return a new spur gear that conforms to the input paramters
  * @param toothCount
  *            number of teeth
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param shaftRadius
  *            radius of hole at center
  * @param addendum
  *            distance from pitch circle to top of teeth
  * @param dedendum
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param toothTipThickness
  *            thickness of the tip of the tooth
  * @param toothToValleyAngleRatio
  *            the ratio of the angle subtended by the tooth to the angle
  *            subtended by the valley (must be <= .25)
  * @param look
  *            the gear"s appearance object
  */
 public SpurGear(int toothCount, float pitchCircleRadius, float shaftRadius,
     float addendum, float dedendum, float gearThickness,
     float toothTipThickness, float toothToValleyAngleRatio,
     Appearance look) {
   this(toothCount, pitchCircleRadius, addendum, dedendum,
       toothToValleyAngleRatio);
   // Generate the gear"s body disks
   addBodyDisks(shaftRadius, rootRadius, gearThickness, look);
   // Generate the gear"s interior shaft
   addCylinderSkins(shaftRadius, gearThickness, InwardNormals, look);
   // Generate the gear"s teeth
   addTeeth(pitchCircleRadius, rootRadius, outsideRadius, gearThickness,
       toothTipThickness, toothToValleyAngleRatio, look);
 }
 /**
  * Construct a SpurGear"s teeth by adding the teeth shape nodes
  * 
  * @param pitchCircleRadius
  *            radius at center of teeth
  * @param rootRadius
  *            distance from pitch circle to top of teeth
  * @param outsideRadius
  *            distance from pitch circle to root of teeth
  * @param gearThickness
  *            thickness of the gear
  * @param toothTipThickness
  *            thickness of the tip of the tooth
  * @param toothToValleyAngleRatio
  *            the ratio of the angle subtended by the tooth to the angle
  *            subtended by the valley (must be <= .25)
  * @param look
  *            the gear"s appearance object
  */
 void addTeeth(float pitchCircleRadius, float rootRadius,
     float outsideRadius, float gearThickness, float toothTipThickness,
     float toothToValleyAngleRatio, Appearance look) {
   int index;
   Shape3D newShape;
   // Temporaries that store start angle for each portion of tooth facet
   double toothStartAngle, toothTopStartAngle, toothDeclineStartAngle, toothValleyStartAngle, nextToothStartAngle;
   // The x and y coordinates at each point of a facet and at each
   // point on the gear: at the shaft, the root of the teeth, and
   // the outer point of the teeth
   float xRoot0, yRoot0;
   float xOuter1, yOuter1;
   float xOuter2, yOuter2;
   float xRoot3, yRoot3;
   float xRoot4, yRoot4;
   // The z coordinates for the gear
   final float frontZ = -0.5f * gearThickness;
   final float rearZ = 0.5f * gearThickness;
   // The z coordinates for the tooth tip of the gear
   final float toothTipFrontZ = -0.5f * toothTipThickness;
   final float toothTipRearZ = 0.5f * toothTipThickness;
   int toothFacetVertexCount; // #(vertices) per tooth facet
   int toothFacetCount; // #(facets) per tooth
   int toothFaceTotalVertexCount; // #(vertices) in all teeth
   int toothFaceStripCount[] = new int[toothCount];
   // per tooth vertex count
   int topVertexCount; // #(vertices) for teeth tops
   int topStripCount[] = new int[1]; // #(vertices) in strip/strip
   // Front and rear facing normals for the teeth faces
   Vector3f frontToothNormal = new Vector3f(0.0f, 0.0f, -1.0f);
   Vector3f rearToothNormal = new Vector3f(0.0f, 0.0f, 1.0f);
   // Normals for teeth tops up incline, tooth top, and down incline
   Vector3f leftNormal = new Vector3f(-1.0f, 0.0f, 0.0f);
   Vector3f rightNormal = new Vector3f(1.0f, 0.0f, 0.0f);
   Vector3f outNormal = new Vector3f(1.0f, 0.0f, 0.0f);
   Vector3f inNormal = new Vector3f(-1.0f, 0.0f, 0.0f);
   // Temporary variables for storing coordinates and vectors
   Point3f coordinate = new Point3f(0.0f, 0.0f, 0.0f);
   Point3f tempCoordinate1 = new Point3f(0.0f, 0.0f, 0.0f);
   Point3f tempCoordinate2 = new Point3f(0.0f, 0.0f, 0.0f);
   Point3f tempCoordinate3 = new Point3f(0.0f, 0.0f, 0.0f);
   Vector3f tempVector1 = new Vector3f(0.0f, 0.0f, 0.0f);
   Vector3f tempVector2 = new Vector3f(0.0f, 0.0f, 0.0f);
   /*
    * Construct the gear"s front facing teeth facets 0______2 / /\ / / \ / / \
    * //___________\ 1 3
    */
   toothFacetVertexCount = 4;
   toothFaceTotalVertexCount = toothFacetVertexCount * toothCount;
   for (int i = 0; i < toothCount; i++)
     toothFaceStripCount[i] = toothFacetVertexCount;
   TriangleStripArray frontGearTeeth = new TriangleStripArray(
       toothFaceTotalVertexCount, GeometryArray.COORDINATES
           | GeometryArray.NORMALS, toothFaceStripCount);
   for (int count = 0; count < toothCount; count++) {
     index = count * toothFacetVertexCount;
     toothStartAngle = gearStartAngle + circularPitchAngle
         * (double) count;
     toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;
     toothDeclineStartAngle = toothStartAngle
         + toothDeclineAngleIncrement;
     toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
     xRoot0 = rootRadius * (float) Math.cos(toothStartAngle);
     yRoot0 = rootRadius * (float) Math.sin(toothStartAngle);
     xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);
     yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);
     xOuter2 = outsideRadius * (float) Math.cos(toothDeclineStartAngle);
     yOuter2 = outsideRadius * (float) Math.sin(toothDeclineStartAngle);
     xRoot3 = rootRadius * (float) Math.cos(toothValleyStartAngle);
     yRoot3 = rootRadius * (float) Math.sin(toothValleyStartAngle);
     tempCoordinate1.set(xRoot0, yRoot0, frontZ);
     tempCoordinate2.set(xRoot3, yRoot3, frontZ);
     tempVector1.sub(tempCoordinate2, tempCoordinate1);
     tempCoordinate2.set(xOuter1, yOuter1, toothTipFrontZ);
     tempVector2.sub(tempCoordinate2, tempCoordinate1);
     frontToothNormal.cross(tempVector1, tempVector2);
     frontToothNormal.normalize();
     coordinate.set(xOuter1, yOuter1, toothTipFrontZ);
     frontGearTeeth.setCoordinate(index, coordinate);
     frontGearTeeth.setNormal(index, frontToothNormal);
     coordinate.set(xRoot0, yRoot0, frontZ);
     frontGearTeeth.setCoordinate(index + 1, coordinate);
     frontGearTeeth.setNormal(index + 1, frontToothNormal);
     coordinate.set(xOuter2, yOuter2, toothTipFrontZ);
     frontGearTeeth.setCoordinate(index + 2, coordinate);
     frontGearTeeth.setNormal(index + 2, frontToothNormal);
     coordinate.set(xRoot3, yRoot3, frontZ);
     frontGearTeeth.setCoordinate(index + 3, coordinate);
     frontGearTeeth.setNormal(index + 3, frontToothNormal);
   }
   newShape = new Shape3D(frontGearTeeth, look);
   this.addChild(newShape);
   /*
    * Construct the gear"s rear facing teeth facets (Using Quads) 1______2 / \ / \ / \
    * /____________\ 0 3
    */
   toothFacetVertexCount = 4;
   toothFaceTotalVertexCount = toothFacetVertexCount * toothCount;
   QuadArray rearGearTeeth = new QuadArray(toothCount
       * toothFacetVertexCount, GeometryArray.COORDINATES
       | GeometryArray.NORMALS);
   for (int count = 0; count < toothCount; count++) {
     index = count * toothFacetVertexCount;
     toothStartAngle = gearStartAngle + circularPitchAngle
         * (double) count;
     toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;
     toothDeclineStartAngle = toothStartAngle
         + toothDeclineAngleIncrement;
     toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
     xRoot0 = rootRadius * (float) Math.cos(toothStartAngle);
     yRoot0 = rootRadius * (float) Math.sin(toothStartAngle);
     xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);
     yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);
     xOuter2 = outsideRadius * (float) Math.cos(toothDeclineStartAngle);
     yOuter2 = outsideRadius * (float) Math.sin(toothDeclineStartAngle);
     xRoot3 = rootRadius * (float) Math.cos(toothValleyStartAngle);
     yRoot3 = rootRadius * (float) Math.sin(toothValleyStartAngle);
     tempCoordinate1.set(xRoot0, yRoot0, rearZ);
     tempCoordinate2.set(xRoot3, yRoot3, rearZ);
     tempVector1.sub(tempCoordinate2, tempCoordinate1);
     tempCoordinate2.set(xOuter1, yOuter1, toothTipRearZ);
     tempVector2.sub(tempCoordinate2, tempCoordinate1);
     rearToothNormal.cross(tempVector2, tempVector1);
     rearToothNormal.normalize();
     coordinate.set(xRoot0, yRoot0, rearZ);
     rearGearTeeth.setCoordinate(index, coordinate);
     rearGearTeeth.setNormal(index, rearToothNormal);
     coordinate.set(xOuter1, yOuter1, toothTipRearZ);
     rearGearTeeth.setCoordinate(index + 1, coordinate);
     rearGearTeeth.setNormal(index + 1, rearToothNormal);
     coordinate.set(xOuter2, yOuter2, toothTipRearZ);
     rearGearTeeth.setCoordinate(index + 2, coordinate);
     rearGearTeeth.setNormal(index + 2, rearToothNormal);
     coordinate.set(xRoot3, yRoot3, rearZ);
     rearGearTeeth.setCoordinate(index + 3, coordinate);
     rearGearTeeth.setNormal(index + 3, rearToothNormal);
   }
   newShape = new Shape3D(rearGearTeeth, look);
   this.addChild(newShape);
   /*
    * Construct the gear"s top teeth faces (As seen from above) Root0
    * Outer1 Outer2 Root3 Root4 (RearZ) 0_______3 2_______5 4_______7
    * 6_______9 |0 3| |4 7| |8 11| |12 15| | | | | | | | | | | | | | | | |
    * |1_____2| |5_____6| |9____10| |13___14| 1 2 3 4 5 6 7 8 Root0 Outer1
    * Outer2 Root3 Root4 (FrontZ)
    * 
    * Quad 0123 uses a left normal Quad 2345 uses an out normal Quad 4567
    * uses a right normal Quad 6789 uses an out normal
    */
   topVertexCount = 8 * toothCount + 2;
   topStripCount[0] = topVertexCount;
   toothFacetVertexCount = 4;
   toothFacetCount = 4;
   QuadArray topGearTeeth = new QuadArray(toothCount
       * toothFacetVertexCount * toothFacetCount,
       GeometryArray.COORDINATES | GeometryArray.NORMALS);
   for (int count = 0; count < toothCount; count++) {
     index = count * toothFacetCount * toothFacetVertexCount;
     toothStartAngle = gearStartAngle + circularPitchAngle
         * (double) count;
     toothTopStartAngle = toothStartAngle + toothTopAngleIncrement;
     toothDeclineStartAngle = toothStartAngle
         + toothDeclineAngleIncrement;
     toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
     nextToothStartAngle = toothStartAngle + circularPitchAngle;
     xRoot0 = rootRadius * (float) Math.cos(toothStartAngle);
     yRoot0 = rootRadius * (float) Math.sin(toothStartAngle);
     xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);
     yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);
     xOuter2 = outsideRadius * (float) Math.cos(toothDeclineStartAngle);
     yOuter2 = outsideRadius * (float) Math.sin(toothDeclineStartAngle);
     xRoot3 = rootRadius * (float) Math.cos(toothValleyStartAngle);
     yRoot3 = rootRadius * (float) Math.sin(toothValleyStartAngle);
     xRoot4 = rootRadius * (float) Math.cos(nextToothStartAngle);
     yRoot4 = rootRadius * (float) Math.sin(nextToothStartAngle);
     // Compute normal for quad 1
     tempCoordinate1.set(xRoot0, yRoot0, frontZ);
     tempCoordinate2.set(xOuter1, yOuter1, toothTipFrontZ);
     tempVector1.sub(tempCoordinate2, tempCoordinate1);
     leftNormal.cross(frontNormal, tempVector1);
     leftNormal.normalize();
     // Coordinate labeled 0 in the quad
     coordinate.set(xRoot0, yRoot0, rearZ);
     topGearTeeth.setCoordinate(index, coordinate);
     topGearTeeth.setNormal(index, leftNormal);
     // Coordinate labeled 1 in the quad
     coordinate.set(tempCoordinate1);
     topGearTeeth.setCoordinate(index + 1, coordinate);
     topGearTeeth.setNormal(index + 1, leftNormal);
     // Coordinate labeled 2 in the quad
     topGearTeeth.setCoordinate(index + 2, tempCoordinate2);
     topGearTeeth.setNormal(index + 2, leftNormal);
     topGearTeeth.setCoordinate(index + 5, tempCoordinate2);
     // Coordinate labeled 3 in the quad
     coordinate.set(xOuter1, yOuter1, toothTipRearZ);
     topGearTeeth.setCoordinate(index + 3, coordinate);
     topGearTeeth.setNormal(index + 3, leftNormal);
     topGearTeeth.setCoordinate(index + 4, coordinate);
     // Compute normal for quad 2
     tempCoordinate1.set(xOuter1, yOuter1, toothTipFrontZ);
     tempCoordinate2.set(xOuter2, yOuter2, toothTipFrontZ);
     tempVector1.sub(tempCoordinate2, tempCoordinate1);
     outNormal.cross(frontNormal, tempVector1);
     outNormal.normalize();
     topGearTeeth.setNormal(index + 4, outNormal);
     topGearTeeth.setNormal(index + 5, outNormal);
     // Coordinate labeled 4 in the quad
     topGearTeeth.setCoordinate(index + 6, tempCoordinate2);
     topGearTeeth.setNormal(index + 6, outNormal);
     topGearTeeth.setCoordinate(index + 9, tempCoordinate2);
     // Coordinate labeled 5 in the quad
     coordinate.set(xOuter2, yOuter2, toothTipRearZ);
     topGearTeeth.setCoordinate(index + 7, coordinate);
     topGearTeeth.setNormal(index + 7, outNormal);
     topGearTeeth.setCoordinate(index + 8, coordinate);
     // Compute normal for quad 3
     tempCoordinate1.set(xOuter2, yOuter2, toothTipFrontZ);
     tempCoordinate2.set(xRoot3, yRoot3, frontZ);
     tempVector1.sub(tempCoordinate2, tempCoordinate1);
     rightNormal.cross(frontNormal, tempVector1);
     rightNormal.normalize();
     topGearTeeth.setNormal(index + 8, rightNormal);
     topGearTeeth.setNormal(index + 9, rightNormal);
     // Coordinate labeled 7 in the quad
     topGearTeeth.setCoordinate(index + 10, tempCoordinate2);
     topGearTeeth.setNormal(index + 10, rightNormal);
     topGearTeeth.setCoordinate(index + 13, tempCoordinate2);
     // Coordinate labeled 6 in the quad
     coordinate.set(xRoot3, yRoot3, rearZ);
     topGearTeeth.setCoordinate(index + 11, coordinate);
     topGearTeeth.setNormal(index + 11, rightNormal);
     topGearTeeth.setCoordinate(index + 12, coordinate);
     // Compute normal for quad 4
     tempCoordinate1.set(xRoot3, yRoot3, frontZ);
     tempCoordinate2.set(xRoot4, yRoot4, frontZ);
     tempVector1.sub(tempCoordinate2, tempCoordinate1);
     outNormal.cross(frontNormal, tempVector1);
     outNormal.normalize();
     topGearTeeth.setNormal(index + 12, outNormal);
     topGearTeeth.setNormal(index + 13, outNormal);
     // Coordinate labeled 9 in the quad
     topGearTeeth.setCoordinate(index + 14, tempCoordinate2);
     topGearTeeth.setNormal(index + 14, outNormal);
     // Coordinate labeled 8 in the quad
     coordinate.set(xRoot4, yRoot4, rearZ);
     topGearTeeth.setCoordinate(index + 15, coordinate);
     topGearTeeth.setNormal(index + 15, outNormal);
     // Prepare for the loop by computing the new normal
     toothTopStartAngle = nextToothStartAngle + toothTopAngleIncrement;
     xOuter1 = outsideRadius * (float) Math.cos(toothTopStartAngle);
     yOuter1 = outsideRadius * (float) Math.sin(toothTopStartAngle);
     tempCoordinate1.set(xRoot4, yRoot4, toothTipFrontZ);
     tempCoordinate2.set(xOuter1, yOuter1, toothTipFrontZ);
     tempVector1.sub(tempCoordinate2, tempCoordinate1);
     leftNormal.cross(frontNormal, tempVector1);
     leftNormal.normalize();
   }
   newShape = new Shape3D(topGearTeeth, look);
   this.addChild(newShape);
 }

} /*

* @(#)Gear.java 1.5 98/02/20 14:29:55
* 
* Copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
* 
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear facility.
* Licensee represents and warrants that it will not use or redistribute the
* Software for such purposes.
*/

class Gear extends javax.media.j3d.TransformGroup {

 // Specifiers determining whether to generate outward facing normals or
 // inward facing normals.
 static final int OutwardNormals = 1;
 static final int InwardNormals = -1;
 // The number of teeth in the gear
 int toothCount;
 // Gear start differential angle. All gears are constructed with the
 // center of a tooth at Z-axis angle = 0.
 double gearStartAngle;
 // The Z-rotation angle to place the tooth center at theta = 0
 float toothTopCenterAngle;
 // The Z-rotation angle to place the valley center at theta = 0
 float valleyCenterAngle;
 // The angle about Z subtended by one tooth and its associated valley
 float circularPitchAngle;
 // Increment angles
 float toothValleyAngleIncrement;
 // Front and rear facing normals for the gear"s body
 final Vector3f frontNormal = new Vector3f(0.0f, 0.0f, -1.0f);
 final Vector3f rearNormal = new Vector3f(0.0f, 0.0f, 1.0f);
 Gear(int toothCount) {
   this.toothCount = toothCount;
 }
 void addBodyDisks(float shaftRadius, float bodyOuterRadius,
     float thickness, Appearance look) {
   int gearBodySegmentVertexCount; // #(segments) per tooth-unit
   int gearBodyTotalVertexCount; // #(vertices) in a gear face
   int gearBodyStripCount[] = new int[1]; // per strip (1) vertex count
   // A ray from the gear center, used in normal calculations
   float xDirection, yDirection;
   // The x and y coordinates at each point of a facet and at each
   // point on the gear: at the shaft, the root of the teeth, and
   // the outer point of the teeth
   float xRoot0, yRoot0, xShaft0, yShaft0;
   float xRoot3, yRoot3, xShaft3, yShaft3;
   float xRoot4, yRoot4, xShaft4, yShaft4;
   // Temporary variables for storing coordinates and vectors
   Point3f coordinate = new Point3f(0.0f, 0.0f, 0.0f);
   // Gear start differential angle. All gears are constructed with the
   // center of a tooth at Z-axis angle = 0.
   double gearStartAngle = -1.0 * toothTopCenterAngle;
   // Temporaries that store start angle for each portion of tooth facet
   double toothStartAngle, toothTopStartAngle, toothDeclineStartAngle, toothValleyStartAngle, nextToothStartAngle;
   Shape3D newShape;
   int index;
   // The z coordinates for the body disks
   final float frontZ = -0.5f * thickness;
   final float rearZ = 0.5f * thickness;
   /*
    * Construct the gear"s front body (front facing torus disk) __2__ - | -
    * 4 - /| /- / / | /| \ 0\ / | / / > \ / | / | > \ / | / / | \ / ____|/ | >
    * \-- --__/ | 1 3 5
    *  
    */
   gearBodySegmentVertexCount = 4;
   gearBodyTotalVertexCount = 2 + gearBodySegmentVertexCount * toothCount;
   gearBodyStripCount[0] = gearBodyTotalVertexCount;
   TriangleStripArray frontGearBody = new TriangleStripArray(
       gearBodyTotalVertexCount, GeometryArray.COORDINATES
           | GeometryArray.NORMALS, gearBodyStripCount);
   xDirection = (float) Math.cos(gearStartAngle);
   yDirection = (float) Math.sin(gearStartAngle);
   xShaft0 = shaftRadius * xDirection;
   yShaft0 = shaftRadius * yDirection;
   xRoot0 = bodyOuterRadius * xDirection;
   yRoot0 = bodyOuterRadius * yDirection;
   coordinate.set(xRoot0, yRoot0, frontZ);
   frontGearBody.setCoordinate(0, coordinate);
   frontGearBody.setNormal(0, frontNormal);
   coordinate.set(xShaft0, yShaft0, frontZ);
   frontGearBody.setCoordinate(1, coordinate);
   frontGearBody.setNormal(1, frontNormal);
   for (int count = 0; count < toothCount; count++) {
     index = 2 + count * 4;
     toothStartAngle = gearStartAngle + circularPitchAngle
         * (double) count;
     toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
     nextToothStartAngle = toothStartAngle + circularPitchAngle;
     xDirection = (float) Math.cos(toothValleyStartAngle);
     yDirection = (float) Math.sin(toothValleyStartAngle);
     xShaft3 = shaftRadius * xDirection;
     yShaft3 = shaftRadius * yDirection;
     xRoot3 = bodyOuterRadius * xDirection;
     yRoot3 = bodyOuterRadius * yDirection;
     xDirection = (float) Math.cos(nextToothStartAngle);
     yDirection = (float) Math.sin(nextToothStartAngle);
     xShaft4 = shaftRadius * xDirection;
     yShaft4 = shaftRadius * yDirection;
     xRoot4 = bodyOuterRadius * xDirection;
     yRoot4 = bodyOuterRadius * yDirection;
     coordinate.set(xRoot3, yRoot3, frontZ);
     frontGearBody.setCoordinate(index, coordinate);
     frontGearBody.setNormal(index, frontNormal);
     coordinate.set(xShaft3, yShaft3, frontZ);
     frontGearBody.setCoordinate(index + 1, coordinate);
     frontGearBody.setNormal(index + 1, frontNormal);
     coordinate.set(xRoot4, yRoot4, frontZ);
     frontGearBody.setCoordinate(index + 2, coordinate);
     frontGearBody.setNormal(index + 2, frontNormal);
     coordinate.set(xShaft4, yShaft4, frontZ);
     frontGearBody.setCoordinate(index + 3, coordinate);
     frontGearBody.setNormal(index + 3, frontNormal);
   }
   newShape = new Shape3D(frontGearBody, look);
   this.addChild(newShape);
   // Construct the gear"s rear body (rear facing torus disc)
   TriangleStripArray rearGearBody = new TriangleStripArray(
       gearBodyTotalVertexCount, GeometryArray.COORDINATES
           | GeometryArray.NORMALS, gearBodyStripCount);
   xDirection = (float) Math.cos(gearStartAngle);
   yDirection = (float) Math.sin(gearStartAngle);
   xShaft0 = shaftRadius * xDirection;
   yShaft0 = shaftRadius * yDirection;
   xRoot0 = bodyOuterRadius * xDirection;
   yRoot0 = bodyOuterRadius * yDirection;
   coordinate.set(xShaft0, yShaft0, rearZ);
   rearGearBody.setCoordinate(0, coordinate);
   rearGearBody.setNormal(0, rearNormal);
   coordinate.set(xRoot0, yRoot0, rearZ);
   rearGearBody.setCoordinate(1, coordinate);
   rearGearBody.setNormal(1, rearNormal);
   for (int count = 0; count < toothCount; count++) {
     index = 2 + count * 4;
     toothStartAngle = gearStartAngle + circularPitchAngle
         * (double) count;
     toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
     nextToothStartAngle = toothStartAngle + circularPitchAngle;
     xDirection = (float) Math.cos(toothValleyStartAngle);
     yDirection = (float) Math.sin(toothValleyStartAngle);
     xShaft3 = shaftRadius * xDirection;
     yShaft3 = shaftRadius * yDirection;
     xRoot3 = bodyOuterRadius * xDirection;
     yRoot3 = bodyOuterRadius * yDirection;
     xDirection = (float) Math.cos(nextToothStartAngle);
     yDirection = (float) Math.sin(nextToothStartAngle);
     xShaft4 = shaftRadius * xDirection;
     yShaft4 = shaftRadius * yDirection;
     xRoot4 = bodyOuterRadius * xDirection;
     yRoot4 = bodyOuterRadius * yDirection;
     coordinate.set(xShaft3, yShaft3, rearZ);
     rearGearBody.setCoordinate(index, coordinate);
     rearGearBody.setNormal(index, rearNormal);
     coordinate.set(xRoot3, yRoot3, rearZ);
     rearGearBody.setCoordinate(index + 1, coordinate);
     rearGearBody.setNormal(index + 1, rearNormal);
     coordinate.set(xShaft4, yShaft4, rearZ);
     rearGearBody.setCoordinate(index + 2, coordinate);
     rearGearBody.setNormal(index + 2, rearNormal);
     coordinate.set(xRoot4, yRoot4, rearZ);
     rearGearBody.setCoordinate(index + 3, coordinate);
     rearGearBody.setNormal(index + 3, rearNormal);
   }
   newShape = new Shape3D(rearGearBody, look);
   this.addChild(newShape);
 }
 void addCylinderSkins(float shaftRadius, float length, int normalDirection,
     Appearance look) {
   int insideShaftVertexCount; // #(vertices) for shaft
   int insideShaftStripCount[] = new int[1]; // #(vertices) in strip/strip
   double toothStartAngle, nextToothStartAngle, toothValleyStartAngle;
   // A ray from the gear center, used in normal calculations
   float xDirection, yDirection;
   // The z coordinates for the body disks
   final float frontZ = -0.5f * length;
   final float rearZ = 0.5f * length;
   // Temporary variables for storing coordinates, points, and vectors
   float xShaft3, yShaft3, xShaft4, yShaft4;
   Point3f coordinate = new Point3f(0.0f, 0.0f, 0.0f);
   Vector3f surfaceNormal = new Vector3f();
   Shape3D newShape;
   int index;
   int firstIndex;
   int secondIndex;
   /*
    * Construct gear"s inside shaft cylinder First the tooth"s up, flat
    * outer, and down distances Second the tooth"s flat inner distance
    * 
    * Outward facing vertex order: 0_______2____4 | /| /| | / | / | | / | / |
    * |/______|/___| 1 3 5
    * 
    * Inward facing vertex order: 1_______3____5 |\ |\ | | \ | \ | | \ | \ |
    * |______\|___\| 0 2 4
    */
   insideShaftVertexCount = 4 * toothCount + 2;
   insideShaftStripCount[0] = insideShaftVertexCount;
   TriangleStripArray insideShaft = new TriangleStripArray(
       insideShaftVertexCount, GeometryArray.COORDINATES
           | GeometryArray.NORMALS, insideShaftStripCount);
   xShaft3 = shaftRadius * (float) Math.cos(gearStartAngle);
   yShaft3 = shaftRadius * (float) Math.sin(gearStartAngle);
   if (normalDirection == OutwardNormals) {
     surfaceNormal.set(1.0f, 0.0f, 0.0f);
     firstIndex = 1;
     secondIndex = 0;
   } else {
     surfaceNormal.set(-1.0f, 0.0f, 0.0f);
     firstIndex = 0;
     secondIndex = 1;
   }
   // Coordinate labeled 0 in the strip
   coordinate.set(shaftRadius, 0.0f, frontZ);
   insideShaft.setCoordinate(firstIndex, coordinate);
   insideShaft.setNormal(firstIndex, surfaceNormal);
   // Coordinate labeled 1 in the strip
   coordinate.set(shaftRadius, 0.0f, rearZ);
   insideShaft.setCoordinate(secondIndex, coordinate);
   insideShaft.setNormal(secondIndex, surfaceNormal);
   for (int count = 0; count < toothCount; count++) {
     index = 2 + count * 4;
     toothStartAngle = circularPitchAngle * (double) count;
     toothValleyStartAngle = toothStartAngle + toothValleyAngleIncrement;
     nextToothStartAngle = toothStartAngle + circularPitchAngle;
     xDirection = (float) Math.cos(toothValleyStartAngle);
     yDirection = (float) Math.sin(toothValleyStartAngle);
     xShaft3 = shaftRadius * xDirection;
     yShaft3 = shaftRadius * yDirection;
     if (normalDirection == OutwardNormals)
       surfaceNormal.set(xDirection, yDirection, 0.0f);
     else
       surfaceNormal.set(-xDirection, -yDirection, 0.0f);
     // Coordinate labeled 2 in the strip
     coordinate.set(xShaft3, yShaft3, frontZ);
     insideShaft.setCoordinate(index + firstIndex, coordinate);
     insideShaft.setNormal(index + firstIndex, surfaceNormal);
     // Coordinate labeled 3 in the strip
     coordinate.set(xShaft3, yShaft3, rearZ);
     insideShaft.setCoordinate(index + secondIndex, coordinate);
     insideShaft.setNormal(index + secondIndex, surfaceNormal);
     xDirection = (float) Math.cos(nextToothStartAngle);
     yDirection = (float) Math.sin(nextToothStartAngle);
     xShaft4 = shaftRadius * xDirection;
     yShaft4 = shaftRadius * yDirection;
     if (normalDirection == OutwardNormals)
       surfaceNormal.set(xDirection, yDirection, 0.0f);
     else
       surfaceNormal.set(-xDirection, -yDirection, 0.0f);
     // Coordinate labeled 4 in the strip
     coordinate.set(xShaft4, yShaft4, frontZ);
     insideShaft.setCoordinate(index + 2 + firstIndex, coordinate);
     insideShaft.setNormal(index + 2 + firstIndex, surfaceNormal);
     // Coordinate labeled 5 in the strip
     coordinate.set(xShaft4, yShaft4, rearZ);
     insideShaft.setCoordinate(index + 2 + secondIndex, coordinate);
     insideShaft.setNormal(index + 2 + secondIndex, surfaceNormal);
   }
   newShape = new Shape3D(insideShaft, look);
   this.addChild(newShape);
 }
 public float getToothTopCenterAngle() {
   return toothTopCenterAngle;
 }
 public float getValleyCenterAngle() {
   return valleyCenterAngle;
 }
 public float getCircularPitchAngle() {
   return circularPitchAngle;
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class Java3DFrame extends Applet implements WindowListener, ActionListener,

   ItemListener, CheckboxMenuListener {
 //  Navigation types
 public final static int Walk = 0;
 public final static int Examine = 1;
 //  Should the scene be compiled?
 private boolean shouldCompile = true;
 //  GUI objects for our subclasses
 protected Java3DFrame example = null;
 protected Frame exampleFrame = null;
 protected MenuBar exampleMenuBar = null;
 protected Canvas3D exampleCanvas = null;
 protected TransformGroup exampleViewTransform = null;
 protected TransformGroup exampleSceneTransform = null;
 protected boolean debug = false;
 //  Private GUI objects and state
 private boolean headlightOnOff = true;
 private int navigationType = Examine;
 private CheckboxMenuItem headlightMenuItem = null;
 private CheckboxMenuItem walkMenuItem = null;
 private CheckboxMenuItem examineMenuItem = null;
 private DirectionalLight headlight = null;
 private ExamineViewerBehavior examineBehavior = null;
 private WalkViewerBehavior walkBehavior = null;
 //--------------------------------------------------------------
 //  ADMINISTRATION
 //--------------------------------------------------------------
 /**
  * The main program entry point when invoked as an application. Each example
  * application that extends this class must define their own main.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 public static void main(String[] args) {
   Java3DFrame ex = new Java3DFrame();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 /**
  * Constructs a new Example object.
  * 
  * @return a new Example that draws no 3D content
  */
 public Java3DFrame() {
   // Do nothing
 }
 /**
  * Initializes the application when invoked as an applet.
  */
 public void init() {
   // Collect properties into String array
   String[] args = new String[2];
   // NOTE: to be done still...
   this.initialize(args);
   this.buildUniverse();
   this.showFrame();
   // NOTE: add something to the browser page?
 }
 /**
  * Initializes the Example by parsing command-line arguments, building an
  * AWT Frame, constructing a menubar, and creating the 3D canvas.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void initialize(String[] args) {
   example = this;
   // Parse incoming arguments
   parseArgs(args);
   // Build the frame
   if (debug)
     System.err.println("Building GUI...");
   exampleFrame = new Frame();
   exampleFrame.setSize(640, 480);
   exampleFrame.setTitle("Java 3D Example");
   exampleFrame.setLayout(new BorderLayout());
   // Set up a close behavior
   exampleFrame.addWindowListener(this);
   // Create a canvas
   exampleCanvas = new Canvas3D(null);
   exampleCanvas.setSize(630, 460);
   exampleFrame.add("Center", exampleCanvas);
   // Build the menubar
   exampleMenuBar = this.buildMenuBar();
   exampleFrame.setMenuBar(exampleMenuBar);
   // Pack
   exampleFrame.pack();
   exampleFrame.validate();
   //    exampleFrame.setVisible( true );
 }
 /**
  * Parses incoming command-line arguments. Applications that subclass this
  * class may override this method to support their own command-line
  * arguments.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void parseArgs(String[] args) {
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-d"))
       debug = true;
   }
 }
 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 /**
  * Builds the 3D universe by constructing a virtual universe (via
  * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
  * SimpleUniverse). A headlight is added and a set of behaviors initialized
  * to handle navigation types.
  */
 protected void buildUniverse() {
   //
   //  Create a SimpleUniverse object, which builds:
   //
   //    - a Locale using the given hi-res coordinate origin
   //
   //    - a ViewingPlatform which in turn builds:
   //          - a MultiTransformGroup with which to move the
   //            the ViewPlatform about
   //
   //          - a ViewPlatform to hold the view
   //
   //          - a BranchGroup to hold avatar geometry (if any)
   //
   //          - a BranchGroup to hold view platform
   //            geometry (if any)
   //
   //    - a Viewer which in turn builds:
   //          - a PhysicalBody which characterizes the user"s
   //            viewing preferences and abilities
   //
   //          - a PhysicalEnvironment which characterizes the
   //            user"s rendering hardware and software
   //
   //          - a JavaSoundMixer which initializes sound
   //            support within the 3D environment
   //
   //          - a View which renders the scene into a Canvas3D
   //
   //  All of these actions could be done explicitly, but
   //  using the SimpleUniverse utilities simplifies the code.
   //
   if (debug)
     System.err.println("Building scene graph...");
   SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
       // for the origin -
       // use default
       1, // Number of transforms in MultiTransformGroup
       exampleCanvas, // Canvas3D into which to draw
       null); // URL for user configuration file - use defaults
   //
   //  Get the viewer and create an audio device so that
   //  sound will be enabled in this content.
   //
   Viewer viewer = universe.getViewer();
   viewer.createAudioDevice();
   //
   //  Get the viewing platform created by SimpleUniverse.
   //  From that platform, get the inner-most TransformGroup
   //  in the MultiTransformGroup. That inner-most group
   //  contains the ViewPlatform. It is this inner-most
   //  TransformGroup we need in order to:
   //
   //    - add a "headlight" that always aims forward from
   //       the viewer
   //
   //    - change the viewing direction in a "walk" style
   //
   //  The inner-most TransformGroup"s transform will be
   //  changed by the walk behavior (when enabled).
   //
   ViewingPlatform viewingPlatform = universe.getViewingPlatform();
   exampleViewTransform = viewingPlatform.getViewPlatformTransform();
   //
   //  Create a "headlight" as a forward-facing directional light.
   //  Set the light"s bounds to huge. Since we want the light
   //  on the viewer"s "head", we need the light within the
   //  TransformGroup containing the ViewPlatform. The
   //  ViewingPlatform class creates a handy hook to do this
   //  called "platform geometry". The PlatformGeometry class is
   //  subclassed off of BranchGroup, and is intended to contain
   //  a description of the 3D platform itself... PLUS a headlight!
   //  So, to add the headlight, create a new PlatformGeometry group,
   //  add the light to it, then add that platform geometry to the
   //  ViewingPlatform.
   //
   BoundingSphere allBounds = new BoundingSphere(
       new Point3d(0.0, 0.0, 0.0), 100000.0);
   PlatformGeometry pg = new PlatformGeometry();
   headlight = new DirectionalLight();
   headlight.setColor(White);
   headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
   headlight.setInfluencingBounds(allBounds);
   headlight.setCapability(Light.ALLOW_STATE_WRITE);
   pg.addChild(headlight);
   viewingPlatform.setPlatformGeometry(pg);
   //
   //  Create the 3D content BranchGroup, containing:
   //
   //    - a TransformGroup who"s transform the examine behavior
   //      will change (when enabled).
   //
   //    - 3D geometry to view
   //
   // Build the scene root
   BranchGroup sceneRoot = new BranchGroup();
   // Build a transform that we can modify
   exampleSceneTransform = new TransformGroup();
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
   //
   //  Build the scene, add it to the transform, and add
   //  the transform to the scene root
   //
   if (debug)
     System.err.println("  scene...");
   Group scene = this.buildScene();
   exampleSceneTransform.addChild(scene);
   sceneRoot.addChild(exampleSceneTransform);
   //
   //  Create a pair of behaviors to implement two navigation
   //  types:
   //
   //    - "examine": a style where mouse drags rotate about
   //      the scene"s origin as if it is an object under
   //      examination. This is similar to the "Examine"
   //      navigation type used by VRML browsers.
   //
   //    - "walk": a style where mouse drags rotate about
   //      the viewer"s center as if the viewer is turning
   //      about to look at a scene they are in. This is
   //      similar to the "Walk" navigation type used by
   //      VRML browsers.
   //
   //  Aim the examine behavior at the scene"s TransformGroup
   //  and add the behavior to the scene root.
   //
   //  Aim the walk behavior at the viewing platform"s
   //  TransformGroup and add the behavior to the scene root.
   //
   //  Enable one (and only one!) of the two behaviors
   //  depending upon the current navigation type.
   //
   examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
       // gorup
       // to
       // modify
       exampleFrame); // Parent frame for cusor changes
   examineBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(examineBehavior);
   walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
       // group to
       // modify
       exampleFrame); // Parent frame for cusor changes
   walkBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(walkBehavior);
   if (navigationType == Walk) {
     examineBehavior.setEnable(false);
     walkBehavior.setEnable(true);
   } else {
     examineBehavior.setEnable(true);
     walkBehavior.setEnable(false);
   }
   //
   //  Compile the scene branch group and add it to the
   //  SimpleUniverse.
   //
   if (shouldCompile)
     sceneRoot.rupile();
   universe.addBranchGraph(sceneRoot);
   reset();
 }
 /**
  * Builds the scene. Example application subclasses should replace this
  * method with their own method to build 3D content.
  * 
  * @return a Group containing 3D content to display
  */
 public Group buildScene() {
   // Build the scene group containing nothing
   Group scene = new Group();
   return scene;
 }
 //--------------------------------------------------------------
 //  SET/GET METHODS
 //--------------------------------------------------------------
 /**
  * Sets the headlight on/off state. The headlight faces forward in the
  * direction the viewer is facing. Example applications that add their own
  * lights will typically turn the headlight off. A standard menu item
  * enables the headlight to be turned on and off via user control.
  * 
  * @param onOff
  *            a boolean turning the light on (true) or off (false)
  */
 public void setHeadlightEnable(boolean onOff) {
   headlightOnOff = onOff;
   if (headlight != null)
     headlight.setEnable(headlightOnOff);
   if (headlightMenuItem != null)
     headlightMenuItem.setState(headlightOnOff);
 }
 /**
  * Gets the headlight on/off state.
  * 
  * @return a boolean indicating if the headlight is on or off
  */
 public boolean getHeadlightEnable() {
   return headlightOnOff;
 }
 /**
  * Sets the navigation type to be either Examine or Walk. The Examine
  * navigation type sets up behaviors that use mouse drags to rotate and
  * translate scene content as if it is an object held at arm"s length and
  * under examination. The Walk navigation type uses mouse drags to rotate
  * and translate the viewer as if they are walking through the content. The
  * Examine type is the default.
  * 
  * @param nav
  *            either Walk or Examine
  */
 public void setNavigationType(int nav) {
   if (nav == Walk) {
     navigationType = Walk;
     if (walkMenuItem != null)
       walkMenuItem.setState(true);
     if (examineMenuItem != null)
       examineMenuItem.setState(false);
     if (walkBehavior != null)
       walkBehavior.setEnable(true);
     if (examineBehavior != null)
       examineBehavior.setEnable(false);
   } else {
     navigationType = Examine;
     if (walkMenuItem != null)
       walkMenuItem.setState(false);
     if (examineMenuItem != null)
       examineMenuItem.setState(true);
     if (walkBehavior != null)
       walkBehavior.setEnable(false);
     if (examineBehavior != null)
       examineBehavior.setEnable(true);
   }
 }
 /**
  * Gets the current navigation type, returning either Walk or Examine.
  * 
  * @return either Walk or Examine
  */
 public int getNavigationType() {
   return navigationType;
 }
 /**
  * Sets whether the scene graph should be compiled or not. Normally this is
  * always a good idea. For some example applications that use this Example
  * framework, it is useful to disable compilation - particularly when nodes
  * and node components will need to be made un-live in order to make
  * changes. Once compiled, such components can be made un-live, but they are
  * still unchangable unless appropriate capabilities have been set.
  * 
  * @param onOff
  *            a boolean turning compilation on (true) or off (false)
  */
 public void setCompilable(boolean onOff) {
   shouldCompile = onOff;
 }
 /**
  * Gets whether the scene graph will be compiled or not.
  * 
  * @return a boolean indicating if scene graph compilation is on or off
  */
 public boolean getCompilable() {
   return shouldCompile;
 }
 //These methods will be replaced
 //  Set the view position and direction
 public void setViewpoint(Point3f position, Vector3f direction) {
   Transform3D t = new Transform3D();
   t.set(new Vector3f(position));
   exampleViewTransform.setTransform(t);
   // how to set direction?
 }
 //  Reset transforms
 public void reset() {
   Transform3D trans = new Transform3D();
   exampleSceneTransform.setTransform(trans);
   trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
   exampleViewTransform.setTransform(trans);
   setNavigationType(navigationType);
 }
 //
 //  Gets the URL (with file: prepended) for the current directory.
 //  This is a terrible hack needed in the Alpha release of Java3D
 //  in order to build a full path URL for loading sounds with
 //  MediaContainer. When MediaContainer is fully implemented,
 //  it should handle relative path names, but not yet.
 //
 public String getCurrentDirectory() {
   // Create a bogus file so that we can query it"s path
   File dummy = new File("dummy.tmp");
   String dummyPath = dummy.getAbsolutePath();
   // strip "/dummy.tmp" from end of dummyPath and put into "path"
   if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
     int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
     if (index >= 0) {
       int pathLength = index + 5; // pre-pend "file:"
       char[] charPath = new char[pathLength];
       dummyPath.getChars(0, index, charPath, 5);
       String path = new String(charPath, 0, pathLength);
       path = "file:" + path.substring(5, pathLength);
       return path + File.separator;
     }
   }
   return dummyPath + File.separator;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 /**
  * Builds the example AWT Frame menubar. Standard menus and their options
  * are added. Applications that subclass this class should build their
  * menubar additions within their initialize method.
  * 
  * @return a MenuBar for the AWT Frame
  */
 private MenuBar buildMenuBar() {
   // Build the menubar
   MenuBar menuBar = new MenuBar();
   // File menu
   Menu m = new Menu("File");
   m.addActionListener(this);
   m.add("Exit");
   menuBar.add(m);
   // View menu
   m = new Menu("View");
   m.addActionListener(this);
   m.add("Reset view");
   m.addSeparator();
   walkMenuItem = new CheckboxMenuItem("Walk");
   walkMenuItem.addItemListener(this);
   m.add(walkMenuItem);
   examineMenuItem = new CheckboxMenuItem("Examine");
   examineMenuItem.addItemListener(this);
   m.add(examineMenuItem);
   if (navigationType == Walk) {
     walkMenuItem.setState(true);
     examineMenuItem.setState(false);
   } else {
     walkMenuItem.setState(false);
     examineMenuItem.setState(true);
   }
   m.addSeparator();
   headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
   headlightMenuItem.addItemListener(this);
   headlightMenuItem.setState(headlightOnOff);
   m.add(headlightMenuItem);
   menuBar.add(m);
   return menuBar;
 }
 /**
  * Shows the application"s frame, making it and its menubar, 3D canvas, and
  * 3D content visible.
  */
 public void showFrame() {
   exampleFrame.show();
 }
 /**
  * Quits the application.
  */
 public void quit() {
   System.exit(0);
 }
 /**
  * Handles menu selections.
  * 
  * @param event
  *            an ActionEvent indicating what menu action requires handling
  */
 public void actionPerformed(ActionEvent event) {
   String arg = event.getActionCommand();
   if (arg.equals("Reset view"))
     reset();
   else if (arg.equals("Exit"))
     quit();
 }
 /**
  * Handles checkbox items on a CheckboxMenu. The Example class has none of
  * its own, but subclasses may have some.
  * 
  * @param menu
  *            which CheckboxMenu needs action
  * @param check
  *            which CheckboxMenu item has changed
  */
 public void checkboxChanged(CheckboxMenu menu, int check) {
   // None for us
 }
 /**
  * Handles on/off checkbox items on a standard menu.
  * 
  * @param event
  *            an ItemEvent indicating what requires handling
  */
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   boolean state;
   if (src == headlightMenuItem) {
     state = headlightMenuItem.getState();
     headlight.setEnable(state);
   } else if (src == walkMenuItem)
     setNavigationType(Walk);
   else if (src == examineMenuItem)
     setNavigationType(Examine);
 }
 /**
  * Handles a window closing event notifying the application that the user
  * has chosen to close the application without selecting the "Exit" menu
  * item.
  * 
  * @param event
  *            a WindowEvent indicating the window is closing
  */
 public void windowClosing(WindowEvent event) {
   quit();
 }
 public void windowClosed(WindowEvent event) {
 }
 public void windowOpened(WindowEvent event) {
 }
 public void windowIconified(WindowEvent event) {
 }
 public void windowDeiconified(WindowEvent event) {
 }
 public void windowActivated(WindowEvent event) {
 }
 public void windowDeactivated(WindowEvent event) {
 }
 //  Well known colors, positions, and directions
 public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
 public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
 public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
 public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
 public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
 public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
 public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
 public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
 public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
 public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
 public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
 public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
 public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
 public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
 public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
 public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
 public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
 public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
 public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
 public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
 public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
 public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
 public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
 public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
 public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
 public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
 public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);

} // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {

 public void checkboxChanged(CheckboxMenu menu, int check);

} /**

* ExamineViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm"s length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
* 
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
* 
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
* 
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
* 
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {

 // Previous cursor location
 protected int previousX = 0;
 protected int previousY = 0;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ExamineViewerBehavior() {
   super();
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> positive Y-axis rotation
   //   positive Y mouse delta --> positive X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = deltaY * XRotationFactor;
   double yRotationAngle = deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive Y mouse delta --> positive Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaY = y - previousY;
   if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double zTranslationDistance = deltaY * ZTranslationFactor;
   //
   // Build transforms
   //
   translate.set(0.0, 0.0, zTranslationDistance);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   // Can"t happen
 }

} /*

* 
* Copyright (c) 1998 David R. Nadeau
*  
*/

/**

* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
* 
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
*
*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
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 walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}


      </source>