1 package tim.prune.threedee;
3 import java.awt.event.InputEvent;
6 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * - Redistribution of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * - Redistribution in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * Neither the name of Sun Microsystems, Inc. or the names of
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * This software is provided "AS IS," without a warranty of any
25 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
26 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
28 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
29 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
30 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
31 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
32 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
33 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
34 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
35 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGES.
38 * You acknowledge that this software is not designed, licensed or
39 * intended for use in the design, construction, operation or
40 * maintenance of any nuclear facility.
42 * Copyright (c) 2021 ActivityWorkshop simplifications and renamings,
43 * and restriction to upright orientations.
46 import java.awt.event.MouseEvent;
47 import java.awt.AWTEvent;
49 import javax.media.j3d.Transform3D;
50 import javax.media.j3d.Canvas3D;
52 import javax.vecmath.Vector3d;
53 import javax.vecmath.Point3d;
54 import javax.vecmath.Matrix3d;
56 import com.sun.j3d.utils.behaviors.vp.ViewPlatformAWTBehavior;
57 import com.sun.j3d.utils.universe.ViewingPlatform;
61 * Moves the View around a point of interest when the mouse is dragged with
62 * a mouse button pressed. Includes rotation, zoom, and translation
63 * actions. Zooming can also be obtained by using mouse wheel.
65 * The rotate action rotates the ViewPlatform around the point of interest
66 * when the mouse is moved with the main mouse button pressed. The
67 * rotation is in the direction of the mouse movement, with a default
68 * rotation of 0.01 radians for each pixel of mouse movement.
70 * The zoom action moves the ViewPlatform closer to or further from the
71 * point of interest when the mouse is moved with the middle mouse button
72 * pressed (or Alt-main mouse button on systems without a middle mouse button).
73 * The default zoom action is to translate the ViewPlatform 0.01 units for each
74 * pixel of mouse movement. Moving the mouse up moves the ViewPlatform closer,
75 * moving the mouse down moves the ViewPlatform further away.
77 * The translate action translates the ViewPlatform when the mouse is moved
78 * with the right mouse button pressed. The translation is in the direction
79 * of the mouse movement, with a default translation of 0.01 units for each
80 * pixel of mouse movement.
82 * The actions can be reversed using the <code>REVERSE_</code><i>ACTION</i>
83 * constructor flags. The default action moves the ViewPlatform around the
84 * objects in the scene. The <code>REVERSE_</code><i>ACTION</i> flags can
85 * make the objects in the scene appear to be moving in the direction
86 * of the mouse movement.
88 public class UprightOrbiter extends ViewPlatformAWTBehavior
90 private Transform3D _longitudeTransform = new Transform3D();
91 private Transform3D _latitudeTransform = new Transform3D();
92 private Transform3D _rotateTransform = new Transform3D();
94 // needed for integrateTransforms but don't want to new every time
95 private Transform3D _temp1 = new Transform3D();
96 private Transform3D _temp2 = new Transform3D();
97 private Transform3D _translation = new Transform3D();
98 private Vector3d _transVector = new Vector3d();
99 private Vector3d _distanceVector = new Vector3d();
100 private Vector3d _centerVector = new Vector3d();
101 private Vector3d _invertCenterVector = new Vector3d();
103 private double _deltaYaw = 0.0, _deltaPitch = 0.0;
104 private double _startDistanceFromCenter = 20.0;
105 private double _distanceFromCenter = 20.0;
106 private Point3d _rotationCenter = new Point3d();
107 private Matrix3d _rotMatrix = new Matrix3d();
109 private int _mouseX = 0, _mouseY = 0;
111 private double _xtrans = 0.0, _ytrans = 0.0, _ztrans = 0.0;
113 private static final double MIN_RADIUS = 0.0;
115 // the factor to be applied to wheel zooming so that it does not
116 // look much different with mouse movement zooming.
117 private static final float wheelZoomFactor = 50.0f;
119 private static final double NOMINAL_ZOOM_FACTOR = .01;
120 private static final double NOMINAL_ROT_FACTOR = .008;
121 private static final double NOMINAL_TRANS_FACTOR = .003;
123 private double _pitchAngle = 0.0;
127 * Creates a new OrbitBehaviour
128 * @param inCanvas The Canvas3D to add the behaviour to
129 * @param inInitialPitch pitch angle in degrees
131 public UprightOrbiter(Canvas3D inCanvas, double inInitialPitch)
133 super(inCanvas, MOUSE_LISTENER | MOUSE_MOTION_LISTENER | MOUSE_WHEEL_LISTENER );
134 _pitchAngle = Math.toRadians(inInitialPitch);
137 protected synchronized void processAWTEvents( final AWTEvent[] events )
140 for(int i=0; i<events.length; i++)
141 if (events[i] instanceof MouseEvent)
142 processMouseEvent( (MouseEvent)events[i] );
145 protected void processMouseEvent( final MouseEvent evt )
147 if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
148 _mouseX = evt.getX();
149 _mouseY = evt.getY();
152 else if (evt.getID() == MouseEvent.MOUSE_DRAGGED)
154 int xchange = evt.getX() - _mouseX;
155 int ychange = evt.getY() - _mouseY;
157 if (isRotateEvent(evt))
159 _deltaYaw -= xchange * NOMINAL_ROT_FACTOR;
160 _deltaPitch -= ychange * NOMINAL_ROT_FACTOR;
163 else if (isTranslateEvent(evt))
165 _xtrans -= xchange * NOMINAL_TRANS_FACTOR;
166 _ytrans += ychange * NOMINAL_TRANS_FACTOR;
169 else if (isZoomEvent(evt)) {
170 doZoomOperations( ychange );
172 _mouseX = evt.getX();
173 _mouseY = evt.getY();
176 else if (evt.getID() == MouseEvent.MOUSE_WHEEL )
178 if (isZoomEvent(evt))
180 // if zooming is done through mouse wheel, the number of wheel increments
181 // is multiplied by the wheelZoomFactor, to make zoom speed look natural
182 if ( evt instanceof java.awt.event.MouseWheelEvent)
184 int zoom = ((int)(((java.awt.event.MouseWheelEvent)evt).getWheelRotation()
186 doZoomOperations( zoom );
194 * zoom but stop at MIN_RADIUS
196 private void doZoomOperations( int ychange )
198 if ((_distanceFromCenter - ychange * NOMINAL_ZOOM_FACTOR) > MIN_RADIUS) {
199 _distanceFromCenter -= ychange * NOMINAL_ZOOM_FACTOR;
202 _distanceFromCenter = MIN_RADIUS;
207 * Sets the ViewingPlatform for this behaviour. This method is
208 * called by the ViewingPlatform.
209 * If a sub-calls overrides this method, it must call
210 * super.setViewingPlatform(vp).
211 * NOTE: Applications should <i>not</i> call this method.
214 public void setViewingPlatform(ViewingPlatform vp)
216 super.setViewingPlatform( vp );
220 integrateTransforms();
225 * Reset the orientation and distance of this behaviour to the current
226 * values in the ViewPlatform Transform Group
228 private void resetView()
230 Vector3d centerToView = new Vector3d();
232 targetTG.getTransform( targetTransform );
234 targetTransform.get( _rotMatrix, _transVector );
235 centerToView.sub( _transVector, _rotationCenter );
236 _distanceFromCenter = centerToView.length();
237 _startDistanceFromCenter = _distanceFromCenter;
239 targetTransform.get( _rotMatrix );
240 _rotateTransform.set( _rotMatrix );
242 // compute the initial x/y/z offset
243 _temp1.set(centerToView);
244 _rotateTransform.invert();
245 _rotateTransform.mul(_temp1);
246 _rotateTransform.get(centerToView);
247 _xtrans = centerToView.x;
248 _ytrans = centerToView.y;
249 _ztrans = centerToView.z;
252 _rotateTransform.set( _rotMatrix );
255 protected synchronized void integrateTransforms()
257 // Check if the transform has been changed by another behaviour
258 Transform3D currentXfm = new Transform3D();
259 targetTG.getTransform(currentXfm);
260 if (! targetTransform.equals(currentXfm))
263 // Three-step rotation process, firstly undo the pitch and apply the delta yaw
264 _latitudeTransform.rotX(_pitchAngle);
265 _rotateTransform.mul(_rotateTransform, _latitudeTransform);
266 _longitudeTransform.rotY( _deltaYaw );
267 _rotateTransform.mul(_rotateTransform, _longitudeTransform);
268 // Now update pitch angle according to delta and apply
269 _pitchAngle = Math.min(Math.max(0.0, _pitchAngle - _deltaPitch), Math.PI/2.0);
270 _latitudeTransform.rotX(-_pitchAngle);
271 _rotateTransform.mul(_rotateTransform, _latitudeTransform);
273 _distanceVector.z = _distanceFromCenter - _startDistanceFromCenter;
275 _temp1.set(_distanceVector);
276 _temp1.mul(_rotateTransform, _temp1);
278 // want to look at rotationCenter
279 _transVector.x = _rotationCenter.x + _xtrans;
280 _transVector.y = _rotationCenter.y + _ytrans;
281 _transVector.z = _rotationCenter.z + _ztrans;
283 _translation.set(_transVector);
284 targetTransform.mul(_temp1, _translation);
286 // handle rotationCenter
287 _temp1.set(_centerVector);
288 _temp1.mul(targetTransform);
290 _invertCenterVector.x = -_centerVector.x;
291 _invertCenterVector.y = -_centerVector.y;
292 _invertCenterVector.z = -_centerVector.z;
294 _temp2.set(_invertCenterVector);
295 targetTransform.mul(_temp1, _temp2);
297 Vector3d finalTranslation = new Vector3d();
298 targetTransform.get(finalTranslation);
300 targetTG.setTransform(targetTransform);
302 // reset yaw and pitch deltas
307 private boolean isRotateEvent(MouseEvent evt)
309 final boolean isRightDrag = (evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0;
310 return !evt.isAltDown() && !isRightDrag;
313 private boolean isZoomEvent(MouseEvent evt)
315 if (evt instanceof java.awt.event.MouseWheelEvent) {
318 return evt.isAltDown() && !evt.isMetaDown();
321 private boolean isTranslateEvent(MouseEvent evt)
323 final boolean isRightDrag = (evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0;
324 return !evt.isAltDown() && isRightDrag;