Freshman Honors Seminar
CS U231 Fall 2006 and CS U232 Spring 2007
http://www.ccs.neu.edu/jpt/fhs/
This site will contain materials from both semesters of the freshman honors seminar CS U231 and CS U232 in 2006-2007.
Materials from previous years are at:
Links
Exercises
9/19/06
9/19/06: Introductory JPT Sampler
9/19/06: Class Foo: A miniature Java tutorial
9/20/06
9/21/06
9/26/06
10/4/06
10/5/06
10/12/06
10/23/06
10/26/06
10/26/06: Polygons and Cubic Curves
11/13/06
11/17/06
11/17/06: Dice in Java and Scheme
11/20/06
12/1/06: Additional Applets
- Playing Cards Applet
- Font Sampler Applet
- Color Applets
- Ball Animation Applet
- Spinning Animation Applet
Links to earlier applets posted during Fall 2006
3/2/07
3/25/07
4/1/07
4/1/07: Keys, Animation, Threads
4/1/07
4/5/07
4/5/07: Stack Trace, String Viewer, Bad GUI Applets
4/12/07
4/12/07: Ball Animation with SimpleThreadedAction
4/12/07: Ship: Graphics Composite
Introductory Links
The plan or syllabus for the Freshman Honors Seminar in Computer & Information Science as a Word document or PDF document.
Access to the Sun Java site
Access to the Eclipse site
Access to Eclipse 3.2 for Windows (one version back)
Access to the Java Power Tools.
The main JPT site: http://www.ccs.neu.edu/jpt/
The JPT 2.5.0 site: http://www.ccs.neu.edu/jpt/jpt_2_5/
The JPT 2.5.0 library file jpt.jar: http://www.ccs.neu.edu/jpt/jpt_2_5/lib/jpt.jar
The JPT 2.5.0 javadocs: http://www.ccs.neu.edu/jpt/jpt_2_5/docs/
The JPT 2.5.0 annotated source code: http://www.ccs.neu.edu/jpt/jpt_2_5/src/
The
Methods.java
file that may be used as a template for the Java Power Framework.
The main cascading style sheet for this web site:
fhs.css.
The screen cascading style sheet for this web site:
fhs_screen.css.
9/19/06: Introductory JPT Sampler
The introductory JPT sampler is a class
Methods.java
that uses the Java Power Framework to create a test program
that will execute 3 simple methods.
The first method named WriteHello
utilizes the JPT console object to write the
world “Hello” into the JPT console window.
The second method named SumOfInt contains the
code to sum the integers from 1 up to a limit.
This code is:
public int SumOfInt(int limit) {
int sum = 0;
for (int n = 1; n <= limit; n++)
sum += n;
return sum;
}
This is a very simple Java method that accepts one parameter,
limit, uses a for loop to sum up
the integers from 1 to limit, and then returns
the sum. As you can see, there is absolutely no code in the
method to create a graphical user interface.
Nevertheless, since the Methods class extends the
Java Power Framework class JPF, when the
program is run a button is created to execute this method and
after this button is clicked the following automatic GUI for
evaluating the method is shown:
You can see that the green arrow indicates where the argument should be entered (we used 1000) and the red arrow indicates where the result is returned (in this case 500500). The order of argument(s) on the right and return on the left mimics the structure of a method header.
The third method in the class named
DrawShapesAndText
illustrates both how to put shapes and text into a graphics
window and how to put the same kind of objects into panels.
The method creates 2 circles and 1 square and handles each
of these differently to illustrate the options. The first
circle is added directly as a Paintable to the
graphics window so it is entered as an object with default
settings, that is, its outline is shown in black and it has
no fill. The second circle is installed into a
ShapePaintable explicitly so one can set the
defaults. We choose a red fill and blue outline. Finally,
the square is painted in red on the background layer of the
graphics window and therefore is not represented as an
object in the window object. Rather some pixels have been
painted red and that is it.
The method also puts a simple text string into the window as
a Paintable object.
Since the method enables automatic mouse listening in the graphics window, we can drag all of the items that were entered into the window as objects, namely, the 2 circles and the text string. We cannot drag the square since it is painted on the background layer and is not represented as an object floating above the background layer.
The situation is illustrated by the next two snapshots.
The initial drawing after clicking DrawShapesAndText
The drawing after dragging the 2 circles and the text string
Since we had already created the 2 circles, the square, and the text string, we decided to show how easy it is to put such objects into panels and then to show these panels. The screen snapshots below illustrate this.
The panel named “Funky Panel” is built using a
VTable which is a class that specializes in
vertical panels.
The panel named “Extra Panel” is built using a
TablePanel which is a class that handles
1-dimensional or 2-dimensional tables.
9/19/06: Class Foo: A miniature Java tutorial
This class uses two very simple Java files
Foo.java
and
Methods.java
to illustrate a simple class and its testing.
The class Foo is a tiny class that illustrates
the elemental aspects of creating a class in Java. Here is
the entire code:
/* @(#)Foo.java 14 September 2006 */
/* Useful imports */
import edu.neu.ccs.console.*;
public class Foo implements ConsoleAware {
public int x = 0;
public int y = 0;
public Foo() {}
public Foo(int x, int y) {
this.x = x;
this.y = y;
}
public void print() {
console.out.println("x: " + x);
console.out.println("y: " + y);
console.out.println();
}
public void print(String header) {
if (header != null)
console.out.println(header);
print();
}
}
The class is almost pure Java except that we have introduced the
JPT console object for writing the print
methods that will permit testing. The class is made aware of the
console object via the phrase
implements ConsoleAware in the class definition.
The class is elemental since it defines two simple instance
variables named x and y of type
int, two constructors both named Foo
which is exactly the name of the class, and two functions
or instance methods both named print. Things
could hardly be much simpler.
Even with such a simple class, you may still be puzzled about a very fundamental question:
What exactly does a class definition do?
The simplest description might be to think of a class as an entity that collects things such as variable definitions, function or method definitions, and rules for construction. However this view is somewhat simplistic since thinking of a class merely as a collection box ignores the planning and intelligence that goes into the design of a class and its subsequent usage.
A better description of a class is as a set of architectural plans for the construction of one or more objects. Such plans specify the elements of structure or data that the object should possess plus the behavior that the object will exhibit. It is quite important to distinguish a class, that is, a set of plans, from the objects or instances constructed from these plans.
In the Foo, there are two structural elements defined,
namely, the instance variables x and y of
the primitive type int. These are initialized to a
default value of 0 but this value may be changed in a
constructor or later on since the instances are public.
In a class with more complexity, a class instance variable may be of a type defined by a class. Thus, in general, a class may be built by composition from primitive instance data and/or instance data whose type is defined by one or more classes. In fact, a class may include an instance variable that directly or indirectly refers to another object of the same class. This ability permits the definition of sophisticated data structures.
Class Foo provides two constructors. The first
constructor has no parameters and is called the
default constructor. In this example, the
default constructor does absolutely nothing so
the instances variables remain set to 0.
Let's look more closely at the second constructor:
public Foo(int x, int y) {
this.x = x;
this.y = y;
}
This constructor requires two parameters
int x, int y. These parameters are not
supplied by the Foo class itself but by whatever code
uses the Foo class. Generally, the code that uses a
class is referred to as a caller or client.
Notice that the parameter names x and y
are identical to those of the instance variables. Although this may
seem confusing, many programmers feel that it is also helpful since
it suggests how the parameters should relate to the instance
variables. Within the body of this constructor, we must of course
know how to distinguish the parameters from the instance variables.
We do this by writing this.x and this.y
to mean the x and y that belong to
this object. In Java, this acts as a name by
which an object can refer to itself just as, in English, people
can say “I” or “me” to refer to themselves.
The period is also important since it signifies access.
Thus, for example, the phrase this.x means
access this object’s x.
The parameter data gets stored into the instance data by means of two assignment statements:
this.x = x;
this.y = y;
It is quite unfortunate that the mathematical symbol =
is used in Java to signify assignment. This notation
originated in the C programming language and has spread to many of
the successors to C. The problem is that = is a
symmetric operation in mathematics (a=b is the same as b=a) and is
used to perform a test for equality. In contrast, in programming,
assignment is absolutely not symmetric and furthermore the
effect of assignment is to change the left hand argument using
data from the right hand argument and not to simply perform a
test.
Let's try to describe simply but precisely what assignment does. We may view assignment as a statement of the form:
LHS = RHS
In this statement, LHS must designate a storage location, that is, it must name a variable that stands for a primitive type, a variable that refers to an entire object, an instance variable reference within an object, or an array cell reference.
RHS can be any of the kind of entities that LHS can be but RHS may also be more general. RHS may also be an expression that performs a computation or the return value of a method call.
In general, when the assignment is done, RHS is evaluated and the value is transferred to LHS. How the value is transferred depends on the type of LHS. If LHS is primitive, then the value is copied directly into the storage associated with LHS. If LHS is a reference to some form of object then only the minimal information needed to store the new reference is copied but not the entire object. This makes using objects quite efficient since assignment avoids the overhead and drawbacks of a complete copy of an object’s data.
Either way, LHS changes after an assignment. What was in LHS before the assignment is obliterated. If your program needs to remember what was in LHS before an assignment, you must use a separate assignment to copy that data elsewhere prior to doing the new assignment into LHS.
In contrast, if RHS is a variable, then it does not change under an assignment. Thus, assigment is not at all symmetric and you must be utterly careful in deciding what will be on the LHS (what variable is to change) and what will be on the RHS (what variable, expression, or function call will provide the new value for the LHS).
It is now possible to understand the two assignment statements in the second constructor.
public Foo(int x, int y) {
this.x = x;
this.y = y;
}
The first statement says to take the constructor parameter
x and copy it into this object’s
x. The second assignment says the same thing for
y.
We may now complete our discussion of the simple class
Foo by looking at the two print
methods.
public void print() {
console.out.println("x: " + x);
console.out.println("y: " + y);
console.out.println();
}
public void print(String header) {
if (header != null)
console.out.println(header);
print();
}
Both methods use the JPT console object to print
to the JPT console. The JPT console is convenient since you
can easily change font sizes and also save the contents of
the console window to a file. If you wish to use the built-in
Java tools, replace console by System.
Note that println prints what you ask for and adds
a newline automatically. The out object also has
a print method that just prints what you ask for
and does not add the extra newline.
The first print method in Foo prints
3 lines consisting of the value of x, the value
of y, and a blank line. The expression
"x: " + x
takes the 3 character string x-colon-blank and appends the
value of the instance variable x converted from
its type int to a string. This expression syntax
is built into Java and is quite convenient.
The second print method in Foo prints
an optional 4th line with a header that identifies
which Foo object is being printed. The header is
passed in as a string. If the caller has made an error, the
header might be uninitialized, that is, null.
For safety, we check for this possibility and only print the
header if it is in fact not null.
Note that the test for “not equals” is !=.
If you are interested, the test for “equals” is
==. In other words, simple equality is tested using
a pair of equals signs whereas a single equals sign is used for
assignment. This peculiar set of conventions has been the bane
of students and programmers for 30 years so you must take care to
avoid the trap of using = when you want to test for
equality via ==.
When the header is taken care of in the second print
method, the method then calls the first print method
to take care of the printing of the data. This technique avoids
code duplication and is strongly recommended as a code style.
We now turn to the test class Methods.
/* @(#)Methods.java 14 September 2006 */
import edu.neu.ccs.jpf.*;
/** The sample starter class for Java Power Framework. */
public class Methods extends JPF
{
public static void main(String[] args) {
new Methods();
}
public void TestFoo() {
Foo a = new Foo(2, 3);
a.print();
Foo b = new Foo(5, 7);
b.print("b");
}
}
Since the Methods extends the test framework class
JPF, the test method TestFoo will
give rise to a button in the JPF graphical user interface that
then will execute the method. This automation makes testing
extremely rapid and convenient.
What does TestFoo do? As you can see, it appears
to both declare and construct 2 objects a and
b built using the Foo class. It
also prints these objects.
Let us examine the line
Foo a = new Foo(2, 3);
in detail.
The declaration part is Foo a. This states
that a will either be a reference to an object
of type Foo or will be null which
represents an uninitialized object. If we in fact ended
the statement here with a semicolon, a would be
declared as a Foo object but would be uninitialized.
Since we continue with =, we see that we are about
to do an assignment of the RHS into a. The RHS is
an explicit construction and all explicit constructions
must begin with the keyword new. After this, we
must specify new what. We choose to make a
Foo object which makes sense since a
is a variable declared to refer to a Foo object.
Since class Foo has two constructors, we have two
choices for how to build the Foo object.
Foo a = new Foo(); // default 0, 0
Foo a = new Foo(x, y); // x, y int
We choose the second format so we can set the instance variables
to 2 and 3 respectively.
After building a with values 2,3, we print it, then
we build b with values 5,7, and print it with the
header “b”. This is what appears in the console:
x: 2 y: 3 b x: 5 y: 7
Notice that the test method TestFoo acts as the
caller or client that supplies the parameters to
use in the operations requested from class Foo.
9/19/06: Sum Panel Variations
TextFieldView, TablePanel, SimpleAction, Halo
The methods file
SumPanelTester.java
The vanilla sum panel file
SumPanel.java
The sum panel file using the Halo wrapper
SumPanelWithHalo.java
Before the detailed discussion, we will show screen snapshots of the two variations of the sum panel: vanilla and with halo.
|
|
| Vanilla Sum Panel | Sum Panel With Halo |
You can see immediately that the Sum Panel With Halo frame looks less crowded. We will discuss later why this is so.
Now let us discuss the code starting with SumPanel. The first
block of code introduces the 5 text fields for the data
x1, x2, x3, x4, and the total.
private static int width = TextFieldView.getSampleWidth(20, '0');
private TextFieldView view1 = new TextFieldView("0", width);
private TextFieldView view2 = new TextFieldView("0", width);
private TextFieldView view3 = new TextFieldView("0", width);
private TextFieldView view4 = new TextFieldView("0", width);
private TextFieldView total = new TextFieldView("0", width);
A TextFieldView is a JPT class based on Java’s class
JTextField and its main purpose is to add robust I/O for
multiple data types. As you can see, each text field is initialized to
hold one character 0 and to have the same width. The
width is computed first by a method that returns the width of a string
with 20 zeroes. In other words, the text fields are designed to hold
20 digits.
The next block of code makes an object that encapsulates the behavior we wish to perform, namely, to sum the text fields x1, x2, x3, x4, and then place the result into the text field total.
private SimpleAction sumAction =
new SimpleAction("Sum Data") {
public void perform() { sum(); }
};
At first this code looks peculiar. It creates a SimpleAction
object that is given a string name Sum Data and is also given a
method definition, namely, the perform method is defined and
its definition is tantamount to calling the sum method defined
later in the class. Why this roundabout definition structure?
Our goal is to define the button Sum Data that you see in each of the snapshots. A button must have a label and a behavior to perform when it is clicked by the user. We know both of these. We want the label to be Sum Data and the behavior to be the sum method. The problem is that Java prohibits passing method names directly as arguments to anything so there is no direct way to tell a button what it should do! In some ways, this is an unfortunate restriction made by the designers of Java but we must all live with it. The solution to this dilemma is that we must construct an object that is expected to contain a method with a standard name, in this case, perform, and we implement perform by a one line call to the method sum that we really want to use.
At this point, Java takes over since it has a standard way to construct a
button using an object of the general category of Action.
We will see in a moment that JPT automates the final step from action to
button.
The next blocks of code concern building the graphical user interface or GUI. You will notice that the 5 text fields have 5 corresponding labels and together these form a table with 5 rows and 2 columns. You can also see that the button is centered below this 5-by-2 table. We build the GUI in two steps. We make a 5-by-2 table object for the labels and text fields and then we insert this table into a vertical table with the action/button below. Here is the code.
private Object[][] dataStuff =
new Object[][] {
{ "x1", view1 },
{ "x2", view2 },
{ "x3", view3 },
{ "x4", view4 },
{ "total", total }
};
private TablePanel dataPanel =
new TablePanel(
dataStuff, 10, 10, WEST);
private Object[] mainStuff =
new Object[] { dataPanel, sumAction };
private TablePanel mainPanel =
new TablePanel(
mainStuff, VERTICAL, 10, 10, CENTER);
The first step in making the 5-by-2 table is to create a 2-dimensional
array with 5 rows and 2 entries per row. Each row consists of a label
and its corresponding view. This is what is done in the definition of
dataStuff. This is the raw material for building the
5-by-2 table.
The next step is to create the table dataTable using the
data dataStuff. The constructor specifies the data,
a horizontal and vertical gap between cells of 10, and an alignment of
WEST which means that within cells objects are pushed
towards the west or left edge.
The next step is to combine the dataTable with the action
sumAction to make a second data array in preparation for
making the outer table. This is what is done in the definition of the
1-dimensional array mainStuff.
Finally, we create mainTable using mainStuff.
Since the array is 1-dimensional, we must specify a direction
HORIZONTAL or VERTICAL.
We choose VERTICAL in this case.
For alignment, we specify CENTER which is how
we get the button to be centered under the 5-by-2 table.
One powerful feature of TablePanel is that it can accept
general objects such as String (the labels) or
Action (sumAction) and figure out how to
make them into the appropriate GUI components. Thus
sumAction is used to automatically construct the button
that uses the action name for the button label and the perform
method for the button behavior.
The next step is the constructor for the SumPanel. The
constructor completes any initialization steps that cannot be
conveniently done in the earlier member data definition steps. Here
is the constructor.
public SumPanel() {
add(mainPanel);
addListeners();
frame("Sum Panel");
}
To understand this, let us also look at the class header.
public class SumPanel extends TablePanel
This shows that SumPanel is also a TablePanel.
The first line of the constructor says to take mainPanel
and make it the single component within the SumPanel which
acts as a wrapper. This style of building all the pieces of the GUI in
the data definitions and then having one add statement to
wrap things up is a very common idiom in JPT code.
The second line of the constructor calls the addListeners()
method that we will describe below.
The third line of the constructor says to take the SumPanel
and place it in a frame, that is, a full-fledged window on the
screen that has Sum Panel as its title. This frame is
centered on the screen by default. We call the process of taking a
panel and putting it into a frame self actualization since it
avoids the traditional Java step of separately constructing the frame
and inserting the panel into its so-called content pane.
Let us finally look at the methods in this class of which there are only
two. The first method, sum(), does the algorithmic work of
the class since it implements the behavior in sumAction.
private void sum() {
double x1 = view1.demandDouble();
double x2 = view2.demandDouble();
double x3 = view3.demandDouble();
double x4 = view4.demandDouble();
double x = x1 + x2 + x3 + x4;
total.setViewState(x + "");
}
In the first four lines of sum(), we obtain the four values
to be summed. The method demandDouble() of the
TextFieldView class is used. This method insists that the user
provide valid numeric data and will use error dialog boxes until any
mistakes are fixed. This is an example of a method provided in
TextFieldView that is not available in Java base class
JTextField.
The next line does the mathematics of summation.
The final line converts the number x to a String
via the Java idiom x + "" and then places the
String into the total text field using the method
setViewState which is also defined in
TextFieldView.
The second and last method, addListeners(), is present for
the convenience of the user. A text field will listen for a
special event, namely, if the user presses the return-key while the
cursor is in the text field. If this event happens then any action
that has been specified via addActionListener will be done.
Thus, in this case, the summation action can also be triggered if the
user presses return in one of the first four text fields. This means
that the user does not actually have to click the Sum Data
button.
private void addListeners() {
view1.addActionListener(sumAction);
view2.addActionListener(sumAction);
view3.addActionListener(sumAction);
view4.addActionListener(sumAction);
}
The method addActionListener is inherited by
TextFieldView from the Java base class JTextField.
We now explain what a Halo does and why it is used. A
Halo is a wrapper panel that surrounds another panel and
provides a small border. Its primary use in JPT is to provide focus
if a user makes an input error. By default, if an error occurs in a
TextFieldView, JPT will highlight the smallest panel that
surrounds the text field. This can sometimes be problematic. In the
case of the SumPanel, the smallest panel surrounding any
of the text fields is the 5-by-2 table panel. Thus, no matter where
the error occurs, the entire 5-by-2 panel is highlighted. This is not
so nice for the user.
The Halo class solves this problem by providing a small
panel to wrap around what is inside it so error highlighting will be
focused. Here is the change in the code to construct the array
dataStuff to add the necessary Halo
wrappers.
private Object[][] dataStuff =
new Object[][] {
{ "x1", new Halo(view1) },
{ "x2", new Halo(view2) },
{ "x3", new Halo(view3) },
{ "x4", new Halo(view4) },
{ "total", new Halo(total) }
};
In this code, each Halo uses a default border size of
2 pixels all around each text field.
A Halo may also be used to provide an overall border
to a panel. Here is the change to the code that accomplishes this.
add(new Halo(mainPanel, 10, 10));
Here we use a larger border of 10 pixels all around the main panel.
With this, you have all of the changes between SumPanel
and SumPanelWithHalo.
Additional Comments
The code in the sum panel examples was written in 2005 and we decided to leave the code as is. Let us comment here on the simplications possible with the additions in JPT 2.5.0.
First, classes were added to JPT to deal with horizontal and vertical
table panels, namely, HTable and VTable.
Since, mainPanel is vertical, it may be defined as:
private VTable mainPanel =
new VTable(mainStuff, 10, 10, CENTER);
Next, a large number of methods have been added to
DisplayPanel to make it easy to add borders to panels.
Since most JPT panels extend DisplayPanel, these methods
are widely usable. This means that instead of wrapping
mainPanel in a Halo to get an outer border,
we can instead add the following line at the top of the constructor:
mainPanel.emptyBorder(10);
More complex borders may be added with equal ease. The real purpose
of the Halo class was to provide a small wrapper so that
error highlighting would be focused about the field with an IO error.
It was merely a trick to use a Halo for an empty border.
That trick is now unnecessary.
9/19/06: Playing Cards
The screen snapshot above shows an execution of the Java program
PlayingCards defined in the file
PlayingCards.java.
This program illustrates the following features of Java and JPT.
- How to create a simple program that can launch itself via
the
mainmethod. - How to define a
BufferedPanelgraphics window. - How to read the images of a deck of cards from a web site
using
WebImageTools. - How to specify the behavior that will randomly place the
cards images into the graphics window via the method
showRandomCards(). - How to encapsulate the method
showRandomCards()in aSimpleActionand then to use this action to create aJButtonthat triggers that action. - How to build the GUI by combining the graphics window and
the button using a vertical table panel
VTableobject. - How to install the table panel in the GUI via the
addmethod. - How to define a permutation that will randomly order the playing cards.
- How to define the random position of each playing card.
The playing cards are found on the JPT web site at:
http://www.ccs.neu.edu/jpt/images/jfitz_cards/
The ordering of the cards is hearts, diamonds, spades, and clubs, followed by 2 card backs and 2 jokers. This ordering is specified in the following file within that site:
There are additional “card fragments” on the JPT site
but only the 56 images in “imagelist.txt” are downloaded
into the PlayingCards program.
To obtain all 67 images on the site including the “card fragments”, use instead the file:
The site from which the playing cards were originally downloaded is:
We appreciate the public service that the JFitz site performs by making these playing cards available at no charge.
Added 12/1/06: Playing Cards Applet
Access to the Playing Cards Applet
The Java file
PlayingCardsApplet.java
9/19/06: Sample Fonts
The screen snapshot above shows an execution of the Java program
FontSampler defined in the file
SampleFonts.java.
This file is extremely simple:
/* @(#)SampleFonts.java 13 September 2006 */
import edu.neu.ccs.gui.*;
public class SampleFonts {
public static void main(String[] args) {
FontSampler.main(null);
}
}
As you can see this class simply serves as a bridge to reach the
class FontSampler which is installed in JPT. The
main method of SampleFonts simply
executes the main method of FontSampler.
Therefore, to understand what is happening, you should look at
the file
FontSampler.java.
This program illustrates the following features of Java and JPT.
- How to use the Java class
GraphicsEnvironmentto obtain a string array that contains a list of all “font family names” for all fonts installed on the system. - How to initialize a JPT
Dropdownlist using the list of font family names. This list will allow the user to select a font family name for display of the font sample. - How to set up a small text field for the font size and
two check boxes to enable the user to choose
“Bold” or “Italic” or both.
These use the JPT classes
TextFieldViewandBooleanView. - How to define a
BufferedPanelto serve as the display area for a font sample. - How to combine the GUI elements to build the GUI.
- How to define the method
showFont()that uses the user choices of font name and font size together with the bold and italic settings to create a sample of the font. This method makes use of the JPT classTextPaintable. - How to encapsule the method
showFont()in aSimpleActionobject that is also namedshowFont. - How to attach the
showFontbehavior to the dropdown list, the text field, and the check boxes so that any changes will trigger a refresh of the sample. This technique means that no “Show Font” button is needed.
Added 12/1/06: Font Sampler Applet
Access to the Font Sampler Applet
The Java file
FontSamplerApplet.java
9/19/06: Visual Color List
For the purposes of information, JPT has a class named
VisualColorList that shows a scrollable list of
the named colors that are installed as static objects in the
JPT class Colors. Originally, we planned to
include the code of the VisualColorList class
in these samples.
Instead, we realized that the class VisualColorList
could be improved. We therefore provide the code for the
improved class
VCL.java.
This code will replace the code in VisualColorList
in the next release of JPT.
The scrollable color list in VCL shows a color swatch,
the color name, the color RGB values in decimal, and the color RGB
values in hexadecimal. The decimal values are separated by commas.
The hexadecimal values are preceded by the symbol #
and have no separators; this follows the convention for defining
colors on the web.
In addition to the scrollable color list, class VCL
provides a ColorView in which the user may enter a
color using either decimal or hexadecimal RGB values. This view
permits experiments in which a user can vary the parameters of a
named color and see what happens visually.
This program illustrates the following features of Java and JPT.
- How to build the color table panel using an algorithm.
Specifically, there are far too many named colors to build
the color panel by hand. Therefore, color data is collected
in certain arrays and then the color panel is built using a
TableGeneratorobject that encapsulates the algorithm for specifying all cells of the table. - How to put the color panel into a scroll pane and control the size of its view port so that color swatches do not visually fall across the view port boundary.
- How to obtain the color name data from the
Colorsclass and how to build the decimal and hexadecimal RGB string data. - How to construct the overall GUI.
Added 12/1/06: Color Applets
Access to the Color Applets
The Java file
VCLandCB.java
The Java file
VCLandCB_Applet.java
9/19/06: Functions and Plots
The class FunctionsAndPlots in the file
FunctionsAndPlots.java
is a Java Power Framework tester class that permits
the user to explore expression evaluation, definition
and evaluation of simple functions, and function plots.
It may be viewed as a collection of ingredients that
might be incorporated into a function plot program
to be constructed at some future time.
The initial JPF GUI that is presented when the class
FunctionsAndPlots is launched is shown
below.
The first GUI button “Evaluate” corresponds to the method:
public double Evaluate(double x) {
return x;
}
When the “Evaluate” button is clicked, the GUI shown below is automatically generated:
As an example, we have shown in this GUI the evaluation of the famous mathematical constant known as the “Golden Ratio”. This automatic GUI is the fastest mechanism in JPT to make a panel for evaluation of double precision expressions.
The next GUI button “EvaluateInConsole” launches a method that performs a double precision evaluation loop in the console window.
The next five GUI buttons bring up more sophisticated GUIs
to evaluate expressions and/or define simple functions.
The GUI button “EvaluateExpression” permits the
user to choose between three result types:
BigInteger, double, and
boolean. The next two buttons allow the user
to define simple functions that will then be recognized in
the expression parser. The final two buttons bring up GUIs
that permit both function definition and immediate testing
via expression evaluation. In the buttons with the phrase
“...WithIO”, the user can save function
definitions to disk. Here is the GUI created when the last
of the five buttons is clicked.
In the top pane of the window, the function definition for
cuberoot in terms of the built-in function
root is shown. In the bottom pane, the
result of testing cuberoot on
27 is shown. Note that, for visual purposes,
we have brought up the prior definition of cuberoot
to show both the definition and the test in one snapshot.
Normally, once a definition is made, the text fields are
cleared to prepare for additional definitions.
The “PlotFunctions” button launches an automatic GUI in which the user may enter 4 items.
- A comma-separated list of function names.
- The left endpoint of the plot interval.
- The right endpoint of the plot interval.
- The number of subdivisions of the plot interval.
The function names can be either those of built-in functions,
simple functions added algorithmically, or simple functions
defined interactively by the user. In the screen snapshot
below, we show one built-in function sqrt and
two simple functions cuberoot and
fourthroot. The plot interval is from
-4 to 4 and the number of
subdivisions is 100.
In the screen snapshot below, you may see the plot results.
You will notice, in particular, that since sqrt
and fourthroot are not valid for negative
inputs, the plots simply skip these points for the
two functions.
The final two buttons bring up GUIs for plotting a polynomial or a fourier series that is entered by giving coefficients.
The series of examples in this program deserves careful study and may inspire you to build more elaborate programs. In particular, I recommend that you check out how the data is acquired from the functions and then how the plots are done including scaling, grids, tick marks, and axes.
9/19/06: Class KeyLabelData
This example illustrates the definition and elaboration of a data structure. It consist of two classes.
- The
KeyLabelDatadata structure class in the fileKeyLabelData.java. - The
Methodstester class in the fileMathods.java
The KeyLabelData data structure class is almost 100%
pure Java. The main aspect of the class that relates to JPT is
that we require that the class implement Stringable
which is an interface that requires a method to save the object
data to a suitable string and a method to read the data back from
a correspondingly structured string. Internally, we use JPT to
make the coding of some of the methods faster and easier.
The purpose of the KeyLabelData is to associate a
“search key” string with a list of strings or
“labels”. The inspiration behind this class is a
class that would store the address label information for a
contact information program and would attach a search key to
each contact to make it easy to search for particular contacts.
The class is written in a more general way so that any kind of
string information may be associated with a search key. There
is one simplication, namely, that the number of labels that may
be associated with a particular search key is fixed at the time
of construction. This makes some code easier. A nice feature
of the search is that the search key is automatically broken
into individual words and one can search for any word by
giving a string of characters that start the word. It is not
necessary to type the full word.
Rather than discuss the KeyLabelData class line by
line, we are going to outline the class and tell you what to
look for in examining its code.
The class has 4 constructors that require varying amounts of data.
- The default constructor requires no data, sets no key or labels, and results in a capacity of 6 labels.
- A constructor that asks for the capacity and sets that item but sets no key or labels.
- A constructor that asks for a key and labels and sets those items within a capacity of 6 labels.
- A constructor that asks for the capacity, the key, and the labels and sets all of those items.
The constructors are coded so that the first three make use of the last constructor to minimize repeated code.
The capacity may not be changed after construction. On the other
hand, both the key and the labels may be changed and there are
assorted set methods to accomplish this. There is
also an append method to add a label to the list.
The class has get methods to retrieve information
about what is in the class.
The class implements “search recognition” by a method with the following signature:
public boolean keyStartsWith(String string)
This methods returns true if the given string starts
any word in the search key. The search is case-insensitive so it
can find more matches.
The standard Java method toString() is implemented to
return a string that combines all labels each terminated with a
newline. Hence it may be viewed as the multi-line string of labels.
The Stringable method toStringData() makes
a string by combining the search key plus a newline with all labels
also terminated by a newline. It should be clear that this string
contains all of the critical information within the object.
The Stringable method fromStringData is
designed to reverse the effect of toStringData() to
set the key-label content of the data structure from a string.
Since we have chosen to make the capacity of this data structure fixed
at construction time, we do not include the capacity in the strings
returned by toString() and toStringData().
This is considered internal information.
Finally, the class implements a compareTo methods that
allows the class to implement the Comparable interface.
This in turns permits objects of the class to be sorted.
By Java convention, compareTo should normally be consistent
with the equals method and, in turn, this method should be
consistent with the hashCode method. This leads us to also
define the latter two methods.
From this summary, you can see that the class has facilities for building objects in a flexible way and for setting, getting, searching, and sorting. Thus, it is a simple but rather typical data structure class.
You may examine the tester class Methods for yourself. You
will see that the tests are reasonably simple. You will see that one
construtor and many methods are tested explicitly. In addition, since
object data is printed explicitly in the tests, you can see that other
behavior has also taken place correctly.
We hope to build classes related to KeyLabelData in future
examples.
9/20/06: Tic Tac Toe
The Tic Tac Toe game is elementary and so provides a simple setting in which to illustrate some additional ideas in GUI building. Before further discussion, let us show two screen snapshots from the demo program.
In the first screen snapshot, player X has won the game and the winning sequence is highlighted in yellow. In the second screen snapshot, the game has been played to a tie since neither X nor O has won and no more moves are possible.
The game uses the concept of a tile that has a size,
an object that is painted, and a background color.
There are actually three kinds of tile:
the blank tile that just paints the background, the X tile,
and the O tile.
Notice that the same object may be painted on several tiles.
This distinction is important and is reflected in the code.
The three things that are painted (blank, X, O) are
Paintable objects that remain the same throughout
the program. The 9 tile objects that are visible in the GUI
are TileBox objects that can change their internal
Paintable object and so can change what they display.
In particular, as long as the game is not over, if a blank tile is
clicked then it will change to an X tile or an O tile depending on
what player has the next move. Technically, the tile does not
change its identity only its Paintable contents.
The tiles are arranged in a 3-by-3 grid using a
PaintableSequence.
The initialization code creates a 3-by-3 array of
TileBox objects each with a blank as its contents
and burlywood as its background color. Using the move command,
these tiles are geometrically positioned before being added to
the seqeunce.
The PaintableSequence is in turn placed into a
PaintableComponent which is an extension of the
Java JComponent class designed to hold objects
that are Paintable.
A PaintableComponent entity is also set up to
permit the easy definition of desired mouse actions.
The mouse code that controls the game is quite simple.
When the mouse is clicked on the PaintableComponent,
several checks are made.
If the game is over or if the mouse has not hit a blank tile,
then the mouse code does nothing.
On the other hand, if the mouse has hit a blank tile, then the
contents of that tile is changed to an X or O depending on what
player is next and then the next player is switched to its opposite.
After each click, a check is made to see if the game has been won
or if no more moves are possible.
In effect, one can think of the tile grid embedded within the
PaintableComponent as a complex button that
responds to mouse clicks in a subtle fashion that depends on what
particular tile is clicked. This contrasts with an ordinary button
that behaves in the same way no matter where the mouse is clicked
within the button. The mouse click code illustrates how complex
behavior may be coded in a rather simple fashion.
Access to the Tic Tac Toe Applet .
Here is the Java file
TicTacToe.java
Here is the Java file
TicTacToeApplet.java
9/21/06: Fibonacci Variations
The Fibonacci numbers are a sequence of numbers starting with
1 1 and having the property that each subsequent number
is the sum of the preceding two numbers. Here are the first few
terms in the sequence:
1 1 2 3 5 8 13 21 34 55 89 144 233 ...
This example concerns generating the Fibonacci numbers in a simple graphical user interface (GUI). The interface shows 3 successive terms in the sequence so you can see how the last term is the sum of the previous 2 terms. Here are 4 screen snapshots that show the start of the sequence and 3 successive executions of the “Next Fibonacci” button.
After clicking “Next Fibonacci” a number of times,
we quickly realized that we wanted to be able to push forward
rapidly in the sequence without having to click for each step
individually. This led to the “Repeat Next ...”.
Suppose, we wish to go to the Fibonacci of index 50.
We click “Repeat Next ...”. Then the simple
dialog shown below appears, we enter 50, and click
“OK”.
What happens is that the next action is
repeated until index 50 is reached.
The result is shown in the screen snapshot below.
If you continue to higher values of the index, a problem eventually
occurs. The first time it occurs is at index 92. The
situation is shown in the screen snapshot below.
As you can see, the value of Fibonacci for 92 is
negative. What has happened? The answer is that we have used
data type long to do the arithmetic and this type
does not have enough space to store the actual result. Instead,
the value was computed but wrapped around to a negative number.
Ouch!
There is a way to fix this. Java supports a type
BigInteger that will maintain precision as long as
Java itself has enough memory for the required storage. Given
the amount of memory on modern systems, this is enough for some
rather large computations. Here is the screen snapshot taken
with the variation of Fibonacci that uses BigInteger.
Here is the similar screen snapshot with Fibonacci for
200.
Here is the Fibonacci source code that uses long:
Fibonacci.java
Here is the Fibonacci source code that uses BigInteger:
FibonacciBI.java
Let us suggest some questions for you to consider as you examine and compare both source files.
- How are the 6 text fields and the table in which they are contained constructed?
- How are the 3 actions that define the 3 buttons built?
- How are the 3 methods behind the 3 actions defined?
- How do you get text out of a text field?
- How do you get a number out of a text field?
- How do you put text into a text field?
- How do you put a number into a text field?
- How do you put the result of a calculation into a text field?
- How is the simple dialog that requests an index made to appear on the screen?
- What are the final steps in building the GUI?
- In exactly what places do the programs
Fibonacci.javaandFibonacciBI.javadiffer?
Here are some suggestions that might inspire further projects. The first two are simple and the last two require substantially more thought and work.
- What happens if the start numbers in the right column are
not
1 1 2but some other three numbers? Try experiments to find out some information. - Implement a version of Fibonacci in the console window that asks for an index and prints all Fibonacci numbers from index 0 up to that index.
- Implement a version of Fibonacci in a GUI that asks for an index (perhaps with a dialog or perhaps via a text field) and then creates a scrollable panel that lists all of the Fibonacci numbers from index 0 up to that index.
- The Fibonacci numbers are based on a formula. If
aandbare two successive Fibonacci numbers then the next number is mathematicallya+b. Instead of using this fixed formula, can the formula be entered as a string by the user? Can the JPT parser tools and the notion of SimpleFunction be of use or is this the kind of problem that requires starting from the beginning?
9/26/06: Ball Animation
The Ball Animation should be viewed as a demonstration program that reveals the pattern for building animations using JPT. What is animated is a simple ball that moves back and forth on a horizontal track. There are slider controls that determine the step size, that is how many pixels the ball moves in each step, and the speed, that is, how many steps are executed per second. What is animated is intentionally very simple so the pattern can be shown in as clear a fashion as possible.
Here are two snapshots from the Ball Animation program:
The Ball Animation at Startup
The Ball Animation Running
The initial settings for the ball animation are a step size of 1 pixel and a speed of 1 step per second. With these settings, the animation will be very slow. At the opposite extreme, the step is 10 pixels and the speed is 100 steps per second or as close to that as the processor will permit. Visually, the ball will blur at that speed.
We will now discuss the design of the ball animation program
whose source may be obtained at
BallAnimation.java.
We will separate a discussion of the GUI aspects from the deeper issues
of how to program the animation using a separate “thread”.
GUI Design
The GUI consists of 3 levels: the animation track with the moving ball, the slider controls for step size and speed with their labels, and the buttons that run and stop the animation.
The ball is constructed as a ShapePaintable using an
XCircle object to define the circle shape and associated
settings to select red fill with blue boundary. This ball is inserted
into a PaintableSequence for two reasons. First, this is
the pattern that will needed to animate more than one object. Second,
we can set the default bounds of the sequence to be what we wish as
the size of the track along which the ball will move. Then, we insert
the sequence into a Tile object that sizes itself to the
bounds of the sequence and provides the light gray background that is
the visual representation of the track. When the tile is in turn
inserted into the GUI, it will automatically be placed within a
PaintableComponent object that will take care of the
repaint operations needed when the ball is moved.
The slider controls use the JPT class SliderView which is
a minimal extension of the Java class JSlider. The
extras in SliderView allow one to specify the actions to
be performed as the slider is sliding and when the slider is released.
The Java base class provides a method for defining the spacing of the
tick marks and a simple JPT method allows these marks to be labeled.
There is no provision in the slider classes for displaying the value
of the slider as text so this is done with the labels to the right of
each slider. Although the sliders appear to permit a slider value of
zero, methods in the class intervene to guarantee that the minimum
value of each slider is one. This avoids a division by zero.
The “Run Animation” button is constructed using a
ThreadedAction to guarantee that the animation is done
in a separate thread. What this means is discussed in a moment.
The “Stop Animation” button is built using a
SimpleAction in a manner familiar from earlier samples
on this site.
Animation and Threads
A thread of execution is a collection of program instructions that conceptually appear to act as a unit that does work, looks at or changes data, and interacts with the user, the file system, and perhaps the network. All modern operating systems permit multiple programs to run at once so a user can surf the web, read e-mail, write a document, or manipulate an image all at once. Each program conceptually runs in one or more threads of execution that are distinct from those of all other programs currently running. It is the responsibility of the operating system to split time between all programs so each gets some slice of available processing time and appears to have control of all system resources while it is actually processing.
A simple program uses only a single thread of execution. For a program with a GUI, this thread must pay attention to and respond to the user actions such as typing keys and using the mouse. For this reason, this first thread is often called the “GUI thread”. If the program make few demands on the system then it can do all of its work, that is, run all its algorithms, within the GUI thread. So little is done that the user doesn't even notice any problems. However, if it turns out that the algorithmic work is intensive, then that work will consume all processor cycles allocated to the program and the program will have no cycles to pay attention to the user. In effect, the GUI will appear to freeze or die.
The solution to the problem of GUI freeze is to run intensive algorithms in a second thread independent of the GUI thread. Then the operating system will split time between the two threads provided that the busy thread agrees to take periodic breaks or pauses. This will permit the GUI thread to get needed processor time and therefore to remain alive.
Animation tends to be processor intensive so an animation program should
be planned in such a way that the animation is done in a second thread.
Java has a specific pattern for setting up a separate thread. Since the
lines of code are always the same, we introduced in JPT the class
ThreadedAction that incorporates this code. The key idea
is that a ThreadedAction encapsulates an ordinary action but guarantees
that each time it executes a new thread will be automatically created
to execute the ordinary action it references internally.
Let us see how simple this turns out to be.
Here is the code that sets up the ThreadedAction object that
will run the animation in a separate thread.
private ThreadedAction runAction =
new ThreadedAction
(new SimpleAction("Run Animation") {
public void perform()
{ runAnimation(); }
});
The ThreadedAction constructor must be provided with an
ordinary action that it will use to define the behavior that will be
executed on the separate thread. As you can see above, we provide
this ordinary action by creating a SimpleAction and
that action in turns calls the method runAnimation().
The fact that the behavior will run in a separate thread is now 100%
automatic.
The action to stop the animation runs quickly so it does not need to
be run in a separate thread. It can be defined simply via a
SimpleAction.
private SimpleAction stopAction =
new SimpleAction("Stop Animation") {
public void perform() { stopAnimation(); }
};
Here is the code for the methods runAnimation(),
stopAnimation(), and related helper methods.
/**
The animation loop method that will
execute in a separate thread.
*/
private void runAnimation() {
// set state for run
setRunState();
// perform animation
while(true) {
synchronized (animationLock) {
if (!running)
return;
animate();
}
JPTUtilities.pauseThread(getDelay());
}
}
/**
The method to stop the animation loop.
*/
private void stopAnimation() {
// set state for stop
setStopState();
// perform any desired cleanup activities
}
/**
Set the enable/disable state of the actions
for the Run state.
Set boolean running to true.
*/
private void setRunState() {
synchronized (animationLock) {
runAction. setEnabled(false);
stopAction.setEnabled(true);
running = true;
}
}
/**
Set boolean running to false.
Set the enable/disable state of the actions
for the Stop state.
*/
private void setStopState() {
synchronized (animationLock) {
running = false;
stopAction.setEnabled(false);
runAction. setEnabled(true);
}
}
To understand this, first keep in mind that the actions
runAction and stopAction will be used
to build the buttons “Run Animation” and
“Stop Animation” in the GUI. Clicking the buttons
will execute the actions. This will help us explain the pair
of helper methods. The method setRunState():
- Disables the action
runActionwhich in turn disables the button “Run Animation”. - Enables the action
stopActionwhich in turn enables the button “Stop Animation”. - Sets the control parameter
runningtotrue.
Symmetrically, the method setStopState():
- Disables the action
stopActionwhich in turn disables the button “Stop Animation”. - Enables the action
runActionwhich in turn enables the button “Run Animation”. - Sets the control parameter
runningtofalse.
The effect of this is that when the animation is running then the “Run Animation” button is disabled so you cannot start yet another thread in conflict with the already running thread. At the same time the “Stop Animation” is enabled so it is possible to stop the animation. When the animation is stopped, then the state of the buttons is reversed. This is visible in the two screen snapshots above.
Now let us look carefully at runAnimation().
/**
The animation loop method that will
execute in a separate thread.
*/
private void runAnimation() {
// set state for run
setRunState();
// perform animation
while(true) {
synchronized (animationLock) {
if (!running)
return;
animate();
}
JPTUtilities.pauseThread(getDelay());
}
}
The method setRunState() is run once and then the code
enters an infinite loop signaled by while(true). Hence,
unless something intervenes, this code will run forever. Notice,
however, there is an immediate test of the control variable named
running. If this variable is false, the
loop will exit.
You may ask, how can the variable running become
false if there is no code within the infinite loop to
change the variable? The answer is that the variable can become
false if the “Stop Animation” button is clicked. This
click is recognized in the GUI thread which is running in parallel
to the animation thread. The keyword volatile is
added in the declaration of the variable running to
alert the compiler that running may be altered by
different threads and to therefore avoid compiler optimizations
that would eliminate the test.
If running is true then the test fails
and the loop continues on to execute the animate()
method. The code has been written in this way to create a pattern
that you can follow if you want to do your own animation. All you
need to do is set up your own situation and write you own version
of animate().
Now comes a crucial step. The animation thread must pause to give the GUI thread some life and to control the animation speed. The following method call is made:
JPTUtilities.pauseThread(getDelay());
This code tells the current thread (which is in fact the animation
thread) to pause for the amount of milliseconds given by its parameter.
The delay time is computed by the method getDelay()
which uses information from the speed slider and simple arithmetic. It
might be computed quite differently in your animation program.
Finally, what about the phrase synchronized (animationLock)
that occurs in several places? This code resolves issues of conflict.
If more than one thread is working with certain data, then it is
possible to get that data into an inconsistant state by starting an
update to the data in one thread and then switching threads before the
update is complete. When a section of code is synchronized
relative to some “lock” object, then it can complete its work
before any other thread that synchronizes on the same object is allowed
to proceed with its work. This ensures that data is updated correctly.
It doesn't actually matter what the “lock” object is. What
is crucial is that sections of code that work on common data use the
same lock object for synchronization. One more point. The call to
pauseThread is not within the scope of synchronization.
If it were, the “Stop Animation” button could never actually
execute its code and the animation could never be stopped. Thus the
details of the pattern are very critical.
One additional technical point. If you close the Ball Animation window, this will terminate the GUI thread. Look through the program to see how the closing of the window also ensures that the animation thread comes to a halt.
Hopefully, the Ball Animation can provide a pattern to follow if you wish to explore animation.
Added 12/1/06: Ball Animation Applet
Access to the Ball Animation Applet
The Java file
BallAnimationApplet.java
10/4/06: Table of Factorials
The JPF test file
Methods.java
contains two variations of a method that will print a table of the
factorial function from mathematics. Let us first review what the
factorial function is.
Here is a table of the factorials from 0 to 10.
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
The notation n! reads “n factorial” and
stands for the definition:
n! = 123...(n-1)n
By convention, 0! = 1
For positive n, there is an “inductive formula”
that lets you compute n! from (n-1)!:
n! = n(n-1)!
This formula is the basis for the methods in the Java file given above.
It says that if you already have (n-1)! then you can
compute n! using a simple multiplication.
Since the factorial function grows quickly, we want to use so-called
“big integers” rather than int or
long to store the computations. There are two options:
the Java class BigInteger or
the JPT class XBigInteger. The Java file above shows
both variations so you can pick a style that you would prefer to use
in similar situations.
Since the Java file is heavily commented, we will simply let you read it for yourself.
10/5/06: Classic Shapes
The JPF test file
ClassicShapes.java
illustrates how to constructs what we will call the 7 classic shapes:
rectangle, rounded rectangle, oval, square, rounded square, circle,
and line. These correspond to the following JPT and related Java
classes:
JPT Java
XRect Rectangle2D.Double
XRoundRect RoundRectangle2D.Double
XOval Ellipse2D.Double
XSquare no corresponding Java class
XRoundSquare no corresponding Java class
XCircle no corresponding Java class
XLine2D Line2D.Double
As you can see, the JPT class names are much more consise than the
Java names. The reason the Java names are so complex is that Java
wanted to provide both Double and Float
variations of the shapes. JPT simply uses double.
The constructors for XRect and XOval use
parameters x y w h where x and y
are the coordinates of the left-top corner of the bounding box and where
w and h are the width and height of that box.
The constructor for XRoundRect adds two extra parameters
that provide the rounding radii in the x and y directions respectively.
The constructor for XCircle uses x y r where
x and y are the coordinates of the center of
the circle and r is its radius. The classes
XSquare and XRoundSquare also use the center
rather than the left-top corner. For these classes, the radius means
the radius from the center of the square to its side so in fact is
one-half of the side. The XRoundSquare also adds to its
constructor the arc radius for rounding the corners.
The constructor for XLine2D uses x1 y1 x2 y2
which represent the coordinates of the endpoints of the line.
Note that it is sometimes convenient to define a rectangle, rounded
rectangle, or oval using the opposite corners of the bounding box.
The classes XRect, XRoundRect, and
XOval have a method setX1Y1X2Y2 that lets
you define the bounding box using any two opposite corners. On the
other hand, for squares and circles, this method and related methods
that use the bounding box to define the object are changed to do
nothing at all. This is because there is no valid way mathematically
to use a rectangular bounding box to uniquely define a square or circle.
The classes for squares and circles may be viewed a trivial convenience classes since it is certainly possible to draw squares and circles with the rectangle and oval classes.
Here is a screen snapshot with the execution of the
ShapeSample method in the file
ClassicShapes.java.
You should examine the file to see:
- Exactly how each of the 7 shapes is constructed
- How the shapes are placed into
ShapePaintableobjects so that the desired fill and draw colors may be set as well as the stroke thickness - How the corresponding 7 shape labels are created using
TextPaintableobjects
10/5/06: GeneralPath Shapes
Java has a very powerful shape class named GeneralPath
that permits a user to build up a shape as a path consisting
of segments that may be added incrementally. JPT builds on this
foundation to provide several shape related classes that improve
convenience. First, let us describe GeneralPath.
The class GeneralPath has three constructors that are
the most useful.
GeneralPath() that creates an empty path
with the default winding rule WIND_EVEN_ODD.
GeneralPath(int windingrule)
that creates an empty path
with the specified winding rule.
GeneralPath(Shape shape)
that initializes the path using the data in the given shape.
What is the winding rule? This is a constant that specifies
the mathematical algorithm that will be used to fill the shape with
color or paint should that be desired. The even-odd rule is
the easiest to compute. For each horizontal line, you travel from
left to right and consider a point to be within the shape if you
have crossed the path an odd number of times. The more subtle rule
is based on the so-called winding number. A point is viewed
as within the path if the path winds around the point a non-zero
number of times. This rule is harder to compute but there do exist
reasonable methods to accomplish the task. Java prefers the easier
rule WIND_EVEN_ODD but as a mathematician I prefer the
rule WIND_NON_ZERO which may capture more points. If
the path does not cross itself, both rules give the same result.
Java specifies the winding rule to use via 2 integer constants that are named as follows.
GeneralPath Constants GeneralPath.WIND_EVEN_ODD: 0 GeneralPath.WIND_NON_ZERO: 1
The winding rule may be changed after a GeneralPath is
constructed by the method setWindingRule.
What makes GeneralPath so powerful is that you can build
up a path incrementally by one of 5 operations.
path.moveTo(x0, y0); path.lineTo(x1, y1); path.quadTo(x1, y1, x2, y2); path.curveTo(x1, y1, x2, y2, x3, y3); path.closePath();
When a path is being constructed, the first call must be to the
method moveTo to initialize the start point of the path.
If one of the other 4 operations is called first, the class will throw
an exception.
A minor annoyance in Java is that the parameters must be
float rather than double. This decision
goes back to the early days of Java when programmers were quite
concerned about saving space and performance. Nowadays, with more
memory, space is not such a concern, and with mathematics chips it
is actually faster to compute in double than in float.
The main impact of using float occurs when you write
code and want a float constant. If you write, for
example, 2.397, this will be interpreted as
double. To inform Java that you want a
float, you must write 2.397f. The
extra f characters can be bothersome when you forget
them. There is no need for the f if the constant is
an integer.
We have created a sample file
PathShapes.java
that should help you to understand how to use the class
GeneralPath.
The first method in this class is ShowQuadShape that
creates a GeneralPath that moves to a start point
x0,y0 and then constructs a quadratic curve using the
4 parameters x1,y1,x2,y2.
public void ShowQuadShape
(float x0, float y0,
float x1, float y1,
float x2, float y2)
{
GeneralPath path = new GeneralPath();
path.moveTo(x0, y0);
path.quadTo(x1, y1, x2, y2);
Graphics2D g = window.getBufferGraphics();
Color fill = Colors.transparent;
Color draw = Colors.black;
Path.showShapeStructure(g, path, fill, draw, 2);
window.repaint();
}
Here is the input panel in which the 6 parameters are specified.
Here is the screen snapshot of the quad curve corresponding to these 6 input parameters.
Notice that the middle parameters x1,y1 correspond
to a control point that is not on the curve itself. The
control point together with the end points form a tent-like
structure and the curve falls within the tent. We plan to talk
about the algorithm that draws the curve in class since it is
much easier to explain the drawing process live.
If you examine the graphics code, you will see a call to the JPT
method Path.showShapeStructure. This method shows
a path, the data points on the path, the control points, and the
polygonal frame that connects the data and control points. The
path may also be filled but in the above example the fill color
was set to transparent. Behind the scenes, this method makes a
special Paintable object that displays all of the
features.
The next method in this class is ShowCubicShape that
creates a GeneralPath that moves to a start point
x0,y0 and then constructs a cubic curve using the
6 parameters x1,y1,x2,y2,x3,y3.
The portion of the code for this method that creates the cubic curve is:
GeneralPath path = new GeneralPath();
path.moveTo(x0, y0);
path.curveTo(x1, y1, x2, y2, x3, y3);
Here is the input panel in which the 8 parameters are specified.
Here is the screen snapshot of the cubic curve corresponding to these 8 input parameters.
Notice that the middle parameters x1,y1
and x2,y2 correspond to a pair of
control points that are not on the curve itself. The
control points together with the end points form a zig-zag
structure and the curve falls along this strructure. Again,
we plan to talk about the algorithm that draws the curve in
class since it is much easier to explain the drawing process
live.
Our discussion of the drawing algorithms will be assisted
by additional methods ShowQuadSubdivision and
ShowCubicSubdivision that we will explain in
class.
It is quite interesting to apply the above techniques to see how Java draws a circle. In fact, its turns out that Java uses 4 cubic segments to define a close approximation to a circle that can be drawn much more quickly. Here is the code:
XCircle circle = new XCircle(200, 200, 100);
public void CircleShapeStructure() {
window.clearPanelAndSequence();
Graphics2D g = window.getBufferGraphics();
Color fill = Colors.green;
Color draw = Colors.black;
Path.showShapeStructure(g, circle, fill, draw, 2);
window.repaint();
}
Here is the screen snapshot when this method is run:
This snapshot clearly shows that the circle is in fact approximated by 4 cubic segments.
It is interesting to “reverse engineer” the circle
shapes to discover the 4 cubics used. To help with this, there
is a method Path.shapeToString that will extract
the internals of the shape and put this data into a string that
may be printed. Here is the result of that process.
Circle Path Data WIND_NON_ZERO MOVE[300.0;200.0] CUBIC[300.0;255.22847;255.22847;300.0;200.0;300.0] CUBIC[144.77153;300.0;100.0;255.22847;100.0;200.0] CUBIC[100.0;144.77153;144.77153;100.0;200.0;100.0] CUBIC[255.22847;100.0;300.0;144.77153;300.0;200.0] CLOSE[]
This print out shows that the circle with center
(200,200) and radius 100 is being
constructed as follows by Java.
GeneralPath path = new GeneralPath(); path.moveTo (300.0f, 200.0f); path.curveTo(300.0f, 255.22847f, 255.22847f, 300.0f, 200.0f, 300.0f); path.curveTo(144.77153f, 300.0f, 100.0f, 255.22847f, 100.0f, 200.0f); path.curveTo(100.0f, 144.77153f, 144.77153f, 100.0f, 200.0f, 100.0f); path.curveTo(255.22847f, 100.0f, 300.0f, 144.77153f, 200.0f, 300.0f);
By naming the important parameters, we can take a more abstract view of this circle data and come to a better understanding of how it is structured. Let:
x be the x-coordinate of the circle center
(200 in the example)
y be the y-coordinate of the circle center
(200 in the example)
r be the radius of the circle
(100 in the example)
k = 0.5522847f be the cubic control constant for a circle
s = kr be the radius of the circle multiplied by
the constant k (55.22847f in the example)
We will now describe the 13 points that occur in the above specifications for a circle in more abstract terms. By a vertex, we mean a point that lies on the path. By a control, we mean a point that controls the shape of a curve in the path but is not on the path.
| x-coordinate | y-coordinate | type | location |
|---|---|---|---|
x + r |
y |
vertex | east |
x + r |
y + s |
control | ese |
x + s |
y + r |
control | sse |
x |
y + r |
vertex | south |
x - s |
y + r |
control | ssw |
x - r |
y + s |
control | wsw |
x - r |
y |
vertex | west |
x - r |
y - s |
control | wnw |
x - s |
y - r |
control | nnw |
x |
y - r |
vertex | north |
x + s |
y - r |
control | nne |
x + r |
y - s |
control | ene |
x + r |
y |
vertex | east |
The above table shows that there is a very systematic way in which
the coordinates of the vertices and control points are computed
based on the constant k. This constant has been
chosen so that the cubic segments will provide the best possible
approximation to the corresponding circular arcs.
We wish to pose a question that you may wish to puzzle about and
then follow up with experiments. An ellipse aligned along the
axes is characterized by two radii, the radius a
parallel to the x-axis and the radius b parallel to
the y-axis.
How would the above structural description of the
circle need to be generalized to provide a similar description
for the ellipse with radii
a and b?
We conclude this section with a final example that represents a more complex path. Its code is:
GeneralPath path = new GeneralPath();
path.moveTo(100, 100);
path.lineTo(300, 100);
path.quadTo(350, 200, 300, 300);
path.curveTo(250, 225, 150, 375, 100, 300);
path.closePath();
path.moveTo(100, 50);
path.lineTo(200, 75);
path.lineTo(300, 50);
path.closePath();
path.moveTo(50, 100);
path.curveTo(90, 300, 10, 300, 50, 100);
path.closePath();
You should compare this code with the screen shapshot below to match points in the code with data and control points in the snapshot. This will make your understanding of paths more complete.
10/23/06: Curve Mathematics
We are going to provide a summary of the mathematics behind the curves
provided in the Java class GeneralPath. We will focus on
cubic curves since quadratric curves follow the same pattern but are
simpler. Mathematically, the ideas generalize further, but Java
provides no built-in implementation.
A cubic curve segment is defined by
a start vertex point
P0,
two control points
P1 and P2,
and an end vertex point
P3.
In coordinates, we may view this as:
P0 = (x0, y0) P1 = (x1, y1) P2 = (x2, y2) P3 = (x3, y3)
Java tends to work with the coordinates. Furthermore, when defining
a cubic segment, x0,y0 are taken to be the current point in
the path and the caller supplies just x1,y1,x2,y3,x3,y3
to the method curveTo. In most cases, we will try to use a
more mathematical notation in terms of points but you must recognize
that the formulas must be converted into pairs of formulas for the
x and y coordinates.
The 4 points P0,P1,P2,P3 define a cubic polynomial curve
P(t) in the parameter t as follows.
P(t) = (1-t)^3 P0 + 3 (1-t)^2 t P1 + 3 (1-t) t^2 P2 + t^3 P3
= P0 + 3 t (P1 - P0) + 3 t^2 (P2 - 2 P1 + P0)
+ t^3 (P3 - 3 P2 + 3 P1 - P0)
Here, following mathematical convention, we separate factors by a
space to indicate multiplication, and we let ^ stand
for the “exponent” operation. Note
that:
P(0) = P0 and P(1) = P3
so the above formula defines a cubic polynomial curve in t
that goes from point P0 to point P3
as t goes from 0 to 1.
Therefore, the points P0 and P3 are vertex
points of the curve.
Furthermore, since the points P1 and P2
influence the cubic curve path, these points may be viewed as control
points.
We will now describe the fundamental subdivision algorithm that
permits the curve to be divided into two segments which may once again
be defined by 4 points. This permits cubic curves to be recursively
constructed with extreme efficiency. We will first describe the
general case where the subdivision is based on a parameter
t and we will then specialize to
t = 1/2 which is what is used in practice.
Let 0 <= t <= 1.
The subdivision algorithm is based on repeated
linear interpolation. In general, if we are given two points
A and B and the parameter t,
then we can compute the point that is the fraction t of
the way from A to B by the formula:
(1 - t) A + t B
Notice that
when t = 0 this formula yields A
and
when t = 1 this formula yields B.
Since the formula is a linear function of t, the result
is called the linear interpolation.
The subdivision algorithm uses repeated linear interpolation. The following 6 points are constructed. We leave you to check the details of the algebra if you are so inclined.
Q0 = (1 - t) P0 + t P1 = P0 + t (P1 - P0)
Q1 = (1 - t) P1 + t P2 = P1 + t (P2 - P1)
Q2 = (1 - t) P2 + t P3 = P2 + t (P3 - P2)
R0 = (1 - t) Q0 + t Q1
= P0 + 2 t (P1 - P0) + t^2 (P2 - 2 P1 + P0)
R1 = (1 - t) Q1 + t Q2
= P1 + 2 t (P2 - P1) + t^2 (P3 - 2 P2 + P1)
S0 = (1 - t) R0 + t R1
= P0 + 3 t (P1 - P0) + 3 t^2 (P2 - 2 P1 + P0)
+ t^3 (P3 - 3 P2 + 3 P1 - P0)
Notice that S0 equals the point P(t) on
the cubic curve. Hence the subdivision algorithm can compute
points on the curve. I consider this a mathematical miracle.
The screen snapshot below illustrates this construction. In the
snapshot t = 1/2.
An even deeper mathematical miracle is what follows. Notice that
we have 2 sequences of points P0,Q0,R0,S0 and
S0,R1,Q2,P3 that may themselves be used to define
cubic curves. The mathematical miracle is that these curves
are precisely the left and right segments of the original curve
when subdivided at the point S0 = P(t).
This is why the process is called the subdivision algorithm.
This property of the subdivision algorithm leads the way to the
use of recursion.
For those of you who are interested, here is a more precise
statement of the mathematical facts. Let
Pa(u)
be the cubic that corresponds to P0,Q0,R0,S0 and
Pb(v)
be the cubic that corresponds to S0,R1,Q2,P3.
Pa(u) = (1-u)^3 P0 + 3 (1-u)^2 u Q0 + 3 (1-u) u^2 R0 + u^3 S0 Pb(v) = (1-v)^3 S0 + 3 (1-v)^2 v R1 + 3 (1-v) v^2 Q2 + v^3 P3
Then the mathematical results which show that the 2 new curves correspond to segments of the original curve are:
Pa(u) = P(u t) Pb(v) = P(t + v (1 - t))
We leave you to check the details of the algebra if you are so inclined.
In practice, the subdivision algorithm is usually applied with
t = 1/2 because division by 2
is extremely efficient. Here are the formulas in this special case.
Q0 = (P0 + P1) / 2 Q1 = (P1 + P2) / 2 Q2 = (P2 + P3) / 2 R0 = (Q0 + Q1) / 2 R1 = (Q1 + Q2) / 2 S0 = (R0 + R1) / 2
When the subdivision algorithm is used to draw a curve or to define a curved region to fill, what happens is that subdivision is applied recursively until it is determined that a segment has been reached that is “flat enough” to be rendered as a straight line. The curve is then rendered as a polygon with many small straight lines.
We have created a demo program to illustrate Cubic Subdivision.
This has an application version
CubicSubdivision.java
and an applet version
CubicSubdivisionApplet.java
that is based on the application.
Access to the Cubic Subdivision Applet .
To play with this demo, use the mouse to drag the red points that
correspond to P0,P1,P2,P3. The structure drawing will
transform smoothly as you drag one of these points. If you wish to
see the numerical data, click to the “Cubic Data” pane.
As we were building this demo, we decided that it would be useful
to students to capture the stages we used in separate text files.
This would allow students to see how the demo was developed. We
will provide links to each such stage file and briefly comment on
what was done in that stage. The Java import
statements have been deleted from these text files.
Stage 1 sets up the basic structure of the GUI. Our goal was
to provide a pane to display the cubic structure and permit
mouse interaction and a pane to provide the data for all of
the points. Since we believed that there was not enough
screen real estate to show both panes at once, we decided to
use a Java JTabbedPane. At this stage, we only
set up the pane for the cubic structure. Our plan was to use
a PaintableSequence to hold the cubic, the 6 lines,
the 10 squares for the points, and the 10 labels.
To avoid problems
of graphics inconsistency, we planned to replace the paintable
sequence entirely rather than update it piecemeal and risk the
chance of a partial screen update. Therefore, this stage also
defined a Tile object that will serve as a wrapper
for the paintable sequence. By setting the bounds of the tile,
we can reserve the right amount of space in the GUI before the
paintable sequence is even built. At this stage, the bounds
were set to 600 by 600.
It turned out that there was an additional annoyance. The default
background for a JTabbedPane turned out to be a
shade of blue rather than simply white. For some reason, none
of the Java commands to change colors had an influence on this
blue background. We decided therefore to place the
Tile into a PaintableComponent whose
background we could set directly to white.
Stage 2 sets up the data for:
- The 10 points
P0, P1, P2, P3, Q0, Q1, Q2, R0, R1, S0. - The 4 colors used to paint the squares, the labels, and the lines.
- The
PlotMarksquare that is used for all squares. - The stroke for drawing the curves and the lines.
- The font for the labels.
- The locator for positioning the labels.
Stage 2 defines the following methods to work with this data.
- The method
subdivide()that computes the pointsQ0, Q1, Q2, R0, R1, S0from the pointsP0, P1, P2, P3using midpoint subdivision. - The method
paintStructurethat creates and installs a new structure consisting of the 10 points, the 10 labels, the cubic curve, and the 6 lines. This method callssubdivide(). - Assorted helper methods for
paintStructurethat create the 10 points, the 10 labels, the cubic curve, and the 10 lines.
At this stage, the points P0, P1, P2, P3 are fixed in their
initial position since there is no mouse code for dragging these points.
Stage 3 adds the mouse behavior. If the mouse is pressed close to one of
the 4 vertices P0, P1, P2, P3 then the particular vertex is
noted and dragging is enabled. As the mouse is dragged, all that needs
to be done is to update the position of the vertex and then call
paintStructure. This demonstrates that adding mouse actions
is easy if the underlying methods are already present.
Stage 4 adds the tabbed pane that shows the values of the 10 points
P0, P1, P2, P3, Q0, Q1, Q2, R0, R1, S0. Originally,
the plan was to use 4 rows for P’s,
Q’s,
R’s,
and S0.
However, this occupied too much horizontal width. Therefore, we
decided to use a vertical layout with 10 rows. The data definitions
show the traces of the earlier design but the new code is valid.
Stage 4 also introduced a check box to control whether or not the labels should be displayed. We found in experimentation that it was sometimes hard to see the geometry due to the labels. Thus, the ability to hide the labels was deemed essential.
Stage 5 was added to tidy up loose ends. After making the applet
file, we decided that the cubic structure pane should be 500 by 500
rather than 600 x 600. This required us to change the coordinates
of the some of the P’s. We also noticed a bug
in that the mouse would not always drag a P-square.
We realized that this was due to a difference in the size of a
square from the distance used to test mouse closeness. We made
both numbers equal.
10/26/06: Polygons and Cubic Curves
In the two previous sections, you have seen how to use the Java
class GeneralPath to make shapes consisting of lines,
quadratic curves, and cubic curves. Since you may also introduce
move and close operations, you can make shapes with multiple
sections. This is very powerful and general. However, there is
no simplification in Java for constructing a shape that consists
of a single section that is made up of only lines (a polygon) or
only of cubic curves segments. In this section, we will show you
how JPT makes these special cases easier.
Since a polygon consists of lines, there is no need for control points. On the other hand, to define a cubic curve one must either specify control points or employ an algorithm that will decide what the control points should be based solely on the vertex points. If such an algorithmically automated process provides control points that are “good enough” then much effort can be saved by the programmer and the user.
This section will focus on two JPT classes PolygonShape
and AutomaticCurve that are the most useful in
practice. JPT offers several other classes for more special needs.
Before we get into details, let us show a screen snapshot of the demo program that will illustrate these ideas.
As you can see, the program will create a random shape that is either a polygon or a cubic curve with between 3 and 18 vertices. If you have selected one type of shape on construction you can switch to the opposite type by clicking on the shape radio button choices. Using the mouse, you can drag any of the vertex points to see how the shape changes.
There are two choices that apply to both polygons and cubic curves, namely, the decision about whether the shape is closed or open and the decision about the winding rule that determines the filled region. In this demo, you can flip these choices after the shape is built and see visually exactly what happens. We have discussed the winding rule above and the main thing to keep in mind is that “wind-non-zero” potentially considers more points to be in the shape region than the simpler rule “wind-even-odd”.
In the case of a cubic curve, you must also choose the algorithm that
automatically computes the control points from the vertex points. JPT
provides two such algorithms. The Bezier strategy chooses the control
points to achieve “smooth second derivatives”. Once you
learn calculus, this smoothness condition will make sense. The other
strategy is the chord strategy that computes the control points using
the chord joining the two adjacent vertices as a guide. This chord
vector is multiplied by a numerical factor that controls how tight the
curve is at each vertex. The smaller the factor the tighter the curve.
Factors between 0.25 and 0.333333 tend to
work best but you can experiment with other factors even negative ones.
The option to show labels lets you see where the polygon or curve starts. This is useful when you switch from a closed to an open shape.
Here is the source code for the Polygons And Cubics application
PolygonsAndCubics.java
and here is the additional code for the applet
PolygonsAndCubicsApplet.java
that is based on the application.
Access to the Polygons And Cubics Applet .
We will next talk about how to build a polygon shape using the JPT class
PolygonShape. This class has several constructors but the
most general one is:
public PolygonShape
(float[][] vertex,
ClosureMode closuremode,
WindingRule windingrule)
The options for ClosureMode are:
ClosureMode.CLOSEDClosureMode.OPEN
The options for WindingRule are:
WindingRule.WIND_EVEN_ODDWindingRule.WIND_NON_ZERO
Rather than use integers to specify these choices as in Java, we follow the modern practice of using classes with constant objects for each choice. This means that if you accidentally reverse the order of the choices in the above constructor, you will get a compiler error instead of merrily going forward to discover the error at runtime.
The data points for the polygon are specified by a 2-dimensional array
of float that should have some number n of
rows and have exactly 2 columns.
We have a sample file that illustrates some simple polygons:
PolygonSamples.java.
The first excerpt from this file shows how to make the data for a diamond shape directly.
Color fill = Colors.red;
Color draw = Colors.black;
float[][] diamond =
{ { 200, 100 },
{ 300, 200 },
{ 200, 300 },
{ 100, 200 }
};
PolygonShape diamondShape =
new PolygonShape(diamond);
ShapePaintable diamondPaintable =
new ShapePaintable
(diamondShape, PaintMode.FILL_DRAW, fill, draw);
public void ShowDiamond() {
window.clearPanelAndSequence();
window.appendPaintable(diamondPaintable);
window.repaint();
}
The screen snapshot corresponding to the diamond code is:
The main point of the diamond example is that the diamond
data can be entered directly in the definition of the
float array. Thus, an array in Java can be
initialized quickly in manner somewhat like a list in
Scheme.
It is also possible to initialize a float array
algorithmically. This is illustrated by the following
example of a star.
float[][] star() {
float[][] data = new float[5][2];
float x = 200;
float y = 200;
float r = 100;
int angle = 0;
int delta = 144;
for (int i = 0; i <= 4; i++) {
float dx = r(float) MathUtilities.sindeg(angle);
float dy = - r(float) MathUtilities.cosdeg(angle);
data[i][0] = x + dx;
data[i][1] = y + dy;
angle += delta;
}
return data;
}
PolygonShape starShape =
new PolygonShape
(star(), ClosureMode.CLOSED, WindingRule.WIND_NON_ZERO);
ShapePaintable starPaintable =
new ShapePaintable
(starShape, PaintMode.FILL_DRAW, fill, draw);
public void ShowStar() {
window.clearPanelAndSequence();
window.appendPaintable(starPaintable);
window.repaint();
}
The screen snapshot corresponding to the star code is:
In the star code, the method star() constructs the
float array that is used to make starShape.
You will also notice that I used a brief constructor fot the diamond
shape but the full PolygonShape constructor for the star.
The reason is that I needed to specify the winding rule
WindingRule.WIND_NON_ZERO since otherwise the star would
not fill properly.
Returning to the Polygons and Cubics demo, the code to construct the shape is algorithmic. The data points are constructed randomly via the following method:
void setRandomPoints() {
int n = getPointCount();
points = new float[n][2];
for (int row = 0; row < n; row++)
for (int col = 0; col <= 1; col++)
points[row][col] =
MathUtilities.randomInt(0, size);
}
Here points is a float array member data
variable defined earlier and size is the window size.
Once the points are defined, the shape is updated by the following method:
void updateShape() {
String type = getShapeType();
ClosureMode mode = getClosureMode();
WindingRule rule = getWindingRule();
if (type == POLYGON) {
shape = new PolygonShape(points, mode, rule);
}
else {
String tangenttype = getTangentType();
if (tangenttype == BEZIER)
strategy = Tangent.bezierStrategy();
else {
float f = factorView.demandFloat();
strategy = Tangent.chordStrategy(f);
}
shape = new AutomaticCurve(points, strategy, mode, rule);
}
paintShape();
}
As you can see, this method calls various helper methods that extract
the current user settings from the GUI. In the case of a cubic, the
AutomaticCurve constructor requires an extra parameter
that provides the tangent strategy. This strategy encapsulates the
algorithm that computes the control points from the vertex points.
The two strategies built into JPT are found in class
Tangent as is seen in the above code.
The main constructor for an AutomaticCurve is similar to
that for a PolygonShape except that it has two extra
parameters, an array to specify end tangents if the curve is open and
the tangent strategy parameter:
public AutomaticCurve
(float[][] vertex,
float[][] endTangent,
Tangent.Strategy tangentstrategy,
ClosureMode closuremode,
WindingRule windingrule)
The endTangent parameter allows one to specify the end
tangents if the cubic curve is open via a float[2][2].
The usage of this parameter is more subtle and you may safely omit
it for now. Thus, the constructor we recommend is:
public AutomaticCurve
(float[][] vertex,
Tangent.Strategy tangentstrategy,
ClosureMode closuremode,
WindingRule windingrule)
Here is a little puzzle related to the star. Can you make the star
shape by drawing only the outer outline? If you think about it, you
will need 10 data points not 5 since you need to explicitly know the
intersection points of the 5 star edges. You need some math to
actually figure out the coordinates of the intersection points. It
is also necessary to get the 10 points into a float
array in the correct order. See if you can do it!
11/13/06: Concentration Game
The Concentration Game was originally posted on the JPT 2.3.5 site about two years ago. The code here has been carefully refactored to improve its structure and readability.
In the Concentration Game, the user is presented with a square array of hidden tiles. Upon clicking on a tile, its image is revealed. Upon clicking on a second tile, its image is also revealed. If the two tiles match, then they will continue to be visible. If they do not match, then, on the next click, they will once again be hidden. By remembering the location of tiles already shown, the user can make matches more easily as the game proceeds. The goal is to match tiles with the fewest number of guesses.
In this implementation of Concentration, the user is first presented with a panel that allows a choice of settings.
Using the settings panel, the user may select the choice of images (shapes, photos, letters of the alphabet, or numbers). The user is also given 4 choices of grid size (3-by-3, 4-by-4, 5-by-5, or 6-by-6). The limit of 6-by-6 is based primarily on limitations of screen real estate.
Here are screen snapshots (at 50% of full size) showing samples of the shapes and samples of the photos.
When the images are letters or numbers, the game appears to be more difficult since the memorization is more purely intellectual and has less of a visual component.
The concentration game is available both as an application and as an applet.
Access to the Concentration Applet.
Most of the code for the Concentration Game is the same for the application and the applet. A notable difference is the code for obtaining the images. The application uses images in a directory on the local disk whereas the applet uses web images. Since it is possible that the images may fail to load, the program will revert to shapes if images are not available. We now provide the Java code.
Code Common to the Application and the Applet
The Java file
ConcentrationSettings.java
This class manages the choice of user settings and also gathers all images once and for all so that any version of the Concentration Game may be easily created.
The Java file
ConcentrationGame.java
This class selects the images that will be shown in the tiles and arranges that each image occurs an even number of times so that matches are always possible. This class also creates the GUI for the game.
The Java file
GameTile.java
This class creates a special tile that can selectively show its contents and can control whether or not the mouse is active in the tile.
The Java file
GameShapes.java
This class creates the images corresponding to filled shapes.
The Java file
GameText.java
This class creates the images corresponding to text, that is, the images for letters and numbers.
Application Specific Code
The Java file
GameImages.java for the application.
This class loads the photos from a directory on the local disk for use in the application.
The photos should be 100-by-100 pixels since only that much of each photo will be displayed.
Here is a zip file with the photos:
images.zip.
The same photos are in fact used on the web site for the applet version of the program.
Applet Specific Code
The Java file
GameImages.java for the applet
This class loads the photos from a web directory for use in the applet.
The Java file
ConcentrationApplet.java
This class launches an applet with the ConcentrationSettings as its GUI.
Notes
There are two files with the same name, GameImages.java,
one for the application and one for the applet. The class
ConcentrationSettings makes a static reference to the
class GameImages so by substituting a different file for
the application and for the applet we can easily arrange that the
photos come from different sources.
11/17/06: Random Shape Demos
In class, on November 16, 2006, in response to a student question, I developed two versions of a program that add random circles to a graphics pane each time a button is clicked. Each program installs mouse actions that enable the circles to be dragged with the mouse. When a circle is clicked it moves to the topmost position so it is above all other circles. To the user, both programs appear to have the same GUI and the same behavior.
We will provide a link to the source of each program and then describe briefy the particular features.
The Java file
RandomShapeClass1.java
This program uses BufferedPanel to create its graphics pane.
Since a BufferedPanel has a built-in
PaintableSequence is is easy to add individual circles as
ShapePaintable objects to the sequence. Furthermore, a
BufferedPanel has a built-in set of mouse actions that will
allow the user to drag the items in the sequence with the mouse. Thus,
to get this mouse behavior requires a single line of code. In total,
except for import statements, the entire program is one screen of code.
The Java file
RandomShapeClass2.java
This program manually constructs a graphics pane using a
PaintableSequence that is placed in a Tile
that is in turn placed in a PaintableComponent. The default
bounds of the Tile are set at 500-by-500 so that it maintains its size
regardless of what is in the internal PaintableSequence. In
comparison with the first version of the program, there is only a tiny
change in the code to add a new circle. Where the work comes in is with
the mouse actions since these are not built-in. We must define methods
to implement the mouse press and drag behavior, mouse action objects to
encapsulate these methods as objects, and installation code to attach
the mouse actions to the paintable component wrapper. In total,
except for import statements, the entire program is two and a half screens
of code. You may view the extra code as revealing the kind of code that
is built into BufferedPanel and available automatically.
11/17/06: Dice in Java and Scheme
In Exercises 1, I asked you to build a small demo program that randomly tosses a pair of dice. Here is a screen snapshot of my solution to this problem and its source code.
The Java file
Dice.java
Last evening, Tim Klopotoski sent me an interesting program in Scheme that tosses dice. His words: “Very short. Very beautiful.” Tim explained to me a fact that I considered astounding: Scheme can embed image files into Scheme code. There is no need to reference images via file names or web links. Tim's GUI building is a tour-de-force since he, in effect, manually creates a button and tests for a mouse click. Bravo.
The Scheme file
dice_tosser.scm
11/20/06: Spinning Animation
The screen shapshots below show the control panel for launching spinning animations and one example of a spinning animation.
The Java file
SpinningMethods.java
Class SpinningMethods launches several different
spinning animation examples. The user can in fact have a
number of such animations running at once within the limits
of available processor cycles.
The Java file
SpinningAnimation.java
Class SpinningAnimation defines a panel that can
spin an arbitrary Paintable object. The class is
similar to
BallAnimation above
but is more general since it accepts an arbitrary paintable.
Furthermore, the rotation may be reversed instantly with the
click of a radio button.
The Java file
RegularShape.java
Class RegularShape has static methods to construct
a regular polygon shape and regular star shape.
Star Geometry and Trigonometry
Let us explain in detail the geometry and trigonometry that is
involved in the construction of the star shapes. Each star is
characterized by its center (x,y), its outer
radius r, the number of outer vertices,
and the jump between vertices that determines the
specific star shape. In the snapshot below, vertices equals
7 and jump equals 3 so this would be
described as a 7,3 star. Our goal is to draw the
star as an outline so you can see that we not only need the outer
vertices we also need an equal number of inner vertices. The
inner vertices are determined by where the lines joining the outer
vertices would cross. We have drawn a dashed line to suggest
one such line joining a pair of outer vertices separated by the
jump.
In the diagram, C is the center of the star,
A is the topmost outer vertex,
B is the next inner vertex clockwise from A,
and Z is the outer vertex jump steps from
A.
We are given the outer radius r.
We can compute B if we can compute the
inner radius s from C to B.
We will do this using geometry and trigonometry.
First, the total number of vertices (outer and inner) is
V where:
V = 2vertices
Then, the angle C, in the triangle CAB is
given by:
C = 360.0 / V = 180.0 / vertices
Next, consider the large isosceles triange CAZ. The
large angle at C in this triangle is given by:
C_Large = 360.0jump / vertices
Since CAZ is isosceles, we can compute the angle at
A as:
A = (180.0 - C_Large) / 2 = 90.0 - 180.0jump / vertices
Then, using triangle CAB, we can compute the angle
B as:
B = 180.0 - A - C
Now, using the “Law of Sines” from trigonometry, we have the equation:
r / sindeg(B) = s / sindeg(A)
or
s = rsindeg(A) / sindeg(B)
This is the formula used to compute the star shape in the Java class
RegularShape.java.
Aside: This is yet another example of why students of computing should know some mathematics. Mathematicians have spent thousands of years discovering some nifty things that are quite useful in computing.
Added 12/1/06: Spinning Animation Applet
Access to the Spinning Animation Applet
The Java file
SpinningAnimationApplet.java
3/2/07: Functions Plotter
Access to the Functions Plotter Applet.
Access to the Directions and Discussion for the Functions Plotter Applet.
3/25/07: JPT Image IO
The class JPTImageIO builds on the methods
introduced in Java 5.0 in the class ImageIO.
See the javadocs for that class for more details.
The class provides a static method to write images based on the following entities:
JComponentPaintable
The entity RenderedImage that is used in
the class ImageIO is also supported.
The class has useful helper methods that are public.
In Java 5.0, the following write format names are supported:
BMP JPEG JPG PNG WBMPbmp jpeg jpg png wbmp
Experiments show that writing images using bmp
or png formats produces high quality images but
that writing images with jpg produces images of
low quality with many artifacts.
Below is an example of a star image on a red background that
was saved using bmp:
Below is an example of a star image on a red background that
was saved using jpg:
It is evident that the jpg image produced by the
algorithms supplied by Java 5.0 is far inferior to the
bmp image from the same source. The source in
this case was an image painted in a BufferedPanel.
The Java file:
JPTImageIO.java
The Java test file:
Methods.java
The Java helper file that defines the star shape:
RegularShape.java
4/1/07: Keys, Animation, Threads
On November 18, 2005, we posted a demo that illustrated the tracking of key press-release states and the use of such tracking for animation. This demo utilized several helper classes that were later introduced into JPT during Summer 2006. Below we show a screen snapshot of the demo and then give the slightly revised source code that uses the JPT classes.
The Java file:
KeyTrackerDemo.java
This demo uses two separate threads in addition to the standard “GUI thread”.
The first thread examines the state of the five keys of interest (up arrow, down arrow, left arrow, right arrow, and shift) and echoes this state into a small paintable that is shaped as a large plus sign with 5 tiles. When the shift key is pressed, the center tile is highlighted. When an arrow key is pressed, the corresponding tile (N, S, W, E) is highlighted. The purpose of this thread is to show whether or not a key press or release has been detected by the software.
The second thread moves the paintable square if at least one arrow key is pressed. The purpose of the second thread is to have a longer delay if the paintable is being moved so that the movement delay is not forced to be the same as the much smaller delay that is used to signal detection of a key press or release. This design keeps the square from moving too quickly.
This demo illustrates the fact that one may launch multiple threads besides the default “GUI thread”.
In the demo above, the two threads run for the duration of the program. The methods class below, illustrates a short-term thread that runs only as long as is needed to animate a ball moving up and then moving down.
The Java test file:
Methods.java
4/1/07: Counter Animation
The Counter Animation in similar to the Ball Animation but increments
numbers in a TextPaintable instead of moving a shape.
Therefore, we will just provide access to the applet and the source.
Access to the Counter Applet.
The Java file:
Counter.java
The Java file:
CounterApplet.java
4/5/07: Stack Trace, String Viewer, Bad GUI Applets
The goal of this example is to provide a utility that will
return the stack trace associated with a
Throwable object, normally, an
Exception object. This utility is defined in
the class:
The Java file:
StackTrace.java
Of course, a stack trace is usually multiple lines of text, some of which are quite wide. Hence it is convenient to have additional utilities that will display a string in a scroll pane that is in turn placed into a frame or into an OK dialog box. These are provided in the class:
The Java file:
StringViewer.java
The sample test application does nothing but throw an exception:
The Java file:
BadGUI.java
This bad GUI application is in turn called by an applet that has extra code to trap and display the exception stack trace.
The Java file:
BadGUIApplet.java
This is a model for how to set up an applet if you wish to catch and display exceptions that occur as the GUI is being constructed.
Here is the access to the BadGUIApplet. You will see that an exception is thrown and that the message is displayed in a dialog box.
Here is the heart of the html code for the BadGUIApplet
web page. It is parallel to the applet code for all other applets
on this site.
<h1> Bad GUI Applet </h1> <h4> The applet on this page intentionally throws an exception in the course of building its GUI. The purpose is to demonstrate the technique for viewing the exception stack trace in an OK dialog box. </h4> <p class="centered"> <applet codebase="./bin/" code="BadGUIApplet.class" archive="http://www.ccs.neu.edu/jpt/archive/2.5.0/lib/jpt.jar" width="100" height="100" > </applet> </p>
4/12/07: Ball Animation with SimpleThreadedAction
In earlier examples with animation and threads, it was
necessary to build a SimpleAction and pass it
to a wrapper class ThreadedAction in order to
force the action to execute in a separate thread. As we
have seen, the separate thread is the key to running the
animation with appropriate pauses while the GUI remains
alive.
Recently, my mental light bulb went on and I realized that
it would be very convenient to build the action and make
it run in a separate thread all in one step. This was
easy to do and is available in the class below. The class
SimpleThreadedAction will become part of JPT
in the next release.
The Java file:
SimpleThreadedAction.java
Here is the revised BallAnimation class.
The Java file:
BallAnimation.java
In the prior version of BallAnimation, the
animation action was set up as follows:
private ThreadedAction runAction =
new ThreadedAction
(new SimpleAction("Run Animation") {
public void perform()
{ runAnimation(); }
});
This code is now simplified to:
private SimpleThreadedAction runAction =
new SimpleThreadedAction("Run Animation") {
public void perform()
{ runAnimation(); }
};
Although, this is only slightly shorter, it is conceptually much simpler.
The applet code is in fact the same as before.
The Java file:
BallAnimationApplet.java
Here is access to the applet: Ball Animation Applet.
4/12/07: Ship: Graphics Composite
A key question in building graphics entities for more complex programs such as games is how to build composites, that is, entities that are made up two or more fragments that build the entity. Here is a trivial example of what we mean, a space ship with the rocket thrust coming from its rear.
Certainly, this is the world's most simple-minded space ship but it is adequate to illustrate the idea of a composite of two or more fragments, in this case, the silver triangle that represents the space ship body and the red thrust rectangle that acts as a tail.
The natural JPT class to use to build a composite is
PaintableSequence since it can collect one or more
Paintable fragments that may be drawn in the order
specified during construction.
The class Ship1 given below is a derived class of
PaintableSequence. It uses the constructor to put
together the fragments that combine to make the space ship.
The Java file:
Ship1.java
From the point of view of abstraction, however, the above solution
is a hack since we do not want to view a ship as a
PaintableSequence to which other random fragments may
be added.
We would prefer to build the ship internally and force the outside
world to see it simply as a Paintable entity. This
can be accomplished using the class
PaintableSequenceComposite.
This is done in the class Ship2 given below.
The Java file:
Ship2.java
The key to this design is that the class Ship2 can
access an internal PaintableSequence via a protected
get method but callers in the outside world do not have access to
this sequence. This makes the Ship2 object an object
that uses parts in its construction but whose parts cannot be seen
from the outside.
Here is the test class. To distinguish the ships built from each
class, the Ship1 object is rotated 30 degrees
and the Ship2 object is rotated 60 degrees. In each
class, the turn method illustrates how to add simple
extra functionality.
The Java test file:
Methods.java
5/2/07: LayeredComponent
A significant annoyance in classic Java is that the background of
a component or a panel as set via setBackground can
only be a Color. It would be much more convenient
if the background could consist of images, shapes, text, paints,
etc. This problem is solved via a new class
LayeredComponent that will be incorporated in the next
release of JPT.
A LayeredComponent takes the following ingredients:
- A
foregroundobject that is aJComponentwhich contains the widgets that constitute the foreground of the layered panel. Most often the foreground will be a panel of some sort. - A
backgroundobject that should either be aPaintableor be convertible to aPaintableviaComponentFactory. The background object will form the main content of the background of the layered panel. If the background isnullthen it is ignored. - A
paintobject that will paint on the areas of the background of the layered panel that are not painted upon by the background object. If the paint isnullthen it is ignored. - An
opacityparameter that determines the opacity of the background. Often it is desired to paint a background with opacity less than 1 in order not to overwhelm the foreground content. The opacity affects both the background object and the paint. - A
centeredparameter that determines if the background object should be centered relative to the foreground component.
As you can see, LayeredComponent is general enough to
provide for both background objects, background paint, or both.
Below is the source:
The Java file:
LayeredComponent.java
Here are two screen snapshots from test code.
|
|
| Background at 50% opacity | Background at 100% opacity |
The test code requires 2 images, one of apples in the foreground and one
of a tree in the background. You will notice 2 controls that allow you
to vary how the background is rendered. The slider allow the user to control
the opacity. The radio buttons allow the user to decide whether or not
to center the background object relative to the foreground. This allows
easy experimentation with the LayeredComponent widget.
In practice, of course, the designer would normally make these settings.
During testing, we experimented with placing the slider and radio buttons
within the layered component and setting their own backround to a
transparent color. However, when a user interacts with such controls,
Java makes subtle changes in rendering that produce strange artifacts
because Java assumes that that the control background is opaque and that
rendering shortcuts may be taken. This is therefore a limitation on the
usage of LayeredComponent.
Below is the test code:
The Java test file:
TestLayeredComponent.java
Since the test process requires images (which are kept in an images folder), we provide all of the pieces in a zip file.
The complete zip file:
LayeredComponent.zip