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: // This code is repackaged after the code from David R. Nadeau
036: // Site http://www.sdsc.edu/~nadeau/
037: // Email nadeau@sdsc.edu
038:
039: import java.awt.*;
040: import java.awt.event.*;
041: import java.util.*;
042: import javax.media.j3d.*;
043: import javax.vecmath.*;
044:
045: /**
046: * ViewerBehavior
047: *
048: * @version 1.0, 98/04/16
049: **/
050:
051: /**
052: * Wakeup on mouse button presses, releases, and mouse movements and
053: * generate transforms for a transform group. Classes that extend this
054: * class impose specific symantics, such as "Examine" or "Walk" viewing,
055: * similar to the navigation types used by VRML browsers.
056: *
057: * To support systems with 2 or 1 mouse buttons, the following
058: * alternate mappings are supported while dragging with any mouse
059: * button held down and zero or more keyboard modifiers held down:
060: *
061: * No modifiers = Button 1
062: * ALT = Button 2
063: * Meta = Button 3
064: * Control = Button 3
065: *
066: * The behavior automatically modifies a TransformGroup provided
067: * to the constructor. The TransformGroup's transform can be set
068: * at any time by the application or other behaviors to cause the
069: * viewer's rotation and translation to be reset.
070: */
071:
072: // This class is inspired by the MouseBehavior, MouseRotate,
073: // MouseTranslate, and MouseZoom utility behaviors provided with
074: // Java 3D. This class differs from those utilities in that it:
075: //
076: // (a) encapsulates all three behaviors into one in order to
077: // enforce a specific viewing symantic
078: //
079: // (b) supports set/get of the rotation and translation factors
080: // that control the speed of movement.
081: //
082: // (c) supports the "Control" modifier as an alternative to the
083: // "Meta" modifier not present on PC, Mac, and most non-Sun
084: // keyboards. This makes button3 behavior usable on PCs,
085: // Macs, and other systems with fewer than 3 mouse buttons.
086: public abstract class ViewerBehavior extends Behavior {
087:
088: // Keep track of the transform group who's transform we modify
089: // during mouse motion.
090: protected TransformGroup subjectTransformGroup = null;
091:
092: // Keep a set of wakeup criterion for different mouse-generated
093: // event types.
094: protected WakeupCriterion[] mouseEvents = null;
095: protected WakeupOr mouseCriterion = null;
096:
097: // Track which button was last pressed
098: protected static final int BUTTONNONE = -1;
099: protected static final int BUTTON1 = 0;
100: protected static final int BUTTON2 = 1;
101: protected static final int BUTTON3 = 2;
102: protected int buttonPressed = BUTTONNONE;
103:
104: // Keep a few Transform3Ds for use during event processing. This
105: // avoids having to allocate new ones on each event.
106: protected Transform3D currentTransform = new Transform3D();
107: protected Transform3D transform1 = new Transform3D();
108: protected Transform3D transform2 = new Transform3D();
109: protected Matrix4d matrix = new Matrix4d();
110: protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
111: protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
112:
113: // Unusual X and Y delta limits.
114: protected static final int UNUSUAL_XDELTA = 400;
115: protected static final int UNUSUAL_YDELTA = 400;
116:
117: protected Component parentComponent = null;
118:
119: public static final Cursor DEFAULT_ACTIVE_CURSOR = Cursor
120: .getPredefinedCursor(Cursor.MOVE_CURSOR);
121: protected Cursor activeCursor = DEFAULT_ACTIVE_CURSOR;
122:
123: /**
124: * Construct a viewer behavior that listens to mouse movement
125: * and button presses to generate rotation and translation
126: * transforms written into a transform group given later
127: * with the setTransformGroup( ) method.
128: */
129: public ViewerBehavior() {
130:
131: super ();
132:
133: }
134:
135: /**
136: * Construct a viewer behavior that listens to mouse movement
137: * and button presses to generate rotation and translation
138: * transforms written into a transform group given later
139: * with the setTransformGroup( ) method.
140: *
141: * @param parent The AWT Component that contains the area
142: * generating mouse events.
143: */
144: public ViewerBehavior(Component parent) {
145:
146: super ();
147: parentComponent = parent;
148:
149: }
150:
151: /**
152: * Construct a viewer behavior that listens to mouse movement
153: * and button presses to generate rotation and translation
154: * transforms written into the given transform group.
155: *
156: * @param transformGroup The transform group to be modified
157: * by the behavior.
158: */
159: public ViewerBehavior(TransformGroup transformGroup) {
160:
161: super ();
162: subjectTransformGroup = transformGroup;
163:
164: }
165:
166: /**
167: * Construct a viewer behavior that listens to mouse movement
168: * and button presses to generate rotation and translation
169: * transforms written into the given transform group.
170: *
171: * @param transformGroup The transform group to be modified
172: * by the behavior.
173: * @param parent The AWT Component that contains the area
174: * generating mouse events.
175: */
176: public ViewerBehavior(TransformGroup transformGroup,
177: Component parent) {
178:
179: super ();
180: subjectTransformGroup = transformGroup;
181: parentComponent = parent;
182:
183: }
184:
185: /**
186: * Set the transform group modified by the viewer behavior.
187: * Setting the transform group to null disables the behavior
188: * until the transform group is again set to an existing group.
189: *
190: * @param transformGroup The new transform group to be modified
191: * by the behavior.
192: */
193: public void setTransformGroup(TransformGroup transformGroup) {
194:
195: subjectTransformGroup = transformGroup;
196:
197: }
198:
199: /**
200: * Get the transform group modified by the viewer behavior.
201: */
202: public TransformGroup getTransformGroup() {
203:
204: return subjectTransformGroup;
205:
206: }
207:
208: /**
209: * Sets the parent component who's cursor will be changed during
210: * mouse drags. If no component is given is given to the
211: * constructor, or set via this method, no cursor changes
212: * will be done.
213: *
214: * @param parent the AWT Component, such as a Frame, within which
215: * cursor changes should take place during
216: * mouse drags
217: */
218: public void setParentComponent(Component parent) {
219:
220: parentComponent = parent;
221:
222: }
223:
224: /*
225: * Gets the parent frame within which the cursor changes
226: * during mouse drags.
227: *
228: * @return the AWT Component, such as a Frame, within which
229: * cursor changes should take place during
230: * mouse drags. Returns null if no parent is set.
231: */
232: public Component getParentComponent() {
233:
234: return parentComponent;
235:
236: }
237:
238: /**
239: * Initialize the behavior.
240: */
241: public void initialize() {
242:
243: // Wakeup when the mouse is dragged or when a mouse button
244: // is pressed or released.
245: mouseEvents = new WakeupCriterion[3];
246: mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
247: mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
248: mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
249: mouseCriterion = new WakeupOr(mouseEvents);
250: wakeupOn(mouseCriterion);
251:
252: }
253:
254: /**
255: * Process a new wakeup. Interpret mouse button presses, releases,
256: * and mouse drags.
257: *
258: * @param criteria The wakeup criteria causing the behavior wakeup.
259: */
260: public void processStimulus(Enumeration criteria) {
261:
262: WakeupCriterion wakeup = null;
263: AWTEvent[] event = null;
264: int whichButton = BUTTONNONE;
265:
266: // Process all pending wakeups
267: while (criteria.hasMoreElements()) {
268: wakeup = (WakeupCriterion) criteria.nextElement();
269: if (wakeup instanceof WakeupOnAWTEvent) {
270: event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
271:
272: // Process all pending events
273: for (int i = 0; i < event.length; i++) {
274: if (event[i].getID() != MouseEvent.MOUSE_PRESSED
275: && event[i].getID() != MouseEvent.MOUSE_RELEASED
276: && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
277: // Ignore uninteresting mouse events
278: continue;
279:
280: //
281: // Regretably, Java event handling (or perhaps
282: // underlying OS event handling) doesn't always
283: // catch button bounces (redundant presses and
284: // releases), or order events so that the last
285: // drag event is delivered before a release.
286: // This means we can get stray events that we
287: // filter out here.
288: //
289: if (event[i].getID() == MouseEvent.MOUSE_PRESSED
290: && buttonPressed != BUTTONNONE)
291: // Ignore additional button presses until a release
292: continue;
293:
294: if (event[i].getID() == MouseEvent.MOUSE_RELEASED
295: && buttonPressed == BUTTONNONE)
296: // Ignore additional button releases until a press
297: continue;
298:
299: if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
300: && buttonPressed == BUTTONNONE)
301: // Ignore drags until a press
302: continue;
303:
304: MouseEvent mev = (MouseEvent) event[i];
305: int modifiers = mev.getModifiers();
306:
307: //
308: // Unfortunately, the underlying event handling
309: // doesn't do a "grab" operation when a mouse button
310: // is pressed. This means that once a button is
311: // pressed, if another mouse button or a keyboard
312: // modifier key is pressed, the delivered mouse event
313: // will show that a different button is being held
314: // down. For instance:
315: //
316: // Action Event
317: // Button 1 press Button 1 press
318: // Drag with button 1 down Button 1 drag
319: // ALT press -
320: // Drag with ALT & button 1 down Button 2 drag
321: // Button 1 release Button 2 release
322: //
323: // The upshot is that we can get a button press
324: // without a matching release, and the button
325: // associated with a drag can change mid-drag.
326: //
327: // To fix this, we watch for an initial button
328: // press, and thenceforth consider that button
329: // to be the one held down, even if additional
330: // buttons get pressed, and despite what is
331: // reported in the event. Only when a button is
332: // released, do we end such a grab.
333: //
334:
335: if (buttonPressed == BUTTONNONE) {
336: // No button is pressed yet, figure out which
337: // button is down now and how to direct events
338: if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
339: || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
340: // Button 3 activity (META or CTRL down)
341: whichButton = BUTTON3;
342: } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
343: // Button 2 activity (ALT down)
344: whichButton = BUTTON2;
345: } else {
346: // Button 1 activity (no modifiers down)
347: whichButton = BUTTON1;
348: }
349:
350: // If the event is to press a button, then
351: // record that that button is now down
352: if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
353: buttonPressed = whichButton;
354: } else {
355: // Otherwise a button was pressed earlier and
356: // hasn't been released yet. Assign all further
357: // events to it, even if ALT, META, CTRL, or
358: // another button has been pressed as well.
359: whichButton = buttonPressed;
360: }
361:
362: // Distribute the event
363: switch (whichButton) {
364: case BUTTON1:
365: onButton1(mev);
366: break;
367: case BUTTON2:
368: onButton2(mev);
369: break;
370: case BUTTON3:
371: onButton3(mev);
372: break;
373: default:
374: break;
375: }
376:
377: // If the event is to release a button, then
378: // record that that button is now up
379: if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
380: buttonPressed = BUTTONNONE;
381: }
382: continue;
383: }
384:
385: if (wakeup instanceof WakeupOnElapsedFrames) {
386: onElapsedFrames((WakeupOnElapsedFrames) wakeup);
387: continue;
388: }
389: }
390:
391: // Reschedule us for another wakeup
392: wakeupOn(mouseCriterion);
393:
394: }
395:
396: /**
397: * Default X and Y rotation factors, and XYZ translation factors.
398: */
399: public static final double DEFAULT_XROTATION_FACTOR = 0.02;
400: public static final double DEFAULT_YROTATION_FACTOR = 0.005;
401: public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
402: public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
403: public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
404:
405: protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
406: protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
407: protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
408: protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
409: protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
410:
411: /**
412: * Set the X rotation scaling factor for X-axis rotations.
413: *
414: * @param factor The new scaling factor.
415: */
416: public void setXRotationFactor(double factor) {
417:
418: XRotationFactor = factor;
419:
420: }
421:
422: /**
423: * Get the current X rotation scaling factor for X-axis rotations.
424: */
425: public double getXRotationFactor() {
426:
427: return XRotationFactor;
428:
429: }
430:
431: /**
432: * Set the Y rotation scaling factor for Y-axis rotations.
433: *
434: * @param factor The new scaling factor.
435: */
436: public void setYRotationFactor(double factor) {
437:
438: YRotationFactor = factor;
439:
440: }
441:
442: /**
443: * Get the current Y rotation scaling factor for Y-axis rotations.
444: */
445: public double getYRotationFactor() {
446:
447: return YRotationFactor;
448:
449: }
450:
451: /**
452: * Set the X translation scaling factor for X-axis translations.
453: *
454: * @param factor The new scaling factor.
455: */
456: public void setXTranslationFactor(double factor) {
457:
458: XTranslationFactor = factor;
459:
460: }
461:
462: /**
463: * Get the current X translation scaling factor for X-axis translations.
464: */
465: public double getXTranslationFactor() {
466:
467: return XTranslationFactor;
468:
469: }
470:
471: /**
472: * Set the Y translation scaling factor for Y-axis translations.
473: *
474: * @param factor The new scaling factor.
475: */
476: public void setYTranslationFactor(double factor) {
477:
478: YTranslationFactor = factor;
479:
480: }
481:
482: /**
483: * Get the current Y translation scaling factor for Y-axis translations.
484: */
485: public double getYTranslationFactor() {
486:
487: return YTranslationFactor;
488:
489: }
490:
491: /**
492: * Set the Z translation scaling factor for Z-axis translations.
493: *
494: * @param factor The new scaling factor.
495: */
496: public void setZTranslationFactor(double factor) {
497:
498: ZTranslationFactor = factor;
499:
500: }
501:
502: /**
503: * Get the current Z translation scaling factor for Z-axis translations.
504: */
505: public double getZTranslationFactor() {
506:
507: return ZTranslationFactor;
508:
509: }
510:
511: /**
512: * Set the active cursor to work with when an event occurs.
513: *
514: * @param cursor The cursor active when the user moves.
515: */
516: public void setActiveCursor(Cursor cursor) {
517:
518: activeCursor = cursor;
519:
520: }
521:
522: /**
523: * Get the active cursor we work with when an event occurs.
524: *
525: * @param cursor The cursor active when the user moves.
526: */
527: public Cursor getActiveCursor() {
528:
529: return activeCursor;
530:
531: }
532:
533: /**
534: * Respond to a button1 event (press, release, or drag).
535: *
536: * @param mouseEvent A MouseEvent to respond to.
537: */
538: public abstract void onButton1(MouseEvent mouseEvent);
539:
540: /**
541: * Respond to a button2 event (press, release, or drag).
542: *
543: * @param mouseEvent A MouseEvent to respond to.
544: */
545: public abstract void onButton2(MouseEvent mouseEvent);
546:
547: /**
548: * Responed to a button3 event (press, release, or drag).
549: *
550: * @param mouseEvent A MouseEvent to respond to.
551: */
552: public abstract void onButton3(MouseEvent mouseEvent);
553:
554: /**
555: * Respond to an elapsed frames event (assuming subclass has set up a
556: * wakeup criterion for it).
557: *
558: * @param time A WakeupOnElapsedFrames criterion to respond to.
559: */
560: public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);
561:
562: }
|