001: /*
002: * Copyright (c) 2000 Silvere Martin-Michiellot All Rights Reserved.
003: *
004: * Silvere Martin-Michiellot grants you ("Licensee") a non-exclusive,
005: * royalty free, license to use, modify and redistribute this
006: * software in source and binary code form,
007: * provided that i) this copyright notice and license appear on all copies of
008: * the software; and ii) Licensee does not utilize the software in a manner
009: * which is disparaging to Silvere Martin-Michiellot.
010: *
011: * This software is provided "AS IS," without a warranty of any kind. ALL
012: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
013: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
014: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Silvere Martin-Michiellot
015: * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
016: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
017: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
018: * Silvere Martin-Michiellot OR ITS LICENSORS BE LIABLE
019: * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
020: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
021: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
022: * OR INABILITY TO USE SOFTWARE, EVEN IF Silvere Martin-Michiellot HAS BEEN
023: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
024: *
025: * This software is not designed or intended for use in on-line control of
026: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
027: * the design, construction, operation or maintenance of any nuclear
028: * facility. Licensee represents and warrants that it will not use or
029: * redistribute the Software for such purposes.
030: *
031: */
032:
033: package com.db.behaviors;
034:
035: import java.awt.*;
036: import java.awt.event.*;
037: import java.util.*;
038: import javax.media.j3d.*;
039: import javax.vecmath.*;
040:
041: // This code is repackaged after the code from David R. Nadeau
042: // Site http://www.sdsc.edu/~nadeau/
043: // Email nadeau@sdsc.edu
044:
045: /**
046: * Wakeup on mouse button presses, releases, and mouse movements and
047: * generate transforms in an "examination style" that enables the user
048: * to rotate, translation, and zoom an object as if it is held at
049: * arm's length. Such an examination style is similar to the "Examine"
050: * navigation type used by VRML browsers.
051: *
052: * The behavior maps mouse drags to different transforms depending
053: * upon the mosue button held down:
054: *
055: * Button 1 (left)
056: * Horizontal movement --> Y-axis rotation
057: * Vertical movement --> X-axis rotation
058: *
059: * Button 2 (middle)
060: * Horizontal movement --> nothing
061: * Vertical movement --> Z-axis translation
062: *
063: * Button 3 (right)
064: * Horizontal movement --> X-axis translation
065: * Vertical movement --> Y-axis translation
066: *
067: * To support systems with 2 or 1 mouse buttons, the following
068: * alternate mappings are supported while dragging with any mouse
069: * button held down and zero or more keyboard modifiers held down:
070: *
071: * No modifiers = Button 1
072: * ALT = Button 2
073: * Meta = Button 3
074: * Control = Button 3
075: *
076: * The behavior automatically modifies a TransformGroup provided
077: * to the constructor. The TransformGroup's transform can be set
078: * at any time by the application or other behaviors to cause the
079: * examine rotation and translation to be reset.
080: */
081:
082: // This class is inspired by the MouseBehavior, MouseRotate,
083: // MouseTranslate, and MouseZoom utility behaviors provided with
084: // Java 3D. This class differs from those utilities in that it:
085: //
086: // (a) encapsulates all three behaviors into one in order to
087: // enforce a specific "Examine" symantic
088: //
089: // (b) supports set/get of the rotation and translation factors
090: // that control the speed of movement.
091: //
092: // (c) supports the "Control" modifier as an alternative to the
093: // "Meta" modifier not present on PC, Mac, and most non-Sun
094: // keyboards. This makes button3 behavior usable on PCs,
095: // Macs, and other systems with fewer than 3 mouse buttons.
096: public class ExamineViewerBehavior extends ViewerBehavior {
097:
098: // Previous cursor location
099: protected int previousX = 0;
100: protected int previousY = 0;
101:
102: // Saved standard cursor
103: protected Cursor savedCursor = null;
104:
105: /**
106: * Construct an examine behavior that listens to mouse movement
107: * and button presses to generate rotation and translation
108: * transforms written into a transform group given later
109: * with the setTransformGroup() method.
110: */
111: public ExamineViewerBehavior() {
112:
113: super ();
114:
115: }
116:
117: /**
118: * Construct an examine behavior that listens to mouse movement
119: * and button presses to generate rotation and translation
120: * transforms written into a transform group given later
121: * with the setTransformGroup() method.
122: *
123: * @param parent The AWT Component that contains the area
124: * generating mouse events.
125: */
126: public ExamineViewerBehavior(Component parent) {
127:
128: super (parent);
129:
130: }
131:
132: /**
133: * Construct an examine behavior that listens to mouse movement
134: * and button presses to generate rotation and translation
135: * transforms written into the given transform group.
136: *
137: * @param transformGroup The transform group to be modified
138: * by the behavior.
139: */
140: public ExamineViewerBehavior(TransformGroup transformGroup) {
141:
142: super ();
143: subjectTransformGroup = transformGroup;
144:
145: }
146:
147: /**
148: * Construct an examine behavior that listens to mouse movement
149: * and button presses to generate rotation and translation
150: * transforms written into the given transform group.
151: *
152: * @param transformGroup The transform group to be modified
153: * by the behavior.
154: * @param parent The AWT Component that contains the area
155: * generating mouse events.
156: */
157: public ExamineViewerBehavior(TransformGroup transformGroup,
158: Component parent) {
159:
160: super (parent);
161: subjectTransformGroup = transformGroup;
162:
163: }
164:
165: /**
166: * Respond to a button1 event (press, release, or drag).
167: *
168: * @param mouseEvent A MouseEvent to respond to.
169: */
170: public void onButton1(MouseEvent mev) {
171:
172: if (subjectTransformGroup == null)
173: return;
174:
175: int x = mev.getX();
176: int y = mev.getY();
177:
178: if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
179: // Mouse button pressed: record position
180: previousX = x;
181: previousY = y;
182:
183: // Change to a "move" cursor
184: if (parentComponent != null) {
185: savedCursor = parentComponent.getCursor();
186: parentComponent.setCursor(activeCursor);
187: }
188: return;
189: }
190: if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
191: // Mouse button released: do nothing
192:
193: // Switch the cursor back
194: if (parentComponent != null)
195: parentComponent.setCursor(savedCursor);
196: return;
197: }
198:
199: //
200: // Mouse moved while button down: create a rotation
201: //
202: // Compute the delta in X and Y from the previous
203: // position. Use the delta to compute rotation
204: // angles with the mapping:
205: //
206: // positive X mouse delta --> positive Y-axis rotation
207: // positive Y mouse delta --> positive X-axis rotation
208: //
209: // where positive X mouse movement is to the right, and
210: // positive Y mouse movement is **down** the screen.
211: //
212: int deltaX = x - previousX;
213: int deltaY = y - previousY;
214:
215: if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
216: || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
217: // Deltas are too huge to be believable. Probably a glitch.
218: // Don't record the new XY location, or do anything.
219: return;
220: }
221:
222: double xRotationAngle = deltaY * XRotationFactor;
223: double yRotationAngle = deltaX * YRotationFactor;
224:
225: //
226: // Build transforms
227: //
228: transform1.rotX(xRotationAngle);
229: transform2.rotY(yRotationAngle);
230:
231: // Get and save the current transform matrix
232: subjectTransformGroup.getTransform(currentTransform);
233: currentTransform.get(matrix);
234: translate.set(matrix.m03, matrix.m13, matrix.m23);
235:
236: // Translate to the origin, rotate, then translate back
237: currentTransform.setTranslation(origin);
238: currentTransform.mul(transform1, currentTransform);
239: currentTransform.mul(transform2, currentTransform);
240: currentTransform.setTranslation(translate);
241:
242: // Update the transform group
243: subjectTransformGroup.setTransform(currentTransform);
244:
245: previousX = x;
246: previousY = y;
247:
248: }
249:
250: /**
251: * Respond to a button2 event (press, release, or drag).
252: *
253: * @param mouseEvent A MouseEvent to respond to.
254: */
255: public void onButton2(MouseEvent mev) {
256:
257: if (subjectTransformGroup == null)
258: return;
259:
260: int x = mev.getX();
261: int y = mev.getY();
262:
263: if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
264: // Mouse button pressed: record position
265: previousX = x;
266: previousY = y;
267:
268: // Change to a "move" cursor
269: if (parentComponent != null) {
270: savedCursor = parentComponent.getCursor();
271: parentComponent.setCursor(activeCursor);
272: }
273: return;
274: }
275: if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
276: // Mouse button released: do nothing
277:
278: // Switch the cursor back
279: if (parentComponent != null)
280: parentComponent.setCursor(savedCursor);
281: return;
282:
283: }
284:
285: //
286: // Mouse moved while button down: create a translation
287: //
288: // Compute the delta in Y from the previous
289: // position. Use the delta to compute translation
290: // distances with the mapping:
291: //
292: // positive Y mouse delta --> positive Y-axis translation
293: //
294: // where positive X mouse movement is to the right, and
295: // positive Y mouse movement is **down** the screen.
296: //
297: int deltaY = y - previousY;
298:
299: if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
300: // Deltas are too huge to be believable. Probably a glitch.
301: // Don't record the new XY location, or do anything.
302: return;
303: }
304:
305: double zTranslationDistance = deltaY * ZTranslationFactor;
306:
307: //
308: // Build transforms
309: //
310: translate.set(0.0, 0.0, zTranslationDistance);
311: transform1.set(translate);
312:
313: // Get and save the current transform
314: subjectTransformGroup.getTransform(currentTransform);
315:
316: // Translate as needed
317: currentTransform.mul(transform1, currentTransform);
318:
319: // Update the transform group
320: subjectTransformGroup.setTransform(currentTransform);
321:
322: previousX = x;
323: previousY = y;
324:
325: }
326:
327: /**
328: * Respond to a button3 event (press, release, or drag).
329: *
330: * @param mouseEvent A MouseEvent to respond to.
331: */
332: public void onButton3(MouseEvent mev) {
333:
334: if (subjectTransformGroup == null)
335: return;
336:
337: int x = mev.getX();
338: int y = mev.getY();
339:
340: if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
341: // Mouse button pressed: record position
342: previousX = x;
343: previousY = y;
344:
345: // Change to a "move" cursor
346: if (parentComponent != null) {
347: savedCursor = parentComponent.getCursor();
348: parentComponent.setCursor(activeCursor);
349: }
350: return;
351: }
352: if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
353: // Mouse button released: do nothing
354:
355: // Switch the cursor back
356: if (parentComponent != null)
357: parentComponent.setCursor(savedCursor);
358: return;
359: }
360:
361: //
362: // Mouse moved while button down: create a translation
363: //
364: // Compute the delta in X and Y from the previous
365: // position. Use the delta to compute translation
366: // distances with the mapping:
367: //
368: // positive X mouse delta --> positive X-axis translation
369: // positive Y mouse delta --> negative Y-axis translation
370: //
371: // where positive X mouse movement is to the right, and
372: // positive Y mouse movement is **down** the screen.
373: //
374: int deltaX = x - previousX;
375: int deltaY = y - previousY;
376:
377: if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
378: || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
379: // Deltas are too huge to be believable. Probably a glitch.
380: // Don't record the new XY location, or do anything.
381: return;
382: }
383:
384: double xTranslationDistance = deltaX * XTranslationFactor;
385: double yTranslationDistance = -deltaY * YTranslationFactor;
386:
387: //
388: // Build transforms
389: //
390: translate.set(xTranslationDistance, yTranslationDistance, 0.0);
391: transform1.set(translate);
392:
393: // Get and save the current transform
394: subjectTransformGroup.getTransform(currentTransform);
395:
396: // Translate as needed
397: currentTransform.mul(transform1, currentTransform);
398:
399: // Update the transform group
400: subjectTransformGroup.setTransform(currentTransform);
401:
402: previousX = x;
403: previousY = y;
404:
405: }
406:
407: /**
408: * Respond to an elapsed frames event (assuming subclass has set up a
409: * wakeup criterion for it).
410: *
411: * @param time A WakeupOnElapsedFrames criterion to respond to.
412: */
413: public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
414: // Can't happen
415: }
416:
417: }
|