HelloUniverse.java Source code

Java tutorial

Introduction

Here is the source code for HelloUniverse.java

Source

/*
 * @(#)HelloUniverse.java 1.17 02/10/21 13:58:47
 * 
 * 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.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.util.Enumeration;

import javax.media.j3d.Alpha;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.InputDevice;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Sensor;
import javax.media.j3d.SensorRead;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.WakeupOnElapsedFrames;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.SimpleUniverse;

public class HelloUniverse extends Applet {

    private SimpleUniverse u = null;

    public BranchGroup createSceneGraph() {

        BranchGroup objRoot = new BranchGroup();
        TransformGroup objTrans = new TransformGroup();
        objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        objRoot.addChild(objTrans);
        objTrans.addChild(new ColorCube(0.2));
        Transform3D yAxis = new Transform3D();
        Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 4000, 0, 0, 0, 0, 0);
        RotationInterpolator rotator = new RotationInterpolator(rotationAlpha, objTrans, yAxis, 0.0f,
                (float) Math.PI * 2.0f);
        BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
        rotator.setSchedulingBounds(bounds);
        objTrans.addChild(rotator);
        return objRoot;
    }

    public HelloUniverse() {

    }

    public void init() {
        // These are the string arguments given to the VirtualInputDevice
        // constructor. These are settable parameters. Look in the
        // VirtualInputDevice constructor for a complete list.
        String[] args = new String[10];
        args[0] = "printvalues";
        args[1] = "true";
        args[2] = "yscreeninitloc";
        args[3] = "50";
        args[4] = null;

        InputDevice device = new VirtualInputDevice(args);

        // now create the HelloUniverse Canvas
        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);

        // The InputDevice must be initialized before registering it
        // with the PhysicalEnvironment object.
        device.initialize();

        // Register the VirtualInputDevice with Java 3D
        u.getViewer().getPhysicalEnvironment().addInputDevice(device);

        TransformGroup viewTrans = u.getViewingPlatform().getViewPlatformTransform();
        SensorBehavior s = new SensorBehavior(viewTrans, device.getSensor(0));
        s.setSchedulingBounds(new BoundingSphere(new Point3d(0.0, 0.0, 0.0), Float.MAX_VALUE));
        scene.addChild(s);
        u.addBranchGraph(scene);
    }

    public void destroy() {
        u.cleanup();
    }

    public static void main(String[] args) {
        new MainFrame(new HelloUniverse(), 350, 350);
    }
}

class VirtualInputDevice implements InputDevice {

    private Vector3f position = new Vector3f();

    private Transform3D newTransform = new Transform3D();

    Sensor sensors[] = new Sensor[1];

    // The wheel controls control the view platform orientation
    private RotationControls rotControls;

    // The button position controls control the view platform position
    private PositionControls positionControls;

    private Transform3D rotTransX = new Transform3D();

    private Transform3D rotTransY = new Transform3D();

    private Transform3D rotTransZ = new Transform3D();

    private Vector3f initPos = new Vector3f();

    private int processingMode;

    private SensorRead sensorRead = new SensorRead();

    // These are the settable parameters.
    private boolean printvalues;

    private int xscreeninitloc;

    private int yscreeninitloc;

    private int xscreensize;

    private int yscreensize;

    private float xobjinitloc;

    private float yobjinitloc;

    private float zobjinitloc;

    private float xaxisrotinit;

    private float yaxisrotinit;

    private float zaxisrotinit;

    /*
     * Create a device, and use the string arguments in args to construct the
     * device with user preferences.
     */
    public VirtualInputDevice(String[] args) {

        // default user-definable values
        printvalues = false;
        xscreeninitloc = 400;
        yscreeninitloc = 0;
        xscreensize = 400;
        yscreensize = 200;
        xobjinitloc = 0.0f;
        yobjinitloc = 0.0f;
        zobjinitloc = 2.2f;
        xaxisrotinit = 0.0f;
        yaxisrotinit = 0.0f;
        zaxisrotinit = 0.0f;

        for (int i = 0; i < args.length; i += 2) {
            if (args[i] == null)
                break;
            else if (args[i] == "printvalues")
                printvalues = (Boolean.valueOf(args[i + 1])).booleanValue();
            else if (args[i] == "xscreeninitloc")
                xscreeninitloc = (Integer.valueOf(args[i + 1])).intValue();
            else if (args[i] == "yscreeninitloc")
                yscreeninitloc = (Integer.valueOf(args[i + 1])).intValue();
            else if (args[i] == "xscreensize")
                xscreensize = (Integer.valueOf(args[i + 1])).intValue();
            else if (args[i] == "yscreensize")
                yscreensize = (Integer.valueOf(args[i + 1])).intValue();
            else if (args[i] == "xobjinitloc")
                xobjinitloc = (Float.valueOf(args[i + 1])).floatValue();
            else if (args[i] == "yobjinitloc")
                yobjinitloc = (Float.valueOf(args[i + 1])).floatValue();
            else if (args[i] == "zobjinitloc")
                zobjinitloc = (Integer.valueOf(args[i + 1])).floatValue();
        }

        if (printvalues == true) {
            System.out.println("Initial values for VirtualInputDevice:");
            System.out.println("xscreeninitloc = " + xscreeninitloc);
            System.out.println("yscreeninitloc = " + yscreeninitloc);
            System.out.println("xscreeninitsize = " + xscreensize);
            System.out.println("yscreeninitsize = " + yscreensize);
            System.out.println("xobjinitloc = " + xobjinitloc);
            System.out.println("yobjinitloc = " + yobjinitloc);
            System.out.println("zobjinitloc = " + zobjinitloc);
            System.out.println("xaxisrotinit = " + xaxisrotinit);
            System.out.println("yaxisrotinit = " + yaxisrotinit);
            System.out.println("zaxisrotinit = " + zaxisrotinit);
        }

        // initialize the InputDevice GUI
        Frame deviceFrame = new Frame();
        deviceFrame.setSize(xscreensize, yscreensize);
        deviceFrame.setLocation(xscreeninitloc, yscreeninitloc);
        deviceFrame.setTitle("Virtual Input Device");
        ButtonPositionControls positionControls;
        // initialize position with initial x, y, and z position
        positionControls = new ButtonPositionControls(xobjinitloc, yobjinitloc, zobjinitloc);
        WheelControls rotControls;
        // initialize rotations with initial angles in radians)
        rotControls = new WheelControls(xaxisrotinit, yaxisrotinit, zaxisrotinit);
        positionControls.setDevice(this);
        Panel devicePanel = new Panel();
        devicePanel.setLayout(new BorderLayout());
        devicePanel.add("East", positionControls);
        devicePanel.add("West", rotControls);
        deviceFrame.add(devicePanel);
        deviceFrame.pack();
        deviceFrame.setVisible(true);

        initPos.set(xobjinitloc, yobjinitloc, zobjinitloc);

        this.positionControls = positionControls;
        this.rotControls = rotControls;

        // default processing mode
        processingMode = InputDevice.DEMAND_DRIVEN;

        sensors[0] = new Sensor(this);
    }

    public void close() {
    }

    public int getProcessingMode() {
        return processingMode;
    }

    public int getSensorCount() {
        return sensors.length;
    }

    public Sensor getSensor(int sensorIndex) {
        return sensors[sensorIndex];
    }

    public boolean initialize() {
        return true;
    }

    public void pollAndProcessInput() {

        sensorRead.setTime(System.currentTimeMillis());

        rotTransX.rotX(-rotControls.getXAngle());
        rotTransY.rotY(-rotControls.getYAngle());
        rotTransZ.rotZ(-rotControls.getZAngle());

        positionControls.getPosition(position);
        newTransform.set(position);
        newTransform.mul(rotTransX);

        newTransform.mul(rotTransY);
        newTransform.mul(rotTransZ);

        sensorRead.set(newTransform);
        sensors[0].setNextSensorRead(sensorRead);
    }

    public void processStreamInput() {
    }

    public void setNominalPositionAndOrientation() {

        sensorRead.setTime(System.currentTimeMillis());

        rotTransX.rotX(xaxisrotinit);
        rotTransY.rotY(yaxisrotinit);
        rotTransZ.rotZ(zaxisrotinit);

        position.set(initPos);

        newTransform.set(position);

        newTransform.mul(rotTransX);
        newTransform.mul(rotTransY);
        newTransform.mul(rotTransZ);

        sensorRead.set(newTransform);
        sensors[0].setNextSensorRead(sensorRead);
        rotControls.reset();
        positionControls.setPosition(initPos);
    }

    public void setProcessingMode(int mode) {

        // A typical driver might implement only one of these modes, and
        // throw an exception when there is an attempt to switch modes.
        // However, this example allows one to use any processing mode.

        switch (mode) {
        case InputDevice.DEMAND_DRIVEN:
        case InputDevice.NON_BLOCKING:
        case InputDevice.BLOCKING:
            processingMode = mode;
            break;
        default:
            throw new IllegalArgumentException(
                    "Processing mode must " + "be one of DEMAND_DRIVEN, NON_BLOCKING, or BLOCKING");
        }
    }

}

