Uploaded to www.rart.com/j2me-presentation/html_Source/ March 19, 2002

The Class FlakesME

This is an example of a Rart universe ported to the J2ME environment.

The FlakesME universe requires the rP60ME class which provides math support.

This version has been tested with j2mewtk 1.0.2 and 1.0.3

/*
 * @FlakesME    1.0  010905 
 * 
 * © A-Square, Inc.   
 * 1648 Waters Edge Lane, Reston Virginia, 20190, USA  
 */
 package rartme.univ;
 import javax.microedition.lcdui.*;
 import java.util.Stack;
 import java.lang.Math;
 import rartme.*;


/**
 * Flakes universe
 *	 
 * Source: FlakesME.java
 * Description: In the Flakes universe branches grow from the center in an 
 * approximation of the growth of a hexagonal crystals, as formed by for 
 * example a snow flake. 
 * 
 * The actual drawing of the branches is performed by the class rP60ME,
 * which emulates the geometry including the shaded drawing of branches up
 * to 5 pixels wide.
 *
 * author Jan Aminoff/	© A-Square 1998-2001
 *		
 * This version is adapted to J2ME and the second real universe to be
 * programmed in the ME environment. Compared to the regular Flakes universe,
 * FlakesME shows the growth of a single flake centered on the small screen. 
 * Released with RartME as the FlakesME sample universe, September 2001
 *
 * Proud Author: Jan Aminoff 
 */



public final class FlakesME extends UniverseME {
    
    // Section 1     Identifying the Universe
	// -----------------------------------------------------------------------

	public String getName(){return("FlakesME");}	// Name of the universe.
	public String getDescription(){return(sd);}	// Describing the universe. 
	private String sd=
	         "  In the FLAKES Universe branches grow and "+
			 "branch always at 30 or 60 degree angle and in circular "+
			 "symmetry, approximating the growth of a snow flake.";
	 public String getRartist(){        //	  Name of Rartist.
		return(" Jan Aminoff 2001");}			
	
	// end of Section 1
    
	// Section 2    What the universe does
	// -----------------------------------------------------------------------
	
	// This universe is more complicated than others and so the documentation
	// for Section 2 has been subdivided in subsections as follows
	// Subsection 2a Initiation
	// With methods init, reset, and newFlake
	// Subsection 2b Operation
	// With methods cycle, drawFlake, and eraseFlake (Erases a flake)
	// Subsection 2c Changing
	// with method manageChange
	
	// Subsection 2a Initiation
	// With methods getBackground, init, reset, and newFlake and with 
	// declarations of static variables as well as variables associated with the flake.
	
	// Static Variables

    // The arms of a flake can branch with 5, 2 or one branch. The selection 
	// is made from the following table indexed with a random index.
	private static int branches[]={5,5,2,2,2,2,1,1,1,1};
	
	// The thickness of a branch is selected from the following table where
	// the first index is proportional to distance from center and second 
	// index is random. The effect is that branches near the center are more
	// likely to be thicker.
	private static int thickness [][]= {
		{5,5,5,5,5,5,5,3,3,1},
		{5,5,5,3,3,3,3,3,1,1},		
		{5,3,3,3,3,3,1,1,1,1},
		{3,3,3,3,1,1,1,1,1,1},
		{3,3,1,1,1,1,1,1,1,1}
	};

	// The sixe of the enclosing circle is selected as a percentage of 
	// 120 percent of the minimum screen dimension using this table. 
	// See newFlake.
	private static int size[] = {
				10,20,30,40,45,50,55,60,60,65,
				65,70,70,70,75,75,80,80,85,90};
	
	// Fundamental private variables
	private int R;     // radius of enclosing circle
	private int X = xm/2;     // x-coordinate of center     
	private int Y = ym/2;     // y-coordinate of center
	private int lvl;   // level of brancing
	private int lmax;  // maximum level of branching
	private int rMin1; // current minimum distance from points to circle
	private boolean done;
	private Stack stackA;
	private Stack stackB;
	private int r,rMin, d, dr, w;
	private rP60ME p0,p1,p2; 

