HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

This article will show you how to create an interactive, graphics-based applet by designing only the graphical objects themselves and by specifying their behavior given some external event (i.e., a mouse click or a button press). The ipl and ipl.graphics classes and interfaces will let you design with both notification and interfaces, in order to coordinate communication between your graphical objects and between the graphical objects and the display medium (i.e., a canvas object). This article will also show you how to "wire-up" your interactive applet to buttons in order to give the user control over your graphical objects.

I will assume that you are familiar with the Observer-Observable design pattern of notification. The notification scheme used here is adopted from Code and Mayfield's "Java Design: Building Better Apps & Applets" (1997). The graphical object management scheme is adopted from Todd Sundsted's "HotSpot" object-oriented drawing program.

Introduction
Writing interactive, graphics-based applets requires, at a minimum, creating both graphic objects and a display object. More work is required if you want to implement double buffering to eliminate flashing and if you want to implement some sort of notification scheme, because a change in one graphical object will require a change in another graphical object. Furthermore, keeping track of many graphical objects usually requires the implementation of some sort of a management scheme.

This article shows you how to create an interactive, graphics-based applet where you only need to design what your graphic objects will look like and how they will react to some external event (i.e., a mouse click or a GUI button press). It will do this by presenting a working interactive applet and then, step-by-step, exploring the class design and objects that support the applet. The article will explore package classes and interfaces (i.e., ipl.* and ipl.graphics.*) that will let you design with both notification and interfaces in order to coordinate communication between the graphical objects and between the graphical objects and a canvas object. This article will also show you how to "wire-up" an interactive applet to buttons in order to give users control over the graphical objects. Finally, the package classes and interfaces introduced in the article will provide support for basic computer graphics operations (e.g., object management, double buffering, detecting mouse events inside a complex graphical object). You will not only learn how to manage multiple graphics objects within a notification design scheme and how to make them responsive to external events (i.e., button and mouse events), you will also walk away with a package of classes and interfaces designed to support these capabilities.

Applet Specification
We are going to create an applet that will let users explore a visual illusion known as MÜller-Lyer (see Figure 1).

Figure 1
Figure 1:

The idea is to let users manipulate the bottom comparison line in order to make it as long as the top standard line. As you can see, the applet will contain many graphical objects: 1) a graphical object that specifics and controls the standard line; 2) one that specifies and controls the comparison line; and 3) two objects that control the standard line's and comparison line's arrowheads. Moreover, these graphical objects will behave in specific ways depending on the occurrence of certain external events. Specifically, we want:

  • the two standard line arrowheads to toggle on and off if a mouse down event is detected near either end of the standard line
  • the two comparison line arrowheads to toggle on and off if a mouse down event is detected near either end of the comparison line
  • the comparison line to change its length in response to a set of buttons
  • the two comparison line arrowheads to change the angle in response to a set of buttons
  • the comparison line to turn green if it is equal in length to the standard line or red if it is not
  • the comparison line to move up and down when "dragged" by a mouse
  • the comparison line to reset to a "random" length in response to a button

Observer-Observable Notification Design Pattern
The above interactivity is achieved through the creation of Observer and Observable objects. Most of your graphical objects will be observable objects. A canvas object will be an observer object. This observer object will "observe" the graphical objects (i.e., "observable" objects). As each graphical object responds to external events (i.e., the mouse, a GUI button), they will change themselves and then "notify" the canvas object that it must update (re-draw all the graphical objects) to reflect the change.

The ipl.graphics.Shape Class
The first thing you will do is design your graphical objects, how they will look and how they will act. All graphic objects will be an extension of an abstract ipl.graphics.Shape class. Therefore, you will need to define the methods in Table 1.

Table 1

When you create a graphical object, you will need to decide what it will look like and how it will react to an external event (i.e., a mouse event or a button event). For example, the StandardLineShape class extends the ipl.graphics.Shape class. The constructor initializes the graphical object (i.e., it defines a data structure and assigns initial values - note that ipl.graphics.LineSegment is a very useful data structure for line-based graphics).

