© 2000-2003 A-Square, Inc. Cambridge, MA
Banner3 in its several variations is considered in Exercise 3 of the Introduction to Java Tutorial
final public class Banner3 extends Universe
Section 1 Identifying the universe
Section 2 What the universe does
init() and reset()
importing a gif-file in Banner3d
public void cycle(Graphics g)
public void manageChange(uParameter uP)
Section 3 uParameters as used in the universe
private void setParams()
Modification of the current value of a uParameter
Banner3 extends Universe which is an abstract Java class. The Universe class the key to the Java implementation of Rart. We shall here only comment on the specifics of Banner3. A more general discusssion of Universe is featured in Exercise 4.
The source code of Banner3 as listed would execute as required in Exercise 3a. However, the code includes a number of modifications related to other excercises (Ex3b through Ex3d) which have been commented out. In these comments we will ignore most of these modifications and mostly consider only the code which is executed in Exercise 3a.
final public class Banner3 extends Universe
Banner3 is a class that extends Universe. It is also specified as final. final means that the class can not be subclassed, that is extended by any other class. The reason is that we see any Rart universe as being selfcontained and complete. Someone who wishes to build a universe with similar characteristics would be better off requesting the sourcecode from the original rartist and modifying the source code.
A Rart universe will, we believe, eventually be one of many. For this reason it is important that there is credit given to the rartist. We also would like the rartist to provide a short description in words about the nature of the universe. This gives the rartist the opportunity to explain her/his intent and may also be a help when an observer is looking for a new universe to play with.This information appears in an an About Dialog that is initiated in rartrunner RRL with a long mouseclick.
The required methods are.
public String getName(); - returns the name of the universe, which does not necessarily have to be the name of the class file.
public String getDescription(); - returns a description;
public String getRartist(); - returns the nameof the rartist. I have started to add the year when the universe was first programmed. That is, of course, a common practice among artists. We believe Rart will be around long enough that the year of origination may be more than of passing interest.
init is prescribed for a universe just like it is prescribed for an applet and for the same reason. There are some activities that are done at startup and we prescribe its name and signature so that the rartrunner can exeute it as one of the first actions when starting up a universe. We have in Banner3, identified two types of startup activities. One relates to parameters, which have to be given initial values as described in Section 3 which deals with uParameters. The other startup activity will in this case and in many other cases be the same as when something has changed in the environment and so we have a private method, reset(), that takes care of any such changes both when the universe is started up for the first time and when it has to be restarted for some other reason.
For Banner3 (as used in Exercise 3a) the reset() method looks as follows:
private void reset(){
// Note: The two variables xm and ym are available in the parent class
// Universe. They give the size of the available window at all times.
logo = new ASquareLogo(xm, ym, size);
logo.setMaxSpeed(maxv);
} // end of reset
The only thing that needs to be done is initiating a new logo. As we noted under Logo, Logo sets the maximum speed of the logo based on the size of the window. Strictly speaking, we do not have to give it a value, but we give it here the value of maxv, set to 8 to be consistent with Banner2.
Importing a gif-file in Banner3d
For Banner3d (Exercise 3d) we have to do some things to make sure that the applet imports the gif-file needed for the IEEELogo. This is somewhat tricky because it uses the very general concept of object to get something which you then, because you know what it is, can cast so the object becomes usable to you. You wish ti import a picturefile in gif-format. In the universe the code is part of the init - method and looks as follows:
// Requesting specific resources. In the case of Banner3d, the IEEE logo.
// We have in this case an array of only one element.
String[] logoFilename = {"num3blu.gif"};
resourceObjects = rartrunner.getResourceObjects(logoFilename);
// If we had more then one element, we would use a loop as follows
// for (int i=0; i<resourceObjects.length; i++){.....}
// but in Banner3d only one element which we get as follows:
ieeelogo = (Image)resourceObjects[0];
The signal to the rartrunner is to load in the file with the name "num3blu.gif" and the rartrunner does this essentially without knowing what it is doing. It takes in whatever has been requested and puts it in the array resourceObjects which lives in Universe as instantiated by its descendant, Banner3d. Since we know what the gif file is, and we know it is an imagefile understandable by the Image class, we take that object and cast it to the type Image by writing (Image) in front of it. And we then have an actuall image in the variable ieeelogo and can do whatever one can do with images. Later, for example, we see a not trivial method in Banner3d where we eliminate the white background that the logo comes on. GetRidOfWhite examines every pixel of the logo and converts any white pixel so it becomes transparent.
The cycle method is executed priodically by the rartrunner. In it we describe what the difference is between subsequent frames or, as in this case when we redraw the whole frame, what a frame will look like. Compared to Banner2, however, we do not need to bother about double buffering or changed windowsize. All that is now handled by the RartRunner. Here we clear the window, draw the logo, move the logo and then draw the three strings. In the case of Exercise 3a, the method in its entirety looks as follows:
public void cycle(Graphics g){
// Compare with the update method of Banner2a!
// We reset the whole rectangle (0,0, maxx, maxy)
g.setColor(Color.lightGray);
g.fillRect(0, 0, xm, ym); // Paint the screen light gray
logo.drawLogo(g); // Draw the logo
logo.moveLogo(); // Move the logo
String s1 = "Welcome to Introductory Java";
printString(g, s1, 50, 45, 20);
s1 = "Jan Aminoff, Instructor";
printString(g, s1, 120, 80 ,14);
int nn=number.getCur();
s1 = "Also inventor of Rart, random Art for the Internet.";
printString(g, s1,xm-250,ym-5,10);
s1 = "Java Environment: "+
System.getProperty("java.vendor")+"..."+
System.getProperty("java.version");
printString(g, s1, 5, 15 ,12);
} // end of cycle(g)
A note on printString. The reason we can use printString here is that we have defined it as a utility method in Universe and the Banner3 universe extends Universe. When we were dealing with Banner2 extending the abstract Logo class we could use printString in Banner2 since we had defined printString in Logo.
Compared to Banner2 we also were frustrated by what we thought were incompatibilities, which they turned out not to be. However, we decided to add some information that is actually available at all times as system properties. They are accessed as System.getProperty and even an applet is allowed to know something about its runtime environment!
That was pretty easy and simple, wasn't it. All the relevant method calls collected in one place and all doing simple, understandable things. Now we shall deal with what happens when something changes.
public void manageChange(uParameter uP)
We have elsewhere described how uParameters are defined and how an observer can change uParameter values. manageChange(...) which also is prescribed by Universe handles such changes. In the case of Banner3, there are only three uParameters, two of which are defined always for all universes and one which is defined for Banner3 but not used in Exercise 3a. It is used in Exercises 3b through 3e, however so we will deal with it here. Remember that each uParameter has its unique index which defines its location in the uPs array. The index variables are defined as public static final in Universe, and according to the coding conventions they are given names in all caps. The Banner3 universe has three parameters, with index VIEWSIZE, CYCLETIME, and NUMBER respectively. Section 3 gives the definitions. Here we have to program what to do if any of these parameters are modified.
public void manageChange(uParameter uP){
int type = uP.getIndex();
if (type == VIEWSIZE){ // If the size of the screen has changed.
reset(); // Just start all over
}else if (type == CYCLETIME){ // If the cycletime is modified
cycletime = uP; // Make sure cycletime has right value
}else if (type == NUMBER){ // The number uParameter is used in Exercises 3b through 3d
number = uP; // number has right value
reset(); // Just start all over.
}
} // end of manageChange(uP)
So, if type of change is VIEWSIZE, that is the size of the window has been changed, do the reset(). If we check the code of reset, we see that we generate a new logo which is based on the new windowsize given by the protected variables xm and ym in Universe. And when we come to cycle(g) we see that we really don't do much with the windowsize except that we print the last of the three strings using xm and ym to place it relative the lower right of the window. So we seem to be OK with VIEWSIZE.We do not need to restart Banner3 if cycletime changes since changes due to the uParameter with index CYCLETIME really are handled by the rartrunner. Here we just make sure that cycletime has the right value. With NUMBER we do a reset(). In Exercise3b, we are actually working with ASquareLogo objects stored in an array. The following code, taken from the reset() of Exercise 3b, shows how we use the uParameter number to initiate the maximum number of objects in the array:
for (int i=0; i< number.getMax(); i++){
int s = 10+RND(size);
logoarray[i] = new ASquareLogo(xm, ym, s);
logoarray[i].setMaxSpeed(maxv);
}
That is, we use a for-loop stepping through the logoarray from 0 to number.getMax()-1, to initiate each logo including setting the maximum speed. (The fact that you can use the element notation for an element in the array and immidiately follow it with a period to reference a method inside the element is really cool, if you ask me.). Actually we are really initiating more logos than we will need. A loop is used in cycle(g) to go through the array to draw and then move the logos. This loop is programmed as
for (int i=0; i< number.getCur(); i++){
logoarray[i].drawLogo(g);
logoarray[i].moveLogo();
}
Here we use number.getCur() to extract the current value for the uParameter number and so we draw and move exactly the correct number of logos. I used number.getMax() in reset(). This means that we always have all the logos in the array initiated. But then perhaps we do not need to do a reset() if the current value changes. This possibility is left as an extra exercise to explore by the interested reader. I believe the answer may be yes for Exercise 3b but no for Exercise 3e. But these things you never know unless you test!
We saw immediately above how we handled changes communicated to Banner3 by the two uParameters with index CYCLETIME and NUMBER respectively. (VIEWSIZE exists for all universes and is not actually initiated in the universe itself). Here we shall see how the two uParameters are initiated in the method setParams():
This method is actually excuted once only as part of the init() of the universe. We present it here since this corresponds to the important uParameter section in Universe. setParams() is here printed in its entirety:
private void setParams(){
// First cycletime
// Since "Cycle Time" is a compulsory parameter, the description is
// provided in Universe as CycleTimeDescr.
cycletime = new uParameter
(CYCLETIME,"Cycle Time", 10,50,100, CycleTimeDescr);
// we now have to add the parameter to the array of uParameters
// associated with this universe
adduParameter(cycletime);
// Second number
String ndescr = "Double click to see this parameter change!";
number= new uParameter
(NUMBER, "# of objects",0,5,10, ndescr);
adduParameter(number);
}
The method protected adduParameter(uParameter uP) is defined in Universe. This method adds the uParameter to the array uPs[], which defines the current uParameters of the universe. The elements of this array is what the RartRunners use for communication with the observer as well as with the universe.
For Banner3 we allow the current value of the uParameter cycletime to vary between 10 and 100 milliseconds with 50 as the default current value.
Now, we go to the uParameter number: In this case we have given it a kind of skimpy description. This is because we are running the universe in RRL which does not present the description of the parameter. We allow the current value of the uParameter number to vary from a minimum of 0 to a maximum of 10 with a default of 5.
In rartrunner, RRL, which we are using in Exercise 3, we can not in any way change the current value of cycletime once Banner3 is executing. We can, however, give it an initial different current value by setting the value in the Applet tag of the HTML startup as a parameter with the name of the index variable as HTML parameter name. The following Applet tag accomplishes the setting of the current value of cycletime to 25 milliseconds:
<P><APPLET CODE="RRL.class" WIDTH="390" HEIGHT="114" ALIGN="MIDDLE"
HSPACE="2" VSPACE="2" ALT="( With a java enabled browser you would see animation here)">
<PARAM NAME="UNIVERSE" VALUE="Banner3">
<PARAM NAME="CYCLETIME" VALUE="25">
</APPLET>
</P>
This and the setting of any uParameter current value through the HTML startup for any Applet type rartrunner is only possible because we can use the String of the index variable name in the Applet tag to identify which uParameter we are modifying. This is real deep, and I am proud of it!
However, RRL does have a way of allowing the modification of the current value of the one uParameter with index NUMBER. If you double click, this rartrunner will step the current value of the uParameter with index NUMBER with one tenth of the interval of the range of the uParameter. In this case the rage is 0 to 10 which means that the step is 1. When the stepping reaches maximum, in this case 10, the current value will roll over to the minimum value, in this case 0. This you can try out in, for example, Exercise 3b.
These comments get kind of long, don't they? Take a Break!