Java/3D/Transform 3D

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

AWT Interaction: Transform 3D

   <source lang="java">

/*

* @(#)AWTInteraction.java 1.12 02/10/21 13:35:20
* 
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*  - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*  - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility.
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.Enumeration; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnBehaviorPost; import javax.swing.JButton; import javax.swing.JPanel; import javax.vecmath.Point3d; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.universe.SimpleUniverse; public class AWTInteraction extends Applet { //implements ActionListener {

 TransformGroup objTrans;
 float angle = 0.0f;
 Transform3D trans = new Transform3D();
 JButton rotateB = new JButton("Rotate");
 private SimpleUniverse u = null;
 public BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Create the transform group node and initialize it to the
   // identity. Enable the TRANSFORM_WRITE capability so that
   // our behavior code can modify it at runtime. Add it to the
   // root of the subgraph.
   objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objRoot.addChild(objTrans);
   // Create a simple shape leaf node, add it to the scene graph.
   objTrans.addChild(new ColorCube(0.4));
   // create the AWTInteractionBehavior
   AWTInteractionBehavior awtBehavior = new AWTInteractionBehavior(
       objTrans);
   rotateB.addActionListener(awtBehavior);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   awtBehavior.setSchedulingBounds(bounds);
   objRoot.addChild(awtBehavior);
   return objRoot;
 }
 public AWTInteraction() {
 }
 public void init() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   Canvas3D c = new Canvas3D(config);
   add("Center", c);
   JPanel p = new JPanel();
   p.add(rotateB);
   add("North", p);
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   scene.setCapability(BranchGroup.ALLOW_BOUNDS_READ);
   u = new SimpleUniverse(c);
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   u.getViewingPlatform().setNominalViewingTransform();
   u.addBranchGraph(scene);
 }
 public void destroy() {
   u.cleanup();
 }
 //
 // The following allows HelloUniverse to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   new MainFrame(new AWTInteraction(), 256, 256);
 }

} class AWTInteractionBehavior extends Behavior implements ActionListener {

 private TransformGroup transformGroup;
 private Transform3D trans = new Transform3D();
 private WakeupCriterion criterion;
 private float angle = 0.0f;
 // create a new AWTInteractionBehavior
 public AWTInteractionBehavior(TransformGroup tg) {
   transformGroup = tg;
 }
 // initialize the behavior to wakeup on a behavior post with the id
 // MouseEvent.MOUSE_CLICKED
 public void initialize() {
   criterion = new WakeupOnBehaviorPost(this, MouseEvent.MOUSE_CLICKED);
   wakeupOn(criterion);
 }
 // processStimulus to rotate the cube
 public void processStimulus(Enumeration criteria) {
   angle += Math.toRadians(10.0);
   trans.rotY(angle);
   transformGroup.setTransform(trans);
   wakeupOn(criterion);
 }
 // when the mouse is clicked, postId for the behavior
 public void actionPerformed(ActionEvent e) {
   postId(MouseEvent.MOUSE_CLICKED);
 }

}


      </source>
   
  
 
  



ExTransform -- illustrate use of transforms

   <source lang="java">

// //CLASS //ExTransform -- illustrate use of transforms // //LESSON //Use Transform3D and TransformGroup to translate, rotate, and //scale shapes // //AUTHOR //Michael J. Bailey / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.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.Group; import javax.media.j3d.Light; import javax.media.j3d.Link; import javax.media.j3d.Material; import javax.media.j3d.SharedGroup; import javax.media.j3d.Switch; 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.AxisAngle4d; 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 ExTransform.NameChildMask; import com.sun.j3d.utils.geometry.Box; 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 ExTransform extends Java3DFrame {

 //  nodes that can be updated via a menu:
 Switch switchGroup = null;
 SharedGroup sharedObject = null;
 private int currentSwitch = 0;
 //  Build the scene:
 public Group buildScene() {
   // Turn on the headlight
   setHeadlightEnable(true);
   // Build the scene root
   switchGroup = new Switch();
   switchGroup.setCapability(Switch.ALLOW_SWITCH_WRITE);
   // Create application bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   Transform3D t3d;
   Appearance app = new Appearance();
   Material mat = new Material();
   mat.setAmbientColor(0.2f, 0.8f, 0.4f);
   mat.setDiffuseColor(0.2f, 0.8f, 0.4f);
   mat.setSpecularColor(0.0f, 0.0f, 0.f);
   app.setMaterial(mat);
   // Build the 3D object:
   Box box = new Box(3.0f, 2.0f, 1.0f, app);
   // Build the shared object:
   sharedObject = new SharedGroup();
   sharedObject.addChild(box);
   // Build 4 separate transforms:
   Transform3D id = new Transform3D();
   TransformGroup idGroup = new TransformGroup(id);
   idGroup.addChild(new Link(sharedObject));
   switchGroup.addChild(idGroup);
   Transform3D rot = new Transform3D();
   rot.set(new AxisAngle4d(0., 1., 0., Math.PI / 4.));
   TransformGroup rotGroup = new TransformGroup(rot);
   rotGroup.addChild(new Link(sharedObject));
   switchGroup.addChild(rotGroup);
   Transform3D trans = new Transform3D();
   trans.set(new Vector3d(2., 0., 0.));
   TransformGroup transGroup = new TransformGroup(trans);
   transGroup.addChild(new Link(sharedObject));
   switchGroup.addChild(transGroup);
   Transform3D scale = new Transform3D();
   scale.set(2.0);
   TransformGroup scaleGroup = new TransformGroup(scale);
   scaleGroup.addChild(new Link(sharedObject));
   switchGroup.addChild(scaleGroup);
   switchGroup.setWhichChild(options[currentSwitch].child);
   return switchGroup;
 }
 //
 //  Main (if invoked as an application)
 //
 public static void main(String[] args) {
   ExTransform ex = new ExTransform();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 private NameChildMask[] options = { new NameChildMask("Identity", 0, 0),
     new NameChildMask("Rotation", 1, 0),
     new NameChildMask("Translation", 2, 0),
     new NameChildMask("Scale", 3, 0),
     new NameChildMask("I+R", Switch.CHILD_MASK, 3),
     new NameChildMask("I+T", Switch.CHILD_MASK, 5),
     new NameChildMask("I+S", Switch.CHILD_MASK, 9), };
 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 Transform Example");
   // Add a menu to select among transform options
   Menu mt = new Menu("Transform");
   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
       switchGroup.setWhichChild(options[currentSwitch].child);
       switchGroup.setChildMask(options[currentSwitch].mask);
       return;
     }
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }
 public 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(4);
     if ((m & 1) != 0)
       mask.set(0);
     if ((m & 2) != 0)
       mask.set(1);
     if ((m & 4) != 0)
       mask.set(2);
     if ((m & 8) != 0)
       mask.set(3);
   }
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
*

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

*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
Button 3 (right) *
Horizontal movement --> X-axis translation *
Vertical movement --> Y-axis translation *
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
    *
  • No modifiers = Button 1 *
  • ALT = Button 2 *
  • Meta = Button 3 *
  • Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

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

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

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

} /**

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

/**

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

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

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

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

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

}

      </source>
   
  
 
  



This program uses AWT buttons to allow the user to rotate an object

   <source lang="java">

/* Essential Java 3D Fast Ian Palmer Publisher: Springer-Verlag ISBN: 1-85233-394-4

  • /

import java.awt.BorderLayout; import java.awt.Button; import java.awt.Frame; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Locale; import javax.media.j3d.Material; import javax.media.j3d.Node; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.vecmath.AxisAngle4d; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Box; /**

* This program uses AWT buttons to allow the user to rotate an object. This is
* achieved by altering the transform of a transform group.
* 
* @author I.J.Palmer
* @version 1.0
*/

public class SimpleTransform extends Frame implements ActionListener {

