© 2000-2003 A-Square, Inc. Cambridge, MA
This exercise introduces you to a bona fide Rart® universe.
In Exercise 3, we considered a very simple Rart universe, Banner3. In this exercise we will consider the Eggs universe which is relatively simple but also visually interesting. The Eggs universe works with visual objects described in the Oval class. See the Eggs universe working in RRlet.
While the Eggs universe is conceptually simple, its programming should be of interest to anyone wishing to create their own Rart universe. Note, in particular, the correspondance between the "abstract" Universe and the "real" Eggs universe.
Inspired by the Eggs, we decided to develop a new universe, the Planes universe, based on Eggs. This is to illustrate the creative process as well as to show how development can proceed. We wish to add, or in this case change, functionality step by step to make testing possible so that we know we always have a viable program that is a working universe dealing with working objects.
What are theExercises About
The Eggs Universe
Exercise 4a
Exercise 4b
The Planes Universe
Exercise 4c
Exercise 4d
Exercise 4e
What you should See
We compile and execute the Eggs universe and the Oval class using the rartrunner RRlet environment.
RRlet is the rartrunner intended to display a Rart universe over the Internet. Through a series of dialogboxes initiated with a long moseclick you can moify all uParameters defined for the universe within the range allowed by the rartist.A fairly complete description should enable you to handle HTML in the applet tag as well as make you comfortable with the use of jar-files for distribution of bytecode for Java classes and other resources. Please take the time to scan through The Mechanics of Display and Distribution of a Rart Universe.
If you feel comfortable, you can also go into the HTML startup and modify the size of the applet window, for example to fill out available screen space, or give other than nondefault values to the uParameters.
We modify the Oval class and get a dramatic change in effect. Consider the following code in Oval (The code appears at the bottom of the listing):
private void drawOval(int k, Graphics g){ // Used in drawBand only int w = k*dx; //k = 0,1, ... nb-1 int h = k*dy; g.setXORMode(ovalColor); g.fillOval(x-w,y-h,2*w,2*h); // oval, size k*dx, k*dy }
This is actually the only place where we use the AWT method fillOval to render the ovals. As it happens, the method to render a rectangle has the same syntax. Change the last statement to the following:
g.fillRect(x-w,y-h,2*w,2*h); // rectangle, size k*dx, k*dy
By this minor change we suddenly get rectangles instead of eggs and the character of the universe changes drastically. Of course, you would now wish to change the name of the "Oval" class to "Rectangle" class and there would be a number of other modifications, including a new name for the universe, in order to have code and names match. The compiler or the interpreter does not mind, however, as long as you use the fillRect method correctly. Also, the change between ovals and rectangles is possible in this case only because the coding references the fillOval method in one place only. An interesting side effect of clever coding, if I may say so myself!
See the unmodified source for Eggs and Oval. Make sure you have your copies in the MyEX2b folder, edit the Oval source, compile, and run.
The Planes universe is derived from the Eggs universe. In the Eggs universe the oval shapes interact using the xor mode of painting. In EX4b, the ovals were changed to rectangular shapes. For the Planes universe, I had the idea of slowly rotating planes suspended in space. The planes are Physical objects and so we do not use the xor mode of painting. The tricky part is to make the planes truly appear to rotate. OK, so if it rotates around a vertical axies in the middle, its apparent width is r*COS(fi), if r is its half width and fi the angle to the plane of the screen, so that is easy. The difficult part has to do with light and color. If you look at a surface at an angle, it looks darker than if you look at it straight on. If light falls on a surface the surface looks brighter if the light fall on it sraight on. But then you also have to consider how reflecticve the surface is. So we need to consider shades of colors from dark to light.
This is the first step, in converting the Eggs to Planes. Almost all text handler have a global search and replace metod. You have to use it carefully, however, and verify that each change makes sense. So you can start by replacing "Egg" with "Plane" in the sourcefiles Eggs2a.java and "Oval" with "Plane" in Eggs2a.java and Oval.java. If you do this and save the resulting programs as Planes4c.java and Plane.java, you have something that should compile after some tweeking.
So now to the changed functionality, much of which takes place in the Plane class. The planes rotate around a vertical axis and so they have a vertical height, hh, which remains constant and a horisontal radius rr, which results in an apparent horisontal size varying with the angle fi of the plane. In Eggs, the size can be greater than the screen, but in Planes they can't so you use the primes array to generate hh and rr to some maximum percentage of the available screen (xm pixels wide, ym pixels high). So now we can reprogram the drawPlane method in Plane, which in EX4c just accepts the foreground color as chosen in the Planes4c universe.
Much of this is done in the constructor of Plane, which now looks as follows:
/** * Constructor for a Plane, rotating around its middle vertical axis * @param xmax Width of present screen in pixels * @param ymax Height of screen in pixels * @param speed Max speed of rotation in degrees per 1000 cycles * @param Color c Basic color of plane */ public Plane(int xmax, int ymax, int speed, Color c) { // We may need Debug to develop this universe dB= new Debug(); int debuglevel=2; // Set level to 1 or 2 for tracing outputs. dB.setLevel(debuglevel); // Center of plane is more than xys away from edge x = xys+RND(xmax-xys); // x center of plane y = xys+RND(ymax-xys); // y center of plane status = INITIALIZED; planeColor = c; // Note that dx and dy are selected independently from the primes array. // Causes great variation in the configurations. hh = (primes[2+RND(13)]*ymax)/200; // max size 40% of window height rr = (primes[3+RND(21)]*xmax)/200; // max size 80% of window width fi = 90; //Angle to plane of screen, starts perpendicular dir = 2*RND(2)-1; // 1 or -1, that is clockwise or counterclocwise dv = (float)(dir*((1.0+RND(speed))/1000)); // Increment angle per cycle // dB.dbg(1,"Debug activated at level "+debuglevel); } // End of Plane constructor
Speed of rotation corresponds to the uParameter size in Eggs. Make the global change, where appropriate, from "size" to "speed" and fix up the description of the uParameter speed.. uParameters are given an integer range. So we say that the parameter determines the speed of rotation as degrees per second. That has to be converted in Planes to degrees per cycle and comunicated to Plane, which knows nothing about the cycle time. So what we send to the Plane as maximum speed of rotation is the product of the current maximum speed of rotation in degrees per second times the current cycletime in milliseconds. This gives the plane a number (an integer!) which is 1000 times too big. once we know this we can generate the constant speed of rotation. And it can go either way, fi goes up from 90 degrees or down.
Finally, the status of rotation: The Plane has an internal state that it communicates to the Planes universe as status. For the Eggs it was more complicated, but for the Planes it is either ROTATING1 or ROTATING2 or finished, DONE. Status goes from ROTATING1 to ROTATING2, when the plane passes 0 (or 180) degrees. As you can see in the drawChange method we are working with both an interger fi and a floating point value ffi. This is because the dv, number of degrees per cycle, value can be less than one and we must take care that we actually change the value of fi or ffi. I had a bug where the angle fi just would not change. Today it all works, however. (May be it does not - - There should be five planes visible in the default situation and there are a varying number. Could be that it does not like negative values for apparent width - - fixed in 4d)
All this is enough of changes and we try compilation and execution until it works.
Need to do some fixings.
It looks silly when on startup all planes start perpendicular to screen. We would like to have them start at
random fi. Solution: do a public method in Plane that allow you to set fi:
public void setFi( int phi), if you check out setFi
in Plane, you see that we limit the angle fi
to between 0 and 360 degrees. Even if we know we will not call it with something else, in principle our terrific
Plane class can be used elesewhere and it is good object oriented practice to ensure that only valid inputs are
passed in.We use the new set - method just after we have created our new planes in the planearray as fllows: planearray[i].setFi(RND(360));
In 4c, the basic plane color is given as a color parameter in the Plane constructor. Since we would like to do our own color manipulation, we would rather have the information about the basic color of the plane in the form of an index to an array of standard Java colors. And we change the plane constructor and the call to the constructor in Planes (Two calls, one in reset and one in cycle). We also copy the text declaring the array into Plane.
The big job, in 4d is to deal with shades of color. I used the results from colorMETestplus, a little utility program I developed to test color aspects of J2ME and which is available on the RART CD. I came up with a method with the following signature:
/**
* Produces a color that is linearly changed from original basic Java
* color to white.
* @ idx index to one of 13 Java colors
* @ sat measure of saturation percent from 0 to 100
*/
private Color shadedColor( int idx, int sat)
I don't think the overall result is quite satisfactory so I will see what can be done when we introduce the concept of a lightsource in next version, Planes4e.
Finally, I felt that the default background color was inappropriate and changed the default to black.
So we continue with the colors. Now, we suppose we have a source of incident light at angle si behind us and infinitely far away. si goes from 0 degrees, parallel to the screen, to 90 straight behind us and 180 degrees parallell to the screen again.. The angle between the rotating plane and the incident light is si -fi. If si is less than 90 but more than fi, the observer sees the plane illuminated with incident light at the angle si-fi. If fi is less than fi, the observer sees the plane in shadow. If fi is more than 90 the observer sees an illuminated plane if si is less then fi, the plane illuminated with incident light at angle .si-(fi-90). If si is more than fi the observer sees the shadow side of the plane. So we shall use the angle of incident light to normal of the plane to vary the brightness, rather than the angle of plane to observer.
Also the determination of the initial angle of the plane in Planes is silly. Instead we change the constructor, so it now has a boolean parameter perpendicular, which when true, causes the plane to start out at 90 degree angle to the screen but when false causes the generation of a strating angle as RND(360). Much cleaner and more object oriented!
And the angle of incident light shall be determined by a uParameter with default value 135 degrees (from behind the observers right shoulder).
Also, when normal to plane cuts in half the angle of incident light and observer, the observer should see a flash of light, because the the plane reflects the incident light right back to the observer. Think of the plane being metallic with some reflectivity.
Also, the planes look better when rotating faster. Also, the variations in size are confusing. Shall have all planes same proportion and not so big. And bigger planes should be nearer and be drawn later. Need to sort planes according to size!
And you don't see the rotation very well, so I decided to add a triangle on the illuminated face. And there is no point in selecting the background, looks just silly so the background is plain black. And the shadowside is dark, a very dark gray. But you may wish to vary the size so there is a new uparameter dealing with the maximum size. My son thought they should rotate longer before done so I added a random selection of the number of degrees as 90+(2+RND(5))*360. It turned out to be a lot of work and there may still be some bugs having to do with angles which are tricky.
For example, my carefully crafted shadedColor method in Plane breaks for negative values of cosine. Took time to find and I don't really know if t has been legitimately fixed. If you check the code for the method you will see that I have added a try catch block to pinpoint the exception. I left it in for a hint what you can do if ever your code gets in trouble.
Overall, you can see that the code has expanded. It is also interesting to note that most of the code is now in the Plane class. We have been working with properties of the planes and so you have a benefit of object oriented programming in that things that belong together are together. For example, the property of angle of incident light is determined as a uParameter in Planes4e. However, it is immidiately (in reset) communicated to the Planes class, (The class of all planes in this universe), through a static method, the angle being a static property. Same with the maximum size.
And can we go on and fiddle? Of course! My doughter in law immediately suggested that you import pictures that rotate. Perhaps we could have the planes rotate around an arbitrarily oriented axis. Perhaps the planes should be more threedimensional, and make them cubes? If you think about it, you will also think of thigs todo, just do them, But remember, try to keep it simple, make any additions to a working universe and try it out. Save intermediate universes as a reference.
And have fun!
See the unmodified Eggs universe execute by clicking on the link above. Note that you actually can change parameters in the universe by giving new parameter values in the HTML startup document. The following links to another eggs startup and you can see the universe with different colors. EggsStartup2: Check it out and experiment with different values for the uParameters. Note, that you have to understand from the source code what uParameter is referenced by what parameter index, VIEWSIZE, NUMBER, PARA6 etc.
See the modified Eggs universe become a universe of Waxing and Waning rectangles. Since about all interesting information about the Oval is in the Oval class, you could perhaps also think of other geometric figures of visual interest, equilateral triangles, five or six-pointed stars etc. Or a universe where the objects can be of several kinds selected at random for each initiation. This is what I mean, that there are endless possibilities even with a simple universe like the Eggs universe.
So we have rotating planes. When the color is constant it looks as if the planes jus grow and recede, no feeling of rotation. Needs more work! But at least you can change the colors! Look awful in yellow and blue.It looks thare are less then the expected numbers of planes runnung.
This looks a little better, but some planes move much to slow. Also there is still not a strong feeling of rotation, The default looks better and when you select un exact number of planes, that is what shows.
Always more to do, but this is pretty decent. Not the best universe ever. Not simple enough but perhaps a starting point for rotating solid bodies.