TweetFollow Us on Twitter

Feb 97 Getting Started

Volume Number: 13 (1997)
Issue Number: 2
Column Tag: Getting Started

The ShapeWorld Applet

By Dave Mark

A few months ago I promised an applet that did double-buffered animation. When I started work on the sample applet, I realized that I was definitely jumping the gun and there was still a lot of ground to cover to get to double-buffering. Since then, I've explored the AWT event-handling mechanism, as well as the various shape drawing routines in the Graphics class.

This month I am going to bring these concepts (and a few new ones) together into an applet called ShapeWorld. This month's version of ShapeWorld (I'll extend it next month) randomly scatters some rectangles into a Canvas, then responds to mouse clicks by selecting and deselecting the rectangles. As you'll see next month, ShapeWorld was designed to be extended. For now, let's get the first version up and running.

The ShapeWorld Project

Launch the CodeWarrior IDE and create a new project named ShapeWorld.µ using the Java applet stationery. Create a new source file named ShapeWorld.java and add it to the project. Here's the ShapeWorld.java source code:

import java.awt.*;
import java.util.*;

abstract class Shape
{
 booleanhighlighted;
 ShapeCanvasshapeCanvas;
 int    shapeX, shapeY, shapeWidth, shapeHeight;
 RectangleboundsRect;
 Shape( ShapeCanvas canv, int x, int y,
 int width, int height )
 {
 shapeCanvas = canv;
 highlighted = false;
 
 shapeX = x;
 shapeY = y;
 shapeWidth = width;
 shapeHeight = height;
 boundsRect = new Rectangle( x, y, width, height );
 }
 
 abstract public void draw( Graphics g );
 
 public void setHighlight( boolean newHighlight )
 {
 highlighted = newHighlight;
 }
 
 public boolean isHighlighted()
 {
 return highlighted;
 }
 
 public boolean isPointInShape( int x, int y )
 {
 return boundsRect.inside( x, y );
 }
}

class RectShape extends Shape
{
 RectShape( ShapeCanvas canv, int x, int y,
 int width, int height )
 {
 super( canv, x, y, width, height );
 }
 
 public void draw( Graphics g )
 {
 if ( isHighlighted() )
 {
 g.setColor( Color.black );
 g.fillRect( shapeX, shapeY, shapeWidth, shapeHeight );
 g.setColor( Color.red );
 g.fillRect( shapeX+2, shapeY+2,
 shapeWidth-4, shapeHeight-4 );
 }
 else
 {
 g.setColor( Color.red );
 g.fillRect( shapeX, shapeY, shapeWidth, shapeHeight );
 }
 }
}

class ShapeCanvas extends Canvas
{
 Vector shapes;
 Shape  curShape;
 
 ShapeCanvas( int width, int height )
 {
 shapes = new Vector();
 curShape = null;
 
 setBackground( Color.yellow );
 
 resize( width, height );
 }
 
 public void addShape( Shape newShape )
 {
 shapes.addElement( newShape );
 }
 
 public void paint( Graphics g )
 {
 for ( Enumeration e = shapes.elements(); 
 e.hasMoreElements(); )
 {
 Shape s = (Shape)e.nextElement();
 s.draw( g );
 }
 }
 
 public Shape findInShapeList( int x, int y )
 {
 for ( Enumeration e = shapes.elements(); 
 e.hasMoreElements(); )
 {
 Shape s = (Shape)e.nextElement();
 
 if ( s.isPointInShape( x, y ) )
 {
 s.setHighlight( ! s.isHighlighted() );
 s.draw( getGraphics() );

 return s;
 }
 }
 
 return null;
 }
 
 public void update (Graphics g)
 {
   paint(g);
 }
 
 public boolean mouseDown( Event e, int x, int y )
 {
 curShape = findInShapeList( x, y );
 
 return true;
 }
}

public class ShapeWorld extends java.applet.Applet
{
 ShapeCanvassCanvas;
 final intshapeWidth = 20;
 final intshapeHeight = 20;
 