 protected Canvas3D myCanvas3D = new Canvas3D(null);
 /** The exit button */
 protected Button exitButton = new Button("Exit");
 /** The rotate left button */
 protected Button leftButton = new Button("<-");
 /** The rotate right button */
 protected Button rightButton = new Button("->");
 /** The transform group used to rotate the shape */
 protected TransformGroup rotationGroup;
 /**
  * This function builds the view branch of the scene graph. It creates a
  * branch group and then creates the necessary view elements to give a
  * useful view of our content.
  * 
  * @param c
  *            Canvas3D that will display the view
  * @return BranchGroup that is the root of the view elements
  */
 protected BranchGroup buildViewBranch(Canvas3D c) {
   BranchGroup viewBranch = new BranchGroup();
   Transform3D viewXfm = new Transform3D();
   viewXfm.set(new Vector3f(0.0f, 0.0f, 10.0f));
   TransformGroup viewXfmGroup = new TransformGroup(viewXfm);
   ViewPlatform myViewPlatform = new ViewPlatform();
   PhysicalBody myBody = new PhysicalBody();
   PhysicalEnvironment myEnvironment = new PhysicalEnvironment();
   viewXfmGroup.addChild(myViewPlatform);
   viewBranch.addChild(viewXfmGroup);
   View myView = new View();
   myView.addCanvas3D(c);
   myView.attachViewPlatform(myViewPlatform);
   myView.setPhysicalBody(myBody);
   myView.setPhysicalEnvironment(myEnvironment);
   return viewBranch;
 }
 /**
  * Add some lights so that we can illuminate the scene. This adds one
  * ambient light to bring up the overall lighting level and one directional
  * shape to show the shape of the objects in the scene.
  * 
  * @param b
  *            BranchGroup that the lights are to be added to.
  */
 protected void addLights(BranchGroup b) {
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   Color3f ambLightColour = new Color3f(0.5f, 0.5f, 0.5f);
   AmbientLight ambLight = new AmbientLight(ambLightColour);
   ambLight.setInfluencingBounds(bounds);
   Color3f dirLightColour = new Color3f(1.0f, 1.0f, 1.0f);
   Vector3f dirLightDir = new Vector3f(-1.0f, -1.0f, -1.0f);
   DirectionalLight dirLight = new DirectionalLight(dirLightColour,
       dirLightDir);
   dirLight.setInfluencingBounds(bounds);
   b.addChild(ambLight);
   b.addChild(dirLight);
 }
 /**
  * This builds the content branch of our scene graph. The root of the shapes
  * supplied as a parameter is slightly tilted to reveal its 3D shape. It
  * also uses the addLights function to add some lights to the scene. The
  * group that the shape is added to has its capabilities set so that we can
  * read and write it.
  * 
  * @param shape
  *            Node that represents the geometry for the content
  * @return BranchGroup that is the root of the content branch
  */
 protected BranchGroup buildContentBranch(Node shape) {
   BranchGroup contentBranch = new BranchGroup();
   Transform3D rotateCube = new Transform3D();
   rotateCube.set(new AxisAngle4d(1.0, 1.0, 0.0, Math.PI / 4.0));
   rotationGroup = new TransformGroup(rotateCube);
   //Set the capabilities so that the transform can be accessed
   rotationGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   rotationGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   contentBranch.addChild(rotationGroup);
   rotationGroup.addChild(shape);
   addLights(contentBranch);
   return contentBranch;
 }
 /**
  * This defines the shapes used in the scene. It creates a simple cube using
  * a Box utility class.
  * 
  * @return Node that is the root of the shape hierarchy.
  */
 protected Node buildShape() {
   Appearance app = new Appearance();
   Color3f ambientColour = new Color3f(1.0f, 0.0f, 0.0f);
   Color3f emissiveColour = new Color3f(0.0f, 0.0f, 0.0f);
   Color3f specularColour = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f diffuseColour = new Color3f(1.0f, 0.0f, 0.0f);
   float shininess = 20.0f;
   app.setMaterial(new Material(ambientColour, emissiveColour,
       diffuseColour, specularColour, shininess));
   return new Box(2.0f, 2.0f, 2.0f, app);
 }
 /**
  * This processes the AWT events and performs the appropriate operations.
  * The exit button causes the program to terminate, the left button causes a
  * rotation to be applied to the shape"s transformation to spin it to the
  * left and the right has the similar effect but to the right button.
  * 
  * @param e
  *            ActionEvent that has been performed
  */
 public void actionPerformed(ActionEvent e) {
   if (e.getSource() == exitButton) {
     dispose();
     System.exit(0);
   } else if (e.getSource() == leftButton) {
     //Create a temporary transform
     Transform3D temp = new Transform3D();
     //Read the transform from the shape
     rotationGroup.getTransform(temp);
     //Create a rotation that will be applied
     Transform3D tempDelta = new Transform3D();
     tempDelta.rotY(-0.3);
     //Apply the rotation
     temp.mul(tempDelta);
     //Write the value back into the scene graph
     rotationGroup.setTransform(temp);
   } else if (e.getSource() == rightButton) {
     //Do the same for the right rotation
     Transform3D temp = new Transform3D();
     rotationGroup.getTransform(temp);
     Transform3D tempDelta = new Transform3D();
     tempDelta.rotY(0.3);
     temp.mul(tempDelta);
     rotationGroup.setTransform(temp);
   }
 }
 public SimpleTransform() {
   VirtualUniverse myUniverse = new VirtualUniverse();
   Locale myLocale = new Locale(myUniverse);
   myLocale.addBranchGraph(buildViewBranch(myCanvas3D));
   myLocale.addBranchGraph(buildContentBranch(buildShape()));
   setTitle("SimpleWorld");
   setSize(400, 400);
   setLayout(new BorderLayout());
   Panel bottom = new Panel();
   bottom.add(leftButton);
   bottom.add(rightButton);
   bottom.add(exitButton);
   add(BorderLayout.CENTER, myCanvas3D);
   add(BorderLayout.SOUTH, bottom);
   exitButton.addActionListener(this);
   leftButton.addActionListener(this);
   rightButton.addActionListener(this);
   setVisible(true);
 }
 public static void main(String[] args) {
   SimpleTransform st = new SimpleTransform();
 }

}


      </source>
   
  
 
  



Transform and light

   <source lang="java">

/*

* @(#)LOD.java 1.13 02/10/21 13:44:04
* 
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*  - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*  - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility.
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import javax.media.j3d.AmbientLight; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.DistanceLOD; //import javax.media.j3d.LOD; import javax.media.j3d.Switch; import javax.media.j3d.TransformGroup; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class LOD extends Applet {

 private SimpleUniverse u = null;
 public BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   createLights(objRoot);
   // Create the transform group node and initialize it to the
   // identity. Enable the TRANSFORM_WRITE capability so that
   // our behavior code can modify it at runtime. Add it to the
   // root of the subgraph.
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   objRoot.addChild(objTrans);
   // Create a switch to hold the different levels of detail
   Switch sw = new Switch(0);
   sw.setCapability(javax.media.j3d.Switch.ALLOW_SWITCH_READ);
   sw.setCapability(javax.media.j3d.Switch.ALLOW_SWITCH_WRITE);
   // Create several levels for the switch, with less detailed
   // spheres for the ones which will be used when the sphere is
   // further away
   sw.addChild(new Sphere(0.4f, Sphere.GENERATE_NORMALS, 40));
   sw.addChild(new Sphere(0.4f, Sphere.GENERATE_NORMALS, 20));
   sw.addChild(new Sphere(0.4f, Sphere.GENERATE_NORMALS, 10));
   sw.addChild(new Sphere(0.4f, Sphere.GENERATE_NORMALS, 3));
   // Add the switch to the main group
   objTrans.addChild(sw);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   // set up the DistanceLOD behavior
   float[] distances = new float[3];
   distances[0] = 5.0f;
   distances[1] = 10.0f;
   distances[2] = 25.0f;
   DistanceLOD lod = new DistanceLOD(distances);
   lod.addSwitch(sw);
   lod.setSchedulingBounds(bounds);
   objTrans.addChild(lod);
   // Have Java 3D perform optimizations on this scene graph.
   objRoot.rupile();
   return objRoot;
 }
 private void createLights(BranchGroup graphRoot) {
   // Create a bounds for the light source influence
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   // Set up the global, ambient light
   Color3f alColor = new Color3f(0.2f, 0.2f, 0.2f);
   AmbientLight aLgt = new AmbientLight(alColor);
   aLgt.setInfluencingBounds(bounds);
   graphRoot.addChild(aLgt);
   // Set up the directional (infinite) light source
   Color3f lColor1 = new Color3f(0.9f, 0.9f, 0.9f);
   Vector3f lDir1 = new Vector3f(1.0f, 1.0f, -1.0f);
   DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1);
   lgt1.setInfluencingBounds(bounds);
   graphRoot.addChild(lgt1);
 }
 public LOD() {
 }
 public void init() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   Canvas3D c = new Canvas3D(config);
   add("Center", c);
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   u = new SimpleUniverse(c);
   // only add zoom mouse behavior to viewingPlatform
   ViewingPlatform viewingPlatform = u.getViewingPlatform();
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   viewingPlatform.setNominalViewingTransform();
   // add orbit behavior to the ViewingPlatform, but disable rotate
   // and translate
   OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ZOOM
       | OrbitBehavior.DISABLE_ROTATE
       | OrbitBehavior.DISABLE_TRANSLATE);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   orbit.setSchedulingBounds(bounds);
   viewingPlatform.setViewPlatformBehavior(orbit);
   u.addBranchGraph(scene);
 }
 public void destroy() {
   u.cleanup();
 }
 //
 // The following allows LOD to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   new MainFrame(new LOD(), 512, 512);
 }

}


      </source>
   
  
 
  



Transform Explorer

   <source lang="java">

/*

* %Z%%M% %I% %E% %U%
* 
* ************************************************************** "Copyright (c)
* 2001 Sun Microsystems, Inc. All Rights Reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 
* -Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility."
* 
* ***************************************************************************
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.ruponent; import java.awt.Dimension; import java.awt.Font; import java.awt.GraphicsConfiguration; import java.awt.GridLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.text.NumberFormat; import java.util.Enumeration; import java.util.EventListener; import java.util.EventObject; import java.util.Hashtable; import java.util.Vector; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Font3D; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Link; import javax.media.j3d.Material; import javax.media.j3d.OrientedShape3D; import javax.media.j3d.Screen3D; import javax.media.j3d.SharedGroup; import javax.media.j3d.Switch; import javax.media.j3d.Text3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.JTabbedPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGEncodeParam; import com.sun.image.codec.jpeg.JPEGImageEncoder; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.geometry.Cylinder; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; /*

*  
*/

