Source Code Cross Referenced for WandViewBehavior.java in  » 6.0-JDK-Modules » java-3d » com » sun » j3d » utils » behaviors » vp » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » java 3d » com.sun.j3d.utils.behaviors.vp 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: WandViewBehavior.java,v $
0003:         *
0004:         * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * Redistribution and use in source and binary forms, with or without
0007:         * modification, are permitted provided that the following conditions
0008:         * are met:
0009:         *
0010:         * - Redistribution of source code must retain the above copyright
0011:         *   notice, this list of conditions and the following disclaimer.
0012:         *
0013:         * - Redistribution in binary form must reproduce the above copyright
0014:         *   notice, this list of conditions and the following disclaimer in
0015:         *   the documentation and/or other materials provided with the
0016:         *   distribution.
0017:         *
0018:         * Neither the name of Sun Microsystems, Inc. or the names of
0019:         * contributors may be used to endorse or promote products derived
0020:         * from this software without specific prior written permission.
0021:         *
0022:         * This software is provided "AS IS," without a warranty of any
0023:         * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024:         * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025:         * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026:         * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027:         * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028:         * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029:         * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030:         * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031:         * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032:         * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033:         * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034:         * POSSIBILITY OF SUCH DAMAGES.
0035:         *
0036:         * You acknowledge that this software is not designed, licensed or
0037:         * intended for use in the design, construction, operation or
0038:         * maintenance of any nuclear facility.
0039:         *
0040:         * $Revision: 1.5 $
0041:         * $Date: 2007/10/08 23:54:09 $
0042:         * $State: Exp $
0043:         */
0044:
0045:        package com.sun.j3d.utils.behaviors.vp;
0046:
0047:        import java.util.*;
0048:        import javax.vecmath.*;
0049:        import javax.media.j3d.*;
0050:        import com.sun.j3d.utils.universe.*;
0051:        import com.sun.j3d.utils.behaviors.sensor.*;
0052:
0053:        /**
0054:         * Manipulates view platform transforms using a motion-tracked wand or mouse
0055:         * equipped with a six degree of freedom (6DOF) sensor.  An optional two axis
0056:         * (2D) valuator sensor is also directly supported.  Default operation is set
0057:         * up to enable both direct manipulation of the view transform and translation
0058:         * back and forth along the direction the 6DOF sensor is pointing; rotation
0059:         * is handled by the 2D valuator if available.  An arbitrary number of sensors
0060:         * and action bindings can be customized by accessing this behavior's
0061:         * <code>SensorEventAgent</code> directly.
0062:         * <p>
0063:         * This behavior can be instantiated from the configuration file read by
0064:         * <code>ConfiguredUniverse</code> and fully configured using the
0065:         * <code>ViewPlatformBehaviorProperties</code> command to set the properties
0066:         * described below, but neither <code>ConfiguredUniverse</code> nor
0067:         * <code>SimpleUniverse</code> are required by this behavior.  Conventional
0068:         * <code>set</code> and <code>get</code> accessors are provided for
0069:         * configuring this behavior directly; these methods have the same names as
0070:         * the properties but are prefixed with <code>get</code> and <code>set</code>.
0071:         * Property values are spelled with mixed case strings while the corresponding
0072:         * constant field names for the conventional accessors are spelled with upper
0073:         * case strings and underscores.
0074:         * <p>
0075:         * {@link #Sensor6D Sensor6D} is the 6DOF sensor to use.  This can also be set
0076:         * directly with the appropriate constructor.  This sensor must generate 6
0077:         * degree of freedom position and orientation reads relative to the tracker
0078:         * base in physical units.  By default this behavior provides an echo for the
0079:         * 6DOF sensor which indicates its position and orientation in the virtual
0080:         * world; the echo attributes can be set by the {@link #EchoType EchoType},
0081:         * {@link #EchoSize EchoSize}, {@link #EchoColor EchoColor}, and {@link
0082:         * #EchoTransparency EchoTransparency} properties.  See also the {@link
0083:         * #NominalSensorRotation NominalSensorRotation} property, and the
0084:         * <code>setHotSpot</code> method of the <code>Sensor</code> class.
0085:         * <p>
0086:         * {@link #Sensor2D Sensor2D} is an optional 2D valuator to use in conjunction
0087:         * with the 6DOF sensor.  This can be set directly with the appropriate
0088:         * constructor.  The valuator should generate X and Y reads ranging from [-1.0
0089:         * .. +1.0], with a nominal (deadzone) value of 0.0.  The default
0090:         * configuration expects to find these values along the translation components
0091:         * of the read matrix, at indices 3 and 7, but these indices can be also be
0092:         * specified by the {@link #MatrixIndices2D MatrixIndices2D} property.
0093:         * <p>
0094:         * {@link #ButtonAction6D ButtonAction6D} sets an action for a specific button
0095:         * on a 6DOF sensor.  The actions available are:
0096:         * <ul>
0097:         * <li>
0098:         * <code>GrabView</code> - Directly manipulates the view platform by moving
0099:         * it in inverse response to the sensor's position and orientation,
0100:         * producing the effect of attaching the virtual world to the sensor's
0101:         * movements.  If a button is available then this action is bound to button 0
0102:         * by default.
0103:         * </li>
0104:         * <li>
0105:         * <code>TranslateForward</code> - Translates the view platform forward along
0106:         * the direction the sensor is pointing; the virtual world appears to move
0107:         * towards the sensor.  The default is button 1 if two buttons are available.
0108:         * Related properties are {@link #TranslationSpeed TranslationSpeed}, {@link
0109:         * #AccelerationTime AccelerationTime}, {@link #ConstantSpeedTime
0110:         * ConstantSpeedTime}, and {@link #FastSpeedFactor FastSpeedFactor}.
0111:         * </li>
0112:         * <li>
0113:         * <code>TranslateBackward</code> - Translates the view platform backwards
0114:         * along the direction the sensor is pointing; the virtual world appears to
0115:         * move away from the sensor.  The default is button 2 if three buttons are
0116:         * available. 
0117:         * </li>
0118:         * <li>
0119:         * <code>RotateCCW</code> - Rotates the view platform counter-clockwise about
0120:         * a Y axis; the virtual world appears to rotate clockwise.  This action is
0121:         * not assigned by default.  Related properties are {@link #RotationSpeed
0122:         * RotationSpeed}, {@link #RotationCoords RotationCoords}, {@link
0123:         * #TransformCenterSource TransformCenterSource}, {@link #TransformCenter
0124:         * TransformCenter}, and <code>AccelerationTime</code>.
0125:         * </li>
0126:         * <li>
0127:         * <code>RotateCW</code> - Rotates the view platform clockwise about a Y axis;
0128:         * the virtual world appears to rotate counter-clockwise.  This action is not
0129:         * assigned by default.
0130:         * </li>
0131:         * <li>
0132:         * <code>ScaleUp</code> - Scales the view platform larger so that the virtual
0133:         * world appears to grow smaller.  This action is not assigned by default.
0134:         * Related properties are {@link #ScaleSpeed ScaleSpeed},
0135:         * <code>TransformCenterSource</code>, <code>TransformCenter</code>, and
0136:         * <code>AccelerationTime</code>.
0137:         * </li>
0138:         * <li>
0139:         * <code>ScaleDown</code> - Scales the view platform smaller so that the
0140:         * virtual world appears to grow larger.  This action is not assigned by
0141:         * default.
0142:         * </li>
0143:         * </ul>
0144:         * <p>
0145:         * {@link #ReadAction2D ReadAction2D} sets the action bound to 2D valuator
0146:         * reads; that is, non-zero values generated by the device when no buttons
0147:         * have been pressed.  If the value is (0.0, 0.0) or below the threshold value
0148:         * set by {@link #Threshold2D Threshold2D}, then this behavior does nothing;
0149:         * otherwise, the following actions can be performed:
0150:         * <ul>
0151:         * <li>
0152:         * <code>Rotation</code> - Rotates the view platform.  This is the default 2D
0153:         * valuator action set by this behavior. Related properties are
0154:         * <code>RotationSpeed</code>, <code>RotationCoords</code>,
0155:         * <code>TransformCenterSource</code>, and <code>TransformCenter</code>.
0156:         * </li>
0157:         * <li>
0158:         * <code>Translation</code> - Translates the view platform.  The translation
0159:         * occurs relative to the X and Z basis vectors of either the 6DOF sensor or
0160:         * the view platform if one is not available.  The maximum speed is equal to
0161:         * the product of the <code>TranslationSpeed</code> and
0162:         * <code>FastSpeedFactor</code> property values.
0163:         * </li>
0164:         * <li>
0165:         * <code>Scale</code> - Scales the view platform smaller with positive Y
0166:         * values and larger with negative Y values.  The effect is to increase the
0167:         * apparent size of the virtual world when pushing the valuator forwards and
0168:         * to decrease it when pushing backwards.  Related properties are
0169:         * <code>ScaleSpeed</code>, <code>TransformCenterSource</code>, and
0170:         * <code>TransformCenter</code>.
0171:         * </li>
0172:         * </ul>
0173:         * <p>
0174:         * {@link #ButtonAction2D ButtonAction2D} sets an action for a specific button
0175:         * on the 2D valuator. The available actions are the same as for
0176:         * <code>ReadAction2D</code>.  No actions are bound by default to the 2D
0177:         * valuator buttons.
0178:         * <p>
0179:         * The view transform may be reset to its home transform by pressing a number
0180:         * of buttons simultaneously on the 6DOF sensor.  The minimum number of
0181:         * buttons that must be pressed is set by {@link #ResetViewButtonCount6D
0182:         * ResetViewButtonCount6D}.  This value must be greater than one; the default
0183:         * is three.  This action may be disabled by setting the property value to
0184:         * None.  The corresponding property for the 2D valuator is {@link
0185:         * #ResetViewButtonCount2D ResetViewButtonCount2D}, with a default value of
0186:         * None.  Note, however, that the reset view action will be ineffectual if an
0187:         * action which always modifies the view transform is bound to reads on the
0188:         * sensor used to reset the view, since the reset transform will get
0189:         * overwritten by the read action.
0190:         * <p>
0191:         * The special value <code>None</code> can in general be assigned to any
0192:         * button or read action to prevent any defaults from being bound to it.
0193:         *
0194:         * @see ConfiguredUniverse
0195:         * @see SensorEventAgent
0196:         * @since Java 3D 1.3
0197:         */
0198:        public class WandViewBehavior extends ViewPlatformBehavior {
0199:            /**
0200:             * Indicates a null configuration choice.
0201:             */
0202:            public static final int NONE = 0;
0203:
0204:            /**
0205:             * Indicates that a 6DOF sensor button action should be bound
0206:             * to grabbing the view.  The default is button 0.
0207:             */
0208:            public static final int GRAB_VIEW = 1;
0209:
0210:            /**
0211:             * Indicates that a 6DOF sensor button action should be bound
0212:             * to translating the view forward.  The default is button 1.
0213:             */
0214:            public static final int TRANSLATE_FORWARD = 2;
0215:
0216:            /**
0217:             * Indicates that a 6DOF sensor button action should be bound
0218:             * to translating the view backward.  The default is button 2.
0219:             */
0220:            public static final int TRANSLATE_BACKWARD = 3;
0221:
0222:            /**
0223:             * Indicates that a 6DOF sensor button action should be bound
0224:             * to rotate the view plaform counter-clockwise about a Y axis.
0225:             */
0226:            public static final int ROTATE_CCW = 4;
0227:
0228:            /**
0229:             * Indicates that a 6DOF sensor button action should be bound
0230:             * to rotate the view platform clockwise about a Y axis.
0231:             */
0232:            public static final int ROTATE_CW = 5;
0233:
0234:            /**
0235:             * Indicates that a 6DOF sensor button action should be bound
0236:             * to scaling the view platform larger.
0237:             */
0238:            public static final int SCALE_UP = 6;
0239:
0240:            /**
0241:             * Indicates that a 6DOF sensor button action should be bound
0242:             * to scaling the view platform smaller.
0243:             */
0244:            public static final int SCALE_DOWN = 7;
0245:
0246:            /**
0247:             * Indicates that a 2D sensor button or read action should be bound
0248:             * to translation.  
0249:             */
0250:            public static final int TRANSLATION = 8;
0251:
0252:            /**
0253:             * Indicates that a 2D sensor button or read action should be bound
0254:             * to scaling.  
0255:             */
0256:            public static final int SCALE = 9;
0257:
0258:            /**
0259:             * Indicates that a 2D sensor button or read action should be bound
0260:             * to rotation.  The default is to bind rotation to the 2D sensor reads.
0261:             */
0262:            public static final int ROTATION = 10;
0263:
0264:            /**
0265:             * Indicates that translation, rotation, or scaling speeds are
0266:             * per frame.
0267:             */
0268:            public static final int PER_FRAME = 11;
0269:
0270:            /**
0271:             * Use to indicate that translation, rotation, or scaling speeds are per
0272:             * second.  This is the default.
0273:             */
0274:            public static final int PER_SECOND = 12;
0275:
0276:            /**
0277:             * Indicates that translation speed is in virtual world units.
0278:             */
0279:            public static final int VIRTUAL_UNITS = 13;
0280:
0281:            /**
0282:             * Indicates that translation speed is in physical world units
0283:             * (meters per second or per frame).  This is the default.
0284:             */
0285:            public static final int PHYSICAL_METERS = 14;
0286:
0287:            /**
0288:             * Indicates that rotation speed should be in radians.
0289:             */
0290:            public static final int RADIANS = 15;
0291:
0292:            /**
0293:             * Indicates that rotation speed should be in degrees.  This is the
0294:             * default.
0295:             */
0296:            public static final int DEGREES = 16;
0297:
0298:            /**
0299:             * Indicates that rotation should occur in view platform
0300:             * coordinates.
0301:             */
0302:            public static final int VIEW_PLATFORM = 17;
0303:
0304:            /**
0305:             * Indicates that rotation should occur in head coordinates.
0306:             */
0307:            public static final int HEAD = 18;
0308:
0309:            /**
0310:             * Indicates that rotation should occur in sensor coordinates.
0311:             * This is the default.
0312:             */
0313:            public static final int SENSOR = 19;
0314:
0315:            /**
0316:             * Indicates that rotation or scale should be about a fixed point
0317:             * in virtual world coordinates.
0318:             */
0319:            public static final int VWORLD_FIXED = 20;
0320:
0321:            /**
0322:             * Indicates that rotation or scale should be about a 6DOF sensor
0323:             * hotspot.  This is the default.
0324:             */
0325:            public static final int HOTSPOT = 21;
0326:
0327:            /**
0328:             * Indicates that the 6DOF sensor read action should be bound to
0329:             * displaying the sensor's echo in the virtual world.  This is the
0330:             * default.
0331:             */
0332:            public static final int ECHO = 22;
0333:
0334:            /**
0335:             * Indicates that the echo type is a gnomon displaying the
0336:             * directions of the sensor's local coordinate system axes at the location
0337:             * of the sensor's hotspot.
0338:             */
0339:            public static final int GNOMON = 23;
0340:
0341:            /**
0342:             * Indicates that the echo type is a beam extending from the
0343:             * origin of the sensor's local coordinate system to its hotspot.
0344:             */
0345:            public static final int BEAM = 24;
0346:
0347:            /**
0348:             * Indicates that a button listener or read listener has not been
0349:             * set for a particular target.  This allows this behavior to use that
0350:             * target for a default listener.
0351:             */
0352:            private static final int UNSET = -1;
0353:
0354:            private View view = null;
0355:            private SensorEventAgent eventAgent = null;
0356:            private String sensor6DName = null;
0357:            private String sensor2DName = null;
0358:            private Shape3D echoGeometry = null;
0359:            private BranchGroup echoBranchGroup = null;
0360:            private TransformGroup echoTransformGroup = null;
0361:            private SensorReadListener echoReadListener6D = null;
0362:            private boolean echoBranchGroupAttached = false;
0363:            private WakeupCondition wakeupConditions = new WakeupOnElapsedFrames(
0364:                    0);
0365:            private boolean configured = false;
0366:
0367:            // The rest of these private fields are all configurable through
0368:            // ConfiguredUniverse. 
0369:            private Sensor sensor6D = null;
0370:            private Sensor sensor2D = null;
0371:            private int x2D = 3;
0372:            private int y2D = 7;
0373:            private double threshold2D = 0.0;
0374:
0375:            private int readAction6D = UNSET;
0376:            private int readAction2D = UNSET;
0377:
0378:            private ArrayList buttonActions6D = new ArrayList();
0379:            private ArrayList buttonActions2D = new ArrayList();
0380:
0381:            private double translationSpeed = 0.1;
0382:            private int translationUnits = PHYSICAL_METERS;
0383:            private int translationTimeBase = PER_SECOND;
0384:            private double accelerationTime = 1.0;
0385:            private double constantSpeedTime = 8.0;
0386:            private double fastSpeedFactor = 10.0;
0387:
0388:            private double rotationSpeed = 180.0;
0389:            private int rotationUnits = DEGREES;
0390:            private int rotationTimeBase = PER_SECOND;
0391:            private int rotationCoords = SENSOR;
0392:
0393:            private double scaleSpeed = 2.0;
0394:            private int scaleTimeBase = PER_SECOND;
0395:
0396:            private int transformCenterSource = HOTSPOT;
0397:            private Point3d transformCenter = new Point3d(0.0, 0.0, 0.0);
0398:
0399:            private int resetViewButtonCount6D = 3;
0400:            private int resetViewButtonCount2D = NONE;
0401:
0402:            private int echoType = GNOMON;
0403:            private double echoSize = 0.01;
0404:            private Color3f echoColor = null;
0405:            private float echoTransparency = 0.0f;
0406:            private Transform3D nominalSensorRotation = null;
0407:
0408:            /**
0409:             * Parameterless constructor for this behavior.  This is called when this
0410:             * behavior is instantiated from a configuration file.
0411:             * <p>
0412:             * <b>Syntax:</b><br>(NewViewPlatformBehavior <i>&lt;name&gt;</i>
0413:             * com.sun.j3d.utils.behaviors.vp.WandViewBehavior)
0414:             */
0415:            public WandViewBehavior() {
0416:                // Create an event agent.
0417:                eventAgent = new SensorEventAgent(this );
0418:
0419:                // Set a default SchedulingBounds.
0420:                setSchedulingBounds(new BoundingSphere(new Point3d(0.0, 0.0,
0421:                        0.0), Double.POSITIVE_INFINITY));
0422:            }
0423:
0424:            /**
0425:             * Creates a new instance with the specified sensors and echo parameters.
0426:             * At least one sensor must be non-<code>null</code>.
0427:             * <p>
0428:             * This constructor should only be used if either
0429:             * <code>SimpleUniverse</code> or <code>ConfiguredUniverse</code> is used
0430:             * to set up the view side of the scene graph, or if it is otherwise to be
0431:             * attached to a <code>ViewingPlatform</code>.  If this behavior is not
0432:             * instantiated from a configuration file then it must then be explicitly
0433:             * attached to a <code>ViewingPlatform</code> instance with the
0434:             * <code>ViewingPlatform.setViewPlatformBehavior</code> method.
0435:             * 
0436:             * @param sensor6D a six degree of freedom sensor which generates reads
0437:             *  relative to the tracker base in physical units; may be
0438:             *  <code>null</code>
0439:             * @param sensor2D 2D valuator which generates X and Y reads ranging from
0440:             *  [-1.0 .. +1.0]; may be <code>null</code>
0441:             * @param echoType either <code>GNOMON</code>, <code>BEAM</code>, or
0442:             *  <code>NONE</code> for the 6DOF sensor echo
0443:             * @param echoSize the width of the 6DOF sensor echo in physical meters;
0444:             *  ignored if echoType is <code>NONE</code>
0445:             */
0446:            public WandViewBehavior(Sensor sensor6D, Sensor sensor2D,
0447:                    int echoType, double echoSize) {
0448:                this ();
0449:                this .sensor6D = sensor6D;
0450:                this .sensor2D = sensor2D;
0451:                this .echoType = echoType;
0452:                this .echoSize = echoSize;
0453:            }
0454:
0455:            /**
0456:             * Creates a new instance with the specified sensors and a 6DOF sensor
0457:             * echo parented by the specified <code>TransformGroup</code>.  At least
0458:             * one sensor must be non-<code>null</code>.
0459:             * <p>
0460:             * This constructor should only be used if either
0461:             * <code>SimpleUniverse</code> or <code>ConfiguredUniverse</code> is used
0462:             * to set up the view side of the scene graph, or if it is otherwise to be
0463:             * attached to a <code>ViewingPlatform</code>.  If this behavior is not
0464:             * instantiated from a configuration file then it must then be explicitly
0465:             * attached to a <code>ViewingPlatform</code> instance with the
0466:             * <code>ViewingPlatform.setViewPlatformBehavior</code> method.
0467:             * <p>
0468:             * If the echo <code>TransformGroup</code> is non-<code>null</code>, it
0469:             * will be added to a new <code>BranchGroup</code> and attached to the
0470:             * <code>ViewingPlatform</code>, where its transform will be updated in
0471:             * response to the sensor reads.  Capabilities to allow writing its
0472:             * transform and to read, write, and extend its children will be set.  The
0473:             * echo geometry is assumed to incorporate the position and orientation of
0474:             * the 6DOF sensor hotspot.
0475:             * 
0476:             * @param sensor6D a six degree of freedom sensor which generates reads
0477:             *  relative to the tracker base in physical units; may be
0478:             *  <code>null</code>
0479:             * @param sensor2D 2D valuator which generates X and Y reads ranging from
0480:             *  [-1.0 .. +1.0]; may be <code>null</code>
0481:             * @param echo a <code>TransformGroup</code> containing the visible echo
0482:             *  which will track the 6DOF sensor's position and orientation, or
0483:             *  <code>null</code> for no echo
0484:             */
0485:            public WandViewBehavior(Sensor sensor6D, Sensor sensor2D,
0486:                    TransformGroup echo) {
0487:                this ();
0488:                this .sensor6D = sensor6D;
0489:                this .sensor2D = sensor2D;
0490:                if (echo != null) {
0491:                    echo.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
0492:                    echo.setCapability(Group.ALLOW_CHILDREN_READ);
0493:                    echo.setCapability(Group.ALLOW_CHILDREN_WRITE);
0494:                    echo.setCapability(Group.ALLOW_CHILDREN_EXTEND);
0495:                }
0496:                this .echoTransformGroup = echo;
0497:            }
0498:
0499:            /**
0500:             * Creates a new instance with the specified sensors and a 6DOF sensor
0501:             * echo parented by the specified <code>TransformGroup</code>.  At least
0502:             * one sensor must be non-<code>null</code>.
0503:             * <p>
0504:             * This constructor should only be used if <code>SimpleUniverse</code> or
0505:             * <code>ConfiguredUniverse</code> is <i>not</i> used to set up the view
0506:             * side of the scene graph.  The application must set up the view side
0507:             * itself and supply references to the <code>View</code> and the
0508:             * <code>TransformGroup</code> containing the view platform transform.
0509:             * <code>ViewingPlatform.setViewPlatformBehavior</code> must <i>not</i>
0510:             * be called, and this behavior must be explicitly added to the virtual
0511:             * universe by the application.
0512:             * <p>
0513:             * If the echo <code>TransformGroup</code> is non-<code>null</code>, it
0514:             * will only be used to update its associated transform with the position
0515:             * and orientation of a 6DOF sensor (if supplied).  The application is
0516:             * responsible for adding the echo to the virtual universe.  The echo
0517:             * geometry is assumed to incorporate the position and orientation of the
0518:             * 6DOF sensor hotspot.
0519:             * 
0520:             * @param sensor6D a six degree of freedom sensor which generates reads
0521:             *  relative to the tracker base in physical units; may be
0522:             *  <code>null</code>
0523:             * @param sensor2D 2D valuator which generates X and Y reads ranging from
0524:             *  [-1.0 .. +1.0]; may be <code>null</code>
0525:             * @param view a reference to the <code>View</code> attached to the 
0526:             *  <code>ViewPlatform</code> to be manipulated by this behavior
0527:             * @param viewTransform a <code>TransformGroup</code> containing the view
0528:             *  platform transform; appropriate capabilities to update the transform
0529:             *  must be set
0530:             * @param homeTransform a <code>Transform3D</code> containing the
0531:             *  view transform to be used when the view is reset; may be
0532:             *  <code>null</code> for identity
0533:             * @param echo a <code>TransformGroup</code> containing the visible echo
0534:             *  which will track the 6DOF sensor's position and orientation, or
0535:             *  <code>null</code> for no echo; appropriate capabilities to update the
0536:             *  transform must be set
0537:             */
0538:            public WandViewBehavior(Sensor sensor6D, Sensor sensor2D,
0539:                    View view, TransformGroup viewTransform,
0540:                    Transform3D homeTransform, TransformGroup echo) {
0541:                this ();
0542:                this .sensor6D = sensor6D;
0543:                this .sensor2D = sensor2D;
0544:                this .view = view;
0545:                this .targetTG = viewTransform;
0546:                this .echoTransformGroup = echo;
0547:
0548:                if (homeTransform == null)
0549:                    setHomeTransform(new Transform3D());
0550:                else
0551:                    setHomeTransform(homeTransform);
0552:            }
0553:
0554:            /**
0555:             * Initializes and configures this behavior.
0556:             * NOTE: Applications should <i>not</i> call this method. It is called by
0557:             * the Java 3D behavior scheduler.
0558:             */
0559:            public void initialize() {
0560:                // Don't configure the sensors and echo after the first time.
0561:                if (!configured) {
0562:                    configureSensorActions();
0563:
0564:                    // Configure an echo only if a ViewingPlatform is in use.
0565:                    if (vp != null) {
0566:                        if (echoTransformGroup == null && sensor6D != null
0567:                                && readAction6D == ECHO) {
0568:                            configureEcho();
0569:                        }
0570:                        if (echoTransformGroup != null) {
0571:                            echoBranchGroup = new BranchGroup();
0572:                            echoBranchGroup
0573:                                    .setCapability(BranchGroup.ALLOW_DETACH);
0574:                            echoBranchGroup
0575:                                    .setCapability(BranchGroup.ALLOW_CHILDREN_READ);
0576:                            echoBranchGroup
0577:                                    .setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
0578:
0579:                            echoBranchGroup.addChild(echoTransformGroup);
0580:                            echoBranchGroup.compile();
0581:                        }
0582:                        attachEcho();
0583:                    }
0584:                    configured = true;
0585:                }
0586:                wakeupOn(wakeupConditions);
0587:            }
0588:
0589:            /**
0590:             * Processes a stimulus meant for this behavior.  
0591:             * NOTE: Applications should <i>not</i> call this method. It is called by
0592:             * the Java 3D behavior scheduler.
0593:             */
0594:            public void processStimulus(Enumeration criteria) {
0595:                // Invoke the sensor event dispatcher.
0596:                eventAgent.dispatchEvents();
0597:
0598:                // Wake up on the next frame.
0599:                wakeupOn(wakeupConditions);
0600:            }
0601:
0602:            /**
0603:             * Enables or disables this behavior.  The default state is enabled.
0604:             * @param enable true or false to enable or disable this behavior
0605:             */
0606:            public void setEnable(boolean enable) {
0607:                if (enable == getEnable()) {
0608:                    return;
0609:                } else if (enable) {
0610:                    attachEcho();
0611:                } else {
0612:                    detachEcho();
0613:                }
0614:                super .setEnable(enable);
0615:            }
0616:
0617:            /**
0618:             * Sets the <code>ViewingPlatform</code> for this behavior.  If a subclass
0619:             * overrides this method, it must call
0620:             * <code>super.setViewingPlatform(vp)</code>.  NOTE: Applications should
0621:             * <i>not</i> call this method.  It is called by the
0622:             * <code>ViewingPlatform</code>.
0623:             */
0624:            public void setViewingPlatform(ViewingPlatform vp) {
0625:                super .setViewingPlatform(vp);
0626:                if (vp == null) {
0627:                    detachEcho();
0628:                    return;
0629:                }
0630:
0631:                Viewer[] viewers = vp.getViewers();
0632:                if (viewers != null) {
0633:                    // Get the View from the first Viewer attached to the
0634:                    // ViewingPlatform.  Multiple Viewers are not supported.
0635:                    if (viewers.length != 0 && viewers[0] != null)
0636:                        view = viewers[0].getView();
0637:
0638:                    if (viewers.length > 1)
0639:                        throw new RuntimeException(
0640:                                "multiple Viewers not supported");
0641:                }
0642:                if (view == null) {
0643:                    // Fallback to the first View attached to a live ViewPlatform.
0644:                    view = getView();
0645:                }
0646:                if (view == null) {
0647:                    // This behavior requires a view.  Bail.
0648:                    throw new RuntimeException("a view is not available");
0649:                }
0650:
0651:                // Get the top-most TransformGroup in the ViewingPlatform.
0652:                // ViewPlatformBehavior retrieves the bottom-most which won't work
0653:                // if there are multiple TransformGroups.
0654:                targetTG = vp.getMultiTransformGroup().getTransformGroup(0);
0655:
0656:                // Should be an API for checking if homeTransform is null.
0657:                if (homeTransform == null)
0658:                    setHomeTransform(new Transform3D());
0659:
0660:                attachEcho();
0661:            }
0662:
0663:            /**
0664:             * Attaches the echo BranchGroup to the ViewingPlatform if appropriate.
0665:             */
0666:            private void attachEcho() {
0667:                if (vp != null && echoBranchGroup != null
0668:                        && !echoBranchGroupAttached) {
0669:                    vp.addChild(echoBranchGroup);
0670:                    echoBranchGroupAttached = true;
0671:                }
0672:            }
0673:
0674:            /**
0675:             * Detaches the echo BranchGroup from the ViewingPlatform if appropriate.
0676:             */
0677:            private void detachEcho() {
0678:                if (echoBranchGroup != null && echoBranchGroupAttached) {
0679:                    echoBranchGroup.detach();
0680:                    echoBranchGroupAttached = false;
0681:                }
0682:            }
0683:
0684:            /**
0685:             * Creates the sensor listeners for a 6DOF sensor and/or a 2D valuator
0686:             * sensor using the predefined button and read listeners and the
0687:             * configured action bindings.
0688:             * <p>
0689:             * This is invoked the first time <code>initialize</code> is called.  This
0690:             * method can be overridden by subclasses to modify the configured
0691:             * bindings or introduce other configuration parameters.
0692:             */
0693:            protected void configureSensorActions() {
0694:                SensorButtonListener[] sbls;
0695:                int buttonCount, buttonActionCount;
0696:
0697:                SimpleUniverse universe = null;
0698:                if (vp != null)
0699:                    universe = vp.getUniverse();
0700:                if (universe != null && universe instanceof  ConfiguredUniverse) {
0701:                    // Check if sensors were instantiated from a config file.
0702:                    Map sensorMap = ((ConfiguredUniverse) universe)
0703:                            .getNamedSensors();
0704:
0705:                    if (sensor2D == null && sensor2DName != null) {
0706:                        sensor2D = (Sensor) sensorMap.get(sensor2DName);
0707:                        if (sensor2D == null)
0708:                            throw new IllegalArgumentException("\nsensor "
0709:                                    + sensor2DName + " not found");
0710:                    }
0711:
0712:                    if (sensor6D == null && sensor6DName != null) {
0713:                        sensor6D = (Sensor) sensorMap.get(sensor6DName);
0714:                        if (sensor6D == null)
0715:                            throw new IllegalArgumentException("\nsensor "
0716:                                    + sensor6DName + " not found");
0717:                    }
0718:                }
0719:
0720:                if (sensor6D != null) {
0721:                    // Assign default read action.
0722:                    if (readAction6D == UNSET)
0723:                        readAction6D = ECHO;
0724:
0725:                    // Register the read listener.
0726:                    if (readAction6D == ECHO) {
0727:                        echoReadListener6D = new EchoReadListener6D();
0728:                        eventAgent.addSensorReadListener(sensor6D,
0729:                                echoReadListener6D);
0730:                    }
0731:
0732:                    // Check for button range.
0733:                    buttonCount = sensor6D.getSensorButtonCount();
0734:                    buttonActionCount = buttonActions6D.size();
0735:                    if (buttonActionCount > buttonCount)
0736:                        throw new IllegalArgumentException("\nbutton index "
0737:                                + (buttonActionCount - 1)
0738:                                + " >= number of buttons (" + buttonCount + ")");
0739:
0740:                    // Assign default button actions.
0741:                    if (buttonCount > 2
0742:                            && (buttonActionCount < 3 || buttonActions6D.get(2) == null))
0743:                        setButtonAction6D(2, TRANSLATE_BACKWARD);
0744:                    if (buttonCount > 1
0745:                            && (buttonActionCount < 2 || buttonActions6D.get(1) == null))
0746:                        setButtonAction6D(1, TRANSLATE_FORWARD);
0747:                    if (buttonCount > 0
0748:                            && (buttonActionCount < 1 || buttonActions6D.get(0) == null))
0749:                        setButtonAction6D(0, GRAB_VIEW);
0750:
0751:                    buttonActionCount = buttonActions6D.size();
0752:                    if (buttonActionCount > 0) {
0753:                        // Set up the button listener array.
0754:                        sbls = new SensorButtonListener[buttonCount];
0755:                        for (int i = 0; i < buttonActionCount; i++) {
0756:                            Integer button = (Integer) buttonActions6D.get(i);
0757:                            if (button != null) {
0758:                                int action = button.intValue();
0759:                                if (action == NONE)
0760:                                    sbls[i] = null;
0761:                                else if (action == GRAB_VIEW)
0762:                                    sbls[i] = new GrabViewListener6D();
0763:                                else if (action == TRANSLATE_FORWARD)
0764:                                    sbls[i] = new TranslationListener6D(false);
0765:                                else if (action == TRANSLATE_BACKWARD)
0766:                                    sbls[i] = new TranslationListener6D(true);
0767:                                else if (action == ROTATE_CCW)
0768:                                    sbls[i] = new RotationListener6D(false);
0769:                                else if (action == ROTATE_CW)
0770:                                    sbls[i] = new RotationListener6D(true);
0771:                                else if (action == SCALE_UP)
0772:                                    sbls[i] = new ScaleListener6D(false);
0773:                                else if (action == SCALE_DOWN)
0774:                                    sbls[i] = new ScaleListener6D(true);
0775:                            }
0776:                        }
0777:                        // Register the button listeners.
0778:                        eventAgent.addSensorButtonListeners(sensor6D, sbls);
0779:                    }
0780:
0781:                    // Check for reset view action.
0782:                    if (resetViewButtonCount6D != NONE) {
0783:                        SensorInputAdaptor r = new ResetViewListener(sensor6D,
0784:                                resetViewButtonCount6D);
0785:                        eventAgent.addSensorButtonListener(sensor6D, r);
0786:                        eventAgent.addSensorReadListener(sensor6D, r);
0787:                    }
0788:                }
0789:
0790:                if (sensor2D != null) {
0791:                    // Assign default read action
0792:                    if (readAction2D == UNSET)
0793:                        readAction2D = ROTATION;
0794:
0795:                    // Register the read listener.
0796:                    if (readAction2D == ROTATION) {
0797:                        SensorReadListener r = new RotationListener2D(sensor2D,
0798:                                sensor6D);
0799:                        eventAgent.addSensorReadListener(sensor2D, r);
0800:                    } else if (readAction2D == TRANSLATION) {
0801:                        SensorReadListener r = new TranslationListener2D(
0802:                                sensor2D, sensor6D);
0803:                        eventAgent.addSensorReadListener(sensor2D, r);
0804:                    } else if (readAction2D == SCALE) {
0805:                        SensorReadListener r = new ScaleListener2D(sensor2D,
0806:                                sensor6D);
0807:                        eventAgent.addSensorReadListener(sensor2D, r);
0808:                    }
0809:
0810:                    // Check for button range.
0811:                    buttonCount = sensor2D.getSensorButtonCount();
0812:                    buttonActionCount = buttonActions2D.size();
0813:                    if (buttonActionCount > buttonCount)
0814:                        throw new IllegalArgumentException("\nbutton index "
0815:                                + (buttonActionCount - 1)
0816:                                + " >= number of buttons (" + buttonCount + ")");
0817:
0818:                    // No default button actions are defined for the 2D sensor.
0819:                    if (buttonActionCount > 0) {
0820:                        // Set up the button listener array.
0821:                        sbls = new SensorButtonListener[buttonCount];
0822:                        for (int i = 0; i < buttonActionCount; i++) {
0823:                            Integer button = (Integer) buttonActions2D.get(i);
0824:                            if (button != null) {
0825:                                int action = button.intValue();
0826:                                if (action == NONE)
0827:                                    sbls[i] = null;
0828:                                else if (action == ROTATION)
0829:                                    sbls[i] = new RotationListener2D(sensor2D,
0830:                                            sensor6D);
0831:                                else if (action == TRANSLATION)
0832:                                    sbls[i] = new TranslationListener2D(
0833:                                            sensor2D, sensor6D);
0834:                                else if (action == SCALE)
0835:                                    sbls[i] = new ScaleListener2D(sensor2D,
0836:                                            sensor6D);
0837:                            }
0838:                        }
0839:                        // Register the button listeners.
0840:                        eventAgent.addSensorButtonListeners(sensor2D, sbls);
0841:                    }
0842:
0843:                    // Check for reset view action.
0844:                    if (resetViewButtonCount2D != NONE) {
0845:                        SensorInputAdaptor r = new ResetViewListener(sensor2D,
0846:                                resetViewButtonCount2D);
0847:                        eventAgent.addSensorButtonListener(sensor2D, r);
0848:                        eventAgent.addSensorReadListener(sensor2D, r);
0849:                    }
0850:                }
0851:            }
0852:
0853:            /**
0854:             * Creates a 6DOF sensor echo according to configuration parameters.  This
0855:             * is done only if a 6DOF sensor has been specified, the 6DOF sensor read
0856:             * action has been set to echo the sensor position, the echo transform
0857:             * group has not already been set, and a ViewingPlatform is in use.  This
0858:             * is invoked the first time <code>initialize</code> is called to set this
0859:             * behavior live, but before the echo transform group is added to a
0860:             * <code>BranchGroup</code> and made live.  This method can be overridden
0861:             * to support other echo geometry.
0862:             */
0863:            protected void configureEcho() {
0864:                Point3d hotspot = new Point3d();
0865:                sensor6D.getHotspot(hotspot);
0866:
0867:                if (echoType == GNOMON) {
0868:                    Transform3D gnomonTransform = new Transform3D();
0869:                    if (nominalSensorRotation != null) {
0870:                        gnomonTransform.set(nominalSensorRotation);
0871:                        gnomonTransform.invert();
0872:                    }
0873:                    gnomonTransform.setTranslation(new Vector3d(hotspot));
0874:                    echoGeometry = new SensorGnomonEcho(gnomonTransform,
0875:                            0.1 * echoSize, 0.5 * echoSize, true);
0876:                } else if (echoType == BEAM) {
0877:                    echoGeometry = new SensorBeamEcho(hotspot, echoSize, true);
0878:                }
0879:
0880:                if (echoGeometry != null) {
0881:                    Appearance a = echoGeometry.getAppearance();
0882:                    if (echoColor != null) {
0883:                        Material m = a.getMaterial();
0884:                        m.setDiffuseColor(echoColor);
0885:                    }
0886:                    if (echoTransparency != 0.0f) {
0887:                        TransparencyAttributes ta = a
0888:                                .getTransparencyAttributes();
0889:                        ta.setTransparencyMode(TransparencyAttributes.BLENDED);
0890:                        ta.setTransparency(echoTransparency);
0891:                        // Use order independent additive blend for gnomon.
0892:                        if (echoGeometry instanceof  SensorGnomonEcho)
0893:                            ta
0894:                                    .setDstBlendFunction(TransparencyAttributes.BLEND_ONE);
0895:                    }
0896:                    echoTransformGroup = new TransformGroup();
0897:                    echoTransformGroup
0898:                            .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
0899:                    echoTransformGroup.setCapability(Group.ALLOW_CHILDREN_READ);
0900:                    echoTransformGroup
0901:                            .setCapability(Group.ALLOW_CHILDREN_WRITE);
0902:                    echoTransformGroup
0903:                            .setCapability(Group.ALLOW_CHILDREN_EXTEND);
0904:                    echoTransformGroup.addChild(echoGeometry);
0905:                }
0906:            }
0907:
0908:            /**
0909:             * A base class for implementing some of this behavior's listeners.
0910:             */
0911:            public class ListenerBase extends SensorInputAdaptor {
0912:                /**
0913:                 * The initial transform from view platform coordinates to virtual
0914:                 * world coordinates, set by <code>initAction</code>.
0915:                 */
0916:                protected Transform3D viewPlatformToVworld = new Transform3D();
0917:
0918:                /**
0919:                 * The initial transform from tracker base coordinates to virtual
0920:                 * world coordinates, set by <code>initAction</code>.
0921:                 */
0922:                protected Transform3D trackerToVworld = new Transform3D();
0923:
0924:                /**
0925:                 * The initial transform from sensor coordinates to virtual
0926:                 * world coordinates, set by <code>initAction</code>.
0927:                 */
0928:                protected Transform3D sensorToVworld = new Transform3D();
0929:
0930:                /**
0931:                 * The initial transform from sensor coordinates to tracker base
0932:                 * coordinates, set by <code>initAction</code>.
0933:                 */
0934:                protected Transform3D sensorToTracker = new Transform3D();
0935:
0936:                // Private fields.
0937:                private Transform3D trackerToSensor = new Transform3D();
0938:                private boolean active = false;
0939:
0940:                // Misc. temporary objects.
0941:                private double[] s3Tmp = new double[3];
0942:                private double[] m16Tmp = new double[16];
0943:                private Vector3d v3dTmp = new Vector3d();
0944:                private Transform3D t3dTmp = new Transform3D();
0945:
0946:                /**
0947:                 * Initializes the listener action.  Subclasses must call this before
0948:                 * starting the action, either from <code>pressed</code> or when a 2D
0949:                 * valuator exits the deadzone threshold.
0950:                 * 
0951:                 * @param s reference to a 6DOF sensor if used by the listener; may
0952:                 *  be <code>null</code>
0953:                 */
0954:                protected void initAction(Sensor s) {
0955:                    targetTG.getTransform(viewPlatformToVworld);
0956:                    active = true;
0957:                    if (s == null)
0958:                        return;
0959:
0960:                    // Kludge to get the static trackerToVworld for this
0961:                    // frame.  This is computed from the two separate sensor reads
0962:                    // below, which are close enough to identical to work.  The
0963:                    // Java 3D View class needs a getTrackerBaseToVworld() method
0964:                    // (see Java 3D RFE 4676808).
0965:                    s.getRead(sensorToTracker);
0966:                    view.getSensorToVworld(s, sensorToVworld);
0967:
0968:                    trackerToSensor.invert(sensorToTracker);
0969:                    trackerToVworld.mul(sensorToVworld, trackerToSensor);
0970:                }
0971:
0972:                /**
0973:                 * Ends the action.  Subclasses must be call this from
0974:                 * <code>released</code> or when a 2D valuator enters the deadzone
0975:                 * threshold.
0976:                 * 
0977:                 * @param s reference to a 6DOF sensor if used by the listener; may
0978:                 *  be <code>null</code>
0979:                 */
0980:                protected void endAction(Sensor s) {
0981:                    active = false;
0982:                }
0983:
0984:                /**
0985:                 * Returns true if the listener is currently active; that is, if
0986:                 * <code>initAction</code> has been called but not yet
0987:                 * <code>endAction</code>.
0988:                 *
0989:                 * @return true if the listener is active, false otherwise
0990:                 */
0991:                protected boolean isActive() {
0992:                    return active;
0993:                }
0994:
0995:                public void pressed(SensorEvent e) {
0996:                    initAction(e.getSensor());
0997:                }
0998:
0999:                public void released(SensorEvent e) {
1000:                    endAction(e.getSensor());
1001:                }
1002:
1003:                /**
1004:                 * Gets the physical to virtual scale.
1005:                 */
1006:                protected double getPhysicalToVirtualScale() {
1007:                    view.getCanvas3D(0).getImagePlateToVworld(t3dTmp);
1008:                    t3dTmp.get(m16Tmp);
1009:                    return Math.sqrt(m16Tmp[0] * m16Tmp[0] + m16Tmp[1]
1010:                            * m16Tmp[1] + m16Tmp[2] * m16Tmp[2]);
1011:                }
1012:
1013:                /**
1014:                 * Gets the scale from physical units to view platform units.
1015:                 */
1016:                protected double getPhysicalToViewPlatformScale() {
1017:                    double vpToVirtualScale;
1018:
1019:                    targetTG.getTransform(t3dTmp);
1020:                    t3dTmp.get(m16Tmp);
1021:                    vpToVirtualScale = Math.sqrt(m16Tmp[0] * m16Tmp[0]
1022:                            + m16Tmp[1] * m16Tmp[1] + m16Tmp[2] * m16Tmp[2]);
1023:
1024:                    return getPhysicalToVirtualScale() / vpToVirtualScale;
1025:                }
1026:
1027:                /**
1028:                 * Translates a coordinate system.
1029:                 *
1030:                 * @param transform the coordinate system to be translated
1031:                 * @param translation the vector by which to translate
1032:                 */
1033:                protected void translateTransform(Transform3D transform,
1034:                        Vector3d translation) {
1035:                    transform.get(v3dTmp);
1036:                    v3dTmp.add(translation);
1037:                    transform.setTranslation(v3dTmp);
1038:                }
1039:
1040:                /**
1041:                 * Transforms the target coordinate system about a center point.
1042:                 * This can be used for rotation and scaling.
1043:                 *
1044:                 * @param target the coordinate system to transform
1045:                 * @param center the center point about which to transform
1046:                 * @param transform the transform to apply
1047:                 */
1048:                protected void transformAboutCenter(Transform3D target,
1049:                        Point3d center, Transform3D transform) {
1050:
1051:                    // Translate to the center.
1052:                    target.get(v3dTmp);
1053:                    v3dTmp.sub(center);
1054:                    target.setTranslation(v3dTmp);
1055:
1056:                    // Apply the transform.
1057:                    target.mul(transform, target);
1058:
1059:                    // Translate back.
1060:                    target.get(v3dTmp);
1061:                    v3dTmp.add(center);
1062:                    target.setTranslation(v3dTmp);
1063:                }
1064:
1065:                /**
1066:                 * Equalizes the scale factors in the view tranform, which must be
1067:                 * congruent.  If successful, the <code>ViewingPlatform
1068:                 * TransformGroup</code> is updated; otherwise, its transform is reset
1069:                 * to the home transform.  This should be called if multiple
1070:                 * incremental scale factors are applied to the view transform.
1071:                 *
1072:                 * @param viewPlatformToVworld the view transform
1073:                 */
1074:                protected void conditionViewScale(
1075:                        Transform3D viewPlatformToVworld) {
1076:                    viewPlatformToVworld.normalize();
1077:                    viewPlatformToVworld.get(m16Tmp);
1078:
1079:                    s3Tmp[0] = m16Tmp[0] * m16Tmp[0] + m16Tmp[4] * m16Tmp[4]
1080:                            + m16Tmp[8] * m16Tmp[8];
1081:                    s3Tmp[1] = m16Tmp[1] * m16Tmp[1] + m16Tmp[5] * m16Tmp[5]
1082:                            + m16Tmp[9] * m16Tmp[9];
1083:                    s3Tmp[2] = m16Tmp[2] * m16Tmp[2] + m16Tmp[6] * m16Tmp[6]
1084:                            + m16Tmp[10] * m16Tmp[10];
1085:
1086:                    if (s3Tmp[0] == s3Tmp[1] && s3Tmp[0] == s3Tmp[2])
1087:                        return;
1088:
1089:                    s3Tmp[0] = Math.sqrt(s3Tmp[0]);
1090:                    s3Tmp[1] = Math.sqrt(s3Tmp[1]);
1091:                    s3Tmp[2] = Math.sqrt(s3Tmp[2]);
1092:
1093:                    int closestToOne = 0;
1094:                    if (Math.abs(s3Tmp[1] - 1.0) < Math.abs(s3Tmp[0] - 1.0))
1095:                        closestToOne = 1;
1096:                    if (Math.abs(s3Tmp[2] - 1.0) < Math
1097:                            .abs(s3Tmp[closestToOne] - 1.0))
1098:                        closestToOne = 2;
1099:
1100:                    double scale;
1101:                    for (int i = 0; i < 3; i++) {
1102:                        if (i == closestToOne)
1103:                            continue;
1104:                        scale = s3Tmp[closestToOne] / s3Tmp[i];
1105:                        m16Tmp[i + 0] *= scale;
1106:                        m16Tmp[i + 4] *= scale;
1107:                        m16Tmp[i + 8] *= scale;
1108:                    }
1109:
1110:                    // Set the view transform and bail out if unsuccessful.
1111:                    viewPlatformToVworld.set(m16Tmp);
1112:                    if ((viewPlatformToVworld.getType() & Transform3D.CONGRUENT) == 0)
1113:                        goHome();
1114:                    else
1115:                        targetTG.setTransform(viewPlatformToVworld);
1116:                }
1117:            }
1118:
1119:            /**
1120:             * Implements a 6DOF sensor button listener to directly manipulate the
1121:             * view platform transform.  The view platform moves in inverse response
1122:             * to the sensor's position and orientation to give the effect of
1123:             * attaching the virtual world to the sensor's echo.
1124:             * @see #setButtonAction6D
1125:             */
1126:            public class GrabViewListener6D extends ListenerBase {
1127:                private Transform3D t3d = new Transform3D();
1128:                private Transform3D initialVworldToSensor = new Transform3D();
1129:
1130:                public void pressed(SensorEvent e) {
1131:                    initAction(e.getSensor());
1132:
1133:                    // Save the inverse of the initial sensorToVworld.
1134:                    initialVworldToSensor.invert(sensorToVworld);
1135:                }
1136:
1137:                public void dragged(SensorEvent e) {
1138:                    // Get sensor read relative to the static view at the time of the
1139:                    // button-down.
1140:                    Sensor s = e.getSensor();
1141:                    s.getRead(sensorToTracker);
1142:                    sensorToVworld.mul(trackerToVworld, sensorToTracker);
1143:
1144:                    // Solve for T, where T x initialSensorToVworld = sensorToVworld
1145:                    t3d.mul(sensorToVworld, initialVworldToSensor);
1146:
1147:                    // Move T to the view side by inverting it, and then applying it
1148:                    // to the static view transform.
1149:                    t3d.invert();
1150:                    t3d.mul(viewPlatformToVworld);
1151:                    targetTG.setTransform(t3d);
1152:                }
1153:            }
1154:
1155:            /**
1156:             * Implements a 6DOF sensor button listener that translates the view
1157:             * platform along the direction the sensor is pointing.
1158:             * @see #setButtonAction6D
1159:             * @see #setTranslationSpeed
1160:             * @see #setAccelerationTime
1161:             * @see #setConstantSpeedTime
1162:             * @see #setFastSpeedFactor
1163:             */
1164:            public class TranslationListener6D extends ListenerBase {
1165:                private long buttonDownTime;
1166:                private double speedScaled;
1167:                private double interval0;
1168:                private double interval1;
1169:                private double interval2;
1170:                private Vector3d v3d = new Vector3d();
1171:
1172:                /**
1173:                 * Construct a new translation button listener for a 6DOF sensor.
1174:                 *
1175:                 * @param reverse if true, translate the view platform backwards;
1176:                 *  otherwise, translate the view platform forwards
1177:                 */
1178:                public TranslationListener6D(boolean reverse) {
1179:                    // Compute translation speed intervals.
1180:                    interval0 = accelerationTime;
1181:                    interval1 = interval0 + constantSpeedTime;
1182:                    interval2 = interval1 + accelerationTime;
1183:
1184:                    // Apply virtual to physical scale if needed.
1185:                    if (translationUnits == VIRTUAL_UNITS)
1186:                        speedScaled = translationSpeed
1187:                                / getPhysicalToVirtualScale();
1188:                    else
1189:                        speedScaled = translationSpeed;
1190:
1191:                    if (reverse) {
1192:                        speedScaled = -speedScaled;
1193:                    }
1194:                }
1195:
1196:                public void pressed(SensorEvent e) {
1197:                    initAction(e.getSensor());
1198:                    buttonDownTime = e.getTime();
1199:                }
1200:
1201:                public void dragged(SensorEvent e) {
1202:                    long time = e.getTime();
1203:                    long lastTime = e.getLastTime();
1204:                    double currSpeed, transTime;
1205:                    double frameTime = 1.0;
1206:                    if (translationTimeBase == PER_SECOND)
1207:                        frameTime = (time - lastTime) / 1e9;
1208:
1209:                    // Compute speed based on acceleration intervals.
1210:                    transTime = (time - buttonDownTime) / 1e9;
1211:                    if (transTime <= interval0) {
1212:                        currSpeed = (transTime / accelerationTime)
1213:                                * speedScaled;
1214:                    } else if (transTime > interval1 && transTime < interval2) {
1215:                        currSpeed = ((((transTime - interval1) / accelerationTime) * (fastSpeedFactor - 1.0)) + 1.0)
1216:                                * speedScaled;
1217:                    } else if (transTime >= interval2) {
1218:                        currSpeed = fastSpeedFactor * speedScaled;
1219:                    } else {
1220:                        currSpeed = speedScaled;
1221:                    }
1222:
1223:                    // Transform the translation direction (0, 0, -1).
1224:                    v3d.set(0.0, 0.0, -1.0);
1225:                    if (nominalSensorRotation != null)
1226:                        nominalSensorRotation.transform(v3d);
1227:
1228:                    // To avoid echo frame lag, compute sensorToVworld based on
1229:                    // computed trackerToVworld.  getSensorToVworld() isn't
1230:                    // current for this frame.
1231:                    Sensor s = e.getSensor();
1232:                    s.getRead(sensorToTracker);
1233:                    sensorToVworld.mul(trackerToVworld, sensorToTracker);
1234:                    sensorToVworld.transform(v3d);
1235:
1236:                    // Translate the view platform.
1237:                    v3d.scale(frameTime * currSpeed);
1238:                    translateTransform(viewPlatformToVworld, v3d);
1239:                    targetTG.setTransform(viewPlatformToVworld);
1240:
1241:                    // Translate trackerToVworld.
1242:                    translateTransform(trackerToVworld, v3d);
1243:
1244:                    if (readAction6D == ECHO) {
1245:                        // Translate sensor echo to compensate for the new view
1246:                        // platform movement.
1247:                        translateTransform(sensorToVworld, v3d);
1248:                        updateEcho(s, sensorToVworld);
1249:                    }
1250:                }
1251:            }
1252:
1253:            /**
1254:             * Implements a 6DOF sensor button listener that rotates the view platform
1255:             * about a Y axis.  This axis can be relative to the sensor, user head, or
1256:             * view platform.  The rotation center can be the sensor hotspot or a
1257:             * fixed point in virtual world coordinates.
1258:             * 
1259:             * @see #setButtonAction6D
1260:             * @see #setRotationCoords
1261:             * @see #setTransformCenterSource
1262:             * @see #setTransformCenter
1263:             * @see #setRotationSpeed
1264:             * @see #setAccelerationTime
1265:             */
1266:            public class RotationListener6D extends ListenerBase {
1267:                private boolean reverse;
1268:                private long buttonDownTime;
1269:                private Vector3d axis = new Vector3d();
1270:                private Point3d center = new Point3d();
1271:                private Transform3D t3d = new Transform3D();
1272:                private AxisAngle4d aa4d = new AxisAngle4d();
1273:                private Transform3D headToVworld = new Transform3D();
1274:                private double speedScaled;
1275:
1276:                protected void initAction(Sensor s) {
1277:                    super .initAction(s);
1278:                    if (rotationCoords == HEAD) {
1279:                        view.setUserHeadToVworldEnable(true);
1280:                    }
1281:                }
1282:
1283:                protected void endAction(Sensor s) {
1284:                    super .endAction(s);
1285:                    viewPlatformToVworld.normalize();
1286:                    targetTG.setTransform(viewPlatformToVworld);
1287:                    if (rotationCoords == HEAD) {
1288:                        view.setUserHeadToVworldEnable(false);
1289:                    }
1290:                }
1291:
1292:                /**
1293:                 * Construct a new rotation button listener for a 6DOF sensor.
1294:                 *
1295:                 * @param reverse if true, rotate clockwise; otherwise, rotate
1296:                 *  counter-clockwise
1297:                 */
1298:                public RotationListener6D(boolean reverse) {
1299:                    this .reverse = reverse;
1300:                    if (rotationUnits == DEGREES)
1301:                        speedScaled = rotationSpeed * Math.PI / 180.0;
1302:                    else
1303:                        speedScaled = rotationSpeed;
1304:                }
1305:
1306:                public void pressed(SensorEvent e) {
1307:                    initAction(e.getSensor());
1308:                    buttonDownTime = e.getTime();
1309:                }
1310:
1311:                public void dragged(SensorEvent e) {
1312:                    long time = e.getTime();
1313:                    long lastTime = e.getLastTime();
1314:                    double currSpeed, transTime;
1315:                    double frameTime = 1.0;
1316:                    if (rotationTimeBase == PER_SECOND)
1317:                        frameTime = (time - lastTime) / 1e9;
1318:
1319:                    // Compute speed based on acceleration interval.
1320:                    transTime = (time - buttonDownTime) / 1e9;
1321:                    if (transTime <= accelerationTime) {
1322:                        currSpeed = (transTime / accelerationTime)
1323:                                * speedScaled;
1324:                    } else {
1325:                        currSpeed = speedScaled;
1326:                    }
1327:
1328:                    // Set the rotation axis.
1329:                    if (reverse)
1330:                        axis.set(0.0, -1.0, 0.0);
1331:                    else
1332:                        axis.set(0.0, 1.0, 0.0);
1333:
1334:                    // To avoid echo frame lag, compute sensorToVworld based on
1335:                    // computed trackerToVworld.  getSensorToVworld() isn't current
1336:                    // for this frame.
1337:                    Sensor s = e.getSensor();
1338:                    s.getRead(sensorToTracker);
1339:                    sensorToVworld.mul(trackerToVworld, sensorToTracker);
1340:
1341:                    // Transform rotation axis into target coordinate system.
1342:                    if (rotationCoords == SENSOR) {
1343:                        if (nominalSensorRotation != null)
1344:                            nominalSensorRotation.transform(axis);
1345:
1346:                        sensorToVworld.transform(axis);
1347:                    } else if (rotationCoords == HEAD) {
1348:                        view.getUserHeadToVworld(headToVworld);
1349:                        headToVworld.transform(axis);
1350:                    } else {
1351:                        viewPlatformToVworld.transform(axis);
1352:                    }
1353:
1354:                    // Get the rotation center.
1355:                    if (transformCenterSource == HOTSPOT) {
1356:                        s.getHotspot(center);
1357:                        sensorToVworld.transform(center);
1358:                    } else {
1359:                        center.set(transformCenter);
1360:                    }
1361:
1362:                    // Construct origin-based rotation about axis.
1363:                    aa4d.set(axis, currSpeed * frameTime);
1364:                    t3d.set(aa4d);
1365:
1366:                    // Apply the rotation to the view platform.
1367:                    transformAboutCenter(viewPlatformToVworld, center, t3d);
1368:                    targetTG.setTransform(viewPlatformToVworld);
1369:
1370:                    // Apply the rotation to trackerToVworld.
1371:                    transformAboutCenter(trackerToVworld, center, t3d);
1372:
1373:                    if (readAction6D == ECHO) {
1374:                        // Transform sensor echo to compensate for the new view
1375:                        // platform movement.
1376:                        transformAboutCenter(sensorToVworld, center, t3d);
1377:                        updateEcho(s, sensorToVworld);
1378:                    }
1379:                }
1380:            }
1381:
1382:            /**
1383:             * Implements a 6DOF sensor button listener that scales the view platform.
1384:             * The center of scaling can be the sensor hotspot or a fixed location in
1385:             * virtual world coordinates.
1386:             * 
1387:             * @see #setButtonAction6D
1388:             * @see #setTransformCenterSource
1389:             * @see #setTransformCenter
1390:             * @see #setScaleSpeed
1391:             * @see #setAccelerationTime
1392:             */
1393:            public class ScaleListener6D extends ListenerBase {
1394:                private double direction;
1395:                private long buttonDownTime;
1396:                private Point3d center = new Point3d();
1397:                private Transform3D t3d = new Transform3D();
1398:
1399:                protected void endAction(Sensor s) {
1400:                    super .endAction(s);
1401:                    conditionViewScale(viewPlatformToVworld);
1402:                }
1403:
1404:                /**
1405:                 * Construct a new scale button listener for a 6DOF sensor.
1406:                 *
1407:                 * @param reverse if true, scale the view platform smaller; otherwise,
1408:                 *  scale the view platform larger
1409:                 */
1410:                public ScaleListener6D(boolean reverse) {
1411:                    if (reverse)
1412:                        direction = -1.0;
1413:                    else
1414:                        direction = 1.0;
1415:                }
1416:
1417:                public void pressed(SensorEvent e) {
1418:                    initAction(e.getSensor());
1419:                    buttonDownTime = e.getTime();
1420:                }
1421:
1422:                public void dragged(SensorEvent e) {
1423:                    long time = e.getTime();
1424:                    long lastTime = e.getLastTime();
1425:                    double scale, exp, transTime;
1426:                    double frameTime = 1.0;
1427:                    if (scaleTimeBase == PER_SECOND)
1428:                        frameTime = (time - lastTime) / 1e9;
1429:
1430:                    // Compute speed based on acceleration interval.
1431:                    transTime = (time - buttonDownTime) / 1e9;
1432:                    if (transTime <= accelerationTime) {
1433:                        exp = (transTime / accelerationTime) * frameTime
1434:                                * direction;
1435:                    } else {
1436:                        exp = frameTime * direction;
1437:                    }
1438:                    scale = Math.pow(scaleSpeed, exp);
1439:
1440:                    // To avoid echo frame lag, compute sensorToVworld based on
1441:                    // computed trackerToVworld.  getSensorToVworld() isn't current
1442:                    // for this frame.
1443:                    Sensor s = e.getSensor();
1444:                    s.getRead(sensorToTracker);
1445:                    sensorToVworld.mul(trackerToVworld, sensorToTracker);
1446:
1447:                    // Get the scale center.
1448:                    if (transformCenterSource == HOTSPOT) {
1449:                        s.getHotspot(center);
1450:                        sensorToVworld.transform(center);
1451:                    } else {
1452:                        center.set(transformCenter);
1453:                    }
1454:
1455:                    // Apply the scale to the view platform.
1456:                    t3d.set(scale);
1457:                    transformAboutCenter(viewPlatformToVworld, center, t3d);
1458:
1459:                    // Incremental scaling at the extremes can lead to numerical
1460:                    // instability, so catch BadTransformException to prevent the
1461:                    // behavior thread from being killed.  Using a cumulative scale
1462:                    // matrix avoids this problem to a better extent, but causes the
1463:                    // 6DOF sensor hotspot center to jitter excessively.
1464:                    try {
1465:                        targetTG.setTransform(viewPlatformToVworld);
1466:                    } catch (BadTransformException bt) {
1467:                        conditionViewScale(viewPlatformToVworld);
1468:                    }
1469:
1470:                    // Apply the scale to trackerToVworld.
1471:                    transformAboutCenter(trackerToVworld, center, t3d);
1472:
1473:                    if (readAction6D == ECHO) {
1474:                        // Scale sensor echo to compensate for the new view
1475:                        // platform scale.
1476:                        transformAboutCenter(sensorToVworld, center, t3d);
1477:                        updateEcho(s, sensorToVworld);
1478:                    }
1479:                }
1480:            }
1481:
1482:            /**
1483:             * Implements a 6DOF sensor read listener that updates the orientation and
1484:             * position of the sensor's echo in the virtual world.
1485:             * 
1486:             * @see #setEchoType
1487:             * @see #setEchoSize
1488:             * @see #setReadAction6D
1489:             * @see SensorGnomonEcho
1490:             * @see SensorBeamEcho
1491:             */
1492:            public class EchoReadListener6D implements  SensorReadListener {
1493:                private Transform3D sensorToVworld = new Transform3D();
1494:
1495:                public void read(SensorEvent e) {
1496:                    Sensor s = e.getSensor();
1497:                    view.getSensorToVworld(s, sensorToVworld);
1498:                    updateEcho(s, sensorToVworld);
1499:                }
1500:            }
1501:
1502:            /**
1503:             * Implements a 2D valuator listener that rotates the view platform.  The
1504:             * X and Y values from the valuator should have a continuous range from
1505:             * -1.0 to +1.0, although the rotation speed can be scaled to compensate
1506:             * for a different range.  The X and Y values are found in the sensor's
1507:             * read matrix at the indices specified by
1508:             * <code>setMatrixIndices2D</code>, with defaults of 3 and 7 respectively.
1509:             * <p>
1510:             * The rotation direction is controlled by the direction the 2D valuator
1511:             * is pushed, and the rotation speed is scaled by the magnitude of the 2D
1512:             * valuator read values.
1513:             * <p>
1514:             * This listener will work in conjunction with a 6DOF sensor if supplied
1515:             * in the constructor.  If a 6DOF sensor is provided and
1516:             * <code>setRotationCoords</code> has been called with the value
1517:             * <code>SENSOR</code>, then the rotation is applied in the 6DOF sensor's
1518:             * coordinate system; otherwise the rotation is applied either in head
1519:             * coordinates or in view platform coordinates.  If a 6DOF sensor is
1520:             * provided and <code>setTransformCenterSource</code> has been called with
1521:             * the value <code>HOTSPOT</code>, then rotation is about the 6DOF
1522:             * sensor's hotspot; otherwise, the rotation center is the value set by
1523:             * <code>setTransformCenter</code>.
1524:             *
1525:             * @see #setReadAction2D
1526:             * @see #setButtonAction2D
1527:             * @see #setRotationCoords
1528:             * @see #setTransformCenterSource
1529:             * @see #setTransformCenter
1530:             * @see #setRotationSpeed
1531:             * @see #setThreshold2D
1532:             * @see #setMatrixIndices2D
1533:             */
1534:            public class RotationListener2D extends ListenerBase {
1535:                private Sensor sensor2D, sensor6D;
1536:                private double[] m = new double[16];
1537:                private Vector3d axis = new Vector3d();
1538:                private Point3d center = new Point3d();
1539:                private Transform3D t3d = new Transform3D();
1540:                private AxisAngle4d aa4d = new AxisAngle4d();
1541:                private Transform3D sensor2DRead = new Transform3D();
1542:                private Transform3D headToVworld = new Transform3D();
1543:                private double speedScaled;
1544:
1545:                protected void initAction(Sensor s) {
1546:                    super .initAction(s);
1547:                    if (rotationCoords == HEAD) {
1548:                        view.setUserHeadToVworldEnable(true);
1549:                    }
1550:                    if (s != null && readAction6D == ECHO) {
1551:                        // Disable the 6DOF echo.  It will be updated in this action.
1552:                        eventAgent.removeSensorReadListener(s,
1553:                                echoReadListener6D);
1554:                    }
1555:                }
1556:
1557:                protected void endAction(Sensor s) {
1558:                    super .endAction(s);
1559:                    viewPlatformToVworld.normalize();
1560:                    targetTG.setTransform(viewPlatformToVworld);
1561:                    if (rotationCoords == HEAD) {
1562:                        view.setUserHeadToVworldEnable(false);
1563:                    }
1564:                    if (s != null && readAction6D == ECHO) {
1565:                        eventAgent.addSensorReadListener(s, echoReadListener6D);
1566:                    }
1567:                }
1568:
1569:                /**
1570:                 * Construct an instance of this class with the specified sensors.
1571:                 * 
1572:                 * @param sensor2D the 2D valuator whose X and Y values drive the
1573:                 *  rotation 
1574:                 * @param sensor6D the 6DOF sensor to use if the rotation coordinate
1575:                 *  system is set to <code>SENSOR</code> or the rotation center source
1576:                 *  is <code>HOTSPOT</code>;  may be <code>null</code>
1577:                 */
1578:                public RotationListener2D(Sensor sensor2D, Sensor sensor6D) {
1579:                    this .sensor2D = sensor2D;
1580:                    this .sensor6D = sensor6D;
1581:
1582:                    if (rotationUnits == DEGREES)
1583:                        speedScaled = rotationSpeed * Math.PI / 180.0;
1584:                    else
1585:                        speedScaled = rotationSpeed;
1586:                }
1587:
1588:                public void read(SensorEvent e) {
1589:                    sensor2D.getRead(sensor2DRead);
1590:                    sensor2DRead.get(m);
1591:
1592:                    if (m[x2D] > threshold2D || m[x2D] < -threshold2D
1593:                            || m[y2D] > threshold2D || m[y2D] < -threshold2D) {
1594:                        // Initialize action on threshold crossing.
1595:                        if (!isActive())
1596:                            initAction(sensor6D);
1597:
1598:                        // m[x2D] is the X valuator value and m[y2D] is the Y valuator
1599:                        // value.  Use these to construct the rotation axis.
1600:                        double length = Math.sqrt(m[x2D] * m[x2D] + m[y2D]
1601:                                * m[y2D]);
1602:                        double iLength = 1.0 / length;
1603:                        axis.set(m[y2D] * iLength, -m[x2D] * iLength, 0.0);
1604:
1605:                        if (sensor6D != null) {
1606:                            // To avoid echo frame lag, compute sensorToVworld based
1607:                            // on computed trackerToVworld.  getSensorToVworld() isn't
1608:                            // current for this frame.
1609:                            sensor6D.getRead(sensorToTracker);
1610:                            sensorToVworld
1611:                                    .mul(trackerToVworld, sensorToTracker);
1612:                        }
1613:
1614:                        // Transform rotation axis into target coordinate system.
1615:                        if (sensor6D != null && rotationCoords == SENSOR) {
1616:                            if (nominalSensorRotation != null)
1617:                                nominalSensorRotation.transform(axis);
1618:
1619:                            sensorToVworld.transform(axis);
1620:                        } else if (rotationCoords == HEAD) {
1621:                            view.getUserHeadToVworld(headToVworld);
1622:                            headToVworld.transform(axis);
1623:                        } else {
1624:                            viewPlatformToVworld.transform(axis);
1625:                        }
1626:
1627:                        // Get the rotation center.
1628:                        if (transformCenterSource == HOTSPOT
1629:                                && sensor6D != null) {
1630:                            sensor6D.getHotspot(center);
1631:                            sensorToVworld.transform(center);
1632:                        } else {
1633:                            center.set(transformCenter);
1634:                        }
1635:
1636:                        double frameTime = 1.0;
1637:                        if (rotationTimeBase == PER_SECOND)
1638:                            frameTime = (e.getTime() - e.getLastTime()) / 1e9;
1639:
1640:                        // Construct origin-based rotation about axis.
1641:                        aa4d.set(axis, speedScaled * frameTime * length);
1642:                        t3d.set(aa4d);
1643:
1644:                        // Apply the rotation to the view platform.
1645:                        transformAboutCenter(viewPlatformToVworld, center, t3d);
1646:                        targetTG.setTransform(viewPlatformToVworld);
1647:
1648:                        if (sensor6D != null) {
1649:                            // Apply the rotation to trackerToVworld.
1650:                            transformAboutCenter(trackerToVworld, center, t3d);
1651:                        }
1652:
1653:                        if (sensor6D != null && readAction6D == ECHO) {
1654:                            // Transform sensor echo to compensate for the new view
1655:                            // platform movement.
1656:                            transformAboutCenter(sensorToVworld, center, t3d);
1657:                            updateEcho(sensor6D, sensorToVworld);
1658:                        }
1659:                    } else {
1660:                        // Initialize action on next threshold crossing.
1661:                        if (isActive())
1662:                            endAction(sensor6D);
1663:                    }
1664:                }
1665:
1666:                public void pressed(SensorEvent e) {
1667:                    initAction(sensor6D);
1668:                }
1669:
1670:                public void released(SensorEvent e) {
1671:                    if (isActive())
1672:                        endAction(sensor6D);
1673:                }
1674:
1675:                public void dragged(SensorEvent e) {
1676:                    read(e);
1677:                }
1678:            }
1679:
1680:            /**
1681:             * Implements a 2D valuator listener that translates the view platform.
1682:             * The X and Y values from the valuator should have a continuous range
1683:             * from -1.0 to +1.0, although the translation speed can be scaled to
1684:             * compensate for a different range.  The X and Y values are found in the
1685:             * sensor's read matrix at the indices specified by
1686:             * <code>setMatrixIndices2D</code>, with defaults of 3 and 7 respectively.
1687:             * <p>
1688:             * The translation direction is controlled by the direction the 2D
1689:             * valuator is pushed, and the speed is the translation speed scaled by
1690:             * the fast speed factor and the magnitude of the 2D valuator reads.
1691:             * <p>
1692:             * This listener will work in conjunction with a 6DOF sensor if supplied
1693:             * in the constructor.  If a 6DOF sensor is provided then the translation
1694:             * occurs along the basis vectors of the 6DOF sensor's coordinate system;
1695:             * otherwise, the translation occurs along the view platform's basis
1696:             * vectors.
1697:             * 
1698:             * @see #setReadAction2D
1699:             * @see #setButtonAction2D
1700:             * @see #setTranslationSpeed
1701:             * @see #setFastSpeedFactor
1702:             * @see #setThreshold2D
1703:             * @see #setMatrixIndices2D
1704:             */
1705:            public class TranslationListener2D extends ListenerBase {
1706:                private Sensor sensor2D, sensor6D;
1707:                private double[] m = new double[16];
1708:                private Vector3d v3d = new Vector3d();
1709:                private Transform3D sensor2DRead = new Transform3D();
1710:                private double speedScaled;
1711:
1712:                protected void initAction(Sensor s) {
1713:                    super .initAction(s);
1714:                    if (s != null && readAction6D == ECHO) {
1715:                        // Disable the 6DOF echo.  It will be updated in this action.
1716:                        eventAgent.removeSensorReadListener(s,
1717:                                echoReadListener6D);
1718:                    }
1719:                }
1720:
1721:                protected void endAction(Sensor s) {
1722:                    super .endAction(s);
1723:                    if (s != null && readAction6D == ECHO) {
1724:                        // Enable the 6DOF sensor echo.
1725:                        eventAgent.addSensorReadListener(s, echoReadListener6D);
1726:                    }
1727:                }
1728:
1729:                /**
1730:                 * Construct an instance of this class using the specified sensors.
1731:                 * 
1732:                 * @param sensor2D 2D valuator sensor for translation
1733:                 * @param sensor6D 6DOF sensor for translation direction; may be
1734:                 * <code>null</code>
1735:                 */
1736:                public TranslationListener2D(Sensor sensor2D, Sensor sensor6D) {
1737:                    this .sensor2D = sensor2D;
1738:                    this .sensor6D = sensor6D;
1739:
1740:                    // Apply virtual to physical scale if needed.
1741:                    if (translationUnits == VIRTUAL_UNITS)
1742:                        speedScaled = translationSpeed * fastSpeedFactor
1743:                                / getPhysicalToVirtualScale();
1744:                    else
1745:                        speedScaled = translationSpeed * fastSpeedFactor;
1746:
1747:                    // Apply physical to view platform scale if needed.
1748:                    if (sensor6D == null)
1749:                        speedScaled *= getPhysicalToViewPlatformScale();
1750:                }
1751:
1752:                public void read(SensorEvent e) {
1753:                    sensor2D.getRead(sensor2DRead);
1754:                    sensor2DRead.get(m);
1755:
1756:                    if (m[x2D] > threshold2D || m[x2D] < -threshold2D
1757:                            || m[y2D] > threshold2D || m[y2D] < -threshold2D) {
1758:                        // Initialize action on threshold crossing.
1759:                        if (!isActive())
1760:                            initAction(sensor6D);
1761:
1762:                        // m[x2D] is the X valuator value and m[y2D] is the Y valuator
1763:                        // value.  Use these to construct the translation vector.
1764:                        double length = Math.sqrt(m[x2D] * m[x2D] + m[y2D]
1765:                                * m[y2D]);
1766:                        double iLength = 1.0 / length;
1767:                        v3d.set(m[x2D] * iLength, 0.0, -m[y2D] * iLength);
1768:
1769:                        // Transform translation vector into target coordinate system.
1770:                        if (sensor6D != null) {
1771:                            if (nominalSensorRotation != null)
1772:                                nominalSensorRotation.transform(v3d);
1773:
1774:                            // To avoid echo frame lag, compute sensorToVworld based
1775:                            // on computed trackerToVworld.  getSensorToVworld() isn't
1776:                            // current for this frame.
1777:                            sensor6D.getRead(sensorToTracker);
1778:                            sensorToVworld
1779:                                    .mul(trackerToVworld, sensorToTracker);
1780:                            sensorToVworld.transform(v3d);
1781:                        } else {
1782:                            viewPlatformToVworld.transform(v3d);
1783:                        }
1784:
1785:                        double frameTime = 1.0;
1786:                        if (translationTimeBase == PER_SECOND)
1787:                            frameTime = (e.getTime() - e.getLastTime()) / 1e9;
1788:
1789:                        v3d.scale(frameTime * speedScaled * length);
1790:
1791:                        // Translate the view platform.
1792:                        translateTransform(viewPlatformToVworld, v3d);
1793:                        targetTG.setTransform(viewPlatformToVworld);
1794:
1795:                        if (sensor6D != null) {
1796:                            // Apply the translation to trackerToVworld.
1797:                            translateTransform(trackerToVworld, v3d);
1798:                        }
1799:
1800:                        if (sensor6D != null && readAction6D == ECHO) {
1801:                            // Translate sensor echo to compensate for the new view
1802:                            // platform movement.
1803:                            translateTransform(sensorToVworld, v3d);
1804:                            updateEcho(sensor6D, sensorToVworld);
1805:                        }
1806:                    } else {
1807:                        // Initialize action on next threshold crossing.
1808:                        if (isActive())
1809:                            endAction(sensor6D);
1810:                    }
1811:                }
1812:
1813:                public void pressed(SensorEvent e) {
1814:                    initAction(sensor6D);
1815:                }
1816:
1817:                public void released(SensorEvent e) {
1818:                    if (isActive())
1819:                        endAction(sensor6D);
1820:                }
1821:
1822:                public void dragged(SensorEvent e) {
1823:                    read(e);
1824:                }
1825:            }
1826:
1827:            /**
1828:             * Implements a 2D valuator listener that scales the view platform.
1829:             * Pushing the valuator forwards gives the appearance of the virtual world
1830:             * increasing in size, while pushing the valuator backwards makes the
1831:             * virtual world appear to shrink.  The X and Y values from the valuator
1832:             * should have a continuous range from -1.0 to +1.0, although the scale
1833:             * speed can be adjusted to compensate for a different range.
1834:             * <p>
1835:             * This listener will work in conjunction with a 6DOF sensor if supplied
1836:             * in the constructor.  If <code>setTransformCenterSource</code> has been
1837:             * called with the value <code>HOTSPOT</code>, then scaling is about the
1838:             * 6DOF sensor's hotspot; otherwise, the scaling center is the value set
1839:             * by <code>setTransformCenter</code>.
1840:             * 
1841:             * @see #setReadAction2D
1842:             * @see #setButtonAction2D
1843:             * @see #setScaleSpeed
1844:             * @see #setTransformCenter
1845:             * @see #setThreshold2D
1846:             * @see #setMatrixIndices2D
1847:             */
1848:            public class ScaleListener2D extends ListenerBase {
1849:                private Sensor sensor2D, sensor6D;
1850:                private double[] m = new double[16];
1851:                private Point3d center = new Point3d();
1852:                private Transform3D t3d = new Transform3D();
1853:                private Transform3D sensor2DRead = new Transform3D();
1854:
1855:                protected void initAction(Sensor s) {
1856:                    super .initAction(s);
1857:                    if (s != null && readAction6D == ECHO) {
1858:                        // Disable the 6DOF echo.  It will be updated in this action.
1859:                        eventAgent.removeSensorReadListener(s,
1860:                                echoReadListener6D);
1861:                    }
1862:                }
1863:
1864:                protected void endAction(Sensor s) {
1865:                    super .endAction(s);
1866:                    conditionViewScale(viewPlatformToVworld);
1867:                    if (s != null && readAction6D == ECHO) {
1868:                        // Enable the 6DOF sensor echo.
1869:                        eventAgent.addSensorReadListener(s, echoReadListener6D);
1870:                    }
1871:                }
1872:
1873:                /**
1874:                 * Construct an instance of this class with the specified sensors.
1875:                 * 
1876:                 * @param sensor2D the 2D valuator whose Y value drive the scaling
1877:                 * @param sensor6D the 6DOF sensor to use if the rotation/scale center
1878:                 *  source is <code>HOTSPOT</code>; may be <code>null</code>
1879:                 */
1880:                public ScaleListener2D(Sensor sensor2D, Sensor sensor6D) {
1881:                    this .sensor2D = sensor2D;
1882:                    this .sensor6D = sensor6D;
1883:                }
1884:
1885:                public void read(SensorEvent e) {
1886:                    sensor2D.getRead(sensor2DRead);
1887:                    sensor2DRead.get(m);
1888:
1889:                    if (m[y2D] > threshold2D || m[y2D] < -threshold2D) {
1890:                        // Initialize action on threshold crossing.
1891:                        if (!isActive())
1892:                            initAction(sensor6D);
1893:
1894:                        if (sensor6D != null) {
1895:                            // To avoid echo frame lag, compute sensorToVworld based
1896:                            // on computed trackerToVworld.  getSensorToVworld() isn't
1897:                            // current for this frame.
1898:                            sensor6D.getRead(sensorToTracker);
1899:                            sensorToVworld
1900:                                    .mul(trackerToVworld, sensorToTracker);
1901:                        }
1902:
1903:                        // Get the scale center.
1904:                        if (sensor6D != null
1905:                                && transformCenterSource == HOTSPOT) {
1906:                            sensor6D.getHotspot(center);
1907:                            sensorToVworld.transform(center);
1908:                        } else {
1909:                            center.set(transformCenter);
1910:                        }
1911:
1912:                        // Compute incremental scale for this frame.
1913:                        double frameTime = 1.0;
1914:                        if (scaleTimeBase == PER_SECOND)
1915:                            frameTime = (e.getTime() - e.getLastTime()) / 1e9;
1916:
1917:                        // Map range:       [-1.0 .. 0 .. 1.0] to:
1918:                        // [scaleSpeed**frameTime .. 1 .. 1.0/(scaleSpeed**frameTime)]
1919:                        double scale = Math.pow(scaleSpeed,
1920:                                (-m[y2D] * frameTime));
1921:
1922:                        // Apply the scale to the view platform.
1923:                        t3d.set(scale);
1924:                        transformAboutCenter(viewPlatformToVworld, center, t3d);
1925:
1926:                        // Incremental scaling at the extremes can lead to numerical
1927:                        // instability, so catch BadTransformException to prevent the
1928:                        // behavior thread from being killed.  Using a cumulative
1929:                        // scale matrix avoids this problem to a better extent, but
1930:                        // causes the 6DOF sensor hotspot center to jitter
1931:                        // excessively.
1932:                        try {
1933:                            targetTG.setTransform(viewPlatformToVworld);
1934:                        } catch (BadTransformException bt) {
1935:                            conditionViewScale(viewPlatformToVworld);
1936:                        }
1937:
1938:                        if (sensor6D != null) {
1939:                            // Apply the scale to trackerToVworld.
1940:                            transformAboutCenter(trackerToVworld, center, t3d);
1941:                        }
1942:
1943:                        if (sensor6D != null && readAction6D == ECHO) {
1944:                            // Scale sensor echo to compensate for the new view
1945:                            // platform scale.
1946:                            transformAboutCenter(sensorToVworld, center, t3d);
1947:                            updateEcho(sensor6D, sensorToVworld);
1948:                        }
1949:                    } else {
1950:                        // Initialize action on next threshold crossing.
1951:                        if (isActive())
1952:                            endAction(sensor6D);
1953:                    }
1954:                }
1955:
1956:                public void pressed(SensorEvent e) {
1957:                    initAction(sensor6D);
1958:                }
1959:
1960:                public void released(SensorEvent e) {
1961:                    if (isActive())
1962:                        endAction(sensor6D);
1963:                }
1964:
1965:                public void dragged(SensorEvent e) {
1966:                    read(e);
1967:                }
1968:            }
1969:
1970:            /**
1971:             * Resets the view back to the home transform when a specified number of
1972:             * buttons are down simultaneously.
1973:             * 
1974:             * @see #setResetViewButtonCount6D
1975:             * @see ViewPlatformBehavior#setHomeTransform
1976:             *  ViewPlatformBehavior.setHomeTransform
1977:             */
1978:            public class ResetViewListener extends SensorInputAdaptor {
1979:                private int resetCount;
1980:                private int[] buttonState = null;
1981:                private boolean goHomeNextRead = false;
1982:
1983:                /**
1984:                 * Creates a sensor listener that resets the view when the specified
1985:                 * number of buttons are down simultaneously.
1986:                 *
1987:                 * @param s the sensor to listen to
1988:                 * @param count the number of buttons that must be down simultaneously
1989:                 */
1990:                public ResetViewListener(Sensor s, int count) {
1991:                    resetCount = count;
1992:                    buttonState = new int[s.getSensorButtonCount()];
1993:                }
1994:
1995:                public void pressed(SensorEvent e) {
1996:                    int count = 0;
1997:                    e.getButtonState(buttonState);
1998:                    for (int i = 0; i < buttonState.length; i++)
1999:                        if (buttonState[i] == 1)
2000:                            count++;
2001:
2002:                    if (count >= resetCount)
2003:                        // Ineffectual to reset immediately while other listeners may
2004:                        // be setting the view transform.
2005:                        goHomeNextRead = true;
2006:                }
2007:
2008:                public void read(SensorEvent e) {
2009:                    if (goHomeNextRead) {
2010:                        goHome();
2011:                        goHomeNextRead = false;
2012:                    }
2013:                }
2014:            }
2015:
2016:            /**
2017:             * Updates the echo position and orientation.  The echo is placed at the
2018:             * sensor hotspot position if applicable.  This implementation assumes the
2019:             * hotspot position and orientation have been incorporated into the echo
2020:             * geometry.
2021:             * 
2022:             * @param sensor the sensor to be echoed
2023:             * @param sensorToVworld transform from sensor coordinates to virtual
2024:             *  world coordinates
2025:             * @see #setEchoType
2026:             * @see #setEchoSize
2027:             * @see #setReadAction6D
2028:             * @see SensorGnomonEcho
2029:             * @see SensorBeamEcho
2030:             */
2031:            protected void updateEcho(Sensor sensor, Transform3D sensorToVworld) {
2032:                echoTransformGroup.setTransform(sensorToVworld);
2033:            }
2034:
2035:            /**
2036:             * Property which sets a 6DOF sensor for manipulating the view platform.
2037:             * This sensor must generate 6 degree of freedom orientation and position
2038:             * data in physical meters relative to the sensor's tracker base.
2039:             * <p>
2040:             * This property is set in the configuration file.  The first command form
2041:             * assumes that a <code>ViewingPlatform</code> is being used and that the
2042:             * sensor name can be looked up from a <code>ConfiguredUniverse</code>
2043:             * reference retrieved from <code>ViewingPlatform.getUniverse</code>.  The
2044:             * second form is preferred and accepts the Sensor reference directly.
2045:             * <p>
2046:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2047:             * Sensor6D <i>&lt;sensorName&gt;</i>)
2048:             * <p>
2049:             * <b>Alternative Syntax:</b><br>(ViewPlatformBehaviorProperty
2050:             * <i>&lt;name&gt;</i> Sensor6D (Sensor <i>&lt;sensorName&gt;</i>))
2051:             * 
2052:             * @param sensor array of length 1 containing a <code>String</code> or
2053:             *  a <code>Sensor</code>
2054:             */
2055:            public void Sensor6D(Object[] sensor) {
2056:                if (sensor.length != 1)
2057:                    throw new IllegalArgumentException(
2058:                            "Sensor6D requires a single name or Sensor instance");
2059:
2060:                if (sensor[0] instanceof  String)
2061:                    sensor6DName = (String) sensor[0];
2062:                else if (sensor[0] instanceof  Sensor)
2063:                    sensor6D = (Sensor) sensor[0];
2064:                else
2065:                    throw new IllegalArgumentException(
2066:                            "Sensor6D must be a name or a Sensor instance");
2067:            }
2068:
2069:            /**
2070:             * Returns a reference to the 6DOF sensor used for manipulating the view
2071:             * platform.
2072:             * 
2073:             * @return the 6DOF sensor
2074:             */
2075:            public Sensor getSensor6D() {
2076:                return sensor6D;
2077:            }
2078:
2079:            /**
2080:             * Property which sets a 2D sensor for manipulating the view platform.
2081:             * This is intended to support devices which incorporate a separate 2D
2082:             * valuator along with the 6DOF sensor.  The X and Y values from the
2083:             * valuator should have a continuous range from -1.0 to +1.0, although
2084:             * rotation, translation, and scaling speeds can be scaled to compensate
2085:             * for a different range.  The X and Y values are found in the sensor's
2086:             * read matrix at the indices specified by the
2087:             * <code>MatrixIndices2D</code> property, with defaults of 3 and 7
2088:             * (the X and Y translation components) respectively.
2089:             * <p>
2090:             * This property is set in the configuration file.  The first command form
2091:             * assumes that a <code>ViewingPlatform</code> is being used and that the
2092:             * sensor name can be looked up from a <code>ConfiguredUniverse</code>
2093:             * reference retrieved from <code>ViewingPlatform.getUniverse</code>.  The
2094:             * second form is preferred and accepts the Sensor reference directly.
2095:             * <p>
2096:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2097:             * Sensor2D <i>&lt;sensorName&gt;</i>)
2098:             * <p>
2099:             * <b>Alternative Syntax:</b><br>(ViewPlatformBehaviorProperty
2100:             * <i>&lt;name&gt;</i> Sensor2D (Sensor <i>&lt;sensorName&gt;</i>))
2101:             * 
2102:             * @param sensor array of length 1 containing a <code>String</code> or
2103:             *  a <code>Sensor</code>
2104:             */
2105:            public void Sensor2D(Object[] sensor) {
2106:                if (sensor.length != 1)
2107:                    throw new IllegalArgumentException(
2108:                            "Sensor2D requires a single name or Sensor instance");
2109:
2110:                if (sensor[0] instanceof  String)
2111:                    sensor2DName = (String) sensor[0];
2112:                else if (sensor[0] instanceof  Sensor)
2113:                    sensor2D = (Sensor) sensor[0];
2114:                else
2115:                    throw new IllegalArgumentException(
2116:                            "Sensor2D must be a name or a Sensor instance");
2117:            }
2118:
2119:            /**
2120:             * Returns a reference to the 2D valuator used for manipulating the view
2121:             * platform.
2122:             * 
2123:             * @return the 2D valuator
2124:             */
2125:            public Sensor getSensor2D() {
2126:                return sensor2D;
2127:            }
2128:
2129:            /**
2130:             * Property which sets a button action for the 6DOF sensor.  The choices
2131:             * are <code>TranslateForward</code>, <code>TranslateBackward</code>,
2132:             * <code>GrabView</code>, <code>RotateCCW</code>, <code>RotateCW</code>,
2133:             * <code>ScaleUp</code>, <code>ScaleDown</code>, or <code>None</code>.  By
2134:             * default, button 0 is bound to <code>GrabView</code>, button 1 is bound
2135:             * to <code>TranslateForward</code>, and button 2 is bound to
2136:             * <code>TranslateBackward</code>. If there are fewer than three buttons
2137:             * available, then the default button actions with the lower button
2138:             * indices have precedence.  A value of <code>None</code> indicates that
2139:             * no default action is to be associated with the specified button.
2140:             * <p>
2141:             * <code>TranslateForward</code> moves the view platform forward along the
2142:             * direction the sensor is pointing.  <code>TranslateBackward</code> does
2143:             * the same, in the opposite direction.  <code>GrabView</code> directly
2144:             * manipulates the view by moving it in inverse response to the sensor's
2145:             * position and orientation.  <code>RotateCCW</code> and
2146:             * <code>RotateCW</code> rotate about a Y axis, while <code>ScaleUp</code>
2147:             * and <code>ScaleDown</code> scale the view platform larger and smaller.
2148:             * <p>
2149:             * Specifying a button index that is greater than that available with the
2150:             * 6DOF sensor will result in an <code>ArrayOutOfBoundsException</code>
2151:             * when the behavior is initialized or attached to a
2152:             * <code>ViewingPlatform</code>.
2153:             * <p>
2154:             * This property is set in the configuration file read by
2155:             * <code>ConfiguredUniverse</code>.
2156:             * <p>
2157:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2158:             * ButtonAction6D <i>&lt;button index&gt;</i> 
2159:             * [GrabView | TranslateForward | TranslateBackward | RotateCCW |
2160:             * RotateCW | ScaleUp | ScaleDown | None])
2161:             * 
2162:             * @param action array of length 2 containing a <code>Double</code> and a
2163:             *  <code>String</code>.
2164:             * @see #setButtonAction6D
2165:             * @see #Sensor6D Sensor6D
2166:             * @see #TranslationSpeed TranslationSpeed
2167:             * @see #AccelerationTime AccelerationTime
2168:             * @see #ConstantSpeedTime ConstantSpeedTime
2169:             * @see #FastSpeedFactor FastSpeedFactor 
2170:             * @see #RotationSpeed RotationSpeed
2171:             * @see #RotationCoords RotationCoords
2172:             * @see #ScaleSpeed ScaleSpeed
2173:             * @see #TransformCenterSource TransformCenterSource
2174:             * @see #TransformCenter TransformCenter
2175:             * @see GrabViewListener6D
2176:             * @see TranslationListener6D
2177:             * @see RotationListener6D
2178:             * @see ScaleListener6D
2179:             */
2180:            public void ButtonAction6D(Object[] action) {
2181:                if (!(action.length == 2 && action[0] instanceof  Double && action[1] instanceof  String))
2182:                    throw new IllegalArgumentException(
2183:                            "\nButtonAction6D must be a number and a string");
2184:
2185:                int button = ((Double) action[0]).intValue();
2186:                String actionString = (String) action[1];
2187:
2188:                if (actionString.equals("GrabView"))
2189:                    setButtonAction6D(button, GRAB_VIEW);
2190:                else if (actionString.equals("TranslateForward"))
2191:                    setButtonAction6D(button, TRANSLATE_FORWARD);
2192:                else if (actionString.equals("TranslateBackward"))
2193:                    setButtonAction6D(button, TRANSLATE_BACKWARD);
2194:                else if (actionString.equals("RotateCCW"))
2195:                    setButtonAction6D(button, ROTATE_CCW);
2196:                else if (actionString.equals("RotateCW"))
2197:                    setButtonAction6D(button, ROTATE_CW);
2198:                else if (actionString.equals("ScaleUp"))
2199:                    setButtonAction6D(button, SCALE_UP);
2200:                else if (actionString.equals("ScaleDown"))
2201:                    setButtonAction6D(button, SCALE_DOWN);
2202:                else if (actionString.equals("None"))
2203:                    setButtonAction6D(button, NONE);
2204:                else
2205:                    throw new IllegalArgumentException(
2206:                            "\nButtonAction6D must be GrabView, TranslateForward, "
2207:                                    + "TranslateBackward, RotateCCW, RotateCW, ScaleUp, "
2208:                                    + "ScaleDown, or None");
2209:            }
2210:
2211:            /**
2212:             * Sets a button action for the 6DOF sensor.  The choices are
2213:             * <code>TRANSLATE_FORWARD</code>, <code>TRANSLATE_BACKWARD</code>,
2214:             * <code>GRAB_VIEW</code>, <code>ROTATE_CCW</code>,
2215:             * <code>ROTATE_CW</code>, <code>SCALE_UP</code>, <code>SCALE_DOWN</code>,
2216:             * or <code>NONE</code>.  By default, button 0 is bound to
2217:             * <code>GRAB_VIEW</code>, button 1 is bound to
2218:             * <code>TRANSLATE_FORWARD</code>, and button 2 is bound to
2219:             * <code>TRANSLATE_BACKWARD</code>.  If there are fewer than three buttons
2220:             * available, then the default button actions with the lower button
2221:             * indices have precedence.  A value of <code>NONE</code> indicates that
2222:             * no default action is to be associated with the specified button.
2223:             * <p>
2224:             * <code>TRANSLATE_FORWARD</code> moves the view platform forward along
2225:             * the direction the sensor is pointing.  <code>TRANSLATE_BACKWARD</code>
2226:             * does the same, in the opposite direction.  <code>GRAB_VIEW</code>
2227:             * directly manipulates the view by moving it in inverse response to the
2228:             * sensor's position and orientation.  <code>ROTATE_CCW</code> and
2229:             * <code>ROTATE_CW</code> rotate about a Y axis, while
2230:             * <code>SCALE_UP</code> and <code>SCALE_DOWN</code> scale the view
2231:             * platform larger and smaller.
2232:             * <p>
2233:             * Specifying a button index that is greater that that available with the
2234:             * 6DOF sensor will result in an <code>ArrayOutOfBoundsException</code>
2235:             * when the behavior is initialized or attached to a
2236:             * <code>ViewingPlatform</code>.
2237:             * <p>
2238:             * This method only configures the button listeners pre-defined by
2239:             * this behavior.  For complete control over the button actions, access
2240:             * the <code>SensorEventAgent</code> used by this behavior directly.  
2241:             * 
2242:             * @param button index of the button to bind
2243:             * @param action either <code>TRANSLATE_FORWARD</code>,
2244:             *  <code>TRANSLATE_BACKWARD</code>, <code>GRAB_VIEW</code>,
2245:             *  <code>ROTATE_CCW</code>, <code>ROTATE_CW<code>, </code>SCALE_UP</code>,
2246:             *  <code>SCALE_DOWN</code>, or <code>NONE</code>
2247:             * @see #setTranslationSpeed
2248:             * @see #setAccelerationTime
2249:             * @see #setConstantSpeedTime
2250:             * @see #setFastSpeedFactor
2251:             * @see #setRotationSpeed
2252:             * @see #setRotationCoords
2253:             * @see #setScaleSpeed
2254:             * @see #setTransformCenterSource
2255:             * @see #setTransformCenter
2256:             * @see #getSensorEventAgent
2257:             * @see GrabViewListener6D
2258:             * @see TranslationListener6D
2259:             * @see RotationListener6D
2260:             * @see ScaleListener6D
2261:             */
2262:            public synchronized void setButtonAction6D(int button, int action) {
2263:                if (!(action == TRANSLATE_FORWARD
2264:                        || action == TRANSLATE_BACKWARD || action == GRAB_VIEW
2265:                        || action == ROTATE_CCW || action == ROTATE_CW
2266:                        || action == SCALE_UP || action == SCALE_DOWN || action == NONE))
2267:                    throw new IllegalArgumentException(
2268:                            "\naction must be TRANSLATE_FORWARD, TRANSLATE_BACKWARD, "
2269:                                    + "GRAB_VIEW, ROTATE_CCW, ROTATE_CW, SCALE_UP, SCALE_DOWN, "
2270:                                    + "or NONE");
2271:
2272:                while (button >= buttonActions6D.size()) {
2273:                    buttonActions6D.add(null);
2274:                }
2275:                buttonActions6D.set(button, new Integer(action));
2276:            }
2277:
2278:            /**
2279:             * Gets the action associated with the specified button on the 6DOF sensor.
2280:             * 
2281:             * @return the action associated with the button
2282:             */
2283:            public int getButtonAction6D(int button) {
2284:                if (button >= buttonActions6D.size())
2285:                    return NONE;
2286:
2287:                Integer i = (Integer) buttonActions6D.get(button);
2288:                if (i == null)
2289:                    return NONE;
2290:                else
2291:                    return i.intValue();
2292:            }
2293:
2294:            /**
2295:             * Property which sets the action to be bound to 2D valuator reads.  This
2296:             * action will be performed on each frame whenever no button actions have
2297:             * been invoked and the valuator read value is greater than the threshold
2298:             * range specified by the <code>Threshold2D</code> property.
2299:             * <p>
2300:             * The X and Y values from the valuator should have a continuous range
2301:             * from -1.0 to +1.0, although speeds can be scaled to compensate for a
2302:             * different range.  The X and Y values are found in the sensor's read
2303:             * matrix at the indices specified by <code>MatrixIndices2D</code>, with
2304:             * defaults of 3 and 7 respectively.
2305:             * <p>
2306:             * The default property value of <code>Rotation</code> rotates the view
2307:             * platform in the direction the valuator is pushed.  The rotation
2308:             * coordinate system is set by the <code>RotationCoords</code> property,
2309:             * with a default of <code>Sensor</code>.  The rotation occurs about a
2310:             * point in the virtual world set by the
2311:             * <code>TransformCenterSource</code> and <code>TransformCenter</code>
2312:             * properties, with the default set to rotate about the hotspot of a 6DOF
2313:             * sensor, if one is available, or about the origin of the virtual world
2314:             * if not.  The rotation speed is scaled by the valuator read value up to
2315:             * the maximum speed set with the <code>RotationSpeed</code> property; the
2316:             * default is 180 degrees per second.
2317:             * <p>
2318:             * A property value of <code>Translation</code> moves the view platform in
2319:             * the direction the valuator is pushed.  The translation occurs along the
2320:             * X and Z basis vectors of either a 6DOF sensor or the view platform if a
2321:             * 6DOF sensor is not specified.  The translation speed is scaled by the
2322:             * valuator read value up to a maximum set by the product of the
2323:             * <code>TranslationSpeed</code> and <code>FastSpeedFactor</code> property
2324:             * values.
2325:             * <p>
2326:             * If this property value is to <code>Scale</code>, then the view platform
2327:             * is scaled smaller or larger when the valuator is pushed forward or
2328:             * backward.  The scaling occurs about a point in the virtual world set by
2329:             * the <code>TransformCenterSource</code> and <code>TransformCenter</code>
2330:             * properties.  The scaling speed is set with the <code>ScaleSpeed</code>
2331:             * property, with a default scale factor of 2.0 per second at the extreme
2332:             * negative range of -1.0, and a factor of 0.5 per second at the extreme
2333:             * positive range of +1.0.
2334:             * <p>
2335:             * A value of <code>None</code> prevents <code>Rotation</code> from being
2336:             * bound to the 2D valuator reads.
2337:             * <p>
2338:             * This property is set in the configuration file read by
2339:             * <code>ConfiguredUniverse</code>.
2340:             * <p>
2341:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2342:             * ReadAction2D [Rotation | Translation | Scale | None])
2343:             * 
2344:             * @param action array of length 1 containing a <code>String</code>
2345:             * @see #setReadAction2D
2346:             * @see #RotationCoords RotationCoords
2347:             * @see #RotationSpeed RotationSpeed
2348:             * @see #TransformCenterSource TransformCenterSource
2349:             * @see #TransformCenter TransformCenter
2350:             * @see #TranslationSpeed TranslationSpeed
2351:             * @see #FastSpeedFactor FastSpeedFactor
2352:             * @see #ScaleSpeed ScaleSpeed
2353:             * @see #MatrixIndices2D MatrixIndices2D
2354:             * @see RotationListener2D
2355:             * @see TranslationListener2D
2356:             * @see ScaleListener2D
2357:             */
2358:            public void ReadAction2D(Object[] action) {
2359:                if (!(action.length == 1 && action[0] instanceof  String))
2360:                    throw new IllegalArgumentException(
2361:                            "\nReadAction2D must be a String");
2362:
2363:                String actionString = (String) action[0];
2364:
2365:                if (actionString.equals("Rotation"))
2366:                    setReadAction2D(ROTATION);
2367:                else if (actionString.equals("Translation"))
2368:                    setReadAction2D(TRANSLATION);
2369:                else if (actionString.equals("Scale"))
2370:                    setReadAction2D(SCALE);
2371:                else if (actionString.equals("None"))
2372:                    setReadAction2D(NONE);
2373:                else
2374:                    throw new IllegalArgumentException(
2375:                            "\nReadAction2D must be Rotation, Translation, Scale, "
2376:                                    + "or None");
2377:            }
2378:
2379:            /**
2380:             * Sets the action to be bound to 2D valuator reads.  This action will be
2381:             * performed on each frame whenever no button actions have been invoked
2382:             * and the valuator read value is greater than the threshold range
2383:             * specified by <code>setThreshold2D</code>.
2384:             * <p>
2385:             * The X and Y values from the valuator should have a continuous range
2386:             * from -1.0 to +1.0, although speeds can be scaled to compensate for a
2387:             * different range.  The X and Y values are found in the sensor's read
2388:             * matrix at the indices specified by the <code>setMatrixIndices2D</code>
2389:             * method, with defaults of 3 and 7 respectively.
2390:             * <p>
2391:             * The default action of <code>ROTATION</code> rotates the view platform
2392:             * in the direction the valuator is pushed.  The rotation coordinate
2393:             * system is set by <code>setRotationCoords</code>, with a default of
2394:             * <code>SENSOR</code>.  The rotation occurs about a point in the virtual
2395:             * world set by <code>setTransformCenterSource</code> and
2396:             * <code>setTransformCenter</code>, with the default set to rotate about
2397:             * the hotspot of a 6DOF sensor, if one is available, or about the origin
2398:             * of the virtual world if not.  The rotation speed is scaled by the
2399:             * valuator read value up to the maximum speed set with
2400:             * <code>setRotationSpeed</code>; the default is 180 degrees per second.
2401:             * <p>
2402:             * A value of <code>TRANSLATION</code> moves the view platform in the
2403:             * direction the valuator is pushed.  The translation occurs along the X
2404:             * and Z basis vectors of either a 6DOF sensor or the view platform if a
2405:             * 6DOF sensor is not specified.  The translation speed is scaled by the
2406:             * valuator read value up to a maximum set by the product of the
2407:             * <code>setTranslationSpeed</code> and <code>setFastSpeedFactor</code>
2408:             * values.
2409:             * <p>
2410:             * If the value is to <code>SCALE</code>, then the view platform is scaled
2411:             * smaller or larger when the valuator is pushed forward or backward.  The
2412:             * scaling occurs about a point in the virtual world set by
2413:             * <code>setTransformCenterSource</code> and
2414:             * <code>setTransformCenter</code>.  The scaling speed is set with
2415:             * <code>setScaleSpeed</code>, with a default scale factor of 2.0 per
2416:             * second at the extreme negative range of -1.0, and a factor of 0.5 per
2417:             * second at the extreme positive range of +1.0.
2418:             * <p>
2419:             * A value of <code>NONE</code> prevents <code>ROTATION</code> from being
2420:             * bound by default to the 2D valuator reads.
2421:             * <p>
2422:             * This method only configures the read listeners pre-defined by
2423:             * this behavior.  For complete control over the read actions, access
2424:             * the <code>SensorEventAgent</code> used by this behavior directly.  
2425:             * 
2426:             * @param action either <code>ROTATION</code>, <code>TRANSLATION</code>,
2427:             * <code>SCALE</code>, or <code>NONE</code>
2428:             * @see #setRotationCoords
2429:             * @see #setRotationSpeed
2430:             * @see #setTransformCenterSource
2431:             * @see #setTransformCenter
2432:             * @see #setTranslationSpeed
2433:             * @see #setFastSpeedFactor
2434:             * @see #setScaleSpeed
2435:             * @see #setMatrixIndices2D
2436:             * @see #getSensorEventAgent
2437:             * @see RotationListener2D
2438:             * @see TranslationListener2D
2439:             * @see ScaleListener2D
2440:             */
2441:            public void setReadAction2D(int action) {
2442:                if (!(action == ROTATION || action == TRANSLATION
2443:                        || action == SCALE || action == NONE))
2444:                    throw new IllegalArgumentException(
2445:                            "\nReadAction2D must be ROTATION, TRANSLATION, SCALE, "
2446:                                    + "or NONE");
2447:
2448:                this .readAction2D = action;
2449:            }
2450:
2451:            /**
2452:             * Gets the configured 2D valuator read action.
2453:             * 
2454:             * @return the action associated with the sensor read
2455:             */
2456:            public int getReadAction2D() {
2457:                if (readAction2D == UNSET)
2458:                    return NONE;
2459:                else
2460:                    return readAction2D;
2461:            }
2462:
2463:            /**
2464:             * Property which sets a button action for the 2D valuator.  The possible
2465:             * values are <code>Rotation</code>, <code>Translation</code>,
2466:             * <code>Scale</code>, or <code>None</code>, with a default of
2467:             * <code>None</code>.  These actions are the same as those for
2468:             * <code>ReadAction2D</code>.
2469:             * <p>
2470:             * Specifying a button index that is greater that that available with the
2471:             * 2D valuator will result in an <code>ArrayOutOfBoundsException</code>
2472:             * when the behavior is initialized or attached to a
2473:             * <code>ViewingPlatform</code>.
2474:             * <p>
2475:             * This property is set in the configuration file read by
2476:             * <code>ConfiguredUniverse</code>.
2477:             * <p>
2478:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2479:             * ButtonAction2D <i>&lt;button index&gt;</i> 
2480:             * [Rotation | Translation | Scale | None])
2481:             * 
2482:             * @param action array of length 2 containing a <code>Double</code> and a
2483:             *  <code>String</code>.
2484:             * @see #setButtonAction2D
2485:             * @see #ReadAction2D ReadAction2D
2486:             * @see #RotationCoords RotationCoords
2487:             * @see #RotationSpeed RotationSpeed
2488:             * @see #TransformCenterSource TransformCenterSource
2489:             * @see #TransformCenter TransformCenter
2490:             * @see #TranslationSpeed TranslationSpeed
2491:             * @see #FastSpeedFactor FastSpeedFactor
2492:             * @see #ScaleSpeed ScaleSpeed
2493:             * @see #MatrixIndices2D MatrixIndices2D
2494:             * @see RotationListener2D
2495:             * @see TranslationListener2D
2496:             * @see ScaleListener2D
2497:             */
2498:            public void ButtonAction2D(Object[] action) {
2499:                if (!(action.length == 2 && action[0] instanceof  Double && action[1] instanceof  String))
2500:                    throw new IllegalArgumentException(
2501:                            "\nButtonAction2D must be a number and a string");
2502:
2503:                int button = ((Double) action[0]).intValue();
2504:                String actionString = (String) action[1];
2505:
2506:                if (actionString.equals("Rotation"))
2507:                    setButtonAction2D(button, ROTATION);
2508:                else if (actionString.equals("Translation"))
2509:                    setButtonAction2D(button, TRANSLATION);
2510:                else if (actionString.equals("Scale"))
2511:                    setButtonAction2D(button, SCALE);
2512:                else if (actionString.equals("None"))
2513:                    setButtonAction2D(button, NONE);
2514:                else
2515:                    throw new IllegalArgumentException(
2516:                            "\nButtonAction2D must be Rotation, Translation, Scale "
2517:                                    + "or None");
2518:            }
2519:
2520:            /**
2521:             * Sets a button action for the 2D valuator.  The possible values are
2522:             * <code>ROTATION</code>, <code>TRANSLATION</code>, <code>SCALE</code>, or
2523:             * <code>NONE</code>, with a default of <code>NONE</code>.  These actions
2524:             * are the same as those for <code>setReadAction2D</code>.
2525:             * <p>
2526:             * Specifying a button index that is greater that that available with the
2527:             * 2D valuator will result in an <code>ArrayOutOfBoundsException</code>
2528:             * when the behavior is initialized or attached to a
2529:             * <code>ViewingPlatform</code>.
2530:             * <p>
2531:             * This method only configures the button listeners pre-defined by
2532:             * this behavior.  For complete control over the button actions, access
2533:             * the <code>SensorEventAgent</code> used by this behavior directly.  
2534:             * 
2535:             * @param button index of the button to bind
2536:             * @param action either <code>ROTATION</code>, <code>TRANSLATION</code>,
2537:             *  <code>SCALE</code>, or <code>NONE</code>
2538:             * @see #setReadAction2D
2539:             * @see #setRotationCoords
2540:             * @see #setRotationSpeed
2541:             * @see #setTransformCenterSource
2542:             * @see #setTransformCenter
2543:             * @see #setTranslationSpeed
2544:             * @see #setFastSpeedFactor
2545:             * @see #setScaleSpeed
2546:             * @see #setMatrixIndices2D
2547:             * @see #getSensorEventAgent
2548:             * @see RotationListener2D
2549:             * @see TranslationListener2D
2550:             * @see ScaleListener2D
2551:             */
2552:            public synchronized void setButtonAction2D(int button, int action) {
2553:                if (!(action == ROTATION || action == TRANSLATION
2554:                        || action == SCALE || action == NONE))
2555:                    throw new IllegalArgumentException(
2556:                            "\naction must be ROTATION, TRANSLATION, SCALE, or NONE");
2557:
2558:                while (button >= buttonActions2D.size()) {
2559:                    buttonActions2D.add(null);
2560:                }
2561:                buttonActions2D.set(button, new Integer(action));
2562:            }
2563:
2564:            /**
2565:             * Gets the action associated with the specified button on the 2D valuator.
2566:             * 
2567:             * @return the action associated with the button
2568:             */
2569:            public int getButtonAction2D(int button) {
2570:                if (button >= buttonActions2D.size())
2571:                    return NONE;
2572:
2573:                Integer i = (Integer) buttonActions2D.get(button);
2574:                if (i == null)
2575:                    return NONE;
2576:                else
2577:                    return i.intValue();
2578:            }
2579:
2580:            /**
2581:             * Property which sets the action to be bound to 6DOF sensor reads.  This
2582:             * action will be performed every frame whenever a button action has not
2583:             * been invoked.
2584:             * <p>
2585:             * The default is <code>Echo</code>, which displays a geometric
2586:             * representation of the sensor's current position and orientation in the
2587:             * virtual world.  A value of <code>None</code> indicates that no default
2588:             * action is to be applied to the sensor's read.
2589:             * <p>
2590:             * This property is set in the configuration file read by
2591:             * <code>ConfiguredUniverse</code>.
2592:             * <p>
2593:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2594:             * ReadAction6D [Echo | None])
2595:             * 
2596:             * @param action array of length 1 containing a <code>String</code>
2597:             * @see #setReadAction6D
2598:             * @see EchoReadListener6D
2599:             */
2600:            public void ReadAction6D(Object[] action) {
2601:                if (!(action.length == 1 && action[0] instanceof  String))
2602:                    throw new IllegalArgumentException(
2603:                            "\nReadAction6D must be a String");
2604:
2605:                String actionString = (String) action[0];
2606:
2607:                if (actionString.equals("Echo"))
2608:                    setReadAction6D(ECHO);
2609:                else if (actionString.equals("None"))
2610:                    setReadAction6D(NONE);
2611:                else
2612:                    throw new IllegalArgumentException(
2613:                            "\nReadAction6D must be Echo or None");
2614:            }
2615:
2616:            /**
2617:             * Sets the action to be bound to 6DOF sensor reads.  This action will be
2618:             * performed every frame whenever a button action has not been invoked.
2619:             * <p>
2620:             * The default is <code>ECHO</code>, which displays a geometric
2621:             * representation of the sensor's current position and orientation in the
2622:             * virtual world.  A value of <code>NONE</code> indicates that no default
2623:             * action is to be associated with the sensor's read.
2624:             * <p>
2625:             * This method only configures the read listeners pre-defined by
2626:             * this behavior.  For complete control over the read actions, access
2627:             * the <code>SensorEventAgent</code> used by this behavior directly.  
2628:             * 
2629:             * @param action either <code>ECHO</code> or <code>NONE</code>
2630:             * @see EchoReadListener6D
2631:             * @see #getSensorEventAgent
2632:             */
2633:            public void setReadAction6D(int action) {
2634:                if (!(action == ECHO || action == NONE))
2635:                    throw new IllegalArgumentException(
2636:                            "\naction must be ECHO or NONE");
2637:
2638:                this .readAction6D = action;
2639:            }
2640:
2641:            /**
2642:             * Gets the configured 6DOF sensor read action.
2643:             * 
2644:             * @return the configured 6DOF sensor read action
2645:             */
2646:            public int getReadAction6D() {
2647:                if (readAction6D == UNSET)
2648:                    return NONE;
2649:                else
2650:                    return readAction6D;
2651:            }
2652:
2653:            /**
2654:             * Property which sets the normal translation speed.  The default is 0.1
2655:             * meters/second in physical units.  This property is set in the
2656:             * configuration file read by <code>ConfiguredUniverse</code>.
2657:             * <p>
2658:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2659:             * TranslationSpeed <i>&lt;speed&gt;</i> [PhysicalMeters | VirtualUnits]
2660:             * [PerFrame | PerSecond])
2661:             * 
2662:             * @param speed array of length 3; first element is a <code>Double</code>
2663:             *  for the speed, the second is a <code>String</code> for the units, and
2664:             *  the third is a <code>String</code> for the time base
2665:             * @see #setTranslationSpeed
2666:             */
2667:            public void TranslationSpeed(Object[] speed) {
2668:                if (!(speed.length == 3 && speed[0] instanceof  Double
2669:                        && speed[1] instanceof  String && speed[2] instanceof  String))
2670:                    throw new IllegalArgumentException(
2671:                            "\nTranslationSpeed must be number, units, and time base");
2672:
2673:                double v = ((Double) speed[0]).doubleValue();
2674:                String unitsString = (String) speed[1];
2675:                String timeBaseString = (String) speed[2];
2676:                int units, timeBase;
2677:
2678:                if (unitsString.equals("PhysicalMeters"))
2679:                    units = PHYSICAL_METERS;
2680:                else if (unitsString.equals("VirtualUnits"))
2681:                    units = VIRTUAL_UNITS;
2682:                else
2683:                    throw new IllegalArgumentException(
2684:                            "\nTranslationSpeed units must be "
2685:                                    + "PhysicalMeters or VirtualUnits");
2686:
2687:                if (timeBaseString.equals("PerFrame"))
2688:                    timeBase = PER_FRAME;
2689:                else if (timeBaseString.equals("PerSecond"))
2690:                    timeBase = PER_SECOND;
2691:                else
2692:                    throw new IllegalArgumentException(
2693:                            "\ntime base must be PerFrame or PerSecond");
2694:
2695:                setTranslationSpeed(v, units, timeBase);
2696:            }
2697:
2698:            /**
2699:             * Sets the normal translation speed.  The default is 0.1 physical
2700:             * meters/second.
2701:             * 
2702:             * @param speed how fast to translate
2703:             * @param units either <code>PHYSICAL_METERS</code> or
2704:             *  <code>VIRTUAL_UNITS</code>
2705:             * @param timeBase either <code>PER_SECOND</code> or
2706:             *  <code>PER_FRAME</code>
2707:             */
2708:            public void setTranslationSpeed(double speed, int units,
2709:                    int timeBase) {
2710:                this .translationSpeed = speed;
2711:
2712:                if (units == PHYSICAL_METERS || units == VIRTUAL_UNITS)
2713:                    this .translationUnits = units;
2714:                else
2715:                    throw new IllegalArgumentException(
2716:                            "\ntranslation speed units must be "
2717:                                    + "PHYSICAL_METERS or VIRTUAL_UNITS");
2718:
2719:                if (timeBase == PER_FRAME || timeBase == PER_SECOND)
2720:                    this .translationTimeBase = timeBase;
2721:                else
2722:                    throw new IllegalArgumentException(
2723:                            "\ntranslation time base must be PER_FRAME or PER_SECOND");
2724:            }
2725:
2726:            /**
2727:             * Gets the normal speed at which to translate the view platform.
2728:             * 
2729:             * @return the normal translation speed
2730:             */
2731:            public double getTranslationSpeed() {
2732:                return translationSpeed;
2733:            }
2734:
2735:            /**
2736:             * Gets the translation speed units.
2737:             * 
2738:             * @return the translation units
2739:             */
2740:            public int getTranslationUnits() {
2741:                return translationUnits;
2742:            }
2743:
2744:            /**
2745:             * Gets the time base for translation speed.
2746:             * 
2747:             * @return the translation time base
2748:             */
2749:            public int getTranslationTimeBase() {
2750:                return translationTimeBase;
2751:            }
2752:
2753:            /**
2754:             * Property which sets the time interval for accelerating to the
2755:             * translation, rotation, or scale speeds and for transitioning between
2756:             * the normal and fast translation speeds.  The default is 1 second.  This
2757:             * property is set in the configuration file read by
2758:             * <code>ConfiguredUniverse</code>.<p>
2759:             * 
2760:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2761:             * AccelerationTime <i>&lt;seconds&gt;</i>)
2762:             * 
2763:             * @param time array of length 1 containing a <code>Double</code>
2764:             * @see #setAccelerationTime
2765:             */
2766:            public void AccelerationTime(Object[] time) {
2767:                if (!(time.length == 1 && time[0] instanceof  Double))
2768:                    throw new IllegalArgumentException(
2769:                            "\nAccelerationTime must be a number");
2770:
2771:                setAccelerationTime(((Double) time[0]).doubleValue());
2772:            }
2773:
2774:            /**
2775:             * Sets the time interval for accelerating to the translation, rotation,
2776:             * or scale speeds and for transitioning between the normal and fast
2777:             * translation speeds.  The default is 1 second.
2778:             * 
2779:             * @param time number of seconds to accelerate to normal or fast speed
2780:             */
2781:            public void setAccelerationTime(double time) {
2782:                this .accelerationTime = time;
2783:            }
2784:
2785:            /**
2786:             * Gets the time interval for accelerating to normal speed and for
2787:             * transitioning between the normal and fast translation speeds.
2788:             * 
2789:             * @return the acceleration time
2790:             */
2791:            public double getAccelerationTime() {
2792:                return accelerationTime;
2793:            }
2794:
2795:            /**
2796:             * Property which sets the time interval for which the translation occurs
2797:             * at the normal speed.  The default is 8 seconds.  This property is set
2798:             * in the configuration file read by <code>ConfiguredUniverse</code>.
2799:             * <p>
2800:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2801:             * ConstantSpeedTime <i>&lt;seconds&gt;</i>)
2802:             * 
2803:             * @param time array of length 1 containing a <code>Double</code>
2804:             * @see #setConstantSpeedTime
2805:             */
2806:            public void ConstantSpeedTime(Object[] time) {
2807:                if (!(time.length == 1 && time[0] instanceof  Double))
2808:                    throw new IllegalArgumentException(
2809:                            "\nConstantSpeedTime must be a number");
2810:
2811:                setConstantSpeedTime(((Double) time[0]).doubleValue());
2812:            }
2813:
2814:            /**
2815:             * Sets the time interval for which the translation occurs at the normal
2816:             * speed.  The default is 8 seconds.
2817:             * 
2818:             * @param time number of seconds to translate at a constant speed
2819:             */
2820:            public void setConstantSpeedTime(double time) {
2821:                this .constantSpeedTime = time;
2822:            }
2823:
2824:            /**
2825:             * Gets the time interval for which the translation occurs at the
2826:             * normal speed.
2827:             * 
2828:             * @return the constant speed time
2829:             */
2830:            public double getConstantSpeedTime() {
2831:                return constantSpeedTime;
2832:            }
2833:
2834:            /**
2835:             * Property which sets the fast translation speed factor.  The default is
2836:             * 10 times the normal speed.  This property is set in the configuration
2837:             * file read by </code>ConfiguredUniverse</code>.
2838:             * <p>
2839:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2840:             * FastSpeedFactor <i>&lt;factor&gt;</i>)
2841:             * 
2842:             * @param factor array of length 1 containing a <code>Double</code>
2843:             * @see #setFastSpeedFactor
2844:             */
2845:            public void FastSpeedFactor(Object[] factor) {
2846:                if (!(factor.length == 1 && factor[0] instanceof  Double))
2847:                    throw new IllegalArgumentException(
2848:                            "\nFastSpeedFactor must be a number");
2849:
2850:                setFastSpeedFactor(((Double) factor[0]).doubleValue());
2851:            }
2852:
2853:            /**
2854:             * Sets the fast translation speed factor.  The default is 10 times the
2855:             * normal speed.
2856:             * 
2857:             * @param factor scale by which the normal translation speed is multiplied
2858:             */
2859:            public void setFastSpeedFactor(double factor) {
2860:                this .fastSpeedFactor = factor;
2861:            }
2862:
2863:            /**
2864:             * Gets the factor by which the normal translation speed is multiplied
2865:             * after the constant speed time interval.
2866:             * 
2867:             * @return the fast speed factor
2868:             */
2869:            public double getFastSpeedFactor() {
2870:                return fastSpeedFactor;
2871:            }
2872:
2873:            /**
2874:             * Property which sets the threshold for 2D valuator reads.  The default
2875:             * is 0.0.  It can be set higher to handle noisy valuators.  This property
2876:             * is set in the configuration file read by
2877:             * <code>ConfiguredUniverse</code>.
2878:             * <p>
2879:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2880:             * Threshold2D <i>&lt;threshold&gt;</i>)
2881:             * 
2882:             * @param threshold array of length 1 containing a <code>Double</code>
2883:             * @see #setThreshold2D
2884:             */
2885:            public void Threshold2D(Object[] threshold) {
2886:                if (!(threshold.length == 1 && threshold[0] instanceof  Double))
2887:                    throw new IllegalArgumentException(
2888:                            "\nThreshold2D must be a number");
2889:
2890:                setThreshold2D(((Double) threshold[0]).doubleValue());
2891:            }
2892:
2893:            /**
2894:             * Sets the threshold for 2D valuator reads.  The default is 0.0.  It can
2895:             * be set higher to handle noisy valuators.
2896:             * 
2897:             * @param threshold if the absolute values of both the X and Y valuator
2898:             *  reads are less than this value then the values are ignored
2899:             */
2900:            public void setThreshold2D(double threshold) {
2901:                this .threshold2D = threshold;
2902:            }
2903:
2904:            /**
2905:             * Gets the 2D valuator threshold.
2906:             * 
2907:             * @return the threshold value
2908:             */
2909:            public double getThreshold2D() {
2910:                return threshold2D;
2911:            }
2912:
2913:            /**
2914:             * Property which specifies where to find the X and Y values in the matrix
2915:             * read generated by a 2D valuator.  The defaults are along the
2916:             * translation components of the matrix, at indices 3 and 7.  This
2917:             * property is set in the configuration file read by
2918:             * <code>ConfiguredUniverse</code>.
2919:             * <p>
2920:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2921:             * MatrixIndices2D <i>&lt;X index&gt; &lt;Y index&gt;</i>)
2922:             * 
2923:             * @param indices array of length 2 containing <code>Doubles</code>
2924:             * @see #setMatrixIndices2D
2925:             */
2926:            public void MatrixIndices2D(Object[] indices) {
2927:                if (!(indices.length == 2 && indices[0] instanceof  Double && indices[1] instanceof  Double))
2928:                    throw new IllegalArgumentException(
2929:                            "\nMatrixIndices2D must be a numbers");
2930:
2931:                setMatrixIndices2D(((Double) indices[0]).intValue(),
2932:                        ((Double) indices[1]).intValue());
2933:            }
2934:
2935:            /**
2936:             * Specifies where to find the X and Y values in the matrix read generated
2937:             * by a 2D valuator.  The defaults are along the translation components of
2938:             * the matrix, at indices 3 and 7.
2939:             * 
2940:             * @param xIndex index of the X valuator value
2941:             * @param yIndex index of the Y valuator value
2942:             */
2943:            public void setMatrixIndices2D(int xIndex, int yIndex) {
2944:                this .x2D = xIndex;
2945:                this .y2D = yIndex;
2946:            }
2947:
2948:            /**
2949:             * Gets the index where the X value of a 2D valuator read matrix can be
2950:             * found.
2951:             * 
2952:             * @return the X index in the read matrix
2953:             */
2954:            public int getMatrixXIndex2D() {
2955:                return x2D;
2956:            }
2957:
2958:            /**
2959:             * Gets the index where the Y value of a 2D valuator read matrix can be
2960:             * found.
2961:             * 
2962:             * @return the Y index in the read matrix
2963:             */
2964:            public int getMatrixYIndex2D() {
2965:                return y2D;
2966:            }
2967:
2968:            /**
2969:             * Property which sets the rotation speed.  The default is 180
2970:             * degrees/second.  This property is set in the configuration file read by
2971:             * <code>ConfiguredUniverse</code>.
2972:             * <p>
2973:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
2974:             * RotationSpeed <i>&lt;speed&gt;</i> [Degrees | Radians]
2975:             * [PerFrame | PerSecond])
2976:             * 
2977:             * @param speed array of length 3; first element is a <code>Double</code>
2978:             *  for the speed, the second is a <code>String</code> for the units, and
2979:             *  the third is a <code>String</code> for the time base
2980:             * @see #setRotationSpeed
2981:             */
2982:            public void RotationSpeed(Object[] speed) {
2983:                if (!(speed.length == 3 && speed[0] instanceof  Double
2984:                        && speed[1] instanceof  String && speed[2] instanceof  String))
2985:                    throw new IllegalArgumentException(
2986:                            "\nRotationSpeed must be number, units, and time base");
2987:
2988:                double v = ((Double) speed[0]).doubleValue();
2989:                String unitsString = (String) speed[1];
2990:                String timeBaseString = (String) speed[2];
2991:                int units, timeBase;
2992:
2993:                if (unitsString.equals("Degrees"))
2994:                    units = DEGREES;
2995:                else if (unitsString.equals("Radians"))
2996:                    units = RADIANS;
2997:                else
2998:                    throw new IllegalArgumentException(
2999:                            "\nRotationSpeed units must be Degrees or Radians");
3000:
3001:                if (timeBaseString.equals("PerFrame"))
3002:                    timeBase = PER_FRAME;
3003:                else if (timeBaseString.equals("PerSecond"))
3004:                    timeBase = PER_SECOND;
3005:                else
3006:                    throw new IllegalArgumentException(
3007:                            "\nRotationSpeed time base must be PerFrame or PerSecond");
3008:
3009:                setRotationSpeed(v, units, timeBase);
3010:            }
3011:
3012:            /**
3013:             * Sets the rotation speed.  The default is 180 degrees/second.
3014:             * 
3015:             * @param speed how fast to rotate
3016:             * @param units either <code>DEGREES</code> or <code>RADIANS</code>
3017:             * @param timeBase either <code>PER_SECOND</code> or <code>PER_FRAME</code>
3018:             */
3019:            public void setRotationSpeed(double speed, int units, int timeBase) {
3020:                this .rotationSpeed = speed;
3021:
3022:                if (units == DEGREES || units == RADIANS)
3023:                    this .rotationUnits = units;
3024:                else
3025:                    throw new IllegalArgumentException(
3026:                            "\nrotation speed units must be DEGREES or RADIANS");
3027:
3028:                if (timeBase == PER_FRAME || timeBase == PER_SECOND)
3029:                    this .rotationTimeBase = timeBase;
3030:                else
3031:                    throw new IllegalArgumentException(
3032:                            "\nrotation time base must be PER_FRAME or PER_SECOND");
3033:            }
3034:
3035:            /**
3036:             * Gets the rotation speed.
3037:             *
3038:             * @return the rotation speed
3039:             */
3040:            public double getRotationSpeed() {
3041:                return rotationSpeed;
3042:            }
3043:
3044:            /**
3045:             * Gets the rotation speed units
3046:             * 
3047:             * @return the rotation units
3048:             */
3049:            public int getRotationUnits() {
3050:                return rotationUnits;
3051:            }
3052:
3053:            /**
3054:             * Gets the time base for rotation speed.
3055:             * 
3056:             * @return the rotation time base
3057:             */
3058:            public int getRotationTimeBase() {
3059:                return rotationTimeBase;
3060:            }
3061:
3062:            /**
3063:             * Property which sets the rotation coordinate system.  The default is
3064:             * <code>Sensor</code>, which means that the rotation axis is parallel to
3065:             * the XY plane of the current orientation of a specified 6DOF sensor.  A
3066:             * value of <code>ViewPlatform</code> means the rotation axis is parallel
3067:             * to the XY plane of the view platform.  The latter is also the fallback
3068:             * if a 6DOF sensor is not specified.  If the value is <code>Head</code>,
3069:             * then the rotation occurs in head coordinates.
3070:             * <p>
3071:             * This property is set in the configuration file read by
3072:             * <code>ConfiguredUniverse</code>.
3073:             * <p>
3074:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3075:             * RotationCoords [Sensor | ViewPlatform | Head])
3076:             * 
3077:             * @param coords array of length 1 containing a <code>String</code>
3078:             * @see #setRotationCoords
3079:             */
3080:            public void RotationCoords(Object[] coords) {
3081:                if (!(coords.length == 1 && coords[0] instanceof  String))
3082:                    throw new IllegalArgumentException(
3083:                            "\nRotationCoords must be a String");
3084:
3085:                String coordsString = (String) coords[0];
3086:
3087:                if (coordsString.equals("Sensor"))
3088:                    setRotationCoords(SENSOR);
3089:                else if (coordsString.equals("ViewPlatform"))
3090:                    setRotationCoords(VIEW_PLATFORM);
3091:                else if (coordsString.equals("Head"))
3092:                    setRotationCoords(HEAD);
3093:                else
3094:                    throw new IllegalArgumentException(
3095:                            "\nRotationCoords must be Sensor, ViewPlatform, or Head");
3096:            }
3097:
3098:            /**
3099:             * Sets the rotation coordinate system.  The default is
3100:             * <code>SENSOR</code>, which means that the rotation axis is parallel to
3101:             * the XY plane of the current orientation of a specified 6DOF sensor.  A
3102:             * value of <code>VIEW_PLATFORM</code> means the rotation axis is parallel
3103:             * to the XY plane of the view platform.  The latter is also the fallback
3104:             * if a 6DOF sensor is not specified.  If the value is <code>HEAD</code>,
3105:             * then rotation occurs in head coordinates.
3106:             * 
3107:             * @param coords either <code>SENSOR</code>, <code>VIEW_PLATFORM</code>, or
3108:             *  <code>HEAD</code>
3109:             */
3110:            public void setRotationCoords(int coords) {
3111:                if (!(coords == SENSOR || coords == VIEW_PLATFORM || coords == HEAD))
3112:                    throw new IllegalArgumentException(
3113:                            "\nrotation coordinates be SENSOR, VIEW_PLATFORM, or HEAD");
3114:
3115:                this .rotationCoords = coords;
3116:            }
3117:
3118:            /**
3119:             * Gets the rotation coordinate system.
3120:             * 
3121:             * @return the rotation coordinate system
3122:             */
3123:            public int getRotationCoords() {
3124:                return rotationCoords;
3125:            }
3126:
3127:            /**
3128:             * Property which sets the scaling speed.  The default is 2.0 per second,
3129:             * which means magnification doubles the apparent size of the virtual
3130:             * world every second, and minification halves the apparent size of the
3131:             * virtual world every second.  
3132:             * <p>
3133:             * The scaling applied with each frame is <code>Math.pow(scaleSpeed,
3134:             * frameTime)</code>, where <code>frameTime</code> is the time in seconds
3135:             * that the last frame took to render if the time base is
3136:             * <code>PerSecond</code>, or 1.0 if the time base is
3137:             * <code>PerFrame</code>.  If scaling is performed with the 2D valuator,
3138:             * then the valuator's Y value as specified by
3139:             * <code>MatrixIndices2D</code> is an additional scale applied to the
3140:             * exponent.  If scaling is performed by the 6DOF sensor, then the scale
3141:             * speed can be inverted with a negative exponent by using the appropriate
3142:             * listener constructor flag.
3143:             * <p>
3144:             * This property is set in the configuration file read by
3145:             * <code>ConfiguredUniverse</code>.
3146:             * <p>
3147:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3148:             * ScaleSpeed <i>&lt;speed&gt;</i> [PerFrame | PerSecond])
3149:             * 
3150:             * @param speed array of length 2; first element is a <code>Double</code>
3151:             *  for the speed, and the second is a <code>String</code> for the time
3152:             *  base
3153:             * @see #setScaleSpeed
3154:             */
3155:            public void ScaleSpeed(Object[] speed) {
3156:                if (!(speed.length == 2 && speed[0] instanceof  Double && speed[1] instanceof  String))
3157:                    throw new IllegalArgumentException(
3158:                            "\nScalingSpeed must be a number and a string");
3159:
3160:                double v = ((Double) speed[0]).doubleValue();
3161:                String timeBaseString = (String) speed[2];
3162:                int timeBase;
3163:
3164:                if (timeBaseString.equals("PerFrame"))
3165:                    timeBase = PER_FRAME;
3166:                else if (timeBaseString.equals("PerSecond"))
3167:                    timeBase = PER_SECOND;
3168:                else
3169:                    throw new IllegalArgumentException(
3170:                            "\nScalingSpeed time base must be PerFrame or PerSecond");
3171:
3172:                setScaleSpeed(v, timeBase);
3173:            }
3174:
3175:            /**
3176:             * Sets the scaling speed.  The default is 2.0 per second, which means
3177:             * magnification doubles the apparent size of the virtual world every
3178:             * second, and minification halves the apparent size of the virtual world
3179:             * every second.
3180:             * <p>
3181:             * The scaling applied with each frame is <code>Math.pow(scaleSpeed,
3182:             * frameTime)</code>, where <code>frameTime</code> is the time in seconds
3183:             * that the last frame took to render if the time base is
3184:             * <code>PER_SECOND</code>, or 1.0 if the time base is
3185:             * <code>PER_FRAME</code>.  If scaling is performed with the 2D valuator,
3186:             * then the valuator's Y value as specified by
3187:             * <code>setMatrixIndices2D</code> is an additional scale applied to the
3188:             * exponent.  If scaling is performed by the 6DOF sensor, then the scale
3189:             * speed can be inverted with a negative exponent by using the appropriate
3190:             * listener constructor flag.
3191:             * 
3192:             * @param speed specifies the scale speed
3193:             * @param timeBase either <code>PER_SECOND</code> or <code>PER_FRAME</code>
3194:             */
3195:            public void setScaleSpeed(double speed, int timeBase) {
3196:                this .scaleSpeed = speed;
3197:
3198:                if (timeBase == PER_FRAME || timeBase == PER_SECOND)
3199:                    this .scaleTimeBase = timeBase;
3200:                else
3201:                    throw new IllegalArgumentException(
3202:                            "\nscaling time base must be PER_FRAME or PER_SECOND");
3203:            }
3204:
3205:            /**
3206:             * Gets the scaling speed.
3207:             * 
3208:             * @return the scaling speed
3209:             */
3210:            public double getScaleSpeed() {
3211:                return scaleSpeed;
3212:            }
3213:
3214:            /**
3215:             * Gets the time base for scaling speed.
3216:             * 
3217:             * @return the scaling time base
3218:             */
3219:            public int getScaleTimeBase() {
3220:                return scaleTimeBase;
3221:            }
3222:
3223:            /**
3224:             * Property which sets the source of the center of rotation and scale.
3225:             * The default is <code>Hotspot</code>, which means the center of rotation
3226:             * or scale is a 6DOF sensor's current hotspot location.  The alternative
3227:             * is <code>VworldFixed</code>, which uses the fixed virtual world
3228:             * coordinates specified by the <code>TransformCenter</code> property as
3229:             * the center.  The latter is also the fallback if a 6DOF sensor is not
3230:             * specified.  This property is set in the configuration file read by
3231:             * <code>ConfiguredUniverse</code>.
3232:             * <p>
3233:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3234:             * TransformCenterSource [Hotspot | VworldFixed])
3235:             * 
3236:             * @param source array of length 1 containing a <code>String</code>
3237:             * @see #setTransformCenterSource
3238:             */
3239:            public void TransformCenterSource(Object[] source) {
3240:                if (!(source.length == 1 && source[0] instanceof  String))
3241:                    throw new IllegalArgumentException(
3242:                            "\nTransformCenterSource must be a String");
3243:
3244:                String sourceString = (String) source[0];
3245:
3246:                if (sourceString.equals("Hotspot"))
3247:                    setTransformCenterSource(HOTSPOT);
3248:                else if (sourceString.equals("VworldFixed"))
3249:                    setTransformCenterSource(VWORLD_FIXED);
3250:                else
3251:                    throw new IllegalArgumentException(
3252:                            "\nTransformCenterSource must be Hotspot or "
3253:                                    + "VworldFixed");
3254:            }
3255:
3256:            /**
3257:             * Sets the source of the center of rotation and scale.  The default is
3258:             * <code>HOTSPOT</code>, which means the center of rotation or scale is a
3259:             * 6DOF sensor's current hotspot location.  The alternative is
3260:             * <code>VWORLD_FIXED</code>, which uses the fixed virtual world
3261:             * coordinates specified by <code>setTransformCenter</code> as the center.
3262:             * The latter is also the fallback if a 6DOF sensor is not specified.
3263:             * <p>
3264:             * The transform center source can be dynamically updated while the
3265:             * behavior is running.
3266:             * 
3267:             * @param source either <code>HOTSPOT</code> or <code>VWORLD_FIXED</code>
3268:             */
3269:            public void setTransformCenterSource(int source) {
3270:                if (!(source == HOTSPOT || source == VWORLD_FIXED))
3271:                    throw new IllegalArgumentException(
3272:                            "\nrotation/scale center source must be HOTSPOT or "
3273:                                    + "VWORLD_FIXED");
3274:
3275:                this .transformCenterSource = source;
3276:            }
3277:
3278:            /**
3279:             * Gets the rotation/scale center source.
3280:             * 
3281:             * @return the rotation/scale center source
3282:             */
3283:            public int getTransformCenterSource() {
3284:                return transformCenterSource;
3285:            }
3286:
3287:            /**
3288:             * Property which sets the center of rotation and scale if the
3289:             * <code>TransformCenterSource</code> property is <code>VworldFixed</code>
3290:             * or if a 6DOF sensor is not specified.  The default is (0.0, 0.0, 0.0)
3291:             * in virtual world coordinates.  This property is set in the
3292:             * configuration file read by <code>ConfiguredUniverse</code>.
3293:             * <p>
3294:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3295:             * TransformCenter <i>&lt;Point3d&gt;</i>)
3296:             * 
3297:             * @param center array of length 1 containing a <code>Point3d</code>
3298:             * @see #setTransformCenter
3299:             */
3300:            public void TransformCenter(Object[] center) {
3301:                if (!(center.length == 1 && center[0] instanceof  Point3d))
3302:                    throw new IllegalArgumentException(
3303:                            "\nTransformCenter must be a Point3d");
3304:
3305:                setTransformCenter((Point3d) center[0]);
3306:            }
3307:
3308:            /**
3309:             * Sets the center of rotation and scale if
3310:             * <code>setTransformCenterSource</code> is called with
3311:             * <code>VWORLD_FIXED</code> or if a 6DOF sensor is not specified.  The
3312:             * default is (0.0, 0.0, 0.0) in virtual world coordinates.
3313:             * <p>
3314:             * The transform center can be dynamically updated while the behavior is
3315:             * running. 
3316:             *
3317:             * @param center point in virtual world coordinates about which to rotate
3318:             *  and scale
3319:             */
3320:            public void setTransformCenter(Point3d center) {
3321:                this .transformCenter.set(center);
3322:            }
3323:
3324:            /**
3325:             * Gets the rotation/scale center in virtual world coordinates.
3326:             * @param center <code>Point3d</code> to receive a copy of the
3327:             *  rotation/scale center
3328:             */
3329:            public void getTransformCenter(Point3d center) {
3330:                center.set(transformCenter);
3331:            }
3332:
3333:            /**
3334:             * Property which sets the nominal sensor rotation.  The default is the
3335:             * identity transform.
3336:             * <p>
3337:             * This behavior assumes that when a hand-held wand is pointed directly at
3338:             * a screen in an upright position, then its 6DOF sensor's local
3339:             * coordinate system axes (its basis vectors) are nominally aligned with
3340:             * the image plate basis vectors; specifically, that the sensor's -Z axis
3341:             * points toward the screen, the +Y axis points up, and the +X axis points
3342:             * to the right.  The translation and rotation listeners provided by this
3343:             * behavior assume this orientation to determine the transforms to be
3344:             * applied to the view platform; for example, translation applies along
3345:             * the sensor Z axis, while rotation applies about axes defined in the
3346:             * sensor XY plane.
3347:             * <p>
3348:             * This nominal alignment may not hold true depending upon how the sensor
3349:             * is mounted and how the specific <code>InputDevice</code> supporting the
3350:             * sensor handles its orientation.  The <code>NominalSensorRotation</code>
3351:             * property can be used to correct the alignment by specifying the
3352:             * rotation needed to transform vectors from the nominal sensor coordinate
3353:             * system, aligned with the image plate coordinate system as described
3354:             * above, to the sensor's actual local coordinate system.
3355:             * <p>
3356:             * NOTE: the nominal sensor transform applies <i>only</i> to the
3357:             * translation directions and rotation axes created by the listeners
3358:             * defined in this behavior; for compatibility with the core Java 3D API,
3359:             * sensor reads and the sensor hotspot location are still expressed in the
3360:             * sensor's local coordinate system.
3361:             * <p>
3362:             * This property is set in the configuration file read by
3363:             * <code>ConfiguredUniverse</code>.
3364:             * <p>
3365:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3366:             * NominalSensorRotation [<i>&lt;Matrix4d&gt;</i> |
3367:             * <i>&lt;Matrix3d&gt;</i>])
3368:             * 
3369:             * @param matrix array of length 1 containing a <code>Matrix4d</code> or
3370:             *  <code>Matrix3d</code>
3371:             * @see #setNominalSensorRotation
3372:             */
3373:            public void NominalSensorRotation(Object[] matrix) {
3374:                if (!(matrix.length == 1 && (matrix[0] instanceof  Matrix3d || matrix[0] instanceof  Matrix4d)))
3375:                    throw new IllegalArgumentException(
3376:                            "\nNominalSensorRotation must be a Matrix3d or Matrix4d");
3377:
3378:                Transform3D t3d = new Transform3D();
3379:
3380:                if (matrix[0] instanceof  Matrix3d)
3381:                    t3d.set((Matrix3d) matrix[0]);
3382:                else
3383:                    t3d.set((Matrix4d) matrix[0]);
3384:
3385:                setNominalSensorRotation(t3d);
3386:            }
3387:
3388:            /**
3389:             * Sets the nominal sensor transform.  The default is the identity
3390:             * transform.
3391:             * <p>
3392:             * This behavior assumes that when a hand-held wand is pointed directly at
3393:             * a screen in an upright position, then its 6DOF sensor's local
3394:             * coordinate system axes (its basis vectors) are nominally aligned with
3395:             * the image plate basis vectors; specifically, that the sensor's -Z axis
3396:             * points toward the screen, the +Y axis points up, and the +X axis points
3397:             * to the right.  The translation and rotation listeners provided by this
3398:             * behavior assume this orientation to determine the transforms to be
3399:             * applied to the view platform, in that translation applies along the
3400:             * sensor Z axis, and rotation applies about axes defined in the sensor XY
3401:             * plane.
3402:             * <p>
3403:             * This nominal alignment may not hold true depending upon how the sensor
3404:             * is mounted and how the specific <code>InputDevice</code> supporting the
3405:             * sensor handles its orientation.  <code>setNominalSensorRotation</code>
3406:             * can be called to correct the alignment by specifying the rotation
3407:             * needed to transform vectors from the nominal sensor coordinate system,
3408:             * aligned with the image plate coordinate system as described above, to
3409:             * the sensor's actual local coordinate system.
3410:             * <p>
3411:             * NOTE: the nominal sensor transform applies <i>only</i> to the
3412:             * translation directions and rotation axes created by the listeners
3413:             * defined in this behavior; for compatibility with the core Java 3D API,
3414:             * sensor reads and the sensor hotspot location are still expressed in the
3415:             * sensor's local coordinate system.
3416:             * 
3417:             * @param transform Rotates vectors from the nominal sensor coordinate
3418:             *  system system to the sensor's local coordinate system; only the
3419:             *  rotational components are used.  May be set <code>null</code> for
3420:             *  identity.
3421:             */
3422:            public void setNominalSensorRotation(Transform3D transform) {
3423:                if (transform == null) {
3424:                    nominalSensorRotation = null;
3425:                    return;
3426:                }
3427:
3428:                if (nominalSensorRotation == null)
3429:                    nominalSensorRotation = new Transform3D();
3430:
3431:                // Set transform and make sure it is a rotation only.
3432:                nominalSensorRotation.set(transform);
3433:                nominalSensorRotation.setTranslation(new Vector3d());
3434:            }
3435:
3436:            /**
3437:             * Gets the nominal sensor transform.
3438:             * 
3439:             * @param t3d <code>Transform3D</code> to receive a copy of the
3440:             *  nominal sensor transform
3441:             */
3442:            public void getNominalSensorRotation(Transform3D t3d) {
3443:                if (nominalSensorRotation != null) {
3444:                    t3d.set(nominalSensorRotation);
3445:                } else {
3446:                    t3d.setIdentity();
3447:                }
3448:            }
3449:
3450:            /**
3451:             * Property which sets the number of buttons to be pressed simultaneously
3452:             * on the 6DOF sensor in order to reset the view back to the home
3453:             * transform.  The value must be greater than 1; the default is 3.  A
3454:             * value of <code>None</code> disables this action.  This property is set
3455:             * in the configuration file read by <code>ConfiguredUniverse</code>.
3456:             * <p>
3457:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3458:             * ResetViewButtonCount6D [<i>&lt;count&gt;</i> | None])
3459:             * 
3460:             * @param count array of length 1 containing a <code>Double</code> or
3461:             *  <code>String</code>
3462:             * @see #setResetViewButtonCount6D
3463:             * @see ViewPlatformBehavior#setHomeTransform
3464:             *  ViewPlatformBehavior.setHomeTransform
3465:             */
3466:            public void ResetViewButtonCount6D(Object[] count) {
3467:                if (!(count.length == 1 && (count[0] instanceof  Double || count[0] instanceof  String)))
3468:                    throw new IllegalArgumentException(
3469:                            "\nResetViewButtonCount6D must be a number or None");
3470:
3471:                if (count[0] instanceof  String) {
3472:                    String s = (String) count[0];
3473:                    if (s.equals("None"))
3474:                        setResetViewButtonCount6D(NONE);
3475:                    else
3476:                        throw new IllegalArgumentException(
3477:                                "\nResetViewButtonCount6D string value must be None");
3478:                } else {
3479:                    setResetViewButtonCount6D(((Double) count[0]).intValue());
3480:                }
3481:            }
3482:
3483:            /**
3484:             * Sets the number of buttons to be pressed simultaneously
3485:             * on the 6DOF sensor in order to reset the view back to the home
3486:             * transform.  The value must be greater than 1; the default is 3.  A
3487:             * value of <code>NONE</code> disables this action.
3488:             * 
3489:             * @param count either <code>NONE</code> or button count > 1
3490:             * @see ViewPlatformBehavior#setHomeTransform
3491:             *  ViewPlatformBehavior.setHomeTransform 
3492:             */
3493:            public void setResetViewButtonCount6D(int count) {
3494:                if (count == NONE || count > 1) {
3495:                    resetViewButtonCount6D = count;
3496:                } else {
3497:                    throw new IllegalArgumentException(
3498:                            "reset view button count must be > 1");
3499:                }
3500:            }
3501:
3502:            /**
3503:             * Gets the number of buttons to be pressed simultaneously on the 6DOF
3504:             * sensor in order to reset the view back to the home transform.  A value
3505:             * of <code>NONE</code> indicates this action is disabled.
3506:             * 
3507:             * @return the number of buttons to press simultaneously for a view reset
3508:             */
3509:            public int getResetViewButtonCount6D() {
3510:                return resetViewButtonCount6D;
3511:            }
3512:
3513:            /**
3514:             * Property which sets the number of buttons to be pressed simultaneously
3515:             * on the 2D valuator in order to reset the view back to the home
3516:             * transform.  The value must be greater than 1; the default is
3517:             * <code>None</code>.  A value of <code>None</code> disables this action.
3518:             * This property is set in the configuration file read by
3519:             * <code>ConfiguredUniverse</code>.
3520:             * <p>
3521:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3522:             * ResetViewButtonCount2D [<i>&lt;count&gt;</i> | None])
3523:             * 
3524:             * @param count array of length 1 containing a <code>Double</code> or
3525:             *  <code>String</code>
3526:             * @see #setResetViewButtonCount2D
3527:             * @see ViewPlatformBehavior#setHomeTransform
3528:             *  ViewPlatformBehavior.setHomeTransform
3529:             */
3530:            public void ResetViewButtonCount2D(Object[] count) {
3531:                if (!(count.length == 1 && (count[0] instanceof  Double || count[0] instanceof  String)))
3532:                    throw new IllegalArgumentException(
3533:                            "\nResetViewButtonCount2D must be a number or None");
3534:
3535:                if (count[0] instanceof  String) {
3536:                    String s = (String) count[0];
3537:                    if (s.equals("None"))
3538:                        setResetViewButtonCount2D(NONE);
3539:                    else
3540:                        throw new IllegalArgumentException(
3541:                                "\nResetViewButtonCount2D string value must be None");
3542:                } else {
3543:                    setResetViewButtonCount2D(((Double) count[0]).intValue());
3544:                }
3545:            }
3546:
3547:            /**
3548:             * Sets the number of buttons to be pressed simultaneously on the 2D
3549:             * valuator in order to reset the view back to the home transform.  The
3550:             * value must be greater than 1; the default is <code>NONE</code>.  A
3551:             * value of <code>NONE</code> disables this action.
3552:             * 
3553:             * @param count either <code>NONE</code> or button count > 1
3554:             * @see ViewPlatformBehavior#setHomeTransform
3555:             *  ViewPlatformBehavior.setHomeTransform
3556:             */
3557:            public void setResetViewButtonCount2D(int count) {
3558:                if (count == NONE || count > 1) {
3559:                    resetViewButtonCount2D = count;
3560:                } else {
3561:                    throw new IllegalArgumentException(
3562:                            "reset view button count must be > 1");
3563:                }
3564:            }
3565:
3566:            /**
3567:             * Gets the number of buttons to be pressed simultaneously on the 2D
3568:             * valuator in order to reset the view back to the home transform.  A value
3569:             * of <code>NONE</code> indicates this action is disabled.
3570:             * 
3571:             * @return the number of buttons to press simultaneously for a view reset
3572:             */
3573:            public int getResetViewButtonCount2D() {
3574:                return resetViewButtonCount2D;
3575:            }
3576:
3577:            /**
3578:             * Property which sets the 6DOF sensor echo type.  The default is
3579:             * <code>Gnomon</code>, which displays an object with points indicating
3580:             * the direction of each of the sensor's coordinate system axes at the
3581:             * location of the sensor's hotspot.  The alternative is
3582:             * <code>Beam</code>, which displays a beam from the sensor's origin to
3583:             * the location of the sensor's hotspot; the hotspot must not be (0, 0, 0)
3584:             * or an <code>IllegalArgumentException</code> will result.  The width of
3585:             * each of these echo types is specified by the <code>EchoSize</code>
3586:             * property.  The <code>EchoType</code> property is set in the
3587:             * configuration file read by <code>ConfiguredUniverse</code>.
3588:             * <p>
3589:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3590:             * EchoType [Gnomon | Beam | None])
3591:             * 
3592:             * @param type array of length 1 containing a <code>String</code>
3593:             * @see #setEchoType
3594:             */
3595:            public void EchoType(Object[] type) {
3596:                if (!(type.length == 1 && type[0] instanceof  String))
3597:                    throw new IllegalArgumentException(
3598:                            "\nEchoType must be a String");
3599:
3600:                String typeString = (String) type[0];
3601:
3602:                if (typeString.equals("Gnomon"))
3603:                    setEchoType(GNOMON);
3604:                else if (typeString.equals("Beam"))
3605:                    setEchoType(BEAM);
3606:                else if (typeString.equals("None"))
3607:                    setEchoType(NONE);
3608:                else
3609:                    throw new IllegalArgumentException(
3610:                            "\nEchoType must be Gnomon, Beam, or None");
3611:            }
3612:
3613:            /**
3614:             * Sets the 6DOF sensor echo type.  The default is <code>GNOMON</code>,
3615:             * which displays an object with points indicating the direction of each
3616:             * of the sensor's coordinate axes at the location of the sensor's
3617:             * hotspot.  The alternative is <code>BEAM</code>, which displays a beam
3618:             * from the sensor's origin to the location of the sensor's hotspot; the
3619:             * hotspot must not be (0, 0, 0) or an
3620:             * <code>IllegalArgumentException</code> will result.  The width of each
3621:             * of these echo types is specified by <code>setEchoSize</code>.
3622:             * 
3623:             * @param type <code>GNOMON</code>, <code>BEAM</code>, or
3624:             *  <code>NONE</code> are recognized
3625:             */
3626:            public void setEchoType(int type) {
3627:                this .echoType = type;
3628:            }
3629:
3630:            /**
3631:             * Gets the echo type.
3632:             * 
3633:             * @return the echo type
3634:             */
3635:            public int getEchoType() {
3636:                return echoType;
3637:            }
3638:
3639:            /**
3640:             * Property which sets the size of the 6DOF sensor echo in physical
3641:             * meters.  This is used for the width of the gnomon and beam echoes.  The
3642:             * default is 1 centimeter.  This property is set in the configuration
3643:             * file read by <code>ConfiguredUniverse</code>.
3644:             * <p>
3645:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3646:             * EchoSize <i>&lt;size&gt;</i>)
3647:             * 
3648:             * @param echoSize array of length 1 containing a <code>Double</code>
3649:             * @see #setEchoSize
3650:             */
3651:            public void EchoSize(Object[] echoSize) {
3652:                if (!(echoSize.length == 1 && echoSize[0] instanceof  Double))
3653:                    throw new IllegalArgumentException(
3654:                            "\nEchoSize must be a Double");
3655:
3656:                setEchoSize(((Double) echoSize[0]).doubleValue());
3657:            }
3658:
3659:            /**
3660:             * Sets the size of the 6DOF sensor echo in physical meters.  This is used
3661:             * for the width of the gnomon and beam echoes.  The default is 1
3662:             * centimeter.
3663:             * 
3664:             * @param echoSize the size in meters
3665:             */
3666:            public void setEchoSize(double echoSize) {
3667:                this .echoSize = echoSize;
3668:            }
3669:
3670:            /**
3671:             * Gets the size of the 6DOF sensor echo in meters.
3672:             * 
3673:             * @return the echo size
3674:             */
3675:            public double getEchoSize() {
3676:                return echoSize;
3677:            }
3678:
3679:            /**
3680:             * Property which sets the color of the 6DOF sensor echo.  The default is
3681:             * white.  This property is set in the configuration file read by
3682:             * <code>ConfiguredUniverse</code>.
3683:             * <p>
3684:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3685:             * EchoColor <i>&lt;red&gt; &lt;green&gt; &lt;blue&gt;</i>)
3686:             * 
3687:             * @param color array of length 3 containing <code>Doubles</code>
3688:             * @see #setEchoColor
3689:             */
3690:            public void EchoColor(Object[] color) {
3691:                if (!(color.length == 3 && color[0] instanceof  Double
3692:                        && color[1] instanceof  Double && color[2] instanceof  Double))
3693:                    throw new IllegalArgumentException(
3694:                            "\nEchoColor must be 3 numbers for red, green, and blue");
3695:
3696:                setEchoColor(new Color3f(((Double) color[0]).floatValue(),
3697:                        ((Double) color[1]).floatValue(), ((Double) color[2])
3698:                                .floatValue()));
3699:            }
3700:
3701:            /**
3702:             * Sets the color of the 6DOF sensor echo.  The default is white.  This
3703:             * can be called to set the color before or after the echo geometry is
3704:             * created.
3705:             * 
3706:             * @param color the echo color
3707:             */
3708:            public void setEchoColor(Color3f color) {
3709:                if (echoColor == null)
3710:                    echoColor = new Color3f(color);
3711:                else
3712:                    echoColor.set(color);
3713:
3714:                if (echoGeometry != null) {
3715:                    Appearance a = echoGeometry.getAppearance();
3716:                    Material m = a.getMaterial();
3717:                    m.setDiffuseColor(echoColor);
3718:                }
3719:            }
3720:
3721:            /**
3722:             * Gets the 6DOF sensor echo color.
3723:             * 
3724:             * @param color the <code>Color3f</code> into which to copy the echo color
3725:             */
3726:            public void getEchoColor(Color3f color) {
3727:                if (echoColor == null)
3728:                    color.set(1.0f, 1.0f, 1.0f);
3729:                else
3730:                    color.set(echoColor);
3731:            }
3732:
3733:            /**
3734:             * Property which sets the 6DOF sensor echo transparency.  The default is
3735:             * opaque.  A value of 0.0 is fully opaque and 1.0 is fully transparent.
3736:             * This property is set in the configuration file read by
3737:             * <code>ConfiguredUniverse</code>.
3738:             * <p>
3739:             * <b>Syntax:</b><br>(ViewPlatformBehaviorProperty <i>&lt;name&gt;</i>
3740:             * EchoTransparency <i>&lt;transparency&gt;</i>)
3741:             * 
3742:             * @param transparency array of length 1 containing a <code>Double</code>
3743:             * @see #setEchoTransparency
3744:             */
3745:            public void EchoTransparency(Object[] transparency) {
3746:                if (!(transparency.length == 1 && transparency[0] instanceof  Double))
3747:                    throw new IllegalArgumentException(
3748:                            "\nEchoTransparency must be a number");
3749:
3750:                setEchoTransparency(((Double) transparency[0]).floatValue());
3751:            }
3752:
3753:            /**
3754:             * Sets the 6DOF sensor echo transparency.  The default is opaque.  A
3755:             * value of 0.0 is fully opaque and 1.0 is fully transparent.  This can be
3756:             * called to set the transparency before or after the echo geometry is
3757:             * created.
3758:             * 
3759:             * @param transparency the transparency value
3760:             */
3761:            public void setEchoTransparency(float transparency) {
3762:                echoTransparency = transparency;
3763:
3764:                if (echoGeometry != null) {
3765:                    Appearance a = echoGeometry.getAppearance();
3766:                    TransparencyAttributes ta = a.getTransparencyAttributes();
3767:                    if (echoTransparency == 0.0f) {
3768:                        ta.setTransparencyMode(TransparencyAttributes.NONE);
3769:                        ta.setTransparency(0.0f);
3770:                    } else {
3771:                        ta.setTransparencyMode(TransparencyAttributes.BLENDED);
3772:                        ta.setTransparency(echoTransparency);
3773:                        // Use order independent additive blend for gnomon.
3774:                        if (echoGeometry instanceof  SensorGnomonEcho)
3775:                            ta
3776:                                    .setDstBlendFunction(TransparencyAttributes.BLEND_ONE);
3777:                    }
3778:                }
3779:            }
3780:
3781:            /**
3782:             * Gets the 6DOF sensor echo transparency value.
3783:             * 
3784:             * @return the transparency value
3785:             */
3786:            public float getEchoTransparency() {
3787:                return echoTransparency;
3788:            }
3789:
3790:            /**
3791:             * Sets the transform group containing a 6DOF sensor's echo geometry.
3792:             * This is used to specify a custom echo.  Its transform will be
3793:             * manipulated to represent the position and orientation of the 6DOF
3794:             * sensor.  Capabilities to allow writing its transform and to read,
3795:             * write, and extend its children will be set.
3796:             * <p>
3797:             * This method must be called before the behavior is made live in order to
3798:             * have an effect.
3799:             * 
3800:             * @param echo the <code>TransformGroup</code> containing the
3801:             *  echo geometry
3802:             */
3803:            public void setEchoTransformGroup(TransformGroup echo) {
3804:                echo.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
3805:                echo.setCapability(Group.ALLOW_CHILDREN_READ);
3806:                echo.setCapability(Group.ALLOW_CHILDREN_WRITE);
3807:                echo.setCapability(Group.ALLOW_CHILDREN_EXTEND);
3808:                this .echoTransformGroup = echo;
3809:            }
3810:
3811:            /**
3812:             * Gets the transform group containing a 6DOF sensor's echo geometry.
3813:             * Capabilities to write its transform and read, write, and extend its
3814:             * children are granted.
3815:             * 
3816:             * @return the echo's transform group
3817:             */
3818:            public TransformGroup getEchoTransformGroup() {
3819:                return echoTransformGroup;
3820:            }
3821:
3822:            /**
3823:             * Gets the <code>Shape3D</code> defining the 6DOF sensor's echo geometry
3824:             * and appearance.  The returned <code>Shape3D</code> allows appearance
3825:             * read and write.  If a custom echo was supplied by providing the echo
3826:             * transform group directly then the return value will be
3827:             * <code>null</code>.
3828:             * 
3829:             * @return the echo geometry, or <code>null</code> if a custom echo was
3830:             *  supplied
3831:             */
3832:            public Shape3D getEchoGeometry() {
3833:                return echoGeometry;
3834:            }
3835:
3836:            /**
3837:             * Gets the <code>SensorEventAgent</code> used by this behavior.  Sensor
3838:             * event generation is delegated to this agent.  This can be accessed to
3839:             * manipulate the sensor button and read action bindings directly.
3840:             * 
3841:             * @return the sensor event agent
3842:             */
3843:            public SensorEventAgent getSensorEventAgent() {
3844:                return eventAgent;
3845:            }
3846:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.