class SensorBehavior extends Behavior {

    private WakeupOnElapsedFrames conditions = new WakeupOnElapsedFrames(0);

    private TransformGroup transformGroup;

    private Sensor sensor;

    private Transform3D transform = new Transform3D();

    public SensorBehavior(TransformGroup tg, Sensor sensor) {
        transformGroup = tg;
        this.sensor = sensor;
    }

    public void initialize() {
        wakeupOn(conditions);
    }

    public void processStimulus(Enumeration criteria) {
        sensor.getRead(transform);
        transformGroup.setTransform(transform);
        wakeupOn(conditions);
    }

}

//Classes that implement this interface must be a
//subclass of java.awt.Component

interface PositionControls {

    /**
     * Get the position
     */
    public void getPosition(Vector3f pos);

    /**
     * Set the position
     */
    public void setPosition(Vector3f pos);

    /**
     * Increment added to position each time mouse is pressed or if the mouse is
     * held down each time the Sensor is read
     */
    public void setStepRate(float stepRate);
}

//Classes that implement this interface must be a subclass
//of java.awt.Component

interface RotationControls {

    /**
     * Get the angle of Rotation around the X Axis
     */
    public abstract float getXAngle();

    /**
     * Get the angle or Rotation around the Y Axis
     */
    public abstract float getYAngle();

