Processing Skeleton Project Solutions

From dftwiki3
Jump to: navigation, search

--D. Thiebaut 10:24, 18 June 2012 (EDT)


Here are some solutions to selected exercises from the page on Processing and Skeleton Project. They may not be exactly what was asked for, but will give a general idea of possible approaches.



Exercise 2

Here's a possible solution for Exercise 2. One trick I'm using is to add a small random value to the current directionX and directionY variables. Instead of doing

  directionX = random( 10 );

which would store a random value between 0 and 10 into directionX, I prefer to do something like this:


  directionX = directionX + random( 2 ) - 4;


This ways directionX changes by at most -2 to +2 in value. This makes it for smoother changes in direction.

To help with the smoothness, I do not change direction every time draw() is called, but only some random percentage of the time. This is done by changing the direction only when random( 100 ) is less than 5. This way, on the average, the direction changes on the average after 95 steps out of 100 in the same direction.

  if ( random( 100 )  < 5 ) {

       // change direction

  }

I'm also changing the color in the same if statement. Just for fun ;-)


package tutorial1;

import processing.core.*;

public class Exercise2 extends PApplet {
	
	//--- position and velocity of the circle
	float directionX = 2;  // initial direction components of the circle
	float directionY = 1;
	float x, y;              // location of the center of the circle
	int   transp = 255;
	int   red   = 10;     // for changing the colors of the circles...
	int   green = 120;
	int   blue  = 200;
	
	public void setup() {
		// define the window size, make graphics softer, and make
		// the background white
		size(600, 600);
		smooth();
		background(255);
		
		// define circle position and speed of the circle		
		x = width/2;
		y = height/2;
		directionX = 1;
		directionY = 2;
	}

	public void draw() {
		background( 0x6666ee );   // light blue color in hexadecimal.

		// change direction only 5% of the time there's a move, on the average, and 
		// only if the circle is at least 100 pixels away from an edge (otherwise we
		// could get stuck outside the applet...)
		if ( random(100) < 5 && x > 100 && x < width-100 && y > 100 & y < height-100  ) {
			directionX = directionX  + random( 1 ) - 2; // a random number between -5 and 5
			directionY = directionY + random( 1 ) - 2; // same
			red  = (int ) (255 +  red + random( 5 ) - 10 ) % 255; // do not allow colors to go over 255;
			green = (int) (255 + green + random( 6 ) - 10 ) % 255; 
			blue  = (int) (255 + blue + random( 2 ) - 10 ) % 255;
		}
			
                // new position of center of circle
		x += directionX;
		y += directionY;

                // revert direction if center goes off the applet...
		if ( x > width || x < 0 ) directionX = -directionX;
		if ( y > height || y < 0 ) directionY = -directionY;
		
                // change transparency of circle, from opaque to fully transparent...
		transp -= 1;  
		if ( transp==0 ) transp = 255;

                // draw circle in random color, at new place
		fill( red, green, blue, transp ); 
		ellipse(x, y, 80, 80);
	}
}


Variation

To make the animation stop when the mouse is pressed, we can test mousePressed at the beginning of the draw() function, and decide to return and not do anything if the boolean is true:

         public void draw() {
		
		if ( mousePressed )
			return;
		
		background( 0x6666ee );
		// change direction only 5% of the time there's a move, on the average, and 
		// only if the circle is at least 100 pixels away from an edge (otherwise we
		// could get stuck outside the applet...)
		if ( random(100) < 5 && x > 100 && x < width-100 && y > 100 & y < height-100  ) {
			directionX = directionX  + random( 1 ) - 2; // a random number between -5 and 5
			directionY = directionY + random( 1 ) - 2; // same
			red  = (int ) (255 +  red + random( 5 ) - 10 ) % 255; // do not allow colors to go over 255;
			green = (int) (255 + green + random( 6 ) - 10 ) % 255; 
			blue  = (int) (255 + blue + random( 2 ) - 10 ) % 255;
		}
			
		x += directionX;
		y += directionY;
		if ( x > width || x < 0 ) directionX = -directionX;
		if ( y > height || y < 0 ) directionY = -directionY;
		
		transp -= 1;  
		if ( transp==0 ) transp = 255;

		fill( red, green, blue, transp ); // red
		ellipse(x, y, 80, 80);
	}


Exercise 3

Here's a possible solution for Exercise 3.

package tutorial1;

import processing.core.*;

public class Exercise3 extends PApplet {
    int red, green, blue, transp;  // color and transparency for the circle
    
    public void setup(){
        size(600,600);
        smooth();
        //--- all white background ---
        background(255);  

        //--- fast frame rate for speedy screen updates ---
        frameRate( 60 );   

        //--- initial color and transparency ---
        red = 80;
        green = 160;
        blue  = 240;
        transp = 10;
    }
    
