Mega Code Archive

 
Categories / Java / 3D Graphics
 

Example Switch

/*  *   * Copyright (c) 1998 David R. Nadeau  *    */ import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.Component; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.BitSet; import java.util.Enumeration; import java.util.EventListener; import javax.media.j3d.Appearance; 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.Switch; 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.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.geometry.Sphere; 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 ExSwitch extends Java3DFrame {   //--------------------------------------------------------------   //  SCENE CONTENT   //--------------------------------------------------------------   //   //  Nodes (updated via menu)   //   private Switch swtch = null;   private int currentSwitch = 0;   //   //  Build scene   //   public Group buildScene() {     // Turn on the example headlight     setHeadlightEnable(true);     // Default to walk navigation     setNavigationType(Walk);     // Build the scene group     Group scene = new Group();     if (debug)       System.err.println("  switch shapes...");     // BEGIN EXAMPLE TOPIC     // Build the switch group and allow its switch     // value to be changed via menu items     swtch = new Switch();     swtch.setCapability(Switch.ALLOW_SWITCH_WRITE);     //  Create several shapes to place in a switch group     // Child 0: a red sphere     Appearance app0 = new Appearance();     Material mat0 = new Material();     mat0.setAmbientColor(0.2f, 0.2f, 0.2f);     mat0.setDiffuseColor(1.0f, 0.0f, 0.2f);     mat0.setSpecularColor(0.7f, 0.7f, 0.7f);     app0.setMaterial(mat0);     Transform3D t3d = new Transform3D();     t3d.setTranslation(new Vector3f(-2.0f, 1.5f, 0.0f));     TransformGroup tg0 = new TransformGroup(t3d);     Sphere sph0 = new Sphere(0.5f, // radius         Primitive.GENERATE_NORMALS, // components         16, // facets         app0); // appearance     tg0.addChild(sph0);     swtch.addChild(tg0); // Child 0     // Child 1: a green sphere     Appearance app1 = new Appearance();     Material mat1 = new Material();     mat1.setAmbientColor(0.2f, 0.2f, 0.2f);     mat1.setDiffuseColor(0.0f, 1.0f, 0.0f);     mat1.setSpecularColor(0.7f, 0.7f, 0.7f);     app1.setMaterial(mat1);     t3d.setTranslation(new Vector3f(0.0f, 1.5f, 0.0f));     TransformGroup tg1 = new TransformGroup(t3d);     Sphere sph1 = new Sphere(0.5f, // radius         Primitive.GENERATE_NORMALS, // components         16, // facets         app1); // appearance     tg1.addChild(sph1);     swtch.addChild(tg1); // Child 1     // Child 2: a blue sphere     Appearance app2 = new Appearance();     Material mat2 = new Material();     mat2.setAmbientColor(0.2f, 0.2f, 0.2f);     mat2.setDiffuseColor(0.0f, 0.6f, 1.0f);     mat2.setSpecularColor(0.7f, 0.7f, 0.7f);     app2.setMaterial(mat2);     t3d.setTranslation(new Vector3f(2.0f, 1.5f, 0.0f));     TransformGroup tg2 = new TransformGroup(t3d);     Sphere sph2 = new Sphere(0.5f, // radius         Primitive.GENERATE_NORMALS, // components         16, // facets         app2); // appearance     tg2.addChild(sph2);     swtch.addChild(tg2);     // Set the initial child choice     swtch.setWhichChild(options[currentSwitch].child);     scene.addChild(swtch);     // END EXAMPLE TOPIC     // Build foreground geometry including a floor and     // columns on which the switchable shapes stand     // Load textures     TextureLoader texLoader = new TextureLoader("granite07rev.jpg", this);     Texture columnTex = texLoader.getTexture();     if (columnTex == null)       System.err.println("Cannot load granite07rev.jpg texture");     else {       columnTex.setBoundaryModeS(Texture.WRAP);       columnTex.setBoundaryModeT(Texture.WRAP);       columnTex.setMinFilter(Texture.NICEST);       columnTex.setMagFilter(Texture.NICEST);       columnTex.setMipMapMode(Texture.BASE_LEVEL);       columnTex.setEnable(true);     }     texLoader = new TextureLoader("flooring.jpg", this);     Texture groundTex = texLoader.getTexture();     if (groundTex == null)       System.err.println("Cannot load flooring.jpg texture");     else {       groundTex.setBoundaryModeS(Texture.WRAP);       groundTex.setBoundaryModeT(Texture.WRAP);       groundTex.setMinFilter(Texture.NICEST);       groundTex.setMagFilter(Texture.NICEST);       groundTex.setMipMapMode(Texture.BASE_LEVEL);       groundTex.setEnable(true);     }     //     // Build several columns on the floor     //     if (debug)       System.err.println("  columns...");     SharedGroup column = new SharedGroup();     Appearance columnApp = new Appearance();     Material columnMat = new Material();     columnMat.setAmbientColor(0.6f, 0.6f, 0.6f);     columnMat.setDiffuseColor(1.0f, 1.0f, 1.0f);     columnMat.setSpecularColor(0.0f, 0.0f, 0.0f);     columnApp.setMaterial(columnMat);     TextureAttributes columnTexAtt = new TextureAttributes();     columnTexAtt.setTextureMode(TextureAttributes.MODULATE);     columnTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);     columnApp.setTextureAttributes(columnTexAtt);     if (columnTex != null)       columnApp.setTexture(columnTex);     GothicColumn columnShape = new GothicColumn(1.8f, // height         0.25f, // radius         GothicColumn.BUILD_TOP, // flags         columnApp); // appearance     column.addChild(columnShape);     Vector3f trans = new Vector3f();     Transform3D tr = new Transform3D();     TransformGroup tg;     // Left     trans.set(-2.0f, -1.0f, 0.0f);     tr.set(trans);     tg = new TransformGroup(tr);     tg.addChild(new Link(column));     scene.addChild(tg);     // Middle     trans.set(0.0f, -1.0f, 0.0f);     tr.set(trans);     tg = new TransformGroup(tr);     tg.addChild(new Link(column));     scene.addChild(tg);     // Right     trans.set(2.0f, -1.0f, 0.0f);     tr.set(trans);     tg = new TransformGroup(tr);     tg.addChild(new Link(column));     scene.addChild(tg);     //     //  Add the ground     //     if (debug)       System.err.println("  ground...");     Appearance groundApp = new Appearance();     Material groundMat = new Material();     groundMat.setAmbientColor(0.6f, 0.6f, 0.6f);     groundMat.setDiffuseColor(1.0f, 1.0f, 1.0f);     groundMat.setSpecularColor(0.0f, 0.0f, 0.0f);     groundApp.setMaterial(groundMat);     tr = new Transform3D();     tr.setScale(new Vector3d(4.0, 4.0, 1.0));     TextureAttributes groundTexAtt = new TextureAttributes();     groundTexAtt.setTextureMode(TextureAttributes.MODULATE);     groundTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);     groundTexAtt.setTextureTransform(tr);     groundApp.setTextureAttributes(groundTexAtt);     if (groundTex != null)       groundApp.setTexture(groundTex);     ElevationGrid ground = new ElevationGrid(11, // X dimension         11, // Z dimension         2.0f, // X spacing         2.0f, // Z spacing         // Automatically use zero heights         groundApp); // Appearance     trans.set(0.0f, -1.0f, 0.0f);     tr.set(trans);     tg = new TransformGroup(tr);     tg.addChild(ground);     scene.addChild(tg);     // Add a light     BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,         0.0), // Center         1000.0); // Extent     DirectionalLight light = new DirectionalLight();     light.setEnable(true);     light.setColor(new Color3f(1.0f, 1.0f, 1.0f));     light.setDirection(new Vector3f(0.5f, -1.0f, -0.5f));     light.setInfluencingBounds(worldBounds);     scene.addChild(light);     return scene;   }   //--------------------------------------------------------------   //  USER INTERFACE   //--------------------------------------------------------------   //   //  Main   //   public static void main(String[] args) {     ExSwitch ex = new ExSwitch();     ex.initialize(args);     ex.buildUniverse();     ex.showFrame();   }   //   //  Private class for holding switch options   //   private class NameChildMask {     public String name;     public int child;     public BitSet mask;     public NameChildMask(String n, int c, int m) {       name = n;       child = c;       mask = new BitSet(3);       if ((m & 1) != 0)         mask.set(0);       if ((m & 2) != 0)         mask.set(1);       if ((m & 4) != 0)         mask.set(2);     }   }   //  Switch menu choices   private NameChildMask[] options = {       new NameChildMask("CHILD_ALL", Switch.CHILD_ALL, 0),       new NameChildMask("CHILD_NONE", Switch.CHILD_NONE, 0),       new NameChildMask("Child 0", 0, 0),       new NameChildMask("Child 1", 1, 0),       new NameChildMask("Child 2", 2, 0),       new NameChildMask("Children 0 & 1", Switch.CHILD_MASK, 3),       new NameChildMask("Children 0 & 2", Switch.CHILD_MASK, 5),       new NameChildMask("Children 1 & 2", Switch.CHILD_MASK, 6), };   private CheckboxMenuItem[] switchMenu;   //   //  Initialize the GUI (application and applet)   //   public void initialize(String[] args) {     // Initialize the window, menubar, etc.     super.initialize(args);     exampleFrame.setTitle("Java 3D Switch Example");     // Add a menu to select among switch options     Menu mt = new Menu("Switch");     switchMenu = new CheckboxMenuItem[options.length];     for (int i = 0; i < options.length; i++) {       switchMenu[i] = new CheckboxMenuItem(options[i].name);       switchMenu[i].addItemListener(this);       switchMenu[i].setState(false);       mt.add(switchMenu[i]);     }     exampleMenuBar.add(mt);     currentSwitch = 0;     switchMenu[currentSwitch].setState(true);   }   //   //  Handle checkboxes   //   public void itemStateChanged(ItemEvent event) {     Object src = event.getSource();     // Check if it is switch choice     for (int i = 0; i < switchMenu.length; i++) {       if (src == switchMenu[i]) {         // Update the checkboxes         switchMenu[currentSwitch].setState(false);         currentSwitch = i;         switchMenu[currentSwitch].setState(true);         // Set the switch         swtch.setWhichChild(options[currentSwitch].child);         swtch.setChildMask(options[currentSwitch].mask);         return;       }     }     // Handle all other checkboxes     super.itemStateChanged(event);   } } // //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 //GothicColumn - Gothic-style column used in example scenes // //DESCRIPTION //This class builds a Gothic-column architectural column. // //SEE ALSO //? // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class GothicColumn extends Primitive {   //   //  Construction Flags   //   public final static int BUILD_TAPERED_CROWN = 0x1;   public final static int BUILD_TOP = 0x2;   public final static int BUILD_BOTTOM = 0x4;   //   //  3D nodes   //   private Appearance mainAppearance = null;   //   //  Construct a column   //   public GothicColumn(float height, float radius, Appearance app) {     this(height, radius, 0, app);   }   public GothicColumn(float height, float radius, int flags, Appearance app) {     mainAppearance = app;     // Compute sizes and positions based upon the     // desired main column radius     // Base box     float baseWidth = 2.7f * radius;     float baseDepth = baseWidth;     float baseHeight = 0.75f * radius / 2.0f;     // Base box #2     float base2Width = 0.8f * baseWidth;     float base2Depth = base2Width;     float base2Height = baseHeight / 2.0f;     // Tapered crown     float crownWidth1 = 2.0f * 0.707f * radius;     float crownDepth1 = crownWidth1;     float crownWidth2 = 1.0f * base2Width;     float crownDepth2 = 1.0f * base2Depth;     float crownHeight = 2.0f * baseHeight;     // Box above tapered crown     float crown2Width = 1.1f * base2Width;     float crown2Depth = 1.1f * base2Depth;     float crown2Height = base2Height;     // Final crown box     float crown3Width = 1.1f * baseWidth;     float crown3Depth = 1.1f * baseDepth;     float crown3Height = baseHeight;     // Cylindrical column     //   Extend it up and into the tapered crown     float columnHeight = height - baseHeight - base2Height - crown2Height         - crown3Height;     float columnRadius = radius;     float baseY = baseHeight / 2.0f;     float base2Y = baseHeight + base2Height / 2.0f;     float columnY = baseHeight + base2Height + columnHeight / 2.0f;     float crown2Y = baseHeight + base2Height + columnHeight + crown2Height         / 2.0f;     float crown3Y = baseHeight + base2Height + columnHeight + crown2Height         + crown3Height / 2.0f;     float crownY = crown2Y - crown2Height / 2.0f - crownHeight / 2.0f;     // Column base box     int fl = BUILD_TOP;     if ((flags & BUILD_BOTTOM) != 0)       fl |= BUILD_BOTTOM;     addBox(baseWidth, baseHeight, baseDepth, baseY, fl);     // Column base box #2 (no bottom)     addBox(base2Width, base2Height, base2Depth, base2Y, BUILD_TOP);     // Main column (no top or bottom)     addCylinder(columnRadius, columnHeight, columnY);     // Column crown tapered box (no top or bottom)     if ((flags & BUILD_TAPERED_CROWN) != 0) {       addBox(crownWidth1, crownHeight, crownDepth1, crownY, crownWidth2,           crownDepth2, 0);     }     // Box above tapered crown (no top)     addBox(crown2Width, crown2Height, crown2Depth, crown2Y, BUILD_BOTTOM);     // Final crown box     fl = BUILD_BOTTOM;     if ((flags & BUILD_TOP) != 0)       fl |= BUILD_TOP;     addBox(crown3Width, crown3Height, crown3Depth, crown3Y, fl);   }   //   //  Add an untapered box   //   private void addBox(float width, float height, float depth, float y) {     addBox(width, height, depth, y, width, depth, 0);   }   private void addBox(float width, float height, float depth, float y,       int flags) {     addBox(width, height, depth, y, width, depth, flags);   }   private void addBox(float width, float height, float depth, float y,       float width2, float depth2) {     addBox(width, height, depth, y, width2, depth2, 0);   }   //   //  Add a tapered box   //   private void addBox(float width, float height, float depth, float y,       float width2, float depth2, int flags) {     float[] coordinates = {     // around the bottom         -width / 2.0f, -height / 2.0f, depth / 2.0f, width / 2.0f,         -height / 2.0f, depth / 2.0f, width / 2.0f, -height / 2.0f,         -depth / 2.0f, -width / 2.0f, -height / 2.0f, -depth / 2.0f,         // around the top         -width2 / 2.0f, height / 2.0f, depth2 / 2.0f, width2 / 2.0f,         height / 2.0f, depth2 / 2.0f, width2 / 2.0f, height / 2.0f,         -depth2 / 2.0f, -width2 / 2.0f, height / 2.0f, -depth2 / 2.0f, };     int[] fullCoordinateIndexes = { 0, 1, 5, 4, // front         1, 2, 6, 5, // right         2, 3, 7, 6, // back         3, 0, 4, 7, // left         4, 5, 6, 7, // top         3, 2, 1, 0, // bottom     };     float v = -(width2 - width) / height;     float[] normals = { 0.0f, v, 1.0f, // front         1.0f, v, 0.0f, // right         0.0f, v, -1.0f, // back         -1.0f, v, 0.0f, // left         0.0f, 1.0f, 0.0f, // top         0.0f, -1.0f, 0.0f, // bottom     };     int[] fullNormalIndexes = { 0, 0, 0, 0, // front         1, 1, 1, 1, // right         2, 2, 2, 2, // back         3, 3, 3, 3, // left         4, 4, 4, 4, // top         5, 5, 5, 5, // bottom     };     float[] textureCoordinates = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,         0.0f, 1.0f, };     int[] fullTextureCoordinateIndexes = { 0, 1, 2, 3, // front         0, 1, 2, 3, // right         0, 1, 2, 3, // back         0, 1, 2, 3, // left         0, 1, 2, 3, // top         0, 1, 2, 3, // bottom     };     // Select indexes needed     int[] coordinateIndexes;     int[] normalIndexes;     int[] textureCoordinateIndexes;     if (flags == 0) {       // build neither top or bottom       coordinateIndexes = new int[4 * 4];       textureCoordinateIndexes = new int[4 * 4];       normalIndexes = new int[4 * 4];       for (int i = 0; i < 4 * 4; i++) {         coordinateIndexes[i] = fullCoordinateIndexes[i];         textureCoordinateIndexes[i] = fullTextureCoordinateIndexes[i];         normalIndexes[i] = fullNormalIndexes[i];       }     } else if ((flags & (BUILD_TOP | BUILD_BOTTOM)) == (BUILD_TOP | BUILD_BOTTOM)) {       // build top and bottom       coordinateIndexes = fullCoordinateIndexes;       textureCoordinateIndexes = fullTextureCoordinateIndexes;       normalIndexes = fullNormalIndexes;     } else if ((flags & BUILD_TOP) != 0) {       // build top but not bottom       coordinateIndexes = new int[5 * 4];       textureCoordinateIndexes = new int[5 * 4];       normalIndexes = new int[5 * 4];       for (int i = 0; i < 5 * 4; i++) {         coordinateIndexes[i] = fullCoordinateIndexes[i];         textureCoordinateIndexes[i] = fullTextureCoordinateIndexes[i];         normalIndexes[i] = fullNormalIndexes[i];       }     } else {       // build bottom but not top       coordinateIndexes = new int[5 * 4];       textureCoordinateIndexes = new int[5 * 4];       normalIndexes = new int[5 * 4];       for (int i = 0; i < 4 * 4; i++) {         coordinateIndexes[i] = fullCoordinateIndexes[i];         textureCoordinateIndexes[i] = fullTextureCoordinateIndexes[i];         normalIndexes[i] = fullNormalIndexes[i];       }       for (int i = 5 * 4; i < 6 * 4; i++) {         coordinateIndexes[i - 4] = fullCoordinateIndexes[i];         textureCoordinateIndexes[i - 4] = fullTextureCoordinateIndexes[i];         normalIndexes[i - 4] = fullNormalIndexes[i];       }     }     IndexedQuadArray quads = new IndexedQuadArray(coordinates.length, // number         // of         // vertexes         GeometryArray.COORDINATES | // vertex coordinates given             GeometryArray.NORMALS | // normals given             GeometryArray.TEXTURE_COORDINATE_2, // texture         // coordinates given         coordinateIndexes.length); // number of coordinate indexes     quads.setCoordinates(0, coordinates);     quads.setCoordinateIndices(0, coordinateIndexes);     quads.setNormals(0, normals);     quads.setNormalIndices(0, normalIndexes);     quads.setTextureCoordinates(0, textureCoordinates);     quads.setTextureCoordinateIndices(0, textureCoordinateIndexes);     Shape3D box = new Shape3D(quads, mainAppearance);     Vector3f trans = new Vector3f(0.0f, y, 0.0f);     Transform3D tr = new Transform3D();     tr.set(trans); // translate     TransformGroup tg = new TransformGroup(tr);     tg.addChild(box);     addChild(tg);   }   private final static int NSTEPS = 16;   private void addCylinder(float radius, float height, float y) {     //     //  Compute coordinates, normals, and texture coordinates     //  around the top and bottom of a cylinder     //     float[] coordinates = new float[NSTEPS * 2 * 3]; // xyz     float[] normals = new float[NSTEPS * 2 * 3]; // xyz vector     float[] textureCoordinates = new float[NSTEPS * 2 * 2]; // st     float angle = 0.0f;     float deltaAngle = 2.0f * (float) Math.PI / ((float) NSTEPS - 1);     float s = 0.0f;     float deltaS = 1.0f / ((float) NSTEPS - 1);     int n = 0;     int tn = 0;     float h2 = height / 2.0f;     for (int i = 0; i < NSTEPS; i++) {       // bottom       normals[n + 0] = (float) Math.cos(angle);       normals[n + 1] = 0.0f;       normals[n + 2] = -(float) Math.sin(angle);       coordinates[n + 0] = radius * normals[n + 0];       coordinates[n + 1] = -h2;       coordinates[n + 2] = radius * normals[n + 2];       textureCoordinates[tn + 0] = s;       textureCoordinates[tn + 1] = 0.0f;       n += 3;       tn += 2;       // top       normals[n + 0] = normals[n - 3];       normals[n + 1] = 0.0f;       normals[n + 2] = normals[n - 1];       coordinates[n + 0] = coordinates[n - 3];       coordinates[n + 1] = h2;       coordinates[n + 2] = coordinates[n - 1];       textureCoordinates[tn + 0] = s;       textureCoordinates[tn + 1] = 1.0f;       n += 3;       tn += 2;       angle += deltaAngle;       s += deltaS;     }     //     //  Compute coordinate indexes, normal indexes, and texture     //  coordinate indexes awround the sides of a cylinder.     //  For this application, we don't need top or bottom, so     //  skip them.     //     int[] indexes = new int[NSTEPS * 4];     n = 0;     int p = 0; // panel count     for (int i = 0; i < NSTEPS - 1; i++) {       indexes[n + 0] = p; // bottom left       indexes[n + 1] = p + 2; // bottom right (next panel)       indexes[n + 2] = p + 3; // top right (next panel)       indexes[n + 3] = p + 1; // top left       n += 4;       p += 2;     }     indexes[n + 0] = p; // bottom left     indexes[n + 1] = 0; // bottom right (next panel)     indexes[n + 2] = 1; // top right (next panel)     indexes[n + 3] = p + 1; // top left     IndexedQuadArray quads = new IndexedQuadArray(coordinates.length / 3, // number         // of         // vertexes         GeometryArray.COORDINATES | // format             GeometryArray.NORMALS             | GeometryArray.TEXTURE_COORDINATE_2, indexes.length); // number     // of     // indexes     quads.setCoordinates(0, coordinates);     quads.setTextureCoordinates(0, textureCoordinates);     quads.setNormals(0, normals);     quads.setCoordinateIndices(0, indexes);     quads.setTextureCoordinateIndices(0, indexes);     quads.setNormalIndices(0, indexes);     Shape3D shape = new Shape3D(quads, mainAppearance);     Vector3f trans = new Vector3f(0.0f, y, 0.0f);     Transform3D tr = new Transform3D();     tr.set(trans); // translate     TransformGroup tg = new TransformGroup(tr);     tg.addChild(shape);     addChild(tg);   }   //   //  Control the appearance   //   public void setAppearance(Appearance app) {     mainAppearance = app;   }   //   //  Provide info on the shape and geometry   //   public Shape3D getShape(int partid) {     return null;   }   public int getNumTriangles() {     return 0;   }   public int getNumVertices() {     return 2;   }   /* (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.compile();     universe.addBranchGraph(sceneRoot);     reset();   }   /**    * Builds the scene. Example application subclasses should replace this    * method with their own method to build 3D content.    *     * @return a Group containing 3D content to display    */   public Group buildScene() {     // Build the scene group containing nothing     Group scene = new Group();     return scene;   }   //--------------------------------------------------------------   //  SET/GET METHODS   //--------------------------------------------------------------   /**    * Sets the headlight on/off state. The headlight faces forward in the    * direction the viewer is facing. Example applications that add their own    * lights will typically turn the headlight off. A standard menu item    * enables the headlight to be turned on and off via user control.    *     * @param onOff    *            a boolean turning the light on (true) or off (false)    */   public void setHeadlightEnable(boolean onOff) {     headlightOnOff = onOff;     if (headlight != null)       headlight.setEnable(headlightOnOff);     if (headlightMenuItem != null)       headlightMenuItem.setState(headlightOnOff);   }   /**    * Gets the headlight on/off state.    *     * @return a boolean indicating if the headlight is on or off    */   public boolean getHeadlightEnable() {     return headlightOnOff;   }   /**    * Sets the navigation type to be either Examine or Walk. The Examine    * navigation type sets up behaviors that use mouse drags to rotate and    * translate scene content as if it is an object held at arm's length and    * under examination. The Walk navigation type uses mouse drags to rotate    * and translate the viewer as if they are walking through the content. The    * Examine type is the default.    *     * @param nav    *            either Walk or Examine    */   public void setNavigationType(int nav) {     if (nav == Walk) {       navigationType = Walk;       if (walkMenuItem != null)         walkMenuItem.setState(true);       if (examineMenuItem != null)         examineMenuItem.setState(false);       if (walkBehavior != null)         walkBehavior.setEnable(true);       if (examineBehavior != null)         examineBehavior.setEnable(false);     } else {       navigationType = Examine;       if (walkMenuItem != null)         walkMenuItem.setState(false);       if (examineMenuItem != null)         examineMenuItem.setState(true);       if (walkBehavior != null)         walkBehavior.setEnable(false);       if (examineBehavior != null)         examineBehavior.setEnable(true);     }   }   /**    * Gets the current navigation type, returning either Walk or Examine.    *     * @return either Walk or Examine    */   public int getNavigationType() {     return navigationType;   }   /**    * Sets whether the scene graph should be compiled or not. Normally this is    * always a good idea. For some example applications that use this Example    * framework, it is useful to disable compilation - particularly when nodes    * and node components will need to be made un-live in order to make    * changes. Once compiled, such components can be made un-live, but they are    * still unchangable unless appropriate capabilities have been set.    *     * @param onOff    *            a boolean turning compilation on (true) or off (false)    */   public void setCompilable(boolean onOff) {     shouldCompile = onOff;   }   /**    * Gets whether the scene graph will be compiled or not.    *     * @return a boolean indicating if scene graph compilation is on or off    */   public boolean getCompilable() {     return shouldCompile;   }   //These methods will be replaced   //  Set the view position and direction   public void setViewpoint(Point3f position, Vector3f direction) {     Transform3D t = new Transform3D();     t.set(new Vector3f(position));     exampleViewTransform.setTransform(t);     // how to set direction?   }   //  Reset transforms   public void reset() {     Transform3D trans = new Transform3D();     exampleSceneTransform.setTransform(trans);     trans.set(new Vector3f(0.0f, 0.0f, 10.0f));     exampleViewTransform.setTransform(trans);     setNavigationType(navigationType);   }   //   //  Gets the URL (with file: prepended) for the current directory.   //  This is a terrible hack needed in the Alpha release of Java3D   //  in order to build a full path URL for loading sounds with   //  MediaContainer. When MediaContainer is fully implemented,   //  it should handle relative path names, but not yet.   //   public String getCurrentDirectory() {     // Create a bogus file so that we can query it's path     File dummy = new File("dummy.tmp");     String dummyPath = dummy.getAbsolutePath();     // strip "/dummy.tmp" from end of dummyPath and put into 'path'     if (dummyPath.endsWith(File.separator + "dummy.tmp")) {       int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");       if (index >= 0) {         int pathLength = index + 5; // pre-pend 'file:'         char[] charPath = new char[pathLength];         dummyPath.getChars(0, index, charPath, 5);         String path = new String(charPath, 0, pathLength);         path = "file:" + path.substring(5, pathLength);         return path + File.separator;       }     }     return dummyPath + File.separator;   }   //--------------------------------------------------------------   //  USER INTERFACE   //--------------------------------------------------------------   /**    * Builds the example AWT Frame menubar. Standard menus and their options    * are added. Applications that subclass this class should build their    * menubar additions within their initialize method.    *     * @return a MenuBar for the AWT Frame    */   private MenuBar buildMenuBar() {     // Build the menubar     MenuBar menuBar = new MenuBar();     // File menu     Menu m = new Menu("File");     m.addActionListener(this);     m.add("Exit");     menuBar.add(m);     // View menu     m = new Menu("View");     m.addActionListener(this);     m.add("Reset view");     m.addSeparator();     walkMenuItem = new CheckboxMenuItem("Walk");     walkMenuItem.addItemListener(this);     m.add(walkMenuItem);     examineMenuItem = new CheckboxMenuItem("Examine");     examineMenuItem.addItemListener(this);     m.add(examineMenuItem);     if (navigationType == Walk) {       walkMenuItem.setState(true);       examineMenuItem.setState(false);     } else {       walkMenuItem.setState(false);       examineMenuItem.setState(true);     }     m.addSeparator();     headlightMenuItem = new CheckboxMenuItem("Headlight on/off");     headlightMenuItem.addItemListener(this);     headlightMenuItem.setState(headlightOnOff);     m.add(headlightMenuItem);     menuBar.add(m);     return menuBar;   }   /**    * Shows the application's frame, making it and its menubar, 3D canvas, and    * 3D content visible.    */   public void showFrame() {     exampleFrame.show();   }   /**    * Quits the application.    */   public void quit() {     System.exit(0);   }   /**    * Handles menu selections.    *     * @param event    *            an ActionEvent indicating what menu action requires handling    */   public void actionPerformed(ActionEvent event) {     String arg = event.getActionCommand();     if (arg.equals("Reset view"))       reset();     else if (arg.equals("Exit"))       quit();   }   /**    * Handles checkbox items on a CheckboxMenu. The Example class has none of    * its own, but subclasses may have some.    *     * @param menu    *            which CheckboxMenu needs action    * @param check    *            which CheckboxMenu item has changed    */   public void checkboxChanged(CheckboxMenu menu, int check) {     // None for us   }   /**    * Handles on/off checkbox items on a standard menu.    *     * @param event    *            an ItemEvent indicating what requires handling    */   public void itemStateChanged(ItemEvent event) {     Object src = event.getSource();     boolean state;     if (src == headlightMenuItem) {       state = headlightMenuItem.getState();       headlight.setEnable(state);     } else if (src == walkMenuItem)       setNavigationType(Walk);     else if (src == examineMenuItem)       setNavigationType(Examine);   }   /**    * Handles a window closing event notifying the application that the user    * has chosen to close the application without selecting the "Exit" menu    * item.    *     * @param event    *            a WindowEvent indicating the window is closing    */   public void windowClosing(WindowEvent event) {     quit();   }   public void windowClosed(WindowEvent event) {   }   public void windowOpened(WindowEvent event) {   }   public void windowIconified(WindowEvent event) {   }   public void windowDeiconified(WindowEvent event) {   }   public void windowActivated(WindowEvent event) {   }   public void windowDeactivated(WindowEvent event) {   }   //  Well known colors, positions, and directions   public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);   public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);   public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);   public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);   public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);   public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);   public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);   public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);   public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);   public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);   public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);   public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);   public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);   public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);   public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);   public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);   public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);   public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);   public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);   public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);   public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);   public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);   public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);   public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);   public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);   public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);   public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f); } // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {   public void checkboxChanged(CheckboxMenu menu, int check); } /**  * ExamineViewerBehavior  *   * @version 1.0, 98/04/16  */ /**  * Wakeup on mouse button presses, releases, and mouse movements and generate  * transforms in an "examination style" that enables the user to rotate,  * translation, and zoom an object as if it is held at arm's length. Such an  * examination style is similar to the "Examine" navigation type used by VRML  * browsers.  *   * The behavior maps mouse drags to different transforms depending upon the  * mosue button held down:  *   * Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->  * X-axis rotation  *   * Button 2 (middle) Horizontal movement --> nothing Vertical movement -->  * Z-axis translation  *   * Button 3 (right) Horizontal movement --> X-axis translation Vertical movement  * --> Y-axis translation  *   * To support systems with 2 or 1 mouse buttons, the following alternate  * mappings are supported while dragging with any mouse button held down and  * zero or more keyboard modifiers held down:  *   * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3  *   * The behavior automatically modifies a TransformGroup provided to the  * constructor. The TransformGroup's transform can be set at any time by the  * application or other behaviors to cause the examine rotation and translation  * to be reset.  */ // This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // //    (a) encapsulates all three behaviors into one in order to //        enforce a specific "Examine" symantic // //    (b) supports set/get of the rotation and translation factors //        that control the speed of movement. // //    (c) supports the "Control" modifier as an alternative to the //        "Meta" modifier not present on PC, Mac, and most non-Sun //        keyboards. This makes button3 behavior usable on PCs, //        Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {   // Previous cursor location   protected int previousX = 0;   protected int previousY = 0;   // Saved standard cursor   protected Cursor savedCursor = null;   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    */   public ExamineViewerBehavior() {     super();   }   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    *     * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ExamineViewerBehavior(Component parent) {     super(parent);   }   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    */   public ExamineViewerBehavior(TransformGroup transformGroup) {     super();     subjectTransformGroup = transformGroup;   }   /**    * Construct an examine behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {     super(parent);     subjectTransformGroup = transformGroup;   }   /**    * Respond to a button1 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public void onButton1(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.HAND_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a rotation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute rotation     // angles with the mapping:     //     //   positive X mouse delta --> positive Y-axis rotation     //   positive Y mouse delta --> positive X-axis rotation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = y - previousY;     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xRotationAngle = deltaY * XRotationFactor;     double yRotationAngle = deltaX * YRotationFactor;     //     // Build transforms     //     transform1.rotX(xRotationAngle);     transform2.rotY(yRotationAngle);     // Get and save the current transform matrix     subjectTransformGroup.getTransform(currentTransform);     currentTransform.get(matrix);     translate.set(matrix.m03, matrix.m13, matrix.m23);     // Translate to the origin, rotate, then translate back     currentTransform.setTranslation(origin);     currentTransform.mul(transform1, currentTransform);     currentTransform.mul(transform2, currentTransform);     currentTransform.setTranslation(translate);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Respond to a button2 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public void onButton2(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a translation     //     // Compute the delta in Y from the previous     // position. Use the delta to compute translation     // distances with the mapping:     //     //   positive Y mouse delta --> positive Y-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaY = y - previousY;     if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double zTranslationDistance = deltaY * ZTranslationFactor;     //     // Build transforms     //     translate.set(0.0, 0.0, zTranslationDistance);     transform1.set(translate);     // Get and save the current transform     subjectTransformGroup.getTransform(currentTransform);     // Translate as needed     currentTransform.mul(transform1, currentTransform);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Respond to a button3 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public void onButton3(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a translation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute translation     // distances with the mapping:     //     //   positive X mouse delta --> positive X-axis translation     //   positive Y mouse delta --> negative Y-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = y - previousY;     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xTranslationDistance = deltaX * XTranslationFactor;     double yTranslationDistance = -deltaY * YTranslationFactor;     //     // Build transforms     //     translate.set(xTranslationDistance, yTranslationDistance, 0.0);     transform1.set(translate);     // Get and save the current transform     subjectTransformGroup.getTransform(currentTransform);     // Translate as needed     currentTransform.mul(transform1, currentTransform);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Respond to an elapsed frames event (assuming subclass has set up a wakeup    * criterion for it).    *     * @param time    *            A WakeupOnElapsedFrames criterion to respond to.    */   public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {     // Can't happen   } } /*  *   * Copyright (c) 1998 David R. Nadeau  *    */ /**  * WalkViewerBehavior is a utility class that creates a "walking style"  * navigation symantic.  *   * The behavior wakes up on mouse button presses, releases, and mouse movements  * and generates transforms in a "walk style" that enables the user to walk  * through a scene, translating and turning about as if they are within the  * scene. Such a walk style is similar to the "Walk" navigation type used by  * VRML browsers.  * <P>  * The behavior maps mouse drags to different transforms depending upon the  * mouse button held down:  * <DL>  * <DT>Button 1 (left)  * <DD>Horizontal movement --> Y-axis rotation  * <DD>Vertical movement --> Z-axis translation  *   * <DT>Button 2 (middle)  * <DD>Horizontal movement --> Y-axis rotation  * <DD>Vertical movement --> X-axis rotation  *   * <DT>Button 3 (right)  * <DD>Horizontal movement --> X-axis translation  * <DD>Vertical movement --> Y-axis translation  * </DL>  *   * To support systems with 2 or 1 mouse buttons, the following alternate  * mappings are supported while dragging with any mouse button held down and  * zero or more keyboard modifiers held down:  * <UL>  * <LI>No modifiers = Button 1  * <LI>ALT = Button 2  * <LI>Meta = Button 3  * <LI>Control = Button 3  * </UL>  * The behavior automatically modifies a TransformGroup provided to the  * constructor. The TransformGroup's transform can be set at any time by the  * application or other behaviors to cause the walk rotation and translation to  * be reset.  * <P>  * While a mouse button is down, the behavior automatically changes the cursor  * in a given parent AWT Component. If no parent Component is given, no cursor  * changes are attempted.  *   * @version 1.0, 98/04/16  * @author David R. Nadeau, San Diego Supercomputer Center  */ class WalkViewerBehavior extends ViewerBehavior {   // This class is inspired by the MouseBehavior, MouseRotate,   // MouseTranslate, and MouseZoom utility behaviors provided with   // Java 3D. This class differs from those utilities in that it:   //   //    (a) encapsulates all three behaviors into one in order to   //        enforce a specific "Walk" symantic   //   //    (b) supports set/get of the rotation and translation factors   //        that control the speed of movement.   //   //    (c) supports the "Control" modifier as an alternative to the   //        "Meta" modifier not present on PC, Mac, and most non-Sun   //        keyboards. This makes button3 behavior usable on PCs,   //        Macs, and other systems with fewer than 3 mouse buttons.   // Previous and initial cursor locations   protected int previousX = 0;   protected int previousY = 0;   protected int initialX = 0;   protected int initialY = 0;   // Deadzone size (delta from initial XY for which no   // translate or rotate action is taken   protected static final int DELTAX_DEADZONE = 10;   protected static final int DELTAY_DEADZONE = 10;   // Keep a set of wakeup criterion for animation-generated   // event types.   protected WakeupCriterion[] mouseAndAnimationEvents = null;   protected WakeupOr mouseAndAnimationCriterion = null;   protected WakeupOr savedMouseCriterion = null;   // Saved standard cursor   protected Cursor savedCursor = null;   /**    * Default Rotation and translation scaling factors for animated movements    * (Button 1 press).    */   public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;   public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;   protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;   protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into a    * TransformGroup that must be set using the setTransformGroup method. The    * cursor will be changed during mouse actions if the parent frame is set    * using the setParentComponent method.    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior() {     super();   }   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into a    * TransformGroup that must be set using the setTransformGroup method. The    * cursor will be changed within the given AWT parent Component during mouse    * drags.    *     * @param parent    *            a parent AWT Component within which the cursor will change    *            during mouse drags    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior(Component parent) {     super(parent);   }   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into the given    * TransformGroup. The cursor will be changed during mouse actions if the    * parent frame is set using the setParentComponent method.    *     * @param transformGroup    *            a TransformGroup whos transform is read and written by the    *            behavior    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior(TransformGroup transformGroup) {     super();     subjectTransformGroup = transformGroup;   }   /**    * Constructs a new walk behavior that converts mouse actions into rotations    * and translations. Rotations and translations are written into the given    * TransformGroup. The cursor will be changed within the given AWT parent    * Component during mouse drags.    *     * @param transformGroup    *            a TransformGroup whos transform is read and written by the    *            behavior    *     * @param parent    *            a parent AWT Component within which the cursor will change    *            during mouse drags    *     * @return a new WalkViewerBehavior that needs its TransformGroup and parent    *         Component set    */   public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {     super(parent);     subjectTransformGroup = transformGroup;   }   /**    * Initializes the behavior.    */   public void initialize() {     super.initialize();     savedMouseCriterion = mouseCriterion; // from parent class     mouseAndAnimationEvents = new WakeupCriterion[4];     mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(         MouseEvent.MOUSE_DRAGGED);     mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(         MouseEvent.MOUSE_PRESSED);     mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(         MouseEvent.MOUSE_RELEASED);     mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);     mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);     // Don't use the above criterion until a button 1 down event   }   /**    * Sets the Y rotation animation scaling factor for Y-axis rotations. This    * scaling factor is used to control the speed of Y rotation when button 1    * is pressed and dragged.    *     * @param factor    *            the double Y rotation scaling factor    */   public void setYRotationAnimationFactor(double factor) {     YRotationAnimationFactor = factor;   }   /**    * Gets the current Y animation rotation scaling factor for Y-axis    * rotations.    *     * @return the double Y rotation scaling factor    */   public double getYRotationAnimationFactor() {     return YRotationAnimationFactor;   }   /**    * Sets the Z animation translation scaling factor for Z-axis translations.    * This scaling factor is used to control the speed of Z translation when    * button 1 is pressed and dragged.    *     * @param factor    *            the double Z translation scaling factor    */   public void setZTranslationAnimationFactor(double factor) {     ZTranslationAnimationFactor = factor;   }   /**    * Gets the current Z animation translation scaling factor for Z-axis    * translations.    *     * @return the double Z translation scaling factor    */   public double getZTranslationAnimationFactor() {     return ZTranslationAnimationFactor;   }   /**    * Responds to an elapsed frames event. Such an event is generated on every    * frame while button 1 is held down. On each call, this method computes new    * Y-axis rotation and Z-axis translation values and writes them to the    * behavior's TransformGroup. The translation and rotation amounts are    * computed based upon the distance between the current cursor location and    * the cursor location when button 1 was pressed. As this distance    * increases, the translation or rotation amount increases.    *     * @param time    *            the WakeupOnElapsedFrames criterion to respond to    */   public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {     //     // Time elapsed while button down: create a rotation and     // a translation.     //     // Compute the delta in X and Y from the initial position to     // the previous position. Multiply the delta times a scaling     // factor to compute an offset to add to the current translation     // and rotation. Use the mapping:     //     //   positive X mouse delta --> negative Y-axis rotation     //   positive Y mouse delta --> positive Z-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     if (buttonPressed != BUTTON1)       return;     int deltaX = previousX - initialX;     int deltaY = previousY - initialY;     double yRotationAngle = -deltaX * YRotationAnimationFactor;     double zTranslationDistance = deltaY * ZTranslationAnimationFactor;     //     // Build transforms     //     transform1.rotY(yRotationAngle);     translate.set(0.0, 0.0, zTranslationDistance);     // Get and save the current transform matrix     subjectTransformGroup.getTransform(currentTransform);     currentTransform.get(matrix);     // Translate to the origin, rotate, then translate back     currentTransform.setTranslation(origin);     currentTransform.mul(transform1, currentTransform);     // Translate back from the origin by the original translation     // distance, plus the new walk translation... but force walk     // to travel on a plane by ignoring the Y component of a     // transformed translation vector.     currentTransform.transform(translate);     translate.x += matrix.m03; // add in existing X translation     translate.y = matrix.m13; // use Y translation     translate.z += matrix.m23; // add in existing Z translation     currentTransform.setTranslation(translate);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);   }   /**    * Responds to a button1 event (press, release, or drag). On a press, the    * method adds a wakeup criterion to the behavior's set, callling for the    * behavior to be awoken on each frame. On a button prelease, this criterion    * is removed from the set.    *     * @param mouseEvent    *            the MouseEvent to respond to    */   public void onButton1(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position and change       // the wakeup criterion to include elapsed time wakeups       // so we can animate.       previousX = x;       previousY = y;       initialX = x;       initialY = y;       // Swap criterion... parent class will not reschedule us       mouseCriterion = mouseAndAnimationCriterion;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.HAND_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: restore original wakeup       // criterion which only includes mouse activity, not       // elapsed time       mouseCriterion = savedMouseCriterion;       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     previousX = x;     previousY = y;   }   /**    * Responds to a button2 event (press, release, or drag). On a press, the    * method records the initial cursor location. On a drag, the difference    * between the current and previous cursor location provides a delta that    * controls the amount by which to rotate in X and Y.    *     * @param mouseEvent    *            the MouseEvent to respond to    */   public void onButton2(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       initialX = x;       initialY = y;       // Change to a "rotate" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a rotation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute rotation     // angles with the mapping:     //     //   positive X mouse delta --> negative Y-axis rotation     //   positive Y mouse delta --> negative X-axis rotation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = 0;     if (Math.abs(y - initialY) > DELTAY_DEADZONE) {       // Cursor has moved far enough vertically to consider       // it intentional, so get it's delta.       deltaY = y - previousY;     }     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xRotationAngle = -deltaY * XRotationFactor;     double yRotationAngle = -deltaX * YRotationFactor;     //     // Build transforms     //     transform1.rotX(xRotationAngle);     transform2.rotY(yRotationAngle);     // Get and save the current transform matrix     subjectTransformGroup.getTransform(currentTransform);     currentTransform.get(matrix);     translate.set(matrix.m03, matrix.m13, matrix.m23);     // Translate to the origin, rotate, then translate back     currentTransform.setTranslation(origin);     currentTransform.mul(transform2, currentTransform);     currentTransform.mul(transform1);     currentTransform.setTranslation(translate);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   }   /**    * Responds to a button3 event (press, release, or drag). On a drag, the    * difference between the current and previous cursor location provides a    * delta that controls the amount by which to translate in X and Y.    *     * @param mouseEvent    *            the MouseEvent to respond to    */   public void onButton3(MouseEvent mev) {     if (subjectTransformGroup == null)       return;     int x = mev.getX();     int y = mev.getY();     if (mev.getID() == MouseEvent.MOUSE_PRESSED) {       // Mouse button pressed: record position       previousX = x;       previousY = y;       // Change to a "move" cursor       if (parentComponent != null) {         savedCursor = parentComponent.getCursor();         parentComponent.setCursor(Cursor             .getPredefinedCursor(Cursor.MOVE_CURSOR));       }       return;     }     if (mev.getID() == MouseEvent.MOUSE_RELEASED) {       // Mouse button released: do nothing       // Switch the cursor back       if (parentComponent != null)         parentComponent.setCursor(savedCursor);       return;     }     //     // Mouse moved while button down: create a translation     //     // Compute the delta in X and Y from the previous     // position. Use the delta to compute translation     // distances with the mapping:     //     //   positive X mouse delta --> positive X-axis translation     //   positive Y mouse delta --> negative Y-axis translation     //     // where positive X mouse movement is to the right, and     // positive Y mouse movement is **down** the screen.     //     int deltaX = x - previousX;     int deltaY = y - previousY;     if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA         || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {       // Deltas are too huge to be believable. Probably a glitch.       // Don't record the new XY location, or do anything.       return;     }     double xTranslationDistance = deltaX * XTranslationFactor;     double yTranslationDistance = -deltaY * YTranslationFactor;     //     // Build transforms     //     translate.set(xTranslationDistance, yTranslationDistance, 0.0);     transform1.set(translate);     // Get and save the current transform     subjectTransformGroup.getTransform(currentTransform);     // Translate as needed     currentTransform.mul(transform1);     // Update the transform group     subjectTransformGroup.setTransform(currentTransform);     previousX = x;     previousY = y;   } } // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem's //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener's checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {   // State   protected CheckboxMenuItem[] checks = null;   protected int current = 0;   protected CheckboxMenuListener listener = null;   //  Construct   public CheckboxMenu(String name, NameValue[] items,       CheckboxMenuListener listen) {     this(name, items, 0, listen);   }   public CheckboxMenu(String name, NameValue[] items, int cur,       CheckboxMenuListener listen) {     super(name);     current = cur;     listener = listen;     if (items == null)       return;     checks = new CheckboxMenuItem[items.length];     for (int i = 0; i < items.length; i++) {       checks[i] = new CheckboxMenuItem(items[i].name, false);       checks[i].addItemListener(this);       add(checks[i]);     }     checks[cur].setState(true);   }   //  Handle checkbox changed events   public void itemStateChanged(ItemEvent event) {     Object src = event.getSource();     for (int i = 0; i < checks.length; i++) {       if (src == checks[i]) {         // Update the checkboxes         checks[current].setState(false);         current = i;         checks[current].setState(true);         if (listener != null)           listener.checkboxChanged(this, i);         return;       }     }   }   // Methods to get and set state   public int getCurrent() {     return current;   }   public void setCurrent(int cur) {     if (cur < 0 || cur >= checks.length)       return; // ignore out of range choices     if (checks == null)       return;     checks[current].setState(false);     current = cur;     checks[current].setState(true);   }   public CheckboxMenuItem getSelectedCheckbox() {     if (checks == null)       return null;     return checks[current];   }   public void setSelectedCheckbox(CheckboxMenuItem item) {     if (checks == null)       return;     for (int i = 0; i < checks.length; i++) {       if (item == checks[i]) {         checks[i].setState(false);         current = i;         checks[i].setState(true);       }     }   } } /**  * ViewerBehavior  *   * @version 1.0, 98/04/16  */ /**  * Wakeup on mouse button presses, releases, and mouse movements and generate  * transforms for a transform group. Classes that extend this class impose  * specific symantics, such as "Examine" or "Walk" viewing, similar to the  * navigation types used by VRML browsers.  *   * To support systems with 2 or 1 mouse buttons, the following alternate  * mappings are supported while dragging with any mouse button held down and  * zero or more keyboard modifiers held down:  *   * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3  *   * The behavior automatically modifies a TransformGroup provided to the  * constructor. The TransformGroup's transform can be set at any time by the  * application or other behaviors to cause the viewer's rotation and translation  * to be reset.  */ // This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // //    (a) encapsulates all three behaviors into one in order to //        enforce a specific viewing symantic // //    (b) supports set/get of the rotation and translation factors //        that control the speed of movement. // //    (c) supports the "Control" modifier as an alternative to the //        "Meta" modifier not present on PC, Mac, and most non-Sun //        keyboards. This makes button3 behavior usable on PCs, //        Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {   // Keep track of the transform group who's transform we modify   // during mouse motion.   protected TransformGroup subjectTransformGroup = null;   // Keep a set of wakeup criterion for different mouse-generated   // event types.   protected WakeupCriterion[] mouseEvents = null;   protected WakeupOr mouseCriterion = null;   // Track which button was last pressed   protected static final int BUTTONNONE = -1;   protected static final int BUTTON1 = 0;   protected static final int BUTTON2 = 1;   protected static final int BUTTON3 = 2;   protected int buttonPressed = BUTTONNONE;   // Keep a few Transform3Ds for use during event processing. This   // avoids having to allocate new ones on each event.   protected Transform3D currentTransform = new Transform3D();   protected Transform3D transform1 = new Transform3D();   protected Transform3D transform2 = new Transform3D();   protected Matrix4d matrix = new Matrix4d();   protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);   protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);   // Unusual X and Y delta limits.   protected static final int UNUSUAL_XDELTA = 400;   protected static final int UNUSUAL_YDELTA = 400;   protected Component parentComponent = null;   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    */   public ViewerBehavior() {     super();   }   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into a    * transform group given later with the setTransformGroup( ) method.    *     * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ViewerBehavior(Component parent) {     super();     parentComponent = parent;   }   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    */   public ViewerBehavior(TransformGroup transformGroup) {     super();     subjectTransformGroup = transformGroup;   }   /**    * Construct a viewer behavior that listens to mouse movement and button    * presses to generate rotation and translation transforms written into the    * given transform group.    *     * @param transformGroup    *            The transform group to be modified by the behavior.    * @param parent    *            The AWT Component that contains the area generating mouse    *            events.    */   public ViewerBehavior(TransformGroup transformGroup, Component parent) {     super();     subjectTransformGroup = transformGroup;     parentComponent = parent;   }   /**    * Set the transform group modified by the viewer behavior. Setting the    * transform group to null disables the behavior until the transform group    * is again set to an existing group.    *     * @param transformGroup    *            The new transform group to be modified by the behavior.    */   public void setTransformGroup(TransformGroup transformGroup) {     subjectTransformGroup = transformGroup;   }   /**    * Get the transform group modified by the viewer behavior.    */   public TransformGroup getTransformGroup() {     return subjectTransformGroup;   }   /**    * Sets the parent component who's cursor will be changed during mouse    * drags. If no component is given is given to the constructor, or set via    * this method, no cursor changes will be done.    *     * @param parent    *            the AWT Component, such as a Frame, within which cursor    *            changes should take place during mouse drags    */   public void setParentComponent(Component parent) {     parentComponent = parent;   }   /*    * Gets the parent frame within which the cursor changes during mouse drags.    *     * @return the AWT Component, such as a Frame, within which cursor changes    * should take place during mouse drags. Returns null if no parent is set.    */   public Component getParentComponent() {     return parentComponent;   }   /**    * Initialize the behavior.    */   public void initialize() {     // Wakeup when the mouse is dragged or when a mouse button     // is pressed or released.     mouseEvents = new WakeupCriterion[3];     mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);     mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);     mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);     mouseCriterion = new WakeupOr(mouseEvents);     wakeupOn(mouseCriterion);   }   /**    * Process a new wakeup. Interpret mouse button presses, releases, and mouse    * drags.    *     * @param criteria    *            The wakeup criteria causing the behavior wakeup.    */   public void processStimulus(Enumeration criteria) {     WakeupCriterion wakeup = null;     AWTEvent[] event = null;     int whichButton = BUTTONNONE;     // Process all pending wakeups     while (criteria.hasMoreElements()) {       wakeup = (WakeupCriterion) criteria.nextElement();       if (wakeup instanceof WakeupOnAWTEvent) {         event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();         // Process all pending events         for (int i = 0; i < event.length; i++) {           if (event[i].getID() != MouseEvent.MOUSE_PRESSED               && event[i].getID() != MouseEvent.MOUSE_RELEASED               && event[i].getID() != MouseEvent.MOUSE_DRAGGED)             // Ignore uninteresting mouse events             continue;           //           // Regretably, Java event handling (or perhaps           // underlying OS event handling) doesn't always           // catch button bounces (redundant presses and           // releases), or order events so that the last           // drag event is delivered before a release.           // This means we can get stray events that we           // filter out here.           //           if (event[i].getID() == MouseEvent.MOUSE_PRESSED               && buttonPressed != BUTTONNONE)             // Ignore additional button presses until a release             continue;           if (event[i].getID() == MouseEvent.MOUSE_RELEASED               && buttonPressed == BUTTONNONE)             // Ignore additional button releases until a press             continue;           if (event[i].getID() == MouseEvent.MOUSE_DRAGGED               && buttonPressed == BUTTONNONE)             // Ignore drags until a press             continue;           MouseEvent mev = (MouseEvent) event[i];           int modifiers = mev.getModifiers();           //           // Unfortunately, the underlying event handling           // doesn't do a "grab" operation when a mouse button           // is pressed. This means that once a button is           // pressed, if another mouse button or a keyboard           // modifier key is pressed, the delivered mouse event           // will show that a different button is being held           // down. For instance:           //           // Action Event           //  Button 1 press Button 1 press           //  Drag with button 1 down Button 1 drag           //  ALT press -           //  Drag with ALT & button 1 down Button 2 drag           //  Button 1 release Button 2 release           //           // The upshot is that we can get a button press           // without a matching release, and the button           // associated with a drag can change mid-drag.           //           // To fix this, we watch for an initial button           // press, and thenceforth consider that button           // to be the one held down, even if additional           // buttons get pressed, and despite what is           // reported in the event. Only when a button is           // released, do we end such a grab.           //           if (buttonPressed == BUTTONNONE) {             // No button is pressed yet, figure out which             // button is down now and how to direct events             if (((modifiers & InputEvent.BUTTON3_MASK) != 0)                 || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {               // Button 3 activity (META or CTRL down)               whichButton = BUTTON3;             } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {               // Button 2 activity (ALT down)               whichButton = BUTTON2;             } else {               // Button 1 activity (no modifiers down)               whichButton = BUTTON1;             }             // If the event is to press a button, then             // record that that button is now down             if (event[i].getID() == MouseEvent.MOUSE_PRESSED)               buttonPressed = whichButton;           } else {             // Otherwise a button was pressed earlier and             // hasn't been released yet. Assign all further             // events to it, even if ALT, META, CTRL, or             // another button has been pressed as well.             whichButton = buttonPressed;           }           // Distribute the event           switch (whichButton) {           case BUTTON1:             onButton1(mev);             break;           case BUTTON2:             onButton2(mev);             break;           case BUTTON3:             onButton3(mev);             break;           default:             break;           }           // If the event is to release a button, then           // record that that button is now up           if (event[i].getID() == MouseEvent.MOUSE_RELEASED)             buttonPressed = BUTTONNONE;         }         continue;       }       if (wakeup instanceof WakeupOnElapsedFrames) {         onElapsedFrames((WakeupOnElapsedFrames) wakeup);         continue;       }     }     // Reschedule us for another wakeup     wakeupOn(mouseCriterion);   }   /**    * Default X and Y rotation factors, and XYZ translation factors.    */   public static final double DEFAULT_XROTATION_FACTOR = 0.02;   public static final double DEFAULT_YROTATION_FACTOR = 0.005;   public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;   public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;   public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;   protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;   protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;   protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;   protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;   protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;   /**    * Set the X rotation scaling factor for X-axis rotations.    *     * @param factor    *            The new scaling factor.    */   public void setXRotationFactor(double factor) {     XRotationFactor = factor;   }   /**    * Get the current X rotation scaling factor for X-axis rotations.    */   public double getXRotationFactor() {     return XRotationFactor;   }   /**    * Set the Y rotation scaling factor for Y-axis rotations.    *     * @param factor    *            The new scaling factor.    */   public void setYRotationFactor(double factor) {     YRotationFactor = factor;   }   /**    * Get the current Y rotation scaling factor for Y-axis rotations.    */   public double getYRotationFactor() {     return YRotationFactor;   }   /**    * Set the X translation scaling factor for X-axis translations.    *     * @param factor    *            The new scaling factor.    */   public void setXTranslationFactor(double factor) {     XTranslationFactor = factor;   }   /**    * Get the current X translation scaling factor for X-axis translations.    */   public double getXTranslationFactor() {     return XTranslationFactor;   }   /**    * Set the Y translation scaling factor for Y-axis translations.    *     * @param factor    *            The new scaling factor.    */   public void setYTranslationFactor(double factor) {     YTranslationFactor = factor;   }   /**    * Get the current Y translation scaling factor for Y-axis translations.    */   public double getYTranslationFactor() {     return YTranslationFactor;   }   /**    * Set the Z translation scaling factor for Z-axis translations.    *     * @param factor    *            The new scaling factor.    */   public void setZTranslationFactor(double factor) {     ZTranslationFactor = factor;   }   /**    * Get the current Z translation scaling factor for Z-axis translations.    */   public double getZTranslationFactor() {     return ZTranslationFactor;   }   /**    * Respond to a button1 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public abstract void onButton1(MouseEvent mouseEvent);   /**    * Respond to a button2 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public abstract void onButton2(MouseEvent mouseEvent);   /**    * Responed to a button3 event (press, release, or drag).    *     * @param mouseEvent    *            A MouseEvent to respond to.    */   public abstract void onButton3(MouseEvent mouseEvent);   /**    * Respond to an elapsed frames event (assuming subclass has set up a wakeup    * criterion for it).    *     * @param time    *            A WakeupOnElapsedFrames criterion to respond to.    */   public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent); } // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {   public String name;   public Object value;   public NameValue(String n, Object v) {     name = n;     value = v;   } }