    /**
     * Get the angle or Rotation around the Z Axis
     */
    public abstract float getZAngle();

    /**
     * Reset angles to original angle.
     */
    public abstract void reset();
}

class WheelControls extends Canvas implements RotationControls, MouseMotionListener, MouseListener {

    private final static int NONE = 0;

    private final static int SLIDE_Y = 1;

    private final static int SLIDE_X = 2;

    private final static int SLIDE_Z = 3;

    private int mode = NONE;

    private Dimension size;

    private int thickness;

    private int diameter;

    private int space;

    private int pipSize;

    private int pipOffset; // Amount pip is below wheel

    private int margin; // Margin between edge of Canvas and

    // controls

    private Polygon yPip;

    private Rectangle yBackClip;

    private Polygon xPip;

    private Rectangle xBackClip;

    private Polygon zPip;

    private Rectangle yArea;

    private Rectangle xArea;

    private Rectangle zArea;

    private Point oldMousePos = new Point();

    float yAngle = 0.0f;

    float xAngle = 0.0f;

    float zAngle = 0.0f;

    float yOrigAngle;

    float xOrigAngle;

    float zOrigAngle;

    float angleStep = (float) Math.PI / 30.0f;

    public WheelControls() {
        this(0.0f, 0.0f, 0.0f);
    }

    public WheelControls(float rotX, float rotY, float rotZ) {
        size = new Dimension(200, 200);

        xAngle = constrainAngle(rotX);
        yAngle = constrainAngle(rotY);
        zAngle = constrainAngle(rotZ);

        yOrigAngle = yAngle;
        xOrigAngle = xAngle;
        zOrigAngle = zAngle;

        setSizes();

        yPip = new Polygon();
        yPip.addPoint(0, 0);
        yPip.addPoint(-pipSize / 2, pipSize);
        yPip.addPoint(pipSize / 2, pipSize);

        xPip = new Polygon();
        xPip.addPoint(0, 0);
        xPip.addPoint(pipSize, -pipSize / 2);
        xPip.addPoint(pipSize, pipSize / 2);

        zPip = new Polygon();
        zPip.addPoint(diameter / 2, pipOffset);
        zPip.addPoint(diameter / 2 - pipSize / 2, pipOffset - pipSize);
        zPip.addPoint(diameter / 2 + pipSize / 2, pipOffset - pipSize);

        addMouseListener(this);
        addMouseMotionListener(this);
    }