 public void init()
 {
 int    x, y;
 
 sCanvas = new ShapeCanvas( 440, 290 );
 add( sCanvas );
 
 Random ran = new Random();
 Rectangle b = sCanvas.bounds();
 
 for ( int i=1; i<=10; i++ )
 {
 x = b.x + (int)((float)(b.width) * ran.nextFloat() );
 if ( x > b.x + b.width - shapeWidth )
 x -= shapeWidth;
 
 y = b.y + (int)((float)(b.height) * ran.nextFloat() );
 if ( y > b.y + b.height - shapeHeight )
 y -= shapeHeight;
 
 RectShape r = new RectShape( sCanvas, x, y, 
 shapeWidth, shapeHeight );
 sCanvas.addShape( r );
 }
 }
}

Save your typing, then create a new source code file named ShapeWorld.html and add it to the project. Here's the html:

<title>ShapeWorld</title>
<hr>
<applet codebase="ShapeWorld Classes" code="ShapeWorld.class" width=500 
height=300>
</applet>
<hr>
<a href="ShapeWorld.java">The source.</a>

Running the ShapeWorld Applet

Once your source and html are entered and saved, run the ShapeWorld applet. Figure 1 shows my ShapeWorld applet running in the Metrowerks Java applet runner. The ten shapes all appear in red on a Canvas with a background color of yellow. The shapes are randomly distributed throughout the Canvas.

Figure 1. The ShapeWorld applet with no shapes selected.

Figure 2 shows what happens when I click the mouse inside a shape. In this case, I've selected six of the ten shapes. A two pixel border is used to mark a shape as selected.

Figure 2. The ShapeWorld applet with six shapes selected.

The ShapeWorld Source

ShapeWorld creates four classes. ShapeWorld is the main entry point and extends the java.applet.Applet class. The ShapeCanvas class implements the Canvas. The Shape class is an abstract class which means that I won't be creating any Shape objects. The RectShape class extends the Shape class and is used to create the ten shapes you see in the ShapeWorld Canvas.

As you look through the Shape class, remember that all the variables and functions will be inherited by any classes that extend Shape. highlighted is set to true if the shape is selected, false otherwise. shapeCanvas is the enclosing Canvas. shapeX, shapeY, shapeWidth, and shapeHeight define the boundries of this Shape. boundsRect does the exact same thing, but stores this same info in a Rectangle instead of in individual variables. Why the duplication? With both forms available, I don't have to spend time converting from one form to the other. This might seem like a frivolous waste of memory, but when we are stepping through a large number of shapes, perhaps during a redraw cycle, the little time we save can make a difference.

import java.awt.*;
import java.util.*;

abstract class Shape
{
 booleanhighlighted;
 ShapeCanvasshapeCanvas;
 int    shapeX, shapeY, shapeWidth, shapeHeight;
 RectangleboundsRect;
 

The Shape constructor initializes the Shape variables. The draw() function is declared abstract, which means it must be overridden by any class that extends Shape. Note that an abstract function doesn't have a function body. setHighlight() sets highlighted to a new value. isHighlighted() returns the value of highlighted. Finally, isPointInShape() takes advantage of the Rectangle classes' inside() function to tell you if the specified point is inside the Shape's bounding Rectangle.

 Shape( ShapeCanvas canv, int x, int y,
 int width, int height )
 {
 shapeCanvas = canv;
 highlighted = false;
 
 shapeX = x;
 shapeY = y;
 shapeWidth = width;
 shapeHeight = height;
 
 boundsRect = new Rectangle( x, y, width, height );
 }
 
 abstract public void draw( Graphics g );
 
 public void setHighlight( boolean newHighlight )
 {
 highlighted = newHighlight;
 }
 
 public boolean isHighlighted()
 {
 return highlighted;
 }
 
 public boolean isPointInShape( int x, int y )
 {
 return boundsRect.inside( x, y );
 }
}

RectShape extends the Shape class. The RectShape constructor just passes on its parameters to its superclass constructor, which is the Shape constructor. If the shape is selected, the draw() function draws a black rectangle, insets the rectangle by two pixels on each side, then draws a red rectangle. I could have used two calls to drawRect() to frame the black rectangle followed by a call to fillRect() to fill in the red, but this made the code easier to read.