public class TransformExplorer extends Applet implements

   Java3DExplorerConstants {
 SimpleUniverse u;
 boolean isApplication;
 Canvas3D canvas;
 OffScreenCanvas3D offScreenCanvas;
 View view;
 TransformGroup coneTG;
 // transformation factors for the cone
 Vector3f coneTranslation = new Vector3f(0.0f, 0.0f, 0.0f);
 float coneScale = 1.0f;
 Vector3d coneNUScale = new Vector3d(1.0f, 1.0f, 1.0f);
 Vector3f coneRotateAxis = new Vector3f(1.0f, 0.0f, 0.0f);
 Vector3f coneRotateNAxis = new Vector3f(1.0f, 0.0f, 0.0f);
 float coneRotateAngle = 0.0f;
 AxisAngle4f coneRotateAxisAngle = new AxisAngle4f(coneRotateAxis,
     coneRotateAngle);
 Vector3f coneRefPt = new Vector3f(0.0f, 0.0f, 0.0f);
 // this tells whether to use the compound transformation
 boolean useCompoundTransform = true;
 // These are Transforms are used for the compound transformation
 Transform3D translateTrans = new Transform3D();
 Transform3D scaleTrans = new Transform3D();
 Transform3D rotateTrans = new Transform3D();
 Transform3D refPtTrans = new Transform3D();
 Transform3D refPtInvTrans = new Transform3D();
 // this tells whether to use the uniform or non-uniform scale when
 // updating the compound transform
 boolean useUniformScale = true;
 // The size of the cone
 float coneRadius = 1.0f;
 float coneHeight = 2.0f;
 // The axis indicator, used to show the rotation axis
 RotAxis rotAxis;
 boolean showRotAxis = false;
 float rotAxisLength = 3.0f;
 // The coord sys used to show the coordinate system
 CoordSys coordSys;
 boolean showCoordSys = true;
 float coordSysLength = 5.0f;
 // GUI elements
 String rotAxisString = "Rotation Axis";
 String coordSysString = "Coord Sys";
 JCheckBox rotAxisCheckBox;
 JCheckBox coordSysCheckBox;
 String snapImageString = "Snap Image";
 String outFileBase = "transform";
 int outFileSeq = 0;
 float offScreenScale;
 JLabel coneRotateNAxisXLabel;
 JLabel coneRotateNAxisYLabel;
 JLabel coneRotateNAxisZLabel;
 // Temporaries that are reused
 Transform3D tmpTrans = new Transform3D();
 Vector3f tmpVector = new Vector3f();
 AxisAngle4f tmpAxisAngle = new AxisAngle4f();
 // geometric constant
 Point3f origin = new Point3f();
 Vector3f yAxis = new Vector3f(0.0f, 1.0f, 0.0f);
 // Returns the TransformGroup we will be editing to change the transform
 // on the cone
 TransformGroup createConeTransformGroup() {
   // create a TransformGroup for the cone, allow tranform changes,
   coneTG = new TransformGroup();
   coneTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   coneTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   // Set up an appearance to make the Cone with red ambient,
   // black emmissive, red diffuse and white specular coloring
   Material material = new Material(red, black, red, white, 64);
   // These are the colors used for the book figures:
   //Material material = new Material(white, black, white, black, 64);
   Appearance appearance = new Appearance();
   appearance.setMaterial(material);
   // create the cone and add it to the coneTG
   Cone cone = new Cone(coneRadius, coneHeight, appearance);
   coneTG.addChild(cone);
   return coneTG;
 }
 void setConeTranslation() {
   coneTG.getTransform(tmpTrans); // get the old transform
   tmpTrans.setTranslation(coneTranslation); // set only translation
   coneTG.setTransform(tmpTrans); // set the new transform
 }
 void setConeUScale() {
   coneTG.getTransform(tmpTrans); // get the old transform
   tmpTrans.setScale(coneScale); // set only scale
   coneTG.setTransform(tmpTrans); // set the new transform
 }
 void setConeNUScale() {
   coneTG.getTransform(tmpTrans); // get the old transform
   System.out.println("coneNUScale.x = " + coneNUScale.x);
   tmpTrans.setScale(coneNUScale);// set only scale
   coneTG.setTransform(tmpTrans); // set the new transform
 }
 void setConeRotation() {
   coneTG.getTransform(tmpTrans); // get the old transform
   tmpTrans.setRotation(coneRotateAxisAngle); // set only rotation
   coneTG.setTransform(tmpTrans); // set the new transform
 }
 void updateUsingCompoundTransform() {
   // set the component transformations
   translateTrans.set(coneTranslation);
   if (useUniformScale) {
     scaleTrans.set(coneScale);
   } else {
     scaleTrans.setIdentity();
     scaleTrans.setScale(coneNUScale);
   }
   rotateTrans.set(coneRotateAxisAngle);
   // translate from ref pt to origin
   tmpVector.sub(origin, coneRefPt); // vector from ref pt to origin
   refPtTrans.set(tmpVector);
   // translate from origin to ref pt
   tmpVector.sub(coneRefPt, origin); // vector from origin to ref pt
   refPtInvTrans.set(tmpVector);
   // now build up the transfomation
   // trans = translate * refPtInv * scale * rotate * refPt;
   tmpTrans.set(translateTrans);
   tmpTrans.mul(refPtInvTrans);
   tmpTrans.mul(scaleTrans);
   tmpTrans.mul(rotateTrans);
   tmpTrans.mul(refPtTrans);
   // Copy the transform to the TransformGroup
   coneTG.setTransform(tmpTrans);
 }
 // ensure that the cone rotation axis is a unit vector
 void normalizeConeRotateAxis() {
   // normalize, watch for length == 0, if so, then use default
   float lengthSquared = coneRotateAxis.lengthSquared();
   if (lengthSquared > 0.0001) {
     coneRotateNAxis.scale((float) (1.0 / Math.sqrt(lengthSquared)),
         coneRotateAxis);
   } else {
     coneRotateNAxis.set(1.0f, 0.0f, 0.0f);
   }
 }
 // copy the current axis and angle to the axis angle, convert angle
 // to radians
 void updateConeAxisAngle() {
   coneRotateAxisAngle.set(coneRotateNAxis, (float) Math
       .toRadians(coneRotateAngle));
 }
 void updateConeRotateNormalizedLabels() {
   nf.setMinimumFractionDigits(2);
   nf.setMaximumFractionDigits(2);
   coneRotateNAxisXLabel.setText("X: " + nf.format(coneRotateNAxis.x));
   coneRotateNAxisYLabel.setText("Y: " + nf.format(coneRotateNAxis.y));
   coneRotateNAxisZLabel.setText("Z: " + nf.format(coneRotateNAxis.z));
 }
 BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Create a TransformGroup to scale the scene down by 3.5x
   TransformGroup objScale = new TransformGroup();
   Transform3D scaleTrans = new Transform3D();
   scaleTrans.set(1 / 3.5f); // scale down by 3.5x
   objScale.setTransform(scaleTrans);
   objRoot.addChild(objScale);
   // Create a TransformGroup and initialize it to the
   // identity. Enable the TRANSFORM_WRITE capability so that
   // the mouse behaviors code can modify it at runtime. Add it to the
   // root of the subgraph.
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   objScale.addChild(objTrans);
   // Add the primitives to the scene
   objTrans.addChild(createConeTransformGroup()); // the cone
   rotAxis = new RotAxis(rotAxisLength); // the axis
   objTrans.addChild(rotAxis);
   coordSys = new CoordSys(coordSysLength); // the coordSys
   objTrans.addChild(coordSys);
   BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
   // The book used a white background for the figures
   //Background bg = new Background(new Color3f(1.0f, 1.0f, 1.0f));
   //bg.setApplicationBounds(bounds);
   //objTrans.addChild(bg);
   // Set up the ambient light
   Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
   AmbientLight ambientLightNode = new AmbientLight(ambientColor);
   ambientLightNode.setInfluencingBounds(bounds);
   objRoot.addChild(ambientLightNode);
   // Set up the directional lights
   Color3f light1Color = new Color3f(1.0f, 1.0f, 1.0f);
   Vector3f light1Direction = new Vector3f(0.0f, -0.2f, -1.0f);
   DirectionalLight light1 = new DirectionalLight(light1Color,
       light1Direction);
   light1.setInfluencingBounds(bounds);
   objRoot.addChild(light1);
   return objRoot;
 }
 public TransformExplorer() {
   this(false, 1.0f);
 }
 public TransformExplorer(boolean isApplication, float initOffScreenScale) {
   this.isApplication = isApplication;
   this.offScreenScale = initOffScreenScale;
 }
 public void init() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   canvas = new Canvas3D(config);
   add("Center", canvas);
   u = new SimpleUniverse(canvas);
   if (isApplication) {
     offScreenCanvas = new OffScreenCanvas3D(config, true);
     // set the size of the off-screen canvas based on a scale
     // of the on-screen size
     Screen3D sOn = canvas.getScreen3D();
     Screen3D sOff = offScreenCanvas.getScreen3D();
     Dimension dim = sOn.getSize();
     dim.width *= offScreenScale;
     dim.height *= offScreenScale;
     sOff.setSize(dim);
     sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
         * offScreenScale);
     sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
         * offScreenScale);
     // attach the offscreen canvas to the view
     u.getViewer().getView().addCanvas3D(offScreenCanvas);
   }
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   // get the view
   view = u.getViewer().getView();
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   ViewingPlatform viewingPlatform = u.getViewingPlatform();
   viewingPlatform.setNominalViewingTransform();
   // add an orbit behavior to move the viewing platform
   OrbitBehavior orbit = new OrbitBehavior(canvas, OrbitBehavior.STOP_ZOOM);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   orbit.setSchedulingBounds(bounds);
   viewingPlatform.setViewPlatformBehavior(orbit);
   u.addBranchGraph(scene);
   add("East", guiPanel());
 }
 // create a panel with a tabbed pane holding each of the edit panels
 JPanel guiPanel() {
   JPanel panel = new JPanel();
   panel.setLayout(new BorderLayout());
   JTabbedPane tabbedPane = new JTabbedPane();
   tabbedPane.addTab("Translation", translationPanel());
   tabbedPane.addTab("Scaling", scalePanel());
   tabbedPane.addTab("Rotation", rotationPanel());
   tabbedPane.addTab("Reference Point", refPtPanel());
   panel.add("Center", tabbedPane);
   panel.add("South", configPanel());
   return panel;
 }
 Box translationPanel() {
   Box panel = new Box(BoxLayout.Y_AXIS);
   panel.add(new LeftAlignComponent(new JLabel("Translation Offset")));
   // X translation label, slider, and value label
   FloatLabelJSlider coneTranslateXSlider = new FloatLabelJSlider("X",
       0.1f, -2.0f, 2.0f, coneTranslation.x);
   coneTranslateXSlider.setMajorTickSpacing(1.0f);
   coneTranslateXSlider.setPaintTicks(true);
   coneTranslateXSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneTranslation.x = e.getValue();
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeTranslation();
       }
     }
   });
   panel.add(coneTranslateXSlider);
   // Y translation label, slider, and value label
   FloatLabelJSlider coneTranslateYSlider = new FloatLabelJSlider("Y",
       0.1f, -2.0f, 2.0f, coneTranslation.y);
   coneTranslateYSlider.setMajorTickSpacing(1.0f);
   coneTranslateYSlider.setPaintTicks(true);
   coneTranslateYSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneTranslation.y = e.getValue();
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeTranslation();
       }
     }
   });
   panel.add(coneTranslateYSlider);
   // Z translation label, slider, and value label
   FloatLabelJSlider coneTranslateZSlider = new FloatLabelJSlider("Z",
       0.1f, -2.0f, 2.0f, coneTranslation.z);
   coneTranslateZSlider.setMajorTickSpacing(1.0f);
   coneTranslateZSlider.setPaintTicks(true);
   coneTranslateZSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneTranslation.z = e.getValue();
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeTranslation();
       }
     }
   });
   panel.add(coneTranslateZSlider);
   return panel;
 }
 Box scalePanel() {
   Box panel = new Box(BoxLayout.Y_AXIS);
   // Uniform Scale
   JLabel uniform = new JLabel("Uniform Scale");
   panel.add(new LeftAlignComponent(uniform));
   FloatLabelJSlider coneScaleSlider = new FloatLabelJSlider("S:", 0.1f,
       0.0f, 3.0f, coneScale);
   coneScaleSlider.setMajorTickSpacing(1.0f);
   coneScaleSlider.setPaintTicks(true);
   coneScaleSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneScale = e.getValue();
       useUniformScale = true;
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeUScale();
       }
     }
   });
   panel.add(coneScaleSlider);
   JLabel nonUniform = new JLabel("Non-Uniform Scale");
   panel.add(new LeftAlignComponent(nonUniform));
   // Non-Uniform Scale
   FloatLabelJSlider coneNUScaleXSlider = new FloatLabelJSlider("X: ",
       0.1f, 0.0f, 3.0f, (float) coneNUScale.x);
   coneNUScaleXSlider.setMajorTickSpacing(1.0f);
   coneNUScaleXSlider.setPaintTicks(true);
   coneNUScaleXSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneNUScale.x = (double) e.getValue();
       useUniformScale = false;
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeNUScale();
       }
     }
   });
   panel.add(coneNUScaleXSlider);
   FloatLabelJSlider coneNUScaleYSlider = new FloatLabelJSlider("Y: ",
       0.1f, 0.0f, 3.0f, (float) coneNUScale.y);
   coneNUScaleYSlider.setMajorTickSpacing(1.0f);
   coneNUScaleYSlider.setPaintTicks(true);
   coneNUScaleYSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneNUScale.y = (double) e.getValue();
       useUniformScale = false;
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeNUScale();
       }
     }
   });
   panel.add(coneNUScaleYSlider);
   FloatLabelJSlider coneNUScaleZSlider = new FloatLabelJSlider("Z: ",
       0.1f, 0.0f, 3.0f, (float) coneNUScale.z);
   coneNUScaleZSlider.setMajorTickSpacing(1.0f);
   coneNUScaleZSlider.setPaintTicks(true);
   coneNUScaleZSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneNUScale.z = (double) e.getValue();
       useUniformScale = false;
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeNUScale();
       }
     }
   });
   panel.add(coneNUScaleZSlider);
   return panel;
 }
 JPanel rotationPanel() {
   JPanel panel = new JPanel();
   panel.setLayout(new GridLayout(0, 1));
   panel.add(new LeftAlignComponent(new JLabel("Rotation Axis")));
   FloatLabelJSlider coneRotateAxisXSlider = new FloatLabelJSlider("X: ",
       0.01f, -1.0f, 1.0f, (float) coneRotateAxis.x);
   coneRotateAxisXSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneRotateAxis.x = e.getValue();
       normalizeConeRotateAxis();
       updateConeAxisAngle();
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeRotation();
       }
       rotAxis.setRotationAxis(coneRotateAxis);
       updateConeRotateNormalizedLabels();
     }
   });
   panel.add(coneRotateAxisXSlider);
   FloatLabelJSlider coneRotateAxisYSlider = new FloatLabelJSlider("Y: ",
       0.01f, -1.0f, 1.0f, (float) coneRotateAxis.y);
   coneRotateAxisYSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneRotateAxis.y = e.getValue();
       normalizeConeRotateAxis();
       updateConeAxisAngle();
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeRotation();
       }
       rotAxis.setRotationAxis(coneRotateAxis);
       updateConeRotateNormalizedLabels();
     }
   });
   panel.add(coneRotateAxisYSlider);
   FloatLabelJSlider coneRotateAxisZSlider = new FloatLabelJSlider("Z: ",
       0.01f, -1.0f, 1.0f, (float) coneRotateAxis.y);
   coneRotateAxisZSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneRotateAxis.z = e.getValue();
       normalizeConeRotateAxis();
       updateConeAxisAngle();
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeRotation();
       }
       rotAxis.setRotationAxis(coneRotateAxis);
       updateConeRotateNormalizedLabels();
     }
   });
   panel.add(coneRotateAxisZSlider);
   JLabel normalizedLabel = new JLabel("Normalized Rotation Axis");
   panel.add(new LeftAlignComponent(normalizedLabel));
   ;
   coneRotateNAxisXLabel = new JLabel("X: 1.000");
   panel.add(new LeftAlignComponent(coneRotateNAxisXLabel));
   coneRotateNAxisYLabel = new JLabel("Y: 0.000");
   panel.add(new LeftAlignComponent(coneRotateNAxisYLabel));
   coneRotateNAxisZLabel = new JLabel("Z: 0.000");
   panel.add(new LeftAlignComponent(coneRotateNAxisZLabel));
   normalizeConeRotateAxis();
   updateConeRotateNormalizedLabels();
   FloatLabelJSlider coneRotateAxisAngleSlider = new FloatLabelJSlider(
       "Angle: ", 1.0f, -180.0f, 180.0f, (float) coneRotateAngle);
   coneRotateAxisAngleSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneRotateAngle = e.getValue();
       updateConeAxisAngle();
       if (useCompoundTransform) {
         updateUsingCompoundTransform();
       } else {
         setConeRotation();
       }
     }
   });
   panel.add(coneRotateAxisAngleSlider);
   return panel;
 }
 Box refPtPanel() {
   Box panel = new Box(BoxLayout.Y_AXIS);
   panel.add(new LeftAlignComponent(new JLabel(
       "Reference Point Coordinates")));
   // X Ref Pt
   FloatLabelJSlider coneRefPtXSlider = new FloatLabelJSlider("X", 0.1f,
       -2.0f, 2.0f, coneRefPt.x);
   coneRefPtXSlider.setMajorTickSpacing(1.0f);
   coneRefPtXSlider.setPaintTicks(true);
   coneRefPtXSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneRefPt.x = e.getValue();
       useCompoundTransform = true;
       updateUsingCompoundTransform();
       rotAxis.setRefPt(coneRefPt);
     }
   });
   panel.add(coneRefPtXSlider);
   // Y Ref Pt
   FloatLabelJSlider coneRefPtYSlider = new FloatLabelJSlider("Y", 0.1f,
       -2.0f, 2.0f, coneRefPt.y);
   coneRefPtYSlider.setMajorTickSpacing(1.0f);
   coneRefPtYSlider.setPaintTicks(true);
   coneRefPtYSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneRefPt.y = e.getValue();
       useCompoundTransform = true;
       updateUsingCompoundTransform();
       rotAxis.setRefPt(coneRefPt);
     }
   });
   panel.add(coneRefPtYSlider);
   // Z Ref Pt
   FloatLabelJSlider coneRefPtZSlider = new FloatLabelJSlider("Z", 0.1f,
       -2.0f, 2.0f, coneRefPt.z);
   coneRefPtZSlider.setMajorTickSpacing(1.0f);
   coneRefPtZSlider.setPaintTicks(true);
   coneRefPtZSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       coneRefPt.z = e.getValue();
       useCompoundTransform = true;
       updateUsingCompoundTransform();
       rotAxis.setRefPt(coneRefPt);
     }
   });
   panel.add(coneRefPtZSlider);
   return panel;
 }
 JPanel configPanel() {
   JPanel panel = new JPanel();
   panel.setLayout(new GridLayout(0, 1));
   panel.add(new JLabel("Display annotation:"));
   // create the check boxes
   rotAxisCheckBox = new JCheckBox(rotAxisString);
   rotAxisCheckBox.setSelected(showRotAxis);
   rotAxisCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       Object source = e.getSource();
       showRotAxis = ((JCheckBox) source).isSelected();
       if (showRotAxis) {
         rotAxis.setWhichChild(Switch.CHILD_ALL);
       } else {
         rotAxis.setWhichChild(Switch.CHILD_NONE);
       }
     }
   });
   panel.add(rotAxisCheckBox);
   coordSysCheckBox = new JCheckBox(coordSysString);
   coordSysCheckBox.setSelected(showCoordSys);
   coordSysCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       Object source = e.getSource();
       showCoordSys = ((JCheckBox) source).isSelected();
       if (showCoordSys) {
         coordSys.setWhichChild(Switch.CHILD_ALL);
       } else {
         coordSys.setWhichChild(Switch.CHILD_NONE);
       }
     }
   });
   panel.add(coordSysCheckBox);
   if (isApplication) {
     JButton snapButton = new JButton(snapImageString);
     snapButton.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         Point loc = canvas.getLocationOnScreen();
         offScreenCanvas.setOffScreenLocation(loc);
         Dimension dim = canvas.getSize();
         dim.width *= offScreenScale;
         dim.height *= offScreenScale;
         nf.setMinimumIntegerDigits(3);
         nf.setMaximumFractionDigits(0);
         offScreenCanvas.snapImageFile(outFileBase
             + nf.format(outFileSeq++), dim.width, dim.height);
         nf.setMinimumIntegerDigits(0);
       }
     });
     panel.add(snapButton);
   }
   return panel;
 }
 public void destroy() {
   u.removeAllLocales();
 }
 // The following allows TransformExplorer to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   float initOffScreenScale = 2.5f;
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-s")) {
       if (args.length >= (i + 1)) {
         initOffScreenScale = Float.parseFloat(args[i + 1]);
         i++;
       }
     }
   }
   new MainFrame(new TransformExplorer(true, initOffScreenScale), 950, 600);
 }

} interface Java3DExplorerConstants {

 // colors
 static Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
 static Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
 static Color3f green = new Color3f(0.0f, 1.0f, 0.0f);
 static Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);
 static Color3f skyBlue = new Color3f(0.6f, 0.7f, 0.9f);
 static Color3f cyan = new Color3f(0.0f, 1.0f, 1.0f);
 static Color3f magenta = new Color3f(1.0f, 0.0f, 1.0f);
 static Color3f yellow = new Color3f(1.0f, 1.0f, 0.0f);
 static Color3f brightWhite = new Color3f(1.0f, 1.5f, 1.5f);
 static Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
 static Color3f darkGrey = new Color3f(0.15f, 0.15f, 0.15f);
 static Color3f medGrey = new Color3f(0.3f, 0.3f, 0.3f);
 static Color3f grey = new Color3f(0.5f, 0.5f, 0.5f);
 static Color3f lightGrey = new Color3f(0.75f, 0.75f, 0.75f);
 // infinite bounding region, used to make env nodes active everywhere
 BoundingSphere infiniteBounds = new BoundingSphere(new Point3d(),
     Double.MAX_VALUE);
 // common values
 static final String nicestString = "NICEST";
 static final String fastestString = "FASTEST";
 static final String antiAliasString = "Anti-Aliasing";
 static final String noneString = "NONE";
 // light type constants
 static int LIGHT_AMBIENT = 1;
 static int LIGHT_DIRECTIONAL = 2;
 static int LIGHT_POSITIONAL = 3;
 static int LIGHT_SPOT = 4;
 // screen capture constants
 static final int USE_COLOR = 1;
 static final int USE_BLACK_AND_WHITE = 2;
 // number formatter
 NumberFormat nf = NumberFormat.getInstance();

} class OffScreenCanvas3D extends Canvas3D {

 OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration,
     boolean offScreen) {
   super(graphicsConfiguration, offScreen);
 }
 private BufferedImage doRender(int width, int height) {
   BufferedImage bImage = new BufferedImage(width, height,
       BufferedImage.TYPE_INT_RGB);
   ImageComponent2D buffer = new ImageComponent2D(
       ImageComponent.FORMAT_RGB, bImage);
   //buffer.setYUp(true);
   setOffScreenBuffer(buffer);
   renderOffScreenBuffer();
   waitForOffScreenRendering();
   bImage = getOffScreenBuffer().getImage();
   return bImage;
 }
 void snapImageFile(String filename, int width, int height) {
   BufferedImage bImage = doRender(width, height);
   /*
    * JAI: RenderedImage fImage = JAI.create("format", bImage,
    * DataBuffer.TYPE_BYTE); JAI.create("filestore", fImage, filename +
    * ".tif", "tiff", null);
    */
   /* No JAI: */
   try {
     FileOutputStream fos = new FileOutputStream(filename + ".jpg");
     BufferedOutputStream bos = new BufferedOutputStream(fos);
     JPEGImageEncoder jie = JPEGCodec.createJPEGEncoder(bos);
     JPEGEncodeParam param = jie.getDefaultJPEGEncodeParam(bImage);
     param.setQuality(1.0f, true);
     jie.setJPEGEncodeParam(param);
     jie.encode(bImage);
     bos.flush();
     fos.close();
   } catch (Exception e) {
     System.out.println(e);
   }
 }

} class FloatLabelJSlider extends JPanel implements ChangeListener,

   Java3DExplorerConstants {
 JSlider slider;
 JLabel valueLabel;
 Vector listeners = new Vector();
 float min, max, resolution, current, scale;
 int minInt, maxInt, curInt;;
 int intDigits, fractDigits;
 float minResolution = 0.001f;
 // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
 // 0.5
 FloatLabelJSlider(String name) {
   this(name, 0.1f, 0.0f, 1.0f, 0.5f);
 }
 FloatLabelJSlider(String name, float resolution, float min, float max,
     float current) {
   this.resolution = resolution;
   this.min = min;
   this.max = max;
   this.current = current;
   if (resolution < minResolution) {
     resolution = minResolution;
   }
   // round scale to nearest integer fraction. i.e. 0.3 => 1/3 = 0.33
   scale = (float) Math.round(1.0f / resolution);
   resolution = 1.0f / scale;
   // get the integer versions of max, min, current
   minInt = Math.round(min * scale);
   maxInt = Math.round(max * scale);
   curInt = Math.round(current * scale);
   // sliders use integers, so scale our floating point value by "scale"
   // to make each slider "notch" be "resolution". We will scale the
   // value down by "scale" when we get the event.
   slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
   slider.addChangeListener(this);
   valueLabel = new JLabel(" ");
   // set the initial value label
   setLabelString();
   // add min and max labels to the slider
   Hashtable labelTable = new Hashtable();
   labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));
   labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));
   slider.setLabelTable(labelTable);
   slider.setPaintLabels(true);
   /* layout to align left */
   setLayout(new BorderLayout());
   Box box = new Box(BoxLayout.X_AXIS);
   add(box, BorderLayout.WEST);
   box.add(new JLabel(name));
   box.add(slider);
   box.add(valueLabel);
 }
 public void setMinorTickSpacing(float spacing) {
   int intSpacing = Math.round(spacing * scale);
   slider.setMinorTickSpacing(intSpacing);
 }
 public void setMajorTickSpacing(float spacing) {
   int intSpacing = Math.round(spacing * scale);
   slider.setMajorTickSpacing(intSpacing);
 }
 public void setPaintTicks(boolean paint) {
   slider.setPaintTicks(paint);
 }
 public void addFloatListener(FloatListener listener) {
   listeners.add(listener);
 }
 public void removeFloatListener(FloatListener listener) {
   listeners.remove(listener);
 }
 public void stateChanged(ChangeEvent e) {
   JSlider source = (JSlider) e.getSource();
   // get the event type, set the corresponding value.
   // Sliders use integers, handle floating point values by scaling the
   // values by "scale" to allow settings at "resolution" intervals.
   // Divide by "scale" to get back to the real value.
   curInt = source.getValue();
   current = curInt / scale;
   valueChanged();
 }
 public void setValue(float newValue) {
   boolean changed = (newValue != current);
   current = newValue;
   if (changed) {
     valueChanged();
   }
 }
 private void valueChanged() {
   // update the label
   setLabelString();
   // notify the listeners
   FloatEvent event = new FloatEvent(this, current);
   for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
     FloatListener listener = (FloatListener) e.nextElement();
     listener.floatChanged(event);
   }
 }
 void setLabelString() {
   // Need to muck around to try to make sure that the width of the label
   // is wide enough for the largest value. Pad the string
   // be large enough to hold the largest value.
   int pad = 5; // fudge to make up for variable width fonts
   float maxVal = Math.max(Math.abs(min), Math.abs(max));
   intDigits = Math.round((float) (Math.log(maxVal) / Math.log(10))) + pad;
   if (min < 0) {
     intDigits++; // add one for the "-"
   }
   // fractDigits is num digits of resolution for fraction. Use base 10 log
   // of scale, rounded up, + 2.
   fractDigits = (int) Math.ceil((Math.log(scale) / Math.log(10)));
   nf.setMinimumFractionDigits(fractDigits);
   nf.setMaximumFractionDigits(fractDigits);
   String value = nf.format(current);
   while (value.length() < (intDigits + fractDigits)) {
     value = value + "  ";
   }
   valueLabel.setText(value);
 }

} class FloatEvent extends EventObject {

 float value;
 FloatEvent(Object source, float newValue) {
   super(source);
   value = newValue;
 }
 float getValue() {
   return value;
 }

} interface FloatListener extends EventListener {

 void floatChanged(FloatEvent e);

} class LogFloatLabelJSlider extends JPanel implements ChangeListener,

   Java3DExplorerConstants {
 JSlider slider;
 JLabel valueLabel;
 Vector listeners = new Vector();
 float min, max, resolution, current, scale;
 double minLog, maxLog, curLog;
 int minInt, maxInt, curInt;;
 int intDigits, fractDigits;
 NumberFormat nf = NumberFormat.getInstance();
 float minResolution = 0.001f;
 double logBase = Math.log(10);
 // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
 // 0.5
 LogFloatLabelJSlider(String name) {
   this(name, 0.1f, 100.0f, 10.0f);
 }
 LogFloatLabelJSlider(String name, float min, float max, float current) {
   this.resolution = resolution;
   this.min = min;
   this.max = max;
   this.current = current;
   if (resolution < minResolution) {
     resolution = minResolution;
   }
   minLog = log10(min);
   maxLog = log10(max);
   curLog = log10(current);
   // resolution is 100 steps from min to max
   scale = 100.0f;
   resolution = 1.0f / scale;
   // get the integer versions of max, min, current
   minInt = (int) Math.round(minLog * scale);
   maxInt = (int) Math.round(maxLog * scale);
   curInt = (int) Math.round(curLog * scale);
   slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
   slider.addChangeListener(this);
   valueLabel = new JLabel(" ");
   // Need to muck around to make sure that the width of the label
   // is wide enough for the largest value. Pad the initial string
   // be large enough to hold the largest value.
   int pad = 5; // fudge to make up for variable width fonts
   intDigits = (int) Math.ceil(maxLog) + pad;
   if (min < 0) {
     intDigits++; // add one for the "-"
   }
   if (minLog < 0) {
     fractDigits = (int) Math.ceil(-minLog);
   } else {
     fractDigits = 0;
   }
   nf.setMinimumFractionDigits(fractDigits);
   nf.setMaximumFractionDigits(fractDigits);
   String value = nf.format(current);
   while (value.length() < (intDigits + fractDigits)) {
     value = value + " ";
   }
   valueLabel.setText(value);
   // add min and max labels to the slider
   Hashtable labelTable = new Hashtable();
   labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));
   labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));
   slider.setLabelTable(labelTable);
   slider.setPaintLabels(true);
   // layout to align left
   setLayout(new BorderLayout());
   Box box = new Box(BoxLayout.X_AXIS);
   add(box, BorderLayout.WEST);
   box.add(new JLabel(name));
   box.add(slider);
   box.add(valueLabel);
 }
 public void setMinorTickSpacing(float spacing) {
   int intSpacing = Math.round(spacing * scale);
   slider.setMinorTickSpacing(intSpacing);
 }
 public void setMajorTickSpacing(float spacing) {
   int intSpacing = Math.round(spacing * scale);
   slider.setMajorTickSpacing(intSpacing);
 }
 public void setPaintTicks(boolean paint) {
   slider.setPaintTicks(paint);
 }
 public void addFloatListener(FloatListener listener) {
   listeners.add(listener);
 }
 public void removeFloatListener(FloatListener listener) {
   listeners.remove(listener);
 }
 public void stateChanged(ChangeEvent e) {
   JSlider source = (JSlider) e.getSource();
   curInt = source.getValue();
   curLog = curInt / scale;
   current = (float) exp10(curLog);
   valueChanged();
 }
 public void setValue(float newValue) {
   boolean changed = (newValue != current);
   current = newValue;
   if (changed) {
     valueChanged();
   }
 }
 private void valueChanged() {
   String value = nf.format(current);
   valueLabel.setText(value);
   // notify the listeners
   FloatEvent event = new FloatEvent(this, current);
   for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
     FloatListener listener = (FloatListener) e.nextElement();
     listener.floatChanged(event);
   }
 }
 double log10(double value) {
   return Math.log(value) / logBase;
 }
 double exp10(double value) {
   return Math.exp(value * logBase);
 }

} class CoordSys extends Switch {

 // Temporaries that are reused
 Transform3D tmpTrans = new Transform3D();
 Vector3f tmpVector = new Vector3f();
 AxisAngle4f tmpAxisAngle = new AxisAngle4f();
 // colors for use in the shapes
 Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
 Color3f grey = new Color3f(0.3f, 0.3f, 0.3f);
 Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
 // geometric constants
 Point3f origin = new Point3f();
 Vector3f yAxis = new Vector3f(0.0f, 1.0f, 0.0f);
 CoordSys(float axisLength) {
   super(Switch.CHILD_ALL);
   float coordSysLength = axisLength;
   float labelOffset = axisLength / 20.0f;
   float axisRadius = axisLength / 500.0f;
   float arrowRadius = axisLength / 125.0f;
   float arrowHeight = axisLength / 50.0f;
   float tickRadius = axisLength / 125.0f;
   float tickHeight = axisLength / 250.0f;
   // Set the Switch to allow changes
   setCapability(Switch.ALLOW_SWITCH_READ);
   setCapability(Switch.ALLOW_SWITCH_WRITE);
   // Set up an appearance to make the Axis have
   // grey ambient, black emmissive, grey diffuse and grey specular
   // coloring.
   //Material material = new Material(grey, black, grey, white, 64);
   Material material = new Material(white, black, white, white, 64);
   Appearance appearance = new Appearance();
   appearance.setMaterial(material);
   // Create a shared group to hold one axis of the coord sys
   SharedGroup coordAxisSG = new SharedGroup();
   // create a cylinder for the central line of the axis
   Cylinder cylinder = new Cylinder(axisRadius, coordSysLength, appearance);
   // cylinder goes from -coordSysLength/2 to coordSysLength in y
   coordAxisSG.addChild(cylinder);
   // create the shared arrowhead
   Cone arrowHead = new Cone(arrowRadius, arrowHeight, appearance);
   SharedGroup arrowHeadSG = new SharedGroup();
   arrowHeadSG.addChild(arrowHead);
   // Create a TransformGroup to move the arrowhead to the top of the
   // axis
   // The arrowhead goes from -arrowHeight/2 to arrowHeight/2 in y.
   // Put it at the top of the axis, coordSysLength / 2
   tmpVector.set(0.0f, coordSysLength / 2 + arrowHeight / 2, 0.0f);
   tmpTrans.set(tmpVector);
   TransformGroup topTG = new TransformGroup();
   topTG.setTransform(tmpTrans);
   topTG.addChild(new Link(arrowHeadSG));
   coordAxisSG.addChild(topTG);
   // create the minus arrowhead
   // Create a TransformGroup to turn the cone upside down:
   // Rotate 180 degrees around Z axis
   tmpAxisAngle.set(0.0f, 0.0f, 1.0f, (float) Math.toRadians(180));
   tmpTrans.set(tmpAxisAngle);
   // Put the arrowhead at the bottom of the axis
   tmpVector.set(0.0f, -coordSysLength / 2 - arrowHeight / 2, 0.0f);
   tmpTrans.setTranslation(tmpVector);
   TransformGroup bottomTG = new TransformGroup();
   bottomTG.setTransform(tmpTrans);
   bottomTG.addChild(new Link(arrowHeadSG));
   coordAxisSG.addChild(bottomTG);
   // Now add "ticks" at 1, 2, 3, etc.
   // create a shared group for the tick
   Cylinder tick = new Cylinder(tickRadius, tickHeight, appearance);
   SharedGroup tickSG = new SharedGroup();
   tickSG.addChild(tick);
   // transform each instance and add it to the coord axis group
   int maxTick = (int) (coordSysLength / 2);
   int minTick = -maxTick;
   for (int i = minTick; i <= maxTick; i++) {
     if (i == 0)
       continue; // no tick at 0
     // use a TransformGroup to offset to the tick location
     TransformGroup tickTG = new TransformGroup();
     tmpVector.set(0.0f, (float) i, 0.0f);
     tmpTrans.set(tmpVector);
     tickTG.setTransform(tmpTrans);
     // then link to an instance of the Tick shared group
     tickTG.addChild(new Link(tickSG));
     // add the TransformGroup to the coord axis
     coordAxisSG.addChild(tickTG);
   }
   // add a Link to the axis SharedGroup to the coordSys
   addChild(new Link(coordAxisSG)); // Y axis
   // Create TransformGroups for the X and Z axes
   TransformGroup xAxisTG = new TransformGroup();
   // rotate 90 degrees around Z axis
   tmpAxisAngle.set(0.0f, 0.0f, 1.0f, (float) Math.toRadians(90));
   tmpTrans.set(tmpAxisAngle);
   xAxisTG.setTransform(tmpTrans);
   xAxisTG.addChild(new Link(coordAxisSG));
   addChild(xAxisTG); // X axis
   TransformGroup zAxisTG = new TransformGroup();
   // rotate 90 degrees around X axis
   tmpAxisAngle.set(1.0f, 0.0f, 0.0f, (float) Math.toRadians(90));
   tmpTrans.set(tmpAxisAngle);
   zAxisTG.setTransform(tmpTrans);
   zAxisTG.addChild(new Link(coordAxisSG));
   addChild(zAxisTG); // Z axis
   // Add the labels. First we need a Font3D for the Text3Ds
   // select the default font, plain style, 0.5 tall. Use null for
   // the extrusion so we get "flat" text since we will be putting it
   // into an oriented Shape3D
   Font3D f3d = new Font3D(new Font("Default", Font.PLAIN, 1), null);
   // set up the +X label
   Text3D plusXText = new Text3D(f3d, "+X", origin, Text3D.ALIGN_CENTER,
       Text3D.PATH_RIGHT);
   // orient around the local origin
   OrientedShape3D plusXTextShape = new OrientedShape3D(plusXText,
       appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
   // transform to scale down to 0.15 in height, locate at end of axis
   TransformGroup plusXTG = new TransformGroup();
   tmpVector.set(coordSysLength / 2 + labelOffset, 0.0f, 0.0f);
   tmpTrans.set(0.15f, tmpVector);
   plusXTG.setTransform(tmpTrans);
   plusXTG.addChild(plusXTextShape);
   addChild(plusXTG);
   // set up the -X label
   Text3D minusXText = new Text3D(f3d, "-X", origin, Text3D.ALIGN_CENTER,
       Text3D.PATH_RIGHT);
   // orient around the local origin
   OrientedShape3D minusXTextShape = new OrientedShape3D(minusXText,
       appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
   // transform to scale down to 0.15 in height, locate at end of axis
   TransformGroup minusXTG = new TransformGroup();
   tmpVector.set(-coordSysLength / 2 - labelOffset, 0.0f, 0.0f);
   tmpTrans.set(0.15f, tmpVector);
   minusXTG.setTransform(tmpTrans);
   minusXTG.addChild(minusXTextShape);
   addChild(minusXTG);
   // set up the +Y label
   Text3D plusYText = new Text3D(f3d, "+Y", origin, Text3D.ALIGN_CENTER,
       Text3D.PATH_RIGHT);
   // orient around the local origin
   OrientedShape3D plusYTextShape = new OrientedShape3D(plusYText,
       appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
   // transform to scale down to 0.15 in height, locate at end of axis
   TransformGroup plusYTG = new TransformGroup();
   tmpVector.set(0.0f, coordSysLength / 2 + labelOffset, 0.0f);
   tmpTrans.set(0.15f, tmpVector);
   plusYTG.setTransform(tmpTrans);
   plusYTG.addChild(plusYTextShape);
   addChild(plusYTG);
   // set up the -Y label
   Text3D minusYText = new Text3D(f3d, "-Y", origin, Text3D.ALIGN_CENTER,
       Text3D.PATH_RIGHT);
   // orient around the local origin
   OrientedShape3D minusYTextShape = new OrientedShape3D(minusYText,
       appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
   // transform to scale down to 0.15 in height, locate at end of axis
   TransformGroup minusYTG = new TransformGroup();
   tmpVector.set(0.0f, -coordSysLength / 2 - labelOffset, 0.0f);
   tmpTrans.set(0.15f, tmpVector);
   minusYTG.setTransform(tmpTrans);
   minusYTG.addChild(minusYTextShape);
   addChild(minusYTG);
   // set up the +Z label
   Text3D plusZText = new Text3D(f3d, "+Z", origin, Text3D.ALIGN_CENTER,
       Text3D.PATH_RIGHT);
   // orient around the local origin
   OrientedShape3D plusZTextShape = new OrientedShape3D(plusZText,
       appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
   // transform to scale down to 0.15 in height, locate at end of axis
   TransformGroup plusZTG = new TransformGroup();
   tmpVector.set(0.0f, 0.0f, coordSysLength / 2 + labelOffset);
   tmpTrans.set(0.15f, tmpVector);
   plusZTG.setTransform(tmpTrans);
   plusZTG.addChild(plusZTextShape);
   addChild(plusZTG);
   // set up the -Z label
   Text3D minusZText = new Text3D(f3d, "-Z", origin, Text3D.ALIGN_CENTER,
       Text3D.PATH_RIGHT);
   // orient around the local origin
   OrientedShape3D minusZTextShape = new OrientedShape3D(minusZText,
       appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
   // transform to scale down to 0.15 in height, locate at end of axis
   TransformGroup minusZTG = new TransformGroup();
   tmpVector.set(0.0f, 0.0f, -coordSysLength / 2 - labelOffset);
   tmpTrans.set(0.15f, tmpVector);
   minusZTG.setTransform(tmpTrans);
   minusZTG.addChild(minusZTextShape);
   addChild(minusZTG);
 }

} class LeftAlignComponent extends JPanel {

 LeftAlignComponent(Component c) {
   setLayout(new BorderLayout());
   add(c, BorderLayout.WEST);
 }

} class RotAxis extends Switch implements Java3DExplorerConstants {

   // axis to align with 
   Vector3f    rotAxis = new Vector3f(1.0f, 0.0f, 0.0f); 
   // offset to ref point 
   Vector3f    refPt = new Vector3f(0.0f, 0.0f, 0.0f); 
   TransformGroup  axisTG; // the transform group used to align the axis
   // Temporaries that are reused
   Transform3D    tmpTrans = new Transform3D();
   Vector3f    tmpVector = new Vector3f();
   AxisAngle4f    tmpAxisAngle = new AxisAngle4f();
   // geometric constants
   Point3f    origin = new Point3f();
   Vector3f    yAxis = new Vector3f(0.0f, 1.0f, 0.0f);
   RotAxis(float axisLength) {
 super(Switch.CHILD_NONE);
 setCapability(Switch.ALLOW_SWITCH_READ);
 setCapability(Switch.ALLOW_SWITCH_WRITE);
 // set up the proportions for the arrow
 float axisRadius = axisLength / 120.0f;
 float arrowRadius = axisLength / 50.0f;
 float arrowHeight = axisLength / 30.0f;
 // create the TransformGroup which will be used to orient the axis
 axisTG = new TransformGroup();
 axisTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
 axisTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
 addChild(axisTG);
 // Set up an appearance to make the Axis have 
 // blue ambient, black emmissive, blue diffuse and white specular 
 // coloring.  
 Material material = new Material(blue, black, blue, white, 64);
 Appearance appearance = new Appearance();
 appearance.setMaterial(material);
 // create a cylinder for the central line of the axis
 Cylinder cylinder = new Cylinder(axisRadius, axisLength, appearance); 
 // cylinder goes from -length/2 to length/2 in y
 axisTG.addChild(cylinder);
 // create a SharedGroup for the arrowHead
 Cone arrowHead = new Cone(arrowRadius, arrowHeight, appearance); 
 SharedGroup arrowHeadSG = new SharedGroup();
 arrowHeadSG.addChild(arrowHead);
 // Create a TransformGroup to move the cone to the top of the 
 // cylinder
 tmpVector.set(0.0f, axisLength/2 + arrowHeight / 2, 0.0f);
 tmpTrans.set(tmpVector);
 TransformGroup topTG = new TransformGroup();  
 topTG.setTransform(tmpTrans);
 topTG.addChild(new Link(arrowHeadSG));
 axisTG.addChild(topTG);
 // create the bottom of the arrow
 // Create a TransformGroup to move the cone to the bottom of the 
 // axis so that its pushes into the bottom of the cylinder
 tmpVector.set(0.0f, -(axisLength / 2), 0.0f);
 tmpTrans.set(tmpVector);
 TransformGroup bottomTG = new TransformGroup();  
 bottomTG.setTransform(tmpTrans);
 bottomTG.addChild(new Link(arrowHeadSG));
 axisTG.addChild(bottomTG);
 updateAxisTransform();
   }
   public void setRotationAxis(Vector3f setRotAxis) {
 rotAxis.set(setRotAxis);
 float magSquared = rotAxis.lengthSquared();
 if (magSquared > 0.0001) {
     rotAxis.scale((float)(1.0 / Math.sqrt(magSquared)));
 } else {
     rotAxis.set(1.0f, 0.0f, 0.0f);
 }
 updateAxisTransform();
   }
   public void setRefPt(Vector3f setRefPt) {
 refPt.set(setRefPt);
 updateAxisTransform();
   }
   // set the transform on the axis so that it aligns with the rotation
   // axis and goes through the reference point
   private void updateAxisTransform() {
 // We need to rotate the axis, which is defined along the y-axis,
 // to the direction indicated by the rotAxis.
 // We can do this using a neat trick.  To transform a vector to align
 // with another vector (assuming both vectors have unit length), take 
 // the cross product the the vectors.  The direction of the cross
 // product is the axis, and the length of the cross product is the
 // the sine of the angle, so the inverse sine of the length gives 
 // us the angle
 tmpVector.cross(yAxis, rotAxis);
 float angle = (float)Math.asin(tmpVector.length());
 tmpAxisAngle.set(tmpVector, angle);
 tmpTrans.set(tmpAxisAngle);
 tmpTrans.setTranslation(refPt);
 axisTG.setTransform(tmpTrans);
   }

}


      </source>