    private void setSizes() {
        margin = 10;
        int width = size.width - margin * 2;
        thickness = width * 7 / 100;
        diameter = width * 70 / 100;
        space = width * 10 / 100;
        pipSize = width * 7 / 100;

        pipOffset = thickness / 2;

    }

    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        g.drawOval(margin, margin, diameter, diameter);
        zArea = new Rectangle(margin, margin, diameter, diameter);
        drawZPip(g2, zAngle);

        g.drawRect(margin, margin + diameter + space, diameter, thickness); // Y
        // Wheel
        yArea = new Rectangle(margin, margin + diameter + space, margin + diameter, thickness + pipOffset);
        yBackClip = new Rectangle(margin - thickness, margin + diameter + space + thickness,
                margin + diameter + thickness * 2, thickness);
        drawYPip(g2, yAngle);

        g.drawRect(margin + diameter + space, margin, thickness, diameter); // X
        // Wheel
        xArea = new Rectangle(margin + diameter + space, margin, thickness + pipOffset, margin + diameter);
        xBackClip = new Rectangle(margin + diameter + space + thickness, margin - thickness, thickness,
                margin + diameter + thickness * 2);
        drawXPip(g2, xAngle);

    }

    public float getXAngle() {
        return xAngle;
    }

    public float getYAngle() {
        return yAngle;
    }

    public float getZAngle() {
        return zAngle;
    }

    public void reset() {
        // Overwrite the old pip
        drawYPip((Graphics2D) (this.getGraphics()), yAngle);
        yAngle = yOrigAngle;
        // Draw the new Pip
        drawYPip((Graphics2D) (this.getGraphics()), yAngle);

        // Overwrite the old pip
        drawXPip((Graphics2D) (this.getGraphics()), xAngle);
        xAngle = xOrigAngle;
        // Draw the new Pip
        drawXPip((Graphics2D) (this.getGraphics()), xAngle);

        drawZPip((Graphics2D) (this.getGraphics()), zAngle);

        zAngle = zOrigAngle;

        drawZPip((Graphics2D) (this.getGraphics()), zAngle);
        oldMousePos.setLocation(0, 0);
    }

    private void drawXPip(Graphics2D g2, float angle) {
        AffineTransform trans = new AffineTransform();
        int y;
        int xOrig = margin + diameter + space;
        int yOrig = margin;
        Color origColor = g2.getColor();

        if (angle <= Math.PI) {
            y = yOrig + diameter - (int) ((Math.abs(angle - Math.PI / 2) / (Math.PI / 2)) * diameter / 2);
        } else
            y = yOrig + (int) ((Math.abs((angle - Math.PI * 1.5)) / (Math.PI / 2)) * diameter / 2);

        if (angle < Math.PI / 2 || angle > Math.PI * 1.5)
            g2.setColor(Color.red); // Infront of wheel
        else {
            g2.setColor(Color.black); // Behind Wheel
            g2.setClip(xBackClip);
        }

        g2.setXORMode(getBackground());
        trans.setToTranslation(xOrig + pipOffset, y);
        g2.setTransform(trans);
        g2.fillPolygon(xPip);

        // Reset graphics context
        trans.setToIdentity();
        g2.setTransform(trans);
        g2.setColor(origColor);
        g2.setPaintMode();
    }

    private void drawYPip(Graphics2D g2, float angle) {
        AffineTransform trans = new AffineTransform();
        int x;
        int xOrig = margin;
        int yOrig = margin + diameter + space;
        Color origColor = g2.getColor();

        if (angle <= Math.PI) {
            x = xOrig + diameter - (int) ((Math.abs(angle - Math.PI / 2) / (Math.PI / 2)) * diameter / 2);
        } else
            x = xOrig + (int) ((Math.abs((angle - Math.PI * 1.5)) / (Math.PI / 2)) * diameter / 2);

        if (angle < Math.PI / 2 || angle > Math.PI * 1.5)
            g2.setColor(Color.red); // Infront on wheel
        else {
            g2.setColor(Color.black); // Behind Wheel
            g2.setClip(yBackClip);
        }

        g2.setXORMode(getBackground());
        trans.setToTranslation(x, yOrig + pipOffset);
        g2.setTransform(trans);
        g2.fillPolygon(yPip);

        // Reset graphics context
        trans.setToIdentity();
        g2.setTransform(trans);
        g2.setColor(origColor);
        g2.setPaintMode();
    }

    private void drawZPip(Graphics2D g2, float zAngle) {
        AffineTransform trans = new AffineTransform();
        Color origColor = g2.getColor();

        trans.translate(margin, margin);
        trans.rotate(zAngle, diameter / 2, diameter / 2);

        g2.setXORMode(getBackground());
        g2.setTransform(trans);
        g2.setColor(Color.red);
        g2.fillPolygon(zPip);

        // Reset graphics context
        trans.setToIdentity();
        g2.setTransform(trans);
        g2.setColor(origColor);
        g2.setPaintMode();
    }

    public Dimension getPreferredSize() {
        return size;
    }

    public void setSize(Dimension d) {
        // Set size to smallest dimension
        if (d.width < d.height)
            size.width = size.height = d.width;
        else
            size.width = size.height = d.height;
        setSizes();
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
        if (yArea.contains(e.getPoint())) {
            mode = SLIDE_Y;
            oldMousePos = e.getPoint();
        } else if (xArea.contains(e.getPoint())) {
            mode = SLIDE_X;
            oldMousePos = e.getPoint();
        } else if (zArea.contains(e.getPoint())) {
            mode = SLIDE_Z;
            oldMousePos = e.getPoint();
        }
    }

    public void mouseReleased(MouseEvent e) {
        mode = NONE;
    }

    public void mouseDragged(MouseEvent e) {
        Point pos = e.getPoint();

        int diffX = pos.x - oldMousePos.x;
        int diffY = pos.y - oldMousePos.y;

        switch (mode) {
        case NONE:
            break;
        case SLIDE_Y:
            // Overwrite the old pip
            drawYPip((Graphics2D) ((Canvas) e.getSource()).getGraphics(), yAngle);
            if (diffX < 0)
                yAngle -= angleStep;
            else if (diffX > 0)
                yAngle += angleStep;

            yAngle = constrainAngle(yAngle);

            // Draw the new Pip
            drawYPip((Graphics2D) ((Canvas) e.getSource()).getGraphics(), yAngle);
            oldMousePos = pos;
            break;
        case SLIDE_X:
            // Overwrite the old pip
            drawXPip((Graphics2D) ((Canvas) e.getSource()).getGraphics(), xAngle);
            if (diffY < 0)
                xAngle -= angleStep;
            else if (diffY > 0)
                xAngle += angleStep;

            xAngle = constrainAngle(xAngle);

            // Draw the new Pip
            drawXPip((Graphics2D) ((Canvas) e.getSource()).getGraphics(), xAngle);
            oldMousePos = pos;
            break;
        case SLIDE_Z:
            drawZPip((Graphics2D) ((Canvas) e.getSource()).getGraphics(), zAngle);

            if (diffX < 0)
                zAngle -= angleStep;
            else if (diffX > 0)
                zAngle += angleStep;

            zAngle = constrainAngle(zAngle);
            drawZPip((Graphics2D) ((Canvas) e.getSource()).getGraphics(), zAngle);
            oldMousePos = pos;
            break;
        default:
            throw (new RuntimeException("Internal Error"));
        }
    }

    public void mouseMoved(MouseEvent e) {
    }

    /**
     * Constrain angle to be 0 <angle <2PI
     */
    private float constrainAngle(float angle) {
        if (angle > (float) Math.PI * 2)
            return angle - (float) Math.PI * 2;
        if (angle < 0.0f)
            return angle + (float) Math.PI * 2;
        return angle;
    }
}

