i
i
i
i
i
i
i
i
482 19. Building Interactive Graphics Applications
(e.g., slider bars and mouse) that change the appearance on the display screen.
Thus, the specication from Section 19.1 describes items (2) and (3) from the
above list without explicitly dening what the application state is. Our job in de-
signing a solution is to derive the implicitly dened application state and design
the appropriate service routines.
Figure 19.8 presents our event-driven programming solution. As expected,
the application state (A1) is dened in SystemInitialization(). The AllWorldBalls
set and HeroBall can be derived from the specication in Section 19.1. The Defin-
ingNewHeroBall ag is a transient (temporary) application state designed to sup-
port user actions across multiple events (click-and-drag). Using transient appli-
cation states is a common approach to support consecutive inter-related events.
Figure 19.8 shows the registration of three types of service routines (A2):
user-generated application specicevents(S1a);
an application dened event (S2);
a GUI system-generated event requesting collaboration (S3c).
The timer event denition (A2S2) sets up a periodic alarm for the application to
update the simulation of the free falling balls. The service routines of the user-
generated application specic events (D1-D5) are remarkably similar to the cor-
responding case statements in the control-drivensolution presented in Figure 19.4
(B1-B3). It should not be surprising that this is so, because we are implement-
ing the exact same user actions based on the same specication. Line 3 of the
LMBDownRoutine() (D1L3) demonstrates that, when necessary, our application
can request the GUI system to initiate events. In this case, we signal the GUI
system that an application redraw is necessary. Notice that event service routines
are simply functions in our program. This means, at D1L3 we could also call
RedrawRoutine() (D7) directly. The difference is that a call to RedrawRoutine()
will force a redraw immediately while requesting the generation of a redraw event
allows the GUI system to optimize the number of redraws. For example, if the
user performs a LMB click and starts dragging immediately, with our D1 and D2
implementation, the GUI system can gather the many GenerateRedrawEvent re-
quests in a short period of time and only generate one re-draw event. In this way,
we can avoid performing more redraws than necessary.
In order to achieve a smooth animation, we should perform about 20–40 up-
dates per second. It follows that the SimulationUpdateInterval should be no more
than 50 milliseconds so that the ServiceTimer() routine can be invoked more than
20 times per second. (Notice that a redraw event is requested at the end of the
ServiceTimer() routine.) This means, at the very least, our application is guaran-
teed to receive more than 20 redraw events in one second. For this reason, the
i
i
i
i
i
i
i
i
19.2. Programming Models 483
SystemInitialization() { // (A)
// (A1): Define Application State
AllWorldBalls: A set of defined Balls, initialze to empty
HeroBall = null
DefiningNewHeroBall = false
// (A2): Register Event Service Routines
// S1a: Application Specific User Events
GUISystem::RegisterServiceRoutine(GUISystem:: LMBDown, LMBDownRoutine)
GUISystem::RegisterServiceRoutine(GUISystem:: LMBDrag, LMBDragRoutine)
GUISystem::RegisterServiceRoutine(GUISystem:: LMBUp, LMBUpRoutine)
GUISystem::RegisterServiceRoutine(GUISystem:: RMBDown, RMBDownRoutine)
GUISystem::RegisterServiceRoutine(GUISystem:: SliderBar, SliderBarRoutine)
// S2: Application Define Event
GUISystem::DefineTimerPeriod(SimulationUpdateInterval)
GUISystem::RegisterServiceRoutine(GUISystem:: TimerEvent, ServiceTimer)
// Triggers TimerEvent every: SimulationUpdateInterval period
// S3c: Honor collaboration request from the GUI system
GUISystem::RegisterServiceRoutine(GUISystem:: RedrawEvent, RedrawRoutine)
}
// Event Service Routines (D)
LMBDownRoutine( mousePosition ) // D1: Left Mouse Button Down service routine
HeroBall = CreateHeroBall (mousePosition)
DefiningNewHeroBall = true
GUISystem::GenerateRedrawEvent
LMBDragRoutine( mousePosition ) // D2: Left Mouse Button Drag service routine
RefineRadiusAndVelocityOfHeroBall( mousePosition )
SetSliderBarsWithHeroBallVelocity()
GUISystem::GenerateRedrawEvent // Generates a redraw event
LMBUpRoutine( mousePosition ) // D3: Left Mouse Button Up service routine
InsertHeroBallToAllWorldBalls()
DefiningNewHeroBall = false
RMBDownRoutine ( mousePosition ) // D4: Right Mouse Button Down service routine
HeroBall = SelectHeroBallBasedOn (mousePosition )
if (HeroBall != null) SetSliderBarsWithHeroBallVelocity()
SliderBarRoutine ( sliderBarValues ) // D5: Slider Bar changes service routine
if (HeroBall != null)
SetSliderBarsWithHeroBallVelocity( sliderBarValues )
ServiceTimer ( ) // D6: Timer expired service routine
UpdateSimulation( ) // Move balls by velocities and remove off
-screen ones
EchoToStatusBar( ) // Sets status bar with number of balls on screen
GUISystem:: GenerateRedrawEvent // Generates a redraw event
if (HeroBall != null) // Reflect propoer HeroBall velocity
SetSliderBarsWithHeroBallVelocity( sliderBarValues )
RedrawRoutine ( ) // D7:Redraw event service routine
DrawBalls(AllWorldBalls)
if (DefiningNewHeroBall)
DrawBalls(HeroBall) // Draw the new Hero Ball that is being defined
A2S2: Defines
a Timer Event
D1L3: Force a
Redraw Event
A1: Application
State
Figure 19.8. Programming solution based on the event-driven programming model.
i
i
i
i
i
i
i
i
484 19. Building Interactive Graphics Applications
GenerateRedrawEvent requests in D1 and D2 are really not necessary. The ser-
vicing of our timer events will guarantee us an up-to-date display screen at all
times.
19.2.4 Implementation Notes
The application state of an event-driven program must persist over the entire life
time of the program. In terms of implementation, this means that the application
state must be dened based on variables that are dynamically allocated during run
time and that reside on the heap memory. These are in contrast to local variables
that reside on the stack memory and which do not persist over different function
invocations.
The mapping of user actions to events in the GUI system often results in im-
plicit and/or undened events. In our ball shooting program, the actions to dene
a HeroBall involve left mouse button down and drag. When mapping these ac-
tions to events in our implementation (in Figure 19.4 and Figure 19.8), we realize
that we should also pay attention to the implicit mouse button up event. Another
example is the HeroBall selection action: right mouse button down. In this case,
right mouse button drag and up events are not serviced by our application, and
thus, they are undened (to our application).
When one user action (e.g., “drag out the HeroBall”) is mapped to a group
of consecutive events (e.g., mouse button down, then drag, then up) a nite state
diagram can usually be derived to help design the solution. Figure 19.9 depicts
the nite state diagram for dening the HeroBall in our ball shooting program.
1
Application
State
2 3
LMB U
p
LMB Dra
g
LMB Up
LMB Dra
g
LMB Dow
n
Define
HeroBall
Center
Define
HeroBall
Velocity &
Size
Done
Defining
HeroBall
Service
Routines
LMBUpRoutine()LMBDragRoutine()LMBDownRoutine()
Figure 19.9. State diagram for defining the HeroBall.
i
i
i
i
i
i
i
i
19.2. Programming Models 485
The left mouse button down event puts the program into State 1 where, in our
solution from Figure 19.8, LMBDownRoutine() implements this state and denes
the center of the HeroBall, etc. In this case the transition between states is trig-
gered by the mouse events, and we see that it is physically impossible to move
from State 2 back to State 1. However, we do need to handle the case where the
user action causes a transition from State 1 to State 3 directly (mouse button down
and release without any dragging actions). This state diagram helps us analyze
possible combinations of state transitions and perform appropriate initializations.
Event-drivenapplications interface with the user throughphysical (e.g., mouse
clicks) or simulated GUI elements (e.g., quit button, slider bars). An input GUI
element (e.g., the quit button) is an artifact (e.g., an icon) for the user to direct
changes to the application state, while an output GUI element (e.g., the status
bar) is an avenue for the application to present application state information to
the user as feedback. For both types of elements, information only ows in one
direction—either from the user to the application (input) or from the application
to the user (output). When working with GUI elements that serve both input and
output purposes, special care is required. For example, after the user selects or
denes a HeroBall, the slider bars reects the velocity of the free falling HeroBall
(output), while at any time, the user can manipulate the slider bar to alter the Her-
oBall velocity (input). In this case, the GUI element’s displayed state and the ap-
plication’s internal state are connected. The applicationmust ensure that these two
states are consistent. Notice that in the solution shown in Figure 19.4, this state
consistency is not maintained. When a user clicks the RMB (B2 in Figure 19.4)
to select a HeroBall, the slider bar values are updated properly; however, as the
HeroBall free falls under gravity, the slider bar values are not updated. The so-
lution presented in Figure 19.8 xes this problem by using the ServiceTimer()
function.
Event service routines are functions dened in our program that cause a call-
back from the MainEventLoop in the presence of relevant events. For this reason,
these service routines are also referred to as callback functions. The application
program registers callback functions with the GUI system by passing the address
of the function to the GUI system. This is the registration mechanism implied in
Figure 19.7 and Figure 19.8. Simple GUI systems (e.g., GLUT or FLTK) usually
support this form of registration mechanism. The advantage of this mechanism is
that it is easy to understand, straightforward to program, and often contributes to
a small memory footprint in the resulting program. The main disadvantage of this
mechanism is the lack of organizational structure for the callback functions.
In commercial GUI systems, there are a large numbers of events with which
user applications must deal, and a structured organization of the service routines
can assist the programmability of the GUI system. Modern commercial GUI sys-
i
i
i
i
i
i
i
i
486 19. Building Interactive Graphics Applications
tems are often implemented based on object-oriented languages (e.g., C++ for
MFC, Java for Java Swing). For these systems, many event service registrations
are implemented as sub-classes of an appropriate GUI system class, and they over-
ride corresponding virtual functions. In this way, the event service routines are
organized according to the functionality of GUI elements. The details of different
registration mechanisms will be explained in Section 19.4.1 when we describe the
implementation details.
Event service routines (or callback functions) are simply functions in our pro-
gram. However, these functions also serve the important role as the server of
external asynchronous events. The following are guidelines one should take into
account when implementing event service routines:
1. An event service routine should only service the triggering event and imme-
diately return the control back to the MainEventLoop(). This may seem to
be a “no-brainer. However, because of our familiarity with control-driven
programming, it is often tempting to anticipate/poll subsequent events with
a control structure in the service routine. For example, when servicing the
left mouse button down event, we know that the mouse drag event will hap-
pen next. After allocating and dening the circle center, we have properly
initialized data to work with the HeroBall object. It may seem easier to
simply include a while loop to poll and service mouse drag events. How-
ever, with all the other external events that may happen (e.g., timer event,
external redraw events, etc.), this monopolizing of control in one service
routine is not only a bad design decision, but also it may cause the program
to malfunction.
2. An event service routine should be stateless, and individual invocations
should be independent. In terms of implementation, this essentially means
event service routines should not dene local static variables that record
data from previous invocations. Because we have no control over when,
or how often, events are triggered, when these variables are used as data,
or conditions for changing application states, it can easily lead to disas-
trously and unnecessarily complex solutions. We can always dene extra
state variables in the application state to record temporary state information
that must persist over multiple event services. The DefiningNewHeroBall
ag in Figure 19.8 is one such example.
3. An event service routine should check for invocation conditions regard-
less of common sense logical sequence. For example, although logically,
a mouse drag event can never happen unless a mouse down event has al-
ready occurred, in reality, a user may depress a mouse button from outside
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.100.205