© 2000-2003 A-Square, Inc. Cambridge, MA
>
/*
* @Planes4e 1.0 030115
*
* © A-Square, Inc.
* 175 Richdale Ave, Cambridge MA 02140
*/
import rrlet.*;
import rartbase.*;
import java.awt.*;
import java.lang.*;
/**
* 'Planes rotating
*
* This Program is one in the Rart®, or Random Art, series of programs
* that generate ever changing pictures. Simple geometries and random
* dynamics create visual effects that may be pleasing to the eye.
* It uses the Plane class for objects, the planes, that fill the screen.
*
* In EX4c, we show how to write a new universe Planes starting witn Eggs
* In EX4d, we make Planes more sophisticated introducing color shades
* In EX4e, we Planes is finalized using more color processing
*
* @version 1.0e 030115
*
* @author Jan Aminoff
*/
public final class Planes4e extends Universe {
// Section 1 Identifying the Universe
// ------------------------------------------------------------------------
public String getName() // Name,
{return("Planes4e");} // no more than 20 letters.
public String getDescription()
{return(sd);} // Description,
// no more than 350 letters describing the universe. Will
// be displayed in up to five lines of about 70 characters.
private String sd=" We shall see how we progressively can add features "+
"to a new universe to make it more visually interesting. "+
"The first version, EX4c, just offers monocolor planes."+
"The second version, EX4d, adds shading. And the final version, "+
"EX4e, adds a source of light, the angle of which can be varied. "+
"Also I added some other stuff. Difficult to stop fiddling!";
public String getRartist() // Rartist,
{return("Jan Aminoff 2003");} // no more than 24 characters.
// Section 2 What the universe does
// ------------------------------------------------------------------------
// About colors - constants and defaults
static Color colors[]= // Standard Java Colors
{Color.black,Color.blue,Color.cyan,Color.darkGray,Color.gray,
Color.green,Color.lightGray,Color.magenta,Color.orange,Color.pink,
Color.red,Color.white,Color.yellow };
private int bc=0; // default number of color for background (black )
private int pc=1; // default number of color for foreground (blue )
private Color background = colors[bc]; // Only one background in e
private boolean resetscreen=false; // Signal to recolor background
// Other variables
private int nnmax = 20; // maximum number of Planes, set in reset
private int nn=5; // actual number of Planes, set as number.getCur()
private Plane pls[]=new Plane[nnmax]; // Up to 30 Planes.
private int ss = 30; // current maximum size
private int vv = 15; // Current max rotation speed in degrees per cycle
private int ci; // Current angle of incident light
private int np; // counting planes
private Debug dB; // used when debugging, especially as dB.dbg(n, string)
// The method init() is prescribed by Universe
public void init(){
dB= new Debug();
dB.setLevel(0); // Set level to 1 or 2 for tracing outputs.
setParams(); // See Section 3.
reset(); // See below.
}
private void reset(){
/** Reset is used in init and also in manageChange when restarting
* the universe after change of some parameters.
*/
// Initialize Planearray
nn=number.getCur(); // Have to use possibly modified parameters.
vv= speed.getCur(); // Max rotation speed in degrees per second
ci = lightangle.getCur(); // Angle of light
Plane.setSi(ci);
//int vc = vv*ct.getCur(); // Max rot in degrees per 1000 cycles
pc=pcolor.getCur(); // Index to colors array.
ss = psize.getCur(); // Maxsize
Plane.setSz(ss);
dB.dbg( 1 ,"In Planes.reset, xm: "+xm+", ym: "+ym);
dB.dbg( 1 ,"In Planes.reset, planecolor is "+colornames[pc]);
int idx = 0;
np = 0;
for (int i=0; i < nn+1; i++) pls[i] = null;
for (int i=0; i < nn; i++) {
//pls[i] = new Plane(xm, ym, vv, pc);
addPlane( new Plane(xm, ym, vv, pc , false));
}
} // end reset
/*
* addPlane makes sure the pls is sorted so the lagest planes
* are rendered last. Requires a new public getSize()method in Plane
*/
private void addPlane( Plane p){
//Go through array until we find a bigger plane than p
int news = p.getSize();
dB.dbg(2, "addPlane-line 110: size "+news);
//if (np>nn)displayPLS();
int i = 0;
Plane pi;
// First find a slot i which is either the first slot where entry is null
//or the first where the entry is greater than news
for( i = 0; i<nn ; i++){
pi = pls[i];
if (pi == null) break;
if (news< pi.getSize()){
// move up existing planes freeing the spot for p
int nn = pls.length;
for (int j = nn-1; j>i; j--)
pls[j]=pls[j-1];
break;
}
} // end i
// move up existing planes freeing the spot for p
pls[i] = p;
// if (np > nn) displayPLS();
np++;
} // end addPlane
private void displayPLS(){ // Just checking the array
for (int k = 0; k<nn; k++)
dB.dbg(1, "index "+ k + " size "+
pls[k].getSize());
nc =0;
}
// This method is prescribed by Universe
public Color getBackground(){return background;}
// The following three variables are important in cycle.
private int m; // Index to current waxing or waning egg.
private int status; // See Plane for the significance of status.
// This method is prescribed by Universe. Everything interesting happens
// in the cycle method which is called periodically by the RartRunner
public void cycle ( Graphics g){
/** In init, nn Planes have been initiated in pls. The planes rotate
* clockvisw and counterclockvise for a total of 360 degrees, when it will
* be replaced with a new plane.
*/
// Reset screen always
g.setColor(background);
g.fillRect(0,0,xm,ym);
// We go through all planes
nn = number.getCur();
for (int i=0; i<nn; i++){
Plane pi=pls[i]; // Current Plane.
status = pi.drawChange(g);
if (status == Plane.DONE){ // If plane rotated 360 degrees,
// compact the pls
int j = i;
while (j<nn){
pls[j]=pls[j+1];
j++;
}
// put a new Plane in Planearray,
//pls[i]= new Plane(xm, ym, vv*ct.getCur(), pc, true);
pi = new Plane(xm, ym, vv, pc , true);
addPlane(pi );
dB.dbg( 1 ,"In Planes,cycle, new Plane at i: "+
i+ ", size " + pi.getSize() );
}
}
} // end of cycle(g)
// Section 3 uParameters as used in the universe
// ------------------------------------------------------------------------
// number of Planes
private uParameter number; //Number of Planes - PARAMETER
private int nnmin=1; // nnmax and nn are given values before init
private String ndescr=
"This parameter indicates the maximum number of 'Planes' "+
"visible at any one time. ";
// maximum speed of an Plane
private uParameter speed; // speed of rotation - PARAMETER
private int v=2,vmax=5,vmin=1;
private String sdescr=
"The parameter actually determines the maximum rate of rotation "+
"of all Planes. It is here given in degrees per cycle.";
// cycletime
private uParameter ct; // Cycletime - PARAMETER
// Detailes of the ct parameter can be seen in Universe
// Plane color
private uParameter pcolor; // Plane Color - PARAMETER
private uParameter bcolor; // Background Color - PARAMETER
private String cdescr=
"Plane color may be chosen from the 13 primary colors "+
"offered by Java. Except that black or the shades of grey look "+
"the same and look similar to what you get if you select white." ;
static String colornames[]=
{" black "," blue "," cyan"," dark grey "," grey ",
" green "," light grey "," magenta "," orange ",
" pink "," red "," white "," yellow "};
private uParameter lightangle; // angle of incident light
private String cidescr =
"The angle is the angle of light to the normal of the "+
"plane of the observer window (the screen). Right behind "+
"the observer the angle is 0 degrees.";
private uParameter psize; // Max size of planes
private String szdescr =
"The sizes of the planes vary randomly, within a range with "+
"the maximum about 75 percent of the witdh of the screen.";
private void setParams(){
// nn = 1; // Testline - easier to test with one plane only
number= new uParameter
(NUMBER,"Number of Planes",nnmin,nn,nnmax,ndescr);
//The adduParameter() method is defined in Universe.
adduParameter(number);
speed=new uParameter(PARA4,"Rotation Speed",vmin,v, vmax, sdescr);
adduParameter(speed);
ct= new uParameter
(CYCLETIME,"Cycle Time", 20,100,200, CycleTimeDescr);
// Since "Cycle Time" is a compulsory parameter, Universe
// provides the description CycleTimeDescr.
adduParameter(ct);
// The color parameters pcolor and bcolor have the same description
// and array, colornames, for selection.
pcolor= new uParameter(PARA5,"Plane Color",pc,colornames, cdescr);
adduParameter(pcolor);
psize = new uParameter(PARA6,"Max Plane Size",20,30,75, szdescr);
adduParameter(psize);
lightangle = new uParameter
( PARA7, "Angle of Incident Light", -90, 45, 90, cidescr );
adduParameter(lightangle);
} //end setParams()
// This method is prescribed by universe. Its purpose is to deal with any
// changes in parameters by the user.
public void manageChange(uParameter uP){
/** This method is called by the RartRunner when a user has modified any
* of the parameters. The new information is conveyed in a uParameter.
* In the case of Planes, we have elected to restart the universe for any
* changes even if for some parameters it would not be strictly neccesary.
*/
int idx=uP.getIndex();
dB.dbg( 2,"In Planes, Change in : "+uP.getName());
if (uPs[idx]!=null) uPs[idx]=uP; // ensures that uPs, the array of
// current parameters is up to date
switch (idx){
case VIEWSIZE:
reset(); // Cange in windowsize, just restart.
break;
case NUMBER:
number=uP; // A more sophisticated program could handle
reset(); // changes in the maximum number of Planes,
break; // but this is simpler!
case PARA4: // used for speed of rotation
speed=uP;
reset(); // simple solution.
break;
case PARA5: // used for for Plane color
pcolor=uP;
reset(); // No alternative to restart here.
dB.dbg(1,"Planecolor is now "+colornames[pc]);
break;
case PARA6: // used for max size
psize=uP;
reset(); // restart is easiest
break;
case PARA7: // used for angle of light
lightangle =uP;
reset(); // no alternative to restart.
}
} // end of manageChange()
// Sections 4, and 5 in Universe have no corresponding entries in the
// Planes universe.
// Section 6 Methods related to security.
// -----------------------------------------------------------------------
// The following constructor invokes the Universe constructor which is
// programmed to throw an exception in order to avoid multiple
// instantiations by mistake or bad intent.
public Planes4e( ) throws InstantiationException
{ }
} // end of Planes class
>
>
/*
* @Plane 1.0e 030115
*
* © A-Square, Inc.
* 175 Richdale Ave, Cambridge MA 02140
*/
import java.awt.*;
import java.lang.*;
import rartbase.*;
//import java.lang.math; // for sin, cos and pi
public class Plane{
/** --------------------------------------------------------------------------
* The Plane is a rectangle which rotates around a vertical axis at some speed.
* The effect of rotation is achieved through the modification of the color
* which changes as the angle of the light, which is thought to come from an
* infinitely distant source behind the right the observer, at 45 degrees to
* the plane of the screen.
*
* @version 1.0e 030115
* Used in Exercise 4e in the Introduction to Java tutorial.
*
* @author Jan Aminoff
*/
private int x; // x pos of center of oval
private int y; // y pos of center of oval
private static int xys = 40; // minimum space between center and edge of window
private static int si = 90; // angle of incident light
private static int ss = 30; // max size in percent of screen
private int dir; // dir = 1 for positive rotation, -1 for negative
private int dv; // Speed degrees/cycle set as dir*(5+RND(speed-5))
private int ndegrees; // number of degrees until done 90+(2+RND(5))*360
//private int ffi; // Floating point repr of fi
private int fi; // Angle from plane of screen in degrees.
private int rr; // half max size of plane
private int hh; // half height of plane
private int nc = 0;
public int status;
public static final int INITIALIZED=1;
public static final int ROTATING=2;
public static final int DONE=3;
private static int RND(int x){
return(Universe.RND(x));
}
private static int ABS(int x){
return ((x<0)? -x:x);
}
// About colors - constants and defaults
static Color colors[]= // Standard Java Colors
{Color.black,Color.blue,Color.cyan,Color.darkGray,Color.gray,
Color.green,Color.lightGray,Color.magenta,Color.orange,Color.pink,
Color.red,Color.white,Color.yellow };
private int colorindex;
//Color for shadows, black with hint of blue
private Color shadowColor = new Color (50,50,50); // Dark Gray
//Color for flash, white
private Color flashColor = new Color (255, 255, 255);
private boolean shadow = false;
static int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, //11
41, 43, 43,47, 53, 59, 61, 67, 71, 73, //10
47, 83, 83}; //3
private Debug dB; // used when debugging, especially as dB.dbg(n, string)
// constructor
/**
* 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 Speed of rotation in degrees per cycle
* @param c Basic color of plane, index to color array
* @param boolean perpendicular If true initial fi is 90, else fi random
*/
public Plane(int xmax, int ymax, int speed, int c, boolean perpendicular) {
// We may need Debug to develop this universe
dB= new Debug();
int debuglevel=0; // 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);
y = xys+RND(ymax-xys);
status = INITIALIZED;
colorindex = c;
// Note that dx and dy are selected independently from the primes array.
// Causes great variation in the configurations.
hh = (primes[4+RND(20)]*ymax)*ss/15000; // max size 25% of window height
rr = (int) (1.8*hh ); // same proportions for all planes
fi = (perpendicular)? 90: -180+RND(360); //Angle to plane of screen
dir = 2*RND(2)-1; // 1 or -1, that is clockwise or counterclocwise
dv = 5+RND(speed-5); // Increment angle per cycle
ndegrees = 90+(2+RND(5))*360; // number of degrees until done
//dB.dbg(1, "Plane.Plane dv "+dv);
// dB.dbg(1,"Debug activated at level "+debuglevel);
} // End of Plane constructor
public static void setSi(int shi){
si = (shi<-90)?-90: (shi> 90)? 90: shi;
}
public static void setSz(int s){
ss = (s<0)? 1: (s>70)? 70:s;
}
public int getSize(){
return rr;
}
public int drawChange(Graphics g){
/** The drawChange method provides the functionality of the planes class.
* It redraws the plane after a rotation of nc*dv degrees .
* It returns its status which relates to the number of complete rotations
* passing the plane perpendicular to the screen.
*/
int newstatus=status;
switch(status){
case INITIALIZED:
newstatus = ROTATING;
case ROTATING:
drawPlane4e(g);
fi += dv;
// dB.dbg(1,"Plane.drawChange x "+x+", ffi "+ffi);
if (ABS(fi)>ndegrees) newstatus = DONE;
break;
}
status = newstatus;
return (status);
} // end DrawChange
private void drawPlane4e( Graphics g){
/*
* Draws the plane
* Width is rr*COS(fi+90), Height is constant hh
*
* First determine the color
* Our strategy is to consider the thirteen colors separately, and for
* each color consider how the tree components move. See shadedColor.
*
* In Plane4e, we consider light as coming from a source at an angle si,
* -90 to 90 degrees measured from normal of the screen. When si goes from
* -90 to 90, the light moves from left to right behind the observer. si
* is static. Its value set by setSi() for all planes at the same time.
*
* The color of the plane is determined by the angle of the plane to the
* angle of the observer. To simplify the consideration we deal with
* the angle f which is derived from fi such that f always is in the
* range 0 to 180. f= (fi+90) mod 180.
*
* The color of the plane depends on the angle psi, between the normal
* to the plane f, and the angle of view of the oserver, 0. This angle
* is psi = abs(f/2) and the intensity of the color
* is proportional to cos(psi).
*
* The plane has a shady side and a bright side. The shade is visible to
* the observer when either 90<f+90<si or when f-90<si <90
*
* Finally a flash is seen when the plane is not in shade and the normal
* of the plane bisects the angle between the incident light and the normal
* perpendicular to the screen, ie when the plane is not in shadow and
* abs(si/2)=f
*
*/
int idx = colorindex;
Color pColor= colors[idx];
shadow = false;
boolean flash = false;
int psi=0;
int fn = (fi+90)%180; // fn normal to plane in range 0 to 180
if (((fn>90) && (fn-90>90-si))||((fn<90)&&(fn+90<90-si))) shadow =true;
if (shadow)pColor=shadowColor;
else{
flash =(ABS(si/2+90-fn) <= 2*dv);
if (flash) pColor = flashColor;
else {
psi = ABS(si-fn+90)%90; //Ensures psi <90
double alpha = ABS(psi)*Math.PI / 180;
// special case for white, idx = 11
int sat = (int)(((idx == 11)? 100.0:50.0)*Math.cos(alpha));
// Problem when sat<0. cos is < 0 when abs (psi) >90
pColor = shadedColor(idx, sat);
}
}
dB.dbg(2, "drawPlane, fi="+
fi+ ", si= "+si+", fn="+fn+", psi="+ psi);
dB.dbg(2, "Shadow: "+ ((shadow)? "Yes":"No")+ " Flash: "+
((flash)?"Yes":"No"));
double beta = fi * Math.PI / 180.0 ;
int r = (int) (rr*Math.cos(beta));
boolean leftpointing = (r>0);
r = ABS(r);
// dB.dbg(1, "Plane.drawPlane Width="+2*r+" Height="+hh+ " fi="+fi);
g.setColor(pColor);
g.fillRect(x-r, y-hh, 2*r, 2*hh);
if (!shadow){
int xx = (int)(0.5 * r);
int yy = (int) (0.5 * hh);
dB.dbg(2, "Leftpointing Triangle: "+ ((leftpointing)? "Yes":"No"));
int xps[] = new int[3];
int yps[] = new int[3];
if (leftpointing){
xps[0] =x-xx; yps[0]=y;
xps[1] =x+xx; yps[1]=y-yy;
xps[2] =x+xx; yps[2]=y+yy;
}else{
xps[0] =x-xx; yps[0]=y -yy;
xps[1] =x+xx; yps[1]=y ;
xps[2] =x-xx; yps[2]=y +yy;
}
//g.setColor();
g.setXORMode(Color.black);
g.setColor(pColor);
g.fillPolygon(xps,yps,3);
//g.fillPolygon(xps,yps,3);
}
g.setPaintMode();
} // end drawPlane4e
/**
* 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){ // New for 4d
int dig, dig1; //Digits of Color
Color c1;
try{ // Resulting color
switch(idx){
case 0: // Black Shades of Grey treated the dame
case 3: // Dark Grey
case 4: // Light gray
case 6: // Grey
dig=(int)(sat*255.0/100);
c1= new Color(dig,dig,dig);
break;
case 1: // Blue
dig = (int)(sat*255.0/100);
c1= new Color(dig,dig,255);
break;
case 2: // Cyan
dig = (int)(127.0+sat*128.0/100);
c1 = new Color(dig,255,255);
break;
case 5: // Green
dig = (int)(127.0+sat*128.0/100);
c1 = new Color(dig,255,dig);
break;
case 7: // Magenta
dig = (int)(127.0+sat*128.0/100);
c1 = new Color(255,dig,255);
break;
case 8: // Orange
dig = (int)(127.0+sat*128.0/100);
dig1 = (int)(sat*255.0/100);
c1 = new Color(255,dig,dig1);
break;
case 9: // Pink
dig = (int)(163.0+sat*93.0/100);
c1 = new Color(255,dig,dig);
break;
case 10: // Red
dig = (int)(sat*255.0/100);
c1 = new Color(255,dig,dig);
break;
case 11: // White - Shades from light grey to whitw
dig = (int)(163.0+sat*93.0/100);
c1 = new Color(dig,dig,dig);
break;
case 12: // Yellow
dig = (int)(sat*255.0/100);
c1 = new Color(255,255,dig);
break;
default:
dB.dbg(0,"Color " +idx + " not yet supported.");
c1 = null;
break;
}
}catch(Exception e){
dB.dbg(0, "in shadedColor Exception "+e);
dB.dbg(0, "sat "+sat+" case "+idx);
c1 = colors[idx];
}
return c1;
} // end shadedColor
} // end class Plane
>