class ButtonPositionControls extends Panel implements PositionControls, MouseListener {
    private final static int STILL = 0;

    private final static int MOVING_UP = 1;

    private final static int MOVING_DOWN = 2;

    private final static int MOVING_LEFT = 3;

    private final static int MOVING_RIGHT = 4;

    private final static int MOVING_FORWARD = 5;

    private final static int MOVING_BACK = 6;

    // initial mode
    private int mode = STILL;

    Vector3f position = new Vector3f();

    Vector3f orig_position = new Vector3f();

    private Button leftB = new Button("Move Left");

    private Button rightB = new Button("Move Right");

    private Button upB = new Button("Move Up");

    private Button downB = new Button("Move Down");

    private Button forwardB = new Button("Move Forward");

    private Button backwardB = new Button("Move Back");

    private Button reset = new Button("Reset");

    private InputDevice device;

    private float step_rate = 0.0023f; // movement rate per millisecond

    private long time_last_state_change = System.currentTimeMillis();

    // the constructor arguments are the intitial X, Y, and Z positions
    public ButtonPositionControls(float x, float y, float z) {

        // up, down, right, and left movement buttons
        Panel panPanel = new Panel();
        panPanel.setLayout(new BorderLayout());
        panPanel.add("North", upB);
        panPanel.add("East", rightB);
        panPanel.add("South", downB);
        panPanel.add("West", leftB);

        // forward, backward, and reset buttons
        Panel p = new Panel();
        p.setLayout(new GridLayout(0, 1, 0, 0));
        p.add(forwardB);
        p.add(backwardB);
        p.add(reset);

        // set the initial position
        position.x = x;
        position.y = y;
        position.z = z;
        orig_position.set(position);

        // add a mouse listener to each button
        upB.addMouseListener(this);
        downB.addMouseListener(this);
        leftB.addMouseListener(this);
        rightB.addMouseListener(this);
        forwardB.addMouseListener(this);
        backwardB.addMouseListener(this);
        reset.addMouseListener(this);

        this.setLayout(new BorderLayout());
        add("East", p);
        add("West", panPanel);
    }

