]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/threedee/UprightOrbiter.java
Version 20.4, May 2021
[GpsPrune.git] / src / tim / prune / threedee / UprightOrbiter.java
1 package tim.prune.threedee;
2
3 import java.awt.event.InputEvent;
4
5 /*
6  * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * - Redistribution of source code must retain the above copyright
13  *   notice, this list of conditions and the following disclaimer.
14  *
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
18  *   distribution.
19  *
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.
23  *
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.
37  *
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.
41  * 
42  * Copyright (c) 2021 ActivityWorkshop simplifications and renamings,
43  * and restriction to upright orientations.
44  */
45
46 import java.awt.event.MouseEvent;
47 import java.awt.AWTEvent;
48
49 import javax.media.j3d.Transform3D;
50 import javax.media.j3d.Canvas3D;
51
52 import javax.vecmath.Vector3d;
53 import javax.vecmath.Point3d;
54 import javax.vecmath.Matrix3d;
55
56 import com.sun.j3d.utils.behaviors.vp.ViewPlatformAWTBehavior;
57 import com.sun.j3d.utils.universe.ViewingPlatform;
58
59
60 /**
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.
64  * <p>
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.
69  * <p>
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.
76  * <p>
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.
81  * <p>
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.
87  */
88 public class UprightOrbiter extends ViewPlatformAWTBehavior
89 {
90         private Transform3D _longitudeTransform = new Transform3D();
91         private Transform3D _latitudeTransform = new Transform3D();
92         private Transform3D _rotateTransform = new Transform3D();
93
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();
102
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();
108
109         private int _mouseX = 0, _mouseY = 0;
110
111         private double _xtrans = 0.0, _ytrans = 0.0, _ztrans = 0.0;
112
113         private static final double MIN_RADIUS = 0.0;
114
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;
118
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;
122
123         private double _pitchAngle = 0.0;
124
125
126         /**
127          * Creates a new OrbitBehaviour
128          * @param inCanvas The Canvas3D to add the behaviour to
129          * @param inInitialPitch pitch angle in degrees
130          */
131         public UprightOrbiter(Canvas3D inCanvas, double inInitialPitch)
132         {
133                 super(inCanvas, MOUSE_LISTENER | MOUSE_MOTION_LISTENER | MOUSE_WHEEL_LISTENER );
134                 _pitchAngle = Math.toRadians(inInitialPitch);
135         }
136
137         protected synchronized void processAWTEvents( final AWTEvent[] events )
138         {
139                 motion = false;
140                 for(int i=0; i<events.length; i++)
141                         if (events[i] instanceof MouseEvent) 
142                                 processMouseEvent( (MouseEvent)events[i] );
143         }
144
145         protected void processMouseEvent( final MouseEvent evt )
146         {
147                 if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
148                         _mouseX = evt.getX();
149                         _mouseY = evt.getY();
150                         motion = true;
151                 }
152                 else if (evt.getID() == MouseEvent.MOUSE_DRAGGED)
153                 {
154                         int xchange = evt.getX() - _mouseX;
155                         int ychange = evt.getY() - _mouseY;
156                         // rotate
157                         if (isRotateEvent(evt))
158                         {
159                                 _deltaYaw -= xchange * NOMINAL_ROT_FACTOR;
160                                 _deltaPitch -= ychange * NOMINAL_ROT_FACTOR;
161                         }
162                         // translate
163                         else if (isTranslateEvent(evt))
164                         {
165                                 _xtrans -= xchange * NOMINAL_TRANS_FACTOR;
166                                 _ytrans += ychange * NOMINAL_TRANS_FACTOR;
167                         }
168                         // zoom
169                         else if (isZoomEvent(evt)) {
170                                 doZoomOperations( ychange );
171                         }
172                         _mouseX = evt.getX();
173                         _mouseY = evt.getY();
174                         motion = true;
175                 }
176                 else if (evt.getID() == MouseEvent.MOUSE_WHEEL )
177                 {
178                         if (isZoomEvent(evt))
179                         {
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)
183                                 {
184                                         int zoom = ((int)(((java.awt.event.MouseWheelEvent)evt).getWheelRotation()
185                                                 * wheelZoomFactor));
186                                         doZoomOperations( zoom );
187                                         motion = true;
188                                 }
189                         }
190                 }
191         }
192
193         /*
194          * zoom but stop at MIN_RADIUS
195          */
196         private void doZoomOperations( int ychange )
197         {
198                 if ((_distanceFromCenter - ychange * NOMINAL_ZOOM_FACTOR) > MIN_RADIUS) {
199                         _distanceFromCenter -= ychange * NOMINAL_ZOOM_FACTOR;
200                 }
201                 else {
202                         _distanceFromCenter = MIN_RADIUS;
203                 }
204         }
205
206         /**
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.
212          */
213         @Override
214         public void setViewingPlatform(ViewingPlatform vp)
215         {
216                 super.setViewingPlatform( vp );
217
218                 if (vp != null) {
219                         resetView();
220                         integrateTransforms();
221                 }
222         }
223
224         /**
225          * Reset the orientation and distance of this behaviour to the current
226          * values in the ViewPlatform Transform Group
227          */
228         private void resetView()
229         {
230                 Vector3d centerToView = new Vector3d();
231
232                 targetTG.getTransform( targetTransform );
233
234                 targetTransform.get( _rotMatrix, _transVector );
235                 centerToView.sub( _transVector, _rotationCenter );
236                 _distanceFromCenter = centerToView.length();
237                 _startDistanceFromCenter = _distanceFromCenter;
238
239                 targetTransform.get( _rotMatrix );
240                 _rotateTransform.set( _rotMatrix );
241
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;
250
251                 // reset rotMatrix
252                 _rotateTransform.set( _rotMatrix );
253         }
254
255         protected synchronized void integrateTransforms()
256         {
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))
261                         resetView();
262
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);
272
273                 _distanceVector.z = _distanceFromCenter - _startDistanceFromCenter;
274
275                 _temp1.set(_distanceVector);
276                 _temp1.mul(_rotateTransform, _temp1);
277
278                 // want to look at rotationCenter
279                 _transVector.x = _rotationCenter.x + _xtrans;
280                 _transVector.y = _rotationCenter.y + _ytrans;
281                 _transVector.z = _rotationCenter.z + _ztrans;
282
283                 _translation.set(_transVector);
284                 targetTransform.mul(_temp1, _translation);
285
286                 // handle rotationCenter
287                 _temp1.set(_centerVector);
288                 _temp1.mul(targetTransform);
289
290                 _invertCenterVector.x = -_centerVector.x;
291                 _invertCenterVector.y = -_centerVector.y;
292                 _invertCenterVector.z = -_centerVector.z;
293
294                 _temp2.set(_invertCenterVector);
295                 targetTransform.mul(_temp1, _temp2);
296
297                 Vector3d finalTranslation = new Vector3d();
298                 targetTransform.get(finalTranslation);
299
300                 targetTG.setTransform(targetTransform);
301
302                 // reset yaw and pitch deltas
303                 _deltaYaw = 0.0;
304                 _deltaPitch = 0.0;
305         }
306
307         private boolean isRotateEvent(MouseEvent evt)
308         {
309                 final boolean isRightDrag = (evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0;
310                 return !evt.isAltDown() && !isRightDrag;
311         }
312
313         private boolean isZoomEvent(MouseEvent evt)
314         {
315                 if (evt instanceof java.awt.event.MouseWheelEvent) {
316                         return true;
317                 }
318                 return evt.isAltDown() && !evt.isMetaDown();
319         }
320
321         private boolean isTranslateEvent(MouseEvent evt)
322         {
323                 final boolean isRightDrag = (evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0;
324                 return !evt.isAltDown() && isRightDrag;
325         }
326 }