class RectShape extends Shape
{
 RectShape( ShapeCanvas canv, int x, int y,
 int width, int height )
 {
 super( canv, x, y, width, height );
 }
 
 public void draw( Graphics g )
 {
 if ( isHighlighted() )
 {
 g.setColor( Color.black );
 g.fillRect( shapeX, shapeY, shapeWidth, shapeHeight );
 g.setColor( Color.red );
 g.fillRect( shapeX+2, shapeY+2,
 shapeWidth-4, shapeHeight-4 );
 }
 else
 {
 g.setColor( Color.red );
 g.fillRect( shapeX, shapeY, shapeWidth, shapeHeight );
 }
 }
}

The ShapeCanvas class is my main drawing area. The shapes variable is of the java.util.Vector class. A Vector implements a growable array of objects. I can access the objects via random access (using an integer index) or by stepping through the list. Since I want to tie a list of shapes to my Canvas, a Vector is a natural structure in which to store references to the Shapes. Spend some time reading the java.util.Vector doc to truly appreciate the value of the Vector. In addition, you should read about the Enumeration interface (on the java.util page) that the Vector class implements. The Enumeration interface allows you to step through your Vector using the functions hasMoreElements() and nextElement().

class ShapeCanvas extends Canvas
{
 Vector shapes;
 Shape  curShape;

The ShapeCanvas() constructor creates a new Vector and sets curShape to null. I won't make use of curShape this month, but I will use it next month to keep track of the last selected Shape. The constructor also sets the background color and changes the Canvas' size.

 ShapeCanvas( int width, int height )
 {
 shapes = new Vector();
 curShape = null;
 
 setBackground( Color.yellow );
 
 resize( width, height );
 }

addShape() adds a shape to the Vector. paint() steps through the shapes Vector, retrieving the current Shape and calling its draw() method. This method of stepping through a Vector is extremely useful. Note that nextElement() returns an Object, so I needed to cast the returned value to Shape. By retrieving a Shape instead of a specific derived class such as RectShape(), I can use this code to draw any Shapes stored in the list, no matter the type, as long as each shape is derived from Shape.

 public void addShape( Shape newShape )
 {
 shapes.addElement( newShape );
 }
 
 public void paint( Graphics g )
 {
 for ( Enumeration e = shapes.elements(); 
 e.hasMoreElements(); )
 {
 Shape s = (Shape)e.nextElement();
 s.draw( g );
 }
 }

findInShapeList() also steps through the shapes Vector. In each case, I call each Shape's isPointInShape() function to see if the specified point is in the current Shape. If the point was in the Shape, I flip highlighted and redraw the Shape (since it is now selected.)

 public Shape findInShapeList( int x, int y )
 {
 for ( Enumeration e = shapes.elements(); 
 e.hasMoreElements(); )
 {
 Shape s = (Shape)e.nextElement();
 
 if ( s.isPointInShape( x, y ) )
 {
 s.setHighlight( ! s.isHighlighted() );
 s.draw( getGraphics() );

 return s;
 }
 }
 
 return null;
 }

The update() function calls paint(). The default update() (inherited from the Component class) erases the area to be updated and then calls paint(). By calling paint() without the erase, I avoid an annoying flicker. This trick is well worth remembering. To truly understand what it does, comment out the entire update() method (not just the call to paint() which will prevent drawing from occuring at all!!!)

 public void update (Graphics g)
 {
   paint(g);
 }

The mouseDown() function gets called when I click the mouse in the Canvas. At the moment, I don't do anything interesting with the Shape returned by findInShapeList(), but I will next month.

 public boolean mouseDown( Event e, int x, int y )
 {
 curShape = findInShapeList( x, y );
 
 return true;
 }
}

The last thing I want to point out this month is the use of the Random class. ShapeWorld creates 10 new RectShapes (remember, you can't actually create a Shape since you can't create an instance of an abstract class) using the random values returned by Random(). Random is described on the page java.util.Random. Be sure to check out that page of documentation, as it describes a variety of random number distributions and returned types.

public class ShapeWorld extends java.applet.Applet
{
 ShapeCanvassCanvas;
 final intshapeWidth = 20;
 final intshapeHeight = 20;
 
