< Zurück | Inhalt | Weiter >

Building improved mouse behaviors

The built−in mouse behaviors are pretty basic in their functionality and often don’t offer enough flexibility for use in a real−world application. Application developers typically rewrite these behaviors from scratch, using the source code for the built−in mouse behaviors as a guide in order to integrate mouse processing into UI display logic or implement application−specific features.


I have included three mouse behaviors that fix a number of problems and extend the built−in behaviors. These behaviors classes are:

TornadoMouseRotate

TornadoMouseTranslate

TornadoMouseScale


The behaviors, defined in the org.selman.java3d.book package, have a number of advantages over the built−in behaviors (figure 11.7). Each of the Tornado mouse behaviors can have a registered TornadoChangeListener to receive notifications of mouse behavior processing. The TornadoChangeListener interface allows the following notifications to be handled:


image


Figure 11.7 The MouseNavigateTest example enables the user to interactively rotate, translate, and scale a ColorCube object within the boundaries of the larger cube. The current position, rotation, and scale of the object are displayed in the UI elements below the Canvas3D


onStartDrag—called when a mouse drag is started

onEndDrag—called when a mouse drag is completed

onApplyTransform—called when manipulation is complete and an object is being updated

onAdjustTransform—called when a new Transform3D is being calculated


In addition, the three behaviors also accept behavior−specific interfaces:


RotationChangeListener—Allows TornadoMouseRotate to pass the new angles about the x, y, and z axes to the caller.

ScaleChangeListener—Allows TornadoMouseScale to pass the new scales along the x, y,

and z axes to the caller.

TranslationChangeListener—Allows TornadoMouseTranslate to pass the new translation along the x, y, and z axes to the caller.

These interfaces allow the MouseNavigateTest example to display the rotation, translation, and scale of an object in UI elements while the user is manipulating the object.


One significant problem with the built−in MouseTranslate behavior is that it always moves objects in the x−y plane. This does not cause any problems when the MouseTranslate behavior is added at the top of the scenegraph hierarchy. However, if a TransformGroup is used to rotate the scene such that the x−y plane is no longer parallel to the screen, and a child TransformGroup is added with an attached MouseTranslate behavior, the objects beneath the child TransformGroup will still move in the x−y plane and not parallel to the screen.


The Tornado mouse behaviors have built−in support for fixing this problem, and the abstract method TornadoMouseBehavior.isRelativeToObjectCoordinates controls whether object movement should compensate for TransformGroups above the TransformGroup being manipulated using the mouse. This is pretty hard to describe in words, so I suggest you run the MouseNavigateTest example and you will see that the small cube is translated along a plane parallel to the screen, even though it is a child of a TransformGroup that has itself had a rotation applied.


The Tornado mouse behaviors also put a clamp on the range of changes permitted using translation or scaling. It is very easy using the built−in behaviors to lose objects because they are either translated outside the visible world or scaled wrong (too large or too small). The TornadoMouseTranslate and TornadoMouseScale behaviors accept minimum and maximum values for translation and scaling in the x, y, and z axes and will ensure that the objects are kept within these limits. In the MouseNavigateTest example, it is not possible to translate the small cube outside of the larger cube, and scaling is permitted only between 50 and 200 percent along each axis. More explicit control over the speed of scaling, rotation, and translation is also offered, since each of the constructors accepts various scaling parameters.


Moreover, the Tornado mouse behaviors are not coded to specifically affect a TransformGroup but to accept a generic object via the setObject method. A runtime check is made on the class type of the Object, and if the object is a TransformGroup (which is typical), the TransformGroup is modified using an updated Transform3D. If the Object is not a TransformGroup, it is up to the developer to modify the Object in some way based on interface notifications or in a derived class. The classes were designed with derivation in mind and are highly customizable, so they form a good basis for application−specific derived classes.


 

Example usage of the new behaviors, from MouseNavigateTest.java