Draw
is an applet that lets you draw lines, ovals, text, and
colored circles on the screen. In addition, it allows you to connect
multiple drawing programs together over the network to provide a
collaborative drawing environment.
The program is structured as multiple object-oriented classes:
Draw
: this is the top-level class and extends the
Java Applet
class. This
class provides a top-level window and user interface. The
method Draw.init()
sets things up, after which
the Draw
class simply waits for, then processes, user events.
DrawingCanvas
: this class extends the Java Canvas
class. It provides a double-buffered drawing area using instances
backingImg
and backingGraphics
of the
Image
and Graphics
classes respectively. Mouse
and keypress events are passed to an object that does the actual drawing.
DrawObject
: this superclass and its subclasses do most of the work. A
DrawObject
processes mouse events and causes appropriate primitives
to be rendered to the DrawingCanvas
. In addition, this class provides
remote drawing support by transmitting and receiving strings which encode
other sessions' events. This class is subclassed to provide operations
such as DrawLine
, DrawCircle
, etc. Each of these
classes must implement the mouseDown()
, mouseUp()
,
mouseDrag()
, and keyPress()
methods (which are
abstract classes in the DrawObject
base class, the equivalent of pure
virtual methods in C++).
DrawConnection
: this class waits for incoming remote
drawing invocations. It extends the Java Thread
class, so it runs as a
separate thread; otherwise the Draw
applet would hang while
DrawConnection
waited for incoming data.
Draw
. The screen is divided into three
parts: a Panel
of buttons (instance variable buttonpanel
) to the left,
the DrawingCanvas
to
the right, and a Panel
of option controls on the bottom. The buttonpanel
contains buttons for the different drawing modes. The optionpanel
(an instance of Panel
)
contains a Choice
to select the color, a Checkbox
to select fill mode, a
TextField
to specify the network connection, and various Label
s.
chatServer
) does the listening on sockets to tie the Draw
applets together.
Each Draw
applet connects to the chatServer
;
each applet sends its encoded drawing events to the chatServer
,
which relays the events to all other applets.
Note: an applet running with appletviewer
can connect
to any host, but an applet running under Netscape can only connect to
the same host that is providing the applet. Thus, if you load
the applet's page locally with a path
//localhost/
..., you must specify localhost
as the hostname. However, if you load the applet's page from
foo.mit.edu
, you must run the chatServer
on
foo.mit.edu
and specify foo.mit.edu
as the
hostname.
The Draw
applet provides collaborative drawing. It does this
by sending a message to listening applets whenever a relevant low-level event,
such as a mouse movement occur. The listening applets then invoke the
appropriate event handling method on the appropriate object to cause the
display to mirror the sending applet.
Since Java doesn't provide a mechanism for one applet to invoke a method on another applet's objects, we build our own mechanism out of sockets. To invoke a method, we write a message consisting of:
DrawLine
). The constructor for each drawing object class
initializes the variable name
to this character.
mouseDown()
).
int
for
the mouse coordinates).
The methods to send these messages are in the DrawObject
class, for instance DrawObject.sendMouseDown()
. These methods
are invoked from methods in the drawing classes as necessary; for instance,
DrawLine.mouseDown()
calls sendMouseDown()
to
inform other applets of the mouse down event. These messages
are sent through the socket connection to chatServer
, which
echoes the messages to any listening Draw
applets.
The DrawConnection
class runs a separate thread that reads
from the input stream to wait for incoming messages.
For each message, DrawConnection
DrawObject.readMouseDown()
). This method reads the rest of the
arguments from the stream and then invokes the appropriate method on the
object (e.g. mouseDown()
). Finally, this method performs the
appropriate action and displays something on the screen, mirroring what
happened on the source Draw
applet.
Thus DrawObject
and DrawConnection
provide a
mechanism to marshal arguments, send a message to remote applets, unmarshal
the arguments, and invoke a method on a remote object. This mechanism is
provided automatically by distributed object frameworks such as CORBA
but since Java doesn't yet provide such as mechanism, we must do it ourselves.
To see the networked application, you will need to start two Netscape browsers. Go to the above URL in each. When each applet comes up, type lumina.lcs.mit.edu as the hostname in the applet's text dialog. Now, everything you do on one whiteboard will be replicated on the other. (Note that if others are connected to the lumina chatServer at the same time, you will see their drawing commands on your whitebaord as well, and they will see yours.)
Some style notes: we use typewriter font to denote class
names (for example Draw
) and affix parentheses
to denote methods (for example Draw.init()
).
Note that capitalization is significant, so that (for
example) the class DrawLine
is distinct
from the method Graphics.drawLine
.
addItem()
calls in
Draw.init()
to add
colors to the Choice
component. Second, update the event
handling code in Draw.action()
to do the right thing when
your new items are selected. Finally, add a setColor()
call to DrawCircle.mouseDown()
and
DrawText.mouseDown()
to make color
selection work as it does in DrawLine.mouseDown()
.
Font.PLAIN
, Font.BOLD
, Font.ITALIC
)
of text. You can use a Choice
or
a TextEntry
for string-valued input,
and Checkboxes
for selecting the style.
Add code to Draw.init()
which implements these UI
components, and add code to Draw.action()
to handle the
corresponding events. Also, add fields to Options
to
hold the new state values in your applet. Finally, in
DrawText.mouseDown()
, you'll new
a new
Font(name,style,size)
object, and call
setFont()
on the appropriate graphics
object
(of class Graphics
). Note that this approach will not
support changing fonts while in the middle of typing a string; for
extra credit, implement such a capability.
DrawObject
and DrawConnection
classes; however, you must fill in the following gaps in
functionality. First, modify DrawObject.sendOptions()
and DrawObject.readOptions()
to handle all supported
colors, and transmit some encoding of the fill flag
options.fill
. Second, modify the methods in
DrawText
to send events analogous to those sent by
DrawLine
and DrawCircle
.
DrawSpline
(analogous to, e.g., DrawLine
and
DrawCircle
) that subclasses DrawObject
; add
a button to Draw.init()
which activates it; and add code
to Draw.action()
analogous to that of
e.g. DrawLine
. The simplest approach is to collect the
positions of four mouse clicks, then draw the cubic Bezier spline
defined by these four control points. You can use
graphics.drawLine()
to draw the line segments.
For extra credit, make the current spline editable. In
DrawSpline.mouseDown()
, check whether the corresponding
position is close to that of any control point. If so, let the user
drag the control point around, while the spline is dynamically redrawn.
Note: achieving smooth regeneration of the curve without
disturbing the rest of the image will take some thought. The xor approach used
by DrawCircle
won't work well, since your spline-drawing
routine is likely to draw some pixels twice (e.g., at shared endpoints
of line segments, or where the spline self-intersects). One solution
is to save the current image before any interactive spline drawing
begins. Then, whenever you update the spline, first redraw the stored
image, then draw the spline. Here is skeleton code to do this:
class DrawSpline extends DrawObject { Image saveImg = null; public DrawSpline(...) { // Allocate image buffer of proper size saveImg = parent.createImage(parent.size().width, parent.size().height); // Tricky part: tell Draw to update our // image, rather than the screen image parent.update(saveImg.getGraphics()); } void drawTheSpline() { // redraw what was there before graphics.drawImage(saveImg,0,0,null); // render spline segments using a // series of calls to graphics.drawLine() // ... } }
drawRender
class writes a rectangular block
of pixels (some of which are transparent) into the
graphics
to realize a filled, multicolored circle.
For extra credit, extend the drawRender
class
to do something more interesting.
athena% add java
athena% cp -r /mit/imagery4/6.837/ps4 ~/6837ps4
athena% cd ~/6837ps4 athena% javac chatServer.java
athena% add java athena% cd ~/6837ps4 athena% javac Draw.java
athena% appletviewer draw.html
athena% netscape draw.html
athena% add java athena% cd ~/6837ps4 athena% javac Draw.java
athena% xtermIn the new xterm, Start the chatServer:
athena% cd ~/6837ps4 athena% java chatServer
athena% telnet localhost 4321
athena% appletviewer draw.htmlor in Netscape:
athena% netscape file://localhost/mit/uid/6837ps4/draw.html
athena% javac Draw.javaand test the applet by selecting Shift-Reload in Netscape to reload draw.html, or run appletviewer again.
athena% appletviewer draw.html
athena% turnin 4 -c 6.837 draw.html Draw.java *.class