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><name></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><name></i>
2047: * Sensor6D <i><sensorName></i>)
2048: * <p>
2049: * <b>Alternative Syntax:</b><br>(ViewPlatformBehaviorProperty
2050: * <i><name></i> Sensor6D (Sensor <i><sensorName></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><name></i>
2097: * Sensor2D <i><sensorName></i>)
2098: * <p>
2099: * <b>Alternative Syntax:</b><br>(ViewPlatformBehaviorProperty
2100: * <i><name></i> Sensor2D (Sensor <i><sensorName></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><name></i>
2158: * ButtonAction6D <i><button index></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><name></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><name></i>
2479: * ButtonAction2D <i><button index></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><name></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><name></i>
2659: * TranslationSpeed <i><speed></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><name></i>
2761: * AccelerationTime <i><seconds></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><name></i>
2801: * ConstantSpeedTime <i><seconds></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><name></i>
2840: * FastSpeedFactor <i><factor></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><name></i>
2880: * Threshold2D <i><threshold></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><name></i>
2921: * MatrixIndices2D <i><X index> <Y index></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><name></i>
2974: * RotationSpeed <i><speed></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><name></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><name></i>
3148: * ScaleSpeed <i><speed></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><name></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><name></i>
3295: * TransformCenter <i><Point3d></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><name></i>
3366: * NominalSensorRotation [<i><Matrix4d></i> |
3367: * <i><Matrix3d></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><name></i>
3458: * ResetViewButtonCount6D [<i><count></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><name></i>
3522: * ResetViewButtonCount2D [<i><count></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><name></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><name></i>
3646: * EchoSize <i><size></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><name></i>
3685: * EchoColor <i><red> <green> <blue></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><name></i>
3740: * EchoTransparency <i><transparency></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: }
|