 public void init()
 {
 int    x, y;
 
 sCanvas = new ShapeCanvas( 440, 290 );
 add( sCanvas );
 Random ran = new Random();
 Rectangle b = sCanvas.bounds();
 
 for ( int i=1; i<=10; i++ )
 {
 x = b.x + (int)((float)(b.width) * ran.nextFloat() );
 if ( x > b.x + b.width - shapeWidth )
 x -= shapeWidth;
 
 y = b.y + (int)((float)(b.height) * ran.nextFloat() );
 if ( y > b.y + b.height - shapeHeight )
 y -= shapeHeight;
 
 RectShape r = new RectShape( sCanvas, x, y, 
 shapeWidth, shapeHeight );
 sCanvas.addShape( r );
 }
 }
}

Till Next Month...

As promised, next month I'll add some new functionality to ShapeWorld. Before you peek at next month's column, try your hand at adding a new Shape subclass to ShapeWorld. Experiment. Read the doc. I'll see you then...

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Things 2.5.4 - Elegant personal task man...
Things is a task management solution that helps to organize your tasks in an elegant and intuitive way. Things combines powerful features with simplicity through the use of tags and its intelligent... Read more
NeoOffice 2014.10 - Mac-tailored, OpenOf...
NeoOffice is a complete office suite for OS X. With NeoOffice, users can view, edit, and save OpenOffice documents, PDF files, and most Microsoft Word, Excel, and PowerPoint documents. NeoOffice 3.x... Read more
iPhoto Library Manager 4.2 - Manage mult...
iPhoto Library Manager allows you to organize your photos among multiple iPhoto libraries, rather than having to store all of your photos in one giant library. You can browse the photos in all your... Read more
Web Snapper 3.3.8 - Capture entire Web p...
Web Snapper lets you capture Web pages exactly as they appear in your browser. You can send them to a file as images or vector-based, multi-page PDFs. It captures the whole Web page - eliminating the... Read more
TeamViewer 10.0.41404 - Establish remote...
TeamViewer gives you remote control of any computer or Mac over the Internet within seconds, or can be used for online meetings. Find out why more than 200 million users trust TeamViewer! Free for... Read more
Ableton Live 9.1.8 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
VOX 2.5 - Music player that supports man...
VOX is a beautiful music player that supports many filetypes. The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features & support for... Read more
OmniFocus 2.1.2 - GTD task manager with...
OmniFocus helps you manage your tasks the way that you want, freeing you to focus your attention on the things that matter to you most. Capturing tasks and ideas is always a keyboard shortcut away in... Read more
Adobe Flash Player 17.0.0.169 - Plug-in...
Adobe Flash Player is a cross-platform, browser-based application runtime that provides uncompromised viewing of expressive applications, content, and videos across browsers and operating systems.... Read more
iMazing 1.1.3 - Complete iOS device mana...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more

Chainsaw Warrior: Lords of the Night has...
It's time to put the Darkness back in its place now that Chainsaw Warrior: Lords of the Night has officially made it to iOS. | Read more »
A World of Ice and Fire Lets You Stalk 2...
George R. R. Martin’s A World of Ice and Fire, by Random House, is a mobile guide to the epic series. The new update gives you the Journeys map feture that will let you track the movements of 25 different characters. But don't worry, you can protect... | Read more »
Gameloft Announces Battle Odyssey, a New...
Battle Odyssey, Gameloft's newest puzzle RPG, is coming to the App Store next week. Set in the world of Pondera, you will need to control the power of the elements to defend the world from evil. You'll be able to entlist over 500 allies to aid you... | Read more »
Fusion - HDR Camera (Photography)
Fusion - HDR Camera 1.0.0 Device: iOS Universal Category: Photography Price: $1.99, Version: 1.0.0 (iTunes) Description: Fusion creates HDR (high dynamic range) photos by capturing different exposures and then combining them into one... | Read more »
Sago Mini Toolbox (Education)
Sago Mini Toolbox 1.1 Device: iOS Universal Category: Education Price: $2.99, Version: 1.1 (iTunes) Description: Come build with the Sago Mini friends! Use a wrench, try a saw, or hammer some nails. From sewing hand puppets to... | Read more »
You Should Probably Grab Hitman GO While...
Hitman GO is a surprisingly cool (yet also incredibly drastic) departure from the Hitman series. It's well worth playing for any puzzle game fans out there, and at the moment you can get your hands - or garrotte if you will - on it for a mere $0.99... | Read more »
IFTTT is Bringing Do Button and Do Note...
IFTTT has announced Do Button and Do Note for the Apple Watch. Do Button lets you make your own personalized button that can connect to things like your Google Drive, control the temperature in your home with Nest Thermostat, or turn the lights on... | Read more »
How Many Days, Hours, and Minutes Are Le...
Countdown, by Yves Tscherry, is now available on the App Store. The app keeps track of countdowns to your favorite things such as someones birthday or days till the New Year. You can display the time in seconds, minutes, hours, days, weeks, months,... | Read more »
The All-New Misfit 2.0 App is Available...
Misfit has just given their app a complete overhaul. Misfit 2.0 now has a brand new interface with a sleek design and is easier to navigate. You'll be able to sync your Misfit device and look up health and fitness information faster than ever before... | Read more »
Halo: Spartan Strike (Games)
Halo: Spartan Strike 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: Delve into 30 challenging missions through cities and jungles using a devastating arsenal of weapons, abilities and... | Read more »