	private DebugME dB;       // In case needed. See init
	
	private boolean resetScreen = false;
	// Prescribed by UniverseME
	public void init(){
	   // Following establishes a pattern for setting and use of Debug.
	   /*
		 * Set debuglevel to 2 or higher for tracing output.
		 * You can set it here for this class only or for all rartme
		 * classes in the JAD file (Usually RartME.jad) from where it
		 * is available in Universe as protected int dblevel;
		 */
		int dblvl = 0;		// Set dblevel here for this universe
		dblevel = Math.max(dblvl,dblevel);
		dB= new DebugME(true);
		dB.setLevel(dblevel);
		dB.dbg(1,"FlakesME.init, dblevel "+dblevel);
		 
		setParams();    // See Section 3 about uParameters
		reset();
		newFlake();	    // Initiate first flake
	}   // end init
	
	// reset, used in init only in this universe
	private void reset(){
		resetScreen = true;
	}   // end reset
	

	/**
	 * Initiates a flake based on the size of the available screen
	 * with width xm and height ym. Most flakes should be big, but
	 * some small for variety.
	 * Selection is done in two steps for a maximum of 100. First
	 * select with equal probability any entry in the array size and
	 * add randomly 0-9.
	 */ 
	private void newFlake(){
		int rmax = Math.min(xm,ym)/2;
		int percent = (size[RND(20)]+RND(11))*120/100;	
		int r = percent*rmax/100;	//radius
		R=r;	 
		X=xm/2;    
		Y=ym/2;
		lvl=0; // the number of stacklevels
		stackA=new Stack();  // one stack per flake
		stackB=new Stack();
		com=complexity.getCur();
		// lmax gives number of branches or levels, function of the
		// uParameter complexity.
		lmax=1 + RND(com-1);	 //At least one branching
		dB.dbg(3, "FlakesME.newFlake, line 156 com, lmax "+com+", "+lmax);
		//rMin1 is distance to circles edge at previous level	
		rMin1=r;			
		Point P = new Point(X,Y);	 
		p0=new rP60ME(P);			
		// establishes center of flake in the rPoint system
		p0.dir = RND(2);	//0 or 30 degree angle initially
		//Work from branching points from previous level in StackA		
		stackA.push(p0);			//Start at center with one point in StackA
		done =false;
		//Now ready to go into branc drawing mode			
	}   // end newFlake
	
	
	// Subsection 2b Operation
	// With methods cycle, drawNextLevel, drawBranch, eraseFlake
	// and static methods for SQUARE and rMin

	// cycle, prescribed by UniverseME
	public void cycle(Graphics g){
		if (resetScreen){
			clearScreen(g);
            resetScreen=false;
		}	
		// In this universe we deal with only one flake
		if (!done) drawNextLevel(g)	; 
		lvl++;
		if (lvl > lmax){			
			clearScreen(g);			//eliminates the flake and
			newFlake();				//initiates a new one
		}
	} 	// end cycle

	private void clearScreen(Graphics g){
		g.setColor(0);  // Black background
		g.fillRect(0, 0, xm, ym);
	}