In the StandardLineShape constructor you may have noticed the RegisterObject. This object is not part of the ipl.graphics package, but it is a useful device for letting the graphical objects be "aware of" other graphical objects. The StandardLineShape's constructor, "registers" the left and right end points of the standard line with the RegisterObject. In this way, as we will see shortly, the arrowheads of the standard line (themselves a separate graphic object) will use the RegisterObject to position themselves on each end of the standard line.

Next, you will need to define ipl.graphics.Shape's drawMe method to specify what your graphics object will look like. A canvas object - which we will examine in a moment - will call this method whenever the canvas needs to be updated. The drawMe method will receive from the canvas a Graphics object (actually a handle to an off screen buffer, but you need not concern yourself with that). For example, StandardLineShape's drawMe method specifies what the standard line looks like.

(Note: in StandardLineShape's drawMe method, you saw a call to the ToDisplaySpace. This method is defined in the abstract ipl.graphics.Shape class. Typically, when you design your graphical object, you will often build it in a coordinate system that assumes an origin in the left hand bottom corner of a canvas component. I call this "model space." However, when dealing with a canvas object, just the opposite is true! I call the canvas coordinate system "display space." By using ToDisplaySpace, you can build and maintain your model or graphical object in model space, but when it comes time to display your graphical object, just transform the model y-coordinates to display space coordinates using this method).

Once your graphics object is defined, you need to instantiate it in your main applet and tell it how big your canvas area is. Listing 1 is a snippet from the constructor of the main IllusionApplet applet.

The ipl.graphics.Display, ipl.IObserver Interface and ipl.graphics.ShapeMrg Classes
At this point, you need to understand two ipl.graphics classes and an interface. The first, ipl.graphics.Display, handles all double buffering to avoid any flicker and it calls the drawMe method of each graphical object. (It is sensitive to mouse events because it uses an event listener, ipl.graphics.DisplayListener). It also implements the ipl.IObserver interface, which means it is the observer for all the observables (i.e., the graphical objects). Observables notify observers if they change. The second class, ipl.graphics.ShapeMrg, simply maintains a vector of Shape objects (The ShapeMrg object also passes mouse events and external button events along to all the graphical objects. You'll read more about this below). While these objects sound complicated, all you need to do in your main applet is instantiate them. Specifically, after you create all your graphical objects, simply hand them off to the ShapeMrg object. After that, instantiate the Display object (providing the desired width and height for the canvas), and hand the ShapeMrg off to the Display object.

By now you've probably noticed that the StandardLineShape is not an Observable object. This is because it never changes and thus, it never needs to notify the Display object of a change. Nevertheless, when the Display object gets the call to update itself, it will use the ShapeMrg to access all of graphical object's drawMe methods. In this way, StandardLineShape's drawMe will get called, and that shape will be drawn on the canvas. Below we will see how to make a class that extends the Shape object an Observable object and how such an object "notifies" the Display object given a change.

The ipl.IObservable Interface
The StdArrowHeadShape class extends the abstract Shape class and implements the ipl.IObservable interface. Recall that we want to toggle the arrowheads on and off if a mouse down event is detected near either end of the standard line (which is where the arrowheads are attached). If the arrowheads are visible and if a mouse down event is detected near either end of the standard line, the StdArrowHeadShape object must "turn off" its arrowhead and then inform the canvas object that a change has occurred and that the display must be updated. In order for this to happen, StdArrowHeadShape must implement the IObservable interface.

When you implement the IObservable interface, you will need to define the three methods in Table 2.

Table 2

So, how do you define these methods? Easy! Just use the code in Listing 2 in any Shape object that implements this interface. The ipl.ObservableComponent object simply maintains a vector of observer objects--don't worry about this object, just instantiate it in your Observable object and use the code in Listing 2 as is. Bounding Areas - Defining areas of "activation" where users will interact with your graphical objects

Recall that we want to detect a mouse down event on either end of the standard line (where the standard arrowheads will be attached). To do this, we can use the ipl.graphics.BoundingArea to create an "active" area on the canvas that will signal a mouse event. (While the bounding areas used in this applet are quite straightforward, the virtue of this object is that just about any complex bounding area can be created with a sufficient number of straight lines. The BoundingArea object has methods to detect whether or not a mouse event occurred within or on just about any boundary area that you create. Specifically, we are going to define two square areas (each side 10 pixels wide) and position them such that they are centered on each endpoint of the standard line (see Figure 2).

Figure 2
Figure 2:

The private method createBoundingArea, in the StdArrowHeadShape class defines two boundary areas (i.e., baLeft and baRight). The Display object will detect all mouse events. It will pass the event along to the ShapeMrg object which will call the public void mouseDetect method of each graphical (i.e., Shape) object. Each graphical object then decides what to do with the event. The StandardLineShape graphical object will simply ignore the event. However, the mouseDetect method of the StdArrowHeadShape graphical object needs to toggle its arrowheads on and off--note that the call to getActivation in the mouseDetect method is defined in the abstract Shape class. After the StdArrowHeadShape object detects a mouse down event in either area of activation, it notifies the Display object that it has changed. The Display object then calls the drawMe method of each Shape object that implements the Observable interface. StdArrowHeadShape's drawMe method toggles the arrowheads on and off.

How do we let the StdArrowHeadShape object know that the Display object will be its observer? This is done in the main applet. In the main applet, the addIObserver method of the StdArrowHeadShape object called with the Display object as an argument.

The ipl.IRepeat interface
Whenever the "Increase line" or "Decrease line" button is pressed, not only will the comparison line change its length, its arrowheads will change their location in the display. This means that the comparison line object must notify NOT the Display object, but rather it needs to notify the comparison line arrowheads object! This object (CmpArrowHeadShape) then repositions the arrowheads and then the CmpArrowHeadShape object notifies the Display object. The ipl.IRepeat interface is used to make sure that the applet is not updated until ALL the graphical objects have changed themselves. In short, by implementing the ipl.IRepeat interface, the CmpArrowHeadShape object is both an Observable and an Observer. So how do you implement the methods of this interface? Easy! Just use the code in Listing 3 in any Shape class that implements this interface (see CmpArrowHeadShape class).

So, the Display object will observe the CmpArrowHeadShape object to see if there is any change. Again, how do we let the CmpArrowHeadShape object know that the Display object will be its observer? This is done in the main IllusionApplet class. In the main applet, the addIObserver method of the CmpArrowHeadShape object called with the Display object as an argument.

But when do the comparison line arrowheads change? When the comparison line itself either increases or decreases, or changes its vertical position. The object that defines the comparison line, the ComparisonLineShape object, builds the comparison line graphical object in much the same way as the StandardLineShape builds the standard line graphical object. Like the standard line object, it too is an observable object, but its observer is the CmpArrowHeadShape object, not the Display object. When the comparison line object changes, it notifies the CmpArrowHeadShape object. The CmpArrowHeadShape object then changes the position of the arrowheads, and then it notifies the Display object that there has been a change in the display. In the main IllusionApplet class, we tell the ComparisonLineShape object that its observer is the CmpArrowHeadShape object by calling the addIObserver method of the ComparisonLineShape object and sending it the CmpArrowHeadShape object as an argument.

External Button Control and The Comparison Line
The ComparisonLineShape object will respond to both a mouse and an external button. The external button that controls the length of the comparison line is an independent applet (LengthButtonApplet), and it uses the standard applet-to-applet communication mechanism provided by Java to establish communication with your main applet. I'll assume that you know about this technique so I won't go into it here. If a mouse event is detected by a button applet, it will contact the main IllusionApplet applet by calling a ButtonDetect method in the main IllusionApplet. The main IllusionApplet then passes the button label to the ShapeMrg object. The ShapeMrg object dispatches the button label to all the graphical objects. A graphical object then either responds the button event, changes itself and notifies the Display object, or it ignores the event. Note that all the other buttons work similarly. So how does the ComparisonLineShape object receive the button event? Through the implement abstract Shape method buttonDetect.

Conclusion
The package classes and interfaces reviewed in this article facilitate the construction of interactive graphics based applets by:

  • incorporating the notification design pattern by using observable and observer objects in order to coordinate and control dynamic interaction between the graphical objects
  • incorporating objects that define areas of activation in your graphic objects and the means of detecting mouse activity in those areas
  • incorporating a canvas object that uses double buffering to eliminate flicker and a manager to keep track of your graphics objects

With these package classes and interfaces, along with the visual illusion applet classes as a tutorial, you can now design interactive applets where the only code you provide is what your graphic objects will look like and how they will interact with each other and the user. Bon courage!

The class names for the interactive Muller-Lyer applet are shown in Table 3.

Table 3

In addition, thepackage classand interface names are shown in Table 4.

Table 4

References

  1. IObservable, IObserver and IRepeat interface from Java Design: Building Better Apps & Applets by P. Coad & M. Mayfield, Yourdon Press Computer Series, Prentice Hall, 1997.
  2. Public Point intersect(LineSegment Line 1 and line 2) method in the Toolbox class from "Faster LIne Segment Intersection" by Franklin Antonio from Graphics Gems, Academic Press, 1990.
  3. Public pointUsOnLineSegement in the LineSegment Class from "A Fast 2D Point-On-Line Test" by Alan Paeth from Graphics Gems, Academic Press, 1990.
About the Author
Christopher Currie is a doctoral candidate in the Experimental Psychology Program, Human Perception and Performance Group in the Beckman Institute For Advanced Science and Technology at the University of Illinois, Urbana-Champaign. In addition to his research involving eye-movements and scene perception, he is involved in a Web-based project designed to provide laboratory experience to complement psychology lecture courses and to educate the general public about sensation, perception and cognition. Support of the development of the package classes presented in this article was provided by the "Internet Psychology Laboratory" project (http://kahuna.psych.uiuc.edu/ipl2) and the University of Illinois at Urbana-Champaign.Chris can be reached at [email protected]

Download files assoicated with this article
- source code only (44 KB)

Download files assoicated with this article
- source code and HTML table of contents (86 KB)

	

Listing 1. 

         public class IllusionApplet extends Applet{  
             private int canvasWidth, canvasHeight;  
             private RegisterObject ro; 
             /* Shapes (models or observables) used  
              in this applet */  
             private StandardLineShape stdLine;  

             public void init(){  
             // this object helps coordinates & 
                 between shapes  
             ro = new RegisterObject 
              (lineThicknessInPixels);  

             /* create models or observables */  
             stdLine = new  
StandardLineShape(canvasWidth, canvasHeight, ro);  
             } 
  

Listing 2. 

        // methods / public / IObservable implementation  
        public void addIObserver(IObserver anIObserver) {  
        myObservableComponent.addIObserver(anIObserver);  
        }  

        public void deleteIObserver(IObserver anIObserver) {  
        myObservableComponent.deleteIObserver(anIObserver);  
        }  

        public void deleteIObservers() {  
        myObservableComponent.deleteIObservers();  
        } 

Listing 3. 

        // IObservable implementation  
        public void addIObserver(IObserver anIObserver) {  
            myObservableComponent.addIObserver & 
          (anIObserver); }  

        public void deleteIObserver(IObserver anIObserver) {  
            myObservableComponent.deleteIObserver & 
          (anIObserver); }  

        public void deleteIObservers() {  
            myObservableComponent.deleteIObservers(); }  

        // IObserver implementation  
        public void update(Object theObserved,  & 
          Object changeCode){  
           updateMe();  
           myObservableComponent.notifyIObservers & 
            (theObserved,changeCode);  
        } 
  
  

©Board of Trustees, University of Illinois, 1997. Information concerning  use of 
these classes and interfaces  can be obtained from the Department  of 
Psychology, University of Illinois, 603 E. Daniel, Champaign, Illinois 61801. 
Redistribution and  use, with and without modifications, are permitted for any 
purpose provided that  the source code retain the above copyright notice.  All 
publications  or advertising materials mentioning features or use of this 
software must acknowledge that it was developed by the University of Illinois at 
Urbana-Champaign and credit the contributors. THIS SOFTWARE IS PROVIDED BY THE 
UNIVERSITY AND THE CONTRIBUTORS “AS IS” WITH NO WARRANTY OF ANY KIND, EITHER 
EXPRESSED OR IMPLIED. In no event shall the University or the contributors be 
liable for any damages suffered by the users arising out of the  use of this 
software, even if advised of the possibility of such damage. 




 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.