Price Scanner via MacPrices.net

Clearance 13-inch 2.6GHz Retina MacBook Pro a...
B&H Photo has clearance 2014 13″ 2.6GHz/128GB Retina MacBook Pros now available for $1099, or $200 off original MSRP. Shipping is free, and B&H charges NY sales tax only. Read more
Apple refurbished 2014 13-inch Retina MacBook...
The Apple Store has Apple Certified Refurbished 2014 13″ Retina MacBook Pros available for up to $400 off original MSRP, starting at $979. An Apple one-year warranty is included with each model, and... Read more
iMacs on sale for up to $205 off MSRP, NY tax...
B&H Photo has 21″ and 27″ iMacs on sale for up to $205 off MSRP including free shipping plus NY sales tax only: - 21″ 1.4GHz iMac: $1019 $80 off - 21″ 2.7GHz iMac: $1189 $110 off - 21″ 2.9GHz... Read more
Sale! 16GB iPhone 5S for $1 with service
Best Buy is offering 16GB iPhone 5Ss for $1.00 with 2-year activation at a participating cellular provider. Choose free home shipping and activation, or buy online and activate during in-store pickup... Read more
Apple refurbished 2014 MacBook Airs available...
The Apple Store has Apple Certified Refurbished 2014 MacBook Airs available starting at $679. An Apple one-year warranty is included with each MacBook, and shipping is free. These are currently the... Read more
27-inch 3.5GHz 5K iMac on sale for $2349, sav...
 Adorama has the 27″ 3.5GHz 5K iMac in stock today and on sale for $2349 including free shipping plus NY & NJ sales tax only. Their price is $150 off MSRP. For a limited time, Adorama will... Read more
Save up to $380 on an iMac with Apple refurbi...
The Apple Store has Apple Certified Refurbished iMacs available for up to $380 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 27″ 3.5GHz 5K iMac – $2119 $... Read more
iFixIt Teardown Awards 12-IInch Retina MacBoo...
iFixIt has posted its illustrated teardown of the new 12-inch MacBook Retina. They note that this new MacBook is less than half the thickness of the last Apple notebook called just “MacBook” back in... Read more
13-inch 2.5GHz MacBook Pro (refurbished) avai...
The Apple Store has Apple Certified Refurbished 13″ 2.5GHz MacBook Pros available for $829, or $270 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.... Read more
Faithful iPad 2 Gets A Second Career In Retir...
Finally, after four months’ transition, I handed my faithful old 2011 iPad 2 off to my wife at the end of March and switched whole-hog to using the iPad Air 2 I bought back in November. I’d found... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* TV Live Streaming Frameworks Test En...
**Job Summary** Work and contribute towards the engineering of Apple 's state-of-the-art products involving video, audio, and graphics in Interactive Media Group (IMG) at Read more
Event Director, *Apple* Retail Marketing -...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global engagement strategy and team. Delivering an overarching brand Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.