Welcome!

Java Authors: Michael Sheehan, Maureen O'Gara, Jonny Defh, Suresh Krishna Madhuvarsu, RealWire News Distribution

Related Topics: Java

Java: Article

Interactive Graphics-Based Applets

Interactive Graphics-Based Applets

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).

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.

    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.

    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).

    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.

    In addition, thepackage classand interface names are shown in 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.

  • More Stories By Christopher Currie

    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

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.