    public void draw() {
       //--- if user presses mouse key... ---
       if( mousePressed ) {

           //--- change color only 5% of the time... ---
    	   if ( random( 100 ) < 5 ){
                   // +/- random delta in [-5,5] for color and transparency
    		   red = (  (int) (255 + red + random(5)-10 ) ) % 255;
    		   green = (  (int) (255 + green + random(5)-10 ) ) % 255;
    		   blue = (  (int) (255 + blue + random(5)-10 ) ) % 255;
    		   transp = (  (int) (255 + transp + random(5)-10 ) ) % 255;
    	   }
    	   
           //--- define color ---
           fill( red, green, blue, transp );
           stroke( 0 ); // black
        }
        else {
            //--- set circles white, but possibly transparent (nice effect...) ---
            fill(255, 255, 255, transp );
            stroke(255);
        }

       //--- draw circle ---
       ellipse( mouseX, mouseY, 80, 80);
   }
}



Exercise4

To make the ball change direction, all we have to do is change directionX into -directionX, and directionY into -directionY. The ball will just reverse direction. Processing will call a function called mouseClicked() every time the user clicks the mouse if we add this function to our class:

	public void mouseClicked() {
		directionX = -directionX;
		directionY = -directionY;
	}


All you have to do is add it to your class, and try it!
To make the ball turn at a right angle, we need to rotate the direction by 90 degrees, or PI/2. A rotation in two dimensions is described in Wikipedia. All we have to do is multiply the vector that defines the direction of the circle (directionX, directionY) by the rotation matrix taken from Wikipedia:

RotationMatrix2D.png


which translates in

newX = x * cos( θ ) - y * sin( θ )
newY = x * sin( θ ) + y * cos( θ )


Here's the code:


                double newDirectionX = Math.cos( Math.PI/2 ) * directionX - Math.sin( Math.PI/2 ) * directionY;
		double newDirectionY = Math.sin( Math.PI/2 ) * directionX + Math.cos( Math.PI/2 ) * directionY;
		directionX = (float) newDirectionX;
		directionY = (float) newDirectionY;


Since the angle PI/2 is constant throughout the running of the applet, we can precompute cos( PI/2 ) and sin( PI/2 ) which will make the code faster and more efficient:

  • cos( PI/2 ) = 0
  • sin( PI/2 ) = 1

So, we get the simplified code:

                float newDirectionX =  - 1.0f * directionY;
		float newDirectionY =  directionX ;
		directionX =  newDirectionX;
		directionY =  newDirectionY;


and finally, the new function with some additional simplifications:

	public void mouseClicked() {
                float newDirectionX =  - directionY; 
		float newDirectionY =  directionX ;
		directionX =  newDirectionX;
		directionY =  newDirectionY;
	}



Exercise 5

The trick here is to create a new variable, say deltaTransp which works the same for transp as directionX does for x. We initialize deltaTransp to -1 to start with, and keep on adding it to transp until this one becomes negative. Then we change the sign of deltaTransp and this will make transp increase until it becomes larger than 255, at which point with change its sign again, and so on.


        int deltaTransp = -1;

	public void draw() {
                // erase the applet with white
		background(255);
 
		x += directionX;
		y += directionY;
		if ( x > width  || x < 0 ) directionX = -directionX;
		if ( y > height || y < 0 ) directionY = -directionY;
 
                // keep on modifying transparency of cirlce...
		transp += deltaTransp;  

                // if transparency out of bound, change direction of variation...
		if ( transp < 0 || transp > 255 ) deltaTransp = -deltaTransp;
 
                // draw new circle at new position with new transparency
		fill( 255, 0, 0, transp );   // fill with transparent red
		stroke( 255, 0, 0, transp );   // outline in transparent red
		ellipse(x, y, 80, 80);
	}




Exercise 6

You probably duplicated all the variables and indexed them 1, and 2, to differentiate the ones belonging to Circle 1 or Circle 2. That's a good way to go. Here's an attempt to do the same thing below:


// code by Inky
package tutorial1;

import processing.core.*;
 
public class Exercise6 extends PApplet {
	
        // locations and directions of the 2 circles
	int x1; 
	int y1;
	int x2;
	int y2;
	int directionX1;
	int directionY1;
	int directionX2;
	int directionY2;
	
    public void setup() {
        size(600, 600);
        smooth();
        background(255);

        // initialize position of the two circles 
        x1 = width/2;
        y1 = height/2;
        x2 = width/3;
        y2 = height/2;

        // as well as their directions
        directionX1 = 1;
        directionY1 = 2;
        directionX2 = 3;
        directionY2 = 4;
        
    }
 
    public void draw() {
        // paint applet white
        background(255);
        
        // put Circle 1 some delta away from its last position, and correct direction if 
        // going outside the boundaries
        x1 += directionX1;
        y1 += directionY1;
        if ( x1 > width - 40 || x1 < 40 ) directionX1 = -directionX1;
        if ( y1 > height- 40 || y1 < 40 ) directionY1 = -directionY1;

        // display the circle        
        circle( x1, y1, directionX1, directionY1, 255 );
        
        // put Circle 2 some delta away from its last position, and correct direction if
        // necessary
        x2 += directionX2;
        y2 += directionY2;
        if ( x2 > width - 40 || x2 < 40 ) directionX2 = -directionX2;
        if ( y2 > height- 40 || y2 < 40 ) directionY2 = -directionY2;

        circle( x2, y2, directionX2, directionY2, 0);
    }