	//Draws next level of the flake
	void drawNextLevel(Graphics gc){
		int r=R;
		rMin=r;	// rMin measures distance to circles edge during calculation
		int level=lvl;  
		// Cycle until level reaches lmax or distance to edge 2 or less
		if(!done){	
			level++;	
			// number of branches, 1, 2, or 5, see branches
			int nb=(level==1)?1:branches[RND(10)];	 	 
			// thickness of branch, see thickness
			w=(level < 5)?thickness[level][RND(10)]:1;	
			dB.dbg(3, "FlakesME.drawNextLevel r, lmax "+r+", "+lmax);
			int ddr = 2*r/lmax;
			dr=1+RND(ddr);			// length of branch, see ddr
			dr=Math.min(dr,rMin1);	//not further than circles edge
			// Store next level of branching points in StackB 
			// work from A until empty
			while (!stackA.empty()){
				p1=(rP60ME)stackA.pop();	 //next branch point
				switch (nb) {					//nb number of branches
					//Each branch point comes with an inherent direction dir. 
					case 1:		//one branch
						drawBranch(r,0,gc);	//straight ahead
						break;
					case 2:		// two branches
						drawBranch(r,-1,gc);	 //+ and - 30 degreas
						drawBranch(r,1,gc);
						break;
					case 3:		//only in error at this time
						break;
					case 4:		//only in error at this time
						break;
					case 5:		//five branches	  //60 degrees all around
						drawBranch(r,-4,gc);
						drawBranch(r,-2,gc);
						drawBranch(r,0,gc);
						drawBranch(r,2,gc);
						drawBranch(r,4,gc);				
						break;
				}	//switch nb
			}			//stack A empty, done with present level
			if((level > lmax)||(rMin<3)){ // finished?
				done = true;
				lmax = 4*level;
				stackB = null;
				stackA = null;
			} else {	//move points from B to A until B is empty
				while (!stackB.empty()) stackA.push(stackB.pop());	 
				rMin1=rMin;			//saves the value of rMin
			}
			// go to next level
		}	// end while		
	}	// drawNextLevel
	
	/**
	 * extends a branch distance r in the direction di
	 * the method also draws the line as well as the lines rotated around
	 * the circle. In the process a new endpoint is created and pushed onto
	 * stackB for use in the drawing of next again level.
	 */
	private void drawBranch(int r, int di, Graphics gc){
		int di1=(p1.dir+di)%12;	  //dir must be >=0 and <12 around the circle
		// Calculate new endpoint at p2 and draw the line from p1 to p2
		p2=p0.extendBranch60(p1,di1, dr,gc,w);
		// Draw the new line rotated in 60 degree increments around the circle	 
		p0.rotateLine60(p1,p2,gc,w);
		//Save the new endpoint in stackB
		stackB.push(p2);
		rMin=rMin(rMin,r,p2); // keep ruuning track of remaining distance
	}   // end drawBranch
	
	/**
	 * checks distance to the edge of the sector bounded by x=0, a 60 degree
	 * radius and radius r from the center p with relative coordinates x,y.
	 * is used to determine minimum distance from any new branch end point to
  	 * the edge of circle.
	 * Uses SQRT as defined in rP60ME
	 */
	private static int rMin(int rMIN, int r, rP60ME p){
		int rd=(r-rP60ME.SQRT(SQUARE(p.x)+SQUARE(p.y)));
		return Math.min( rMIN,rd);
	} 	// end rMin
	
	static int SQUARE(int x){
		return x*x;
	}
	
	// Subsection 2c Changing
	// with method manageChange

	// manageChange prescribed by UniverseME
    public void manageChange(uParameter uP){
		int idx=uP.getIndex();
		dB.dbg( 3, "FlakesME.mC, Change in: "+uP.getName());
		if (uPs[idx]!=null) uPs[idx]=uP; //ensures that uPs, the list of current parameters is up to date			
		switch (idx){
			case CYCLETIME:
				ct=uP;
				break;
			case PARA4:
				complexity=uP;
				break;	
			}
	}   // end manageChange
		
	// end of Section 2		   
      
	// Section 3   uParameters as used in the universe
    // -----------------------------------------------------------------------
	private uParameter number;  //Number of Flakes requested - PARAMETER
	
	private uParameter complexity;  //A measure of complexity - PARAMETER
	private int com=4,com_max=10,com_min=2;	
	private String cdescr= 
	        "The parameter determines the maximum number of brancings. "+
		    "The actual number is less and random.";
	
	private uParameter ct;
	
    private void setParams(){
		complexity=new uParameter
		    (PARA4,"Complexity",1,com, com_max, cdescr);
		adduParameter(complexity);
    // Universe provides the description for uParameter with index CYCLETIME
		ct= new uParameter
			(CYCLETIME,"Cycle Time", 40,100,1000, CycleTimeDescr);	
		adduParameter(ct);
	}   // end setParam

	// Security related constructor
   public FlakesME() throws InstantiationException {}
}   // End of FlakesME universe