    public void setDevice(InputDevice device) {
        this.device = device;
    }

    public void getPosition(Vector3f pos) {
        calculateMotion();
        pos.set(position);
    }

    public void setPosition(Vector3f pos) {
        position.set(pos);
    }

    public void setStepRate(float stepRate) {
        step_rate = stepRate;
    }

    private void calculateMotion() {

        long current_time = System.currentTimeMillis();
        long elapsed_time = current_time - time_last_state_change;

        switch (mode) {
        case STILL:
            break;
        case MOVING_LEFT:
            position.x = orig_position.x - step_rate * elapsed_time;
            break;
        case MOVING_RIGHT:
            position.x = orig_position.x + step_rate * elapsed_time;
            break;
        case MOVING_UP:
            position.y = orig_position.y + step_rate * elapsed_time;
            break;
        case MOVING_DOWN:
            position.y = orig_position.y - step_rate * elapsed_time;
            break;
        case MOVING_FORWARD:
            position.z = orig_position.z - step_rate * elapsed_time;
            break;
        case MOVING_BACK:
            position.z = orig_position.z + step_rate * elapsed_time;
            break;
        default:
            throw (new RuntimeException("Unknown motion"));
        }
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
        if (e.getSource() == leftB && mode != MOVING_LEFT) {
            time_last_state_change = System.currentTimeMillis();
            mode = MOVING_LEFT;
            orig_position.set(position);
        } else if (e.getSource() == rightB && mode != MOVING_RIGHT) {
            time_last_state_change = System.currentTimeMillis();
            mode = MOVING_RIGHT;
            orig_position.set(position);
        } else if (e.getSource() == upB && mode != MOVING_UP) {
            time_last_state_change = System.currentTimeMillis();
            mode = MOVING_UP;
            orig_position.set(position);
        } else if (e.getSource() == downB && mode != MOVING_DOWN) {
            time_last_state_change = System.currentTimeMillis();
            mode = MOVING_DOWN;
            orig_position.set(position);
        } else if (e.getSource() == forwardB && mode != MOVING_FORWARD) {
            time_last_state_change = System.currentTimeMillis();
            mode = MOVING_FORWARD;
            orig_position.set(position);
        } else if (e.getSource() == backwardB && mode != MOVING_BACK) {
            time_last_state_change = System.currentTimeMillis();
            mode = MOVING_BACK;
            orig_position.set(position);
        } else if (e.getSource() == reset) {
            device.setNominalPositionAndOrientation();
        }
    }

    public void mouseReleased(MouseEvent e) {
        mode = STILL;
    }
}