    // a utility function to display a red circle at the correct position with a given transparency
    public void circle(float x, float y, float directionX, float directionY, int transp ){

        int red = 255;
        int green = 0;
        int blue = 0;

        fill( red, green, blue, transp ); 
        ellipse(x, y, 80, 80);
    }
}



Exercise 7

For this exercise, it's actually not that bad to go through the trouble of adding a new class for the circle. This class becomes a container of the variables of interest, and by moving code around, we get something quite versatile.

So, another (good) approach here is to create a new class for the circle. It will hold x, y, directionX, directionY, transp, and deltaTransp. Then we move the code in draw() that updates the location, direction and transparency of the circle into the new class. Similarly, we move the drawing of the circle inside the class.

We need the circle to have a link to the applet (PApplet object) so that the circle can know the dimensions of the applet, as well as a way to call the ellipse(), fill(), and stroke() functions.

Here's a possible approach:


package tutorial1;
 
import processing.core.*;
 
class CircClass {
	public float directionX = 2;
	public float directionY = 1;
	public int   deltaTransp = -1;
	public float x, y;
	public int   transp = 255;
	PApplet parent;
	
	CircClass( PApplet p, int x, int y, float dx, float dy, int tp, int dtp ) {
		parent = p;
		this.x = x;
		this.y = y;
		directionX = dx;
		directionY = dy;
		deltaTransp = dtp;
		transp = tp;
	}
	
	void update() {
		x += directionX;
		y += directionY;
		if ( x > parent.width  || x < 0 ) directionX = -directionX;
		if ( y > parent.height || y < 0 ) directionY = -directionY;
 
		transp += deltaTransp;  
		if ( transp < 0 || transp > 255 ) deltaTransp = -deltaTransp;
	}	
	
	void draw() {
		parent.fill( 255, 0, 0, transp ); // red
		parent.stroke( 255, 0, 0, transp );
		parent.ellipse(x, y, 80, 80);
	}
	
}

public class Exercise7 extends PApplet {
 
	CircClass c1, c2;
	
	public void setup() {
		// define the window size, make graphics softer, and make
		// the background white
		size(600, 600);
		smooth();
		background(255);
 
		// define the 2 circles' position and speed, as well as transparency...		
		c1 = new CircClass( this, width/2, height/2, 1, 1, 128, -1 );
		c2 = new CircClass( this, width/2, height/2, -1, 2, 10, +1 );
	}
 
	public void draw() {
		background(255);

                // update and draw the circles...
		c1.update();
		c1.draw();
		c2.update();
		c2.draw();
	}
}


Exercise 8

Here's a possible implementation of what Exercise 8 is asking for. The CircClass is the same as for Exercise 6 (and 7), but we've moved it inside the applet definition, so that it won't conflict with the CircClass of the Exercise6 class, which is accessible to all the classes in the project (I'm using the same project for all these different exercise solutions.)

package tutorial1;
 
import java.util.ArrayList;
import java.util.Iterator;

import processing.core.*;
 
public class Exercise8 extends PApplet {

        // local class, used only for the benefit of the Exercise8 applet... 
	class CircClass {
		public float directionX = 2;
		public float directionY = 1;
		public int   deltaTransp = -1;
		public float x, y;
		public int   transp = 255;
		PApplet parent;
		
		CircClass( PApplet p, int x, int y, float dx, float dy, int tp, int dtp ) {
			parent = p;
			this.x = x;
			this.y = y;
			directionX = dx;
			directionY = dy;
			deltaTransp = dtp;
			transp = tp;
		}
		
		void update() {
			x += directionX;
			y += directionY;
			if ( x > parent.width  || x < 0 ) directionX = -directionX;
			if ( y > parent.height || y < 0 ) directionY = -directionY;
	 
			transp += deltaTransp;  
			if ( transp < 0 || transp > 255 ) deltaTransp = -deltaTransp;
		}	
		
		void draw() {
			parent.fill( 255, 0, 0, transp ); // red
			parent.stroke( 255, 0, 0, transp );
			parent.ellipse(x, y, 80, 80);
		}
		
	}

        // array of CircClass objects...
	ArrayList<CircClass> c;
	
     
	public void setup() {
		// define the window size, make graphics softer, and make
		// the background white
		size(600, 600);
		smooth();
		background(255);
 
		// define circle array, and store 20 objects in it with random position, velocity, and transparency. 	
		c = new ArrayList<CircClass>();
		for ( int i = 0; i<20; i++ )
			c.add(  new CircClass( this, (int) random(width), (int) random(height), 
					(int) random(4)-2, (int) random(6)-3, 
				    (int) random(255), (int) random(4)-2 ) );
	}
 
	public void draw() {
		background(255);

                // draw all circles in the array and update their properties...
		for ( Iterator<CircClass> it = c.iterator(); it.hasNext(); ) {
			CircClass c1 = it.next();
			c1.update();
			c1.draw();
		}
	}
}