We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpPrograms › OOP Question
Page Index Toggle Pages: 1
OOP Question (Read 1217 times)
OOP Question
Aug 16th, 2008, 4:17am
 
I just started to learn OOP and worked through some examples in Bens Book "Processing". Btw, it was much easier to understand then the Burrito examples Ira Greenberg used in his book. Anyway working with classes seems to work now but as i tried to make some bouncing balls and wanted them to check if they touch each other i coudnt figure out how to do that or what was wrong in my code.

So my main question is, how do i tell an object to compare itself against all the other instances...

and the ballcollision is just an example cause i guess i would have the same problem if i would like every ball to be connected to every other ball with a line.

this is an example code where collision seems to work because i only created 2 objects and checked if(dist(x1,y1,x2,y2)<=rad)

so i tried it with something like that :

void collision(){
   for(int i = 0; i< numSpots; i++){
   if(dist(x,y,spots[i].x,spots[i].y)<=rad){
       fill(255,9,9);
       ellipse(x,y,30,30);        
    }}}

but it doesnt work. Maybe somebody can tell me how to do that.

Quote:



int numSpots = 2;
Spot[]  spots = new Spot[numSpots];

void setup(){
 size(400,400);
 smooth();
 noStroke();
 for(int i = 0; i<spots.length; i++){
   float x = random(0,width);
   float y= random(0,height);
   float rate = 1.5 + i*random(0.15,0.3);
   spots[i] = new Spot(x,y,40,rate);
 }
}


void draw(){

 fill(0,10);
 rect(0,0,width,height);
 fill(255);


 for(int i = 0; i<spots.length; i++){
   spots[i].move();
   spots[i].display();
   spots[i].collision();
 }

}


class Spot{
 float x, y;
 float rad;
 int dirY=1;
 int dirX=1;
 float speed;

 Spot(float xpos, float ypos,float radius, float sp){
   x=xpos;
   y=ypos;
   rad=radius;
   speed=sp;

 }


 void move(){
   y= y+speed * dirY*0.15 ;
   x= x+speed * dirX ;
 
   if(y>=height || y<=0){
     dirY =dirY * -1;
   }
   if(x>=width || x<=0){
     dirX =dirX * -1;
   }
 }

 void display(){
  fill(255);
   ellipse(x,y,rad,rad);

 }
 void collision(){
        if(dist(spots[0].x, spots[0].y,spots[1].x, spots[1].y)<= 40){
       fill(255,9,9);
       ellipse(x,y,30,30);        
 
   }
 }
}


Re: OOP Question
Reply #1 - Aug 16th, 2008, 8:26am
 
Hmmm, I knew I should have gone with a pizza example instead

a nested for loop helps with the multi-obj colission. Here's a fairly lame ball-ball collision example (book has a more accurate example):

Code:

int ballCount = 30;
Ball[] balls = new Ball[ballCount];

void setup(){
size(400, 400);
noStroke();
fill(120);
smooth();
for (int i=0; i<ballCount; i++){
float xpos = random(50, width-50);
float ypos = random(50, height-50);
float rad = random(3, 10);
float spdx = random(-1, 1);
float spdy = random(-1, 1);
balls[i] = new Ball(xpos, ypos, rad, spdx, spdy) ;
}
}

void draw(){
background(255);
for (int i=0; i<ballCount; i++){
balls[i].create();
balls[i].move();

// simple boundary collision
if (balls[i].x>width-balls[i].radius ||
balls[i].x<balls[i].radius){
balls[i].speedX*=-1;
}
if (balls[i].y>height-balls[i].radius ||
balls[i].y<balls[i].radius){
balls[i].speedY*=-1;
}

// ball-ball detection
for (int j=0; j<ballCount; j++){
if (i!=j && dist(balls[i].x, balls[i].y, balls[j].x, balls[j].y) < balls[i].radius + balls[j].radius){
// really lame dumbass collision
// there's a more accurate (and complicated) example in book
float tempspdX = balls[i].speedX;
float tempspdY = balls[i].speedY;
balls[i].speedX = balls[j].speedX;
balls[i].speedY = balls[j].speedY;
balls[j].speedX = tempspdX;
balls[j].speedY = tempspdY;
}
}
}
}

class Ball{

float x, y, radius, speedX, speedY;

Ball(float x, float y, float radius, float speedX, float speedY){
this.x = x;
this.y = y;
this.radius = radius;
this.speedX = speedX;
this.speedY = speedY;
}

void create(){
ellipse(x, y, radius*2, radius*2);
}

void move(){
x+=speedX;
y+=speedY;
}
}
Re: OOP Question
Reply #2 - Aug 16th, 2008, 11:45am
 
hehe, always take care what you say... seriously, for me using OOP in a programm where i can see something happen was easier to understand, although i like switching alot between both books and work with both of them.

thanks for your help, i guess, using two different variables for counting and check if j!=i was what i couldnt think of. so collision check works now.

After that i wanted to try connecten them with a line..
Programm slows down as the balls increase and i can tell why, cause i do not connect them once, i connect them iTimes with each other.
so i came up with something like this

connect s[0] with s[1],s[2],s[3]
connect s[1] with s[2],s[3]
connect s[2] with s[3]

but its still really slow. Am i still wrong with my code?

Quote:



int numSpots = 10;
Spot[]  spots = new Spot[numSpots];

void setup(){
 size(400,400);
 smooth();
 noStroke();
 for(int i = 0; i<spots.length; i++){
   float x = random(0,width);
   float y= random(0,height);
   float rate = 1.5 + i*random(0.15,0.3);
   spots[i] = new Spot(x,y,40,rate);
 }
}


void draw(){

 fill(0,310);
 rect(0,0,width,height);
 fill(255);


 for(int i = 0; i<spots.length; i++){
   spots[i].move();
   spots[i].display();
   spots[i].collision();
   spots[i].lines();
 }

}


class Spot{
 float x, y;
 float rad;
 int dirY=1;
 int dirX=1;
 float speed;

 Spot(float xpos, float ypos,float radius, float sp){
   x=xpos;
   y=ypos;
   rad=radius;
   speed=sp;

 }


 void move(){
   y= y+speed * dirY*0.15 ;
   x= x+speed * dirX ;

   if(y>=height || y<=0){
     dirY =dirY * -1;
   }
   if(x>=width || x<=0){
     dirX =dirX * -1;
   }
 }

 void display(){
   fill(255);
   ellipse(x,y,rad,rad);

 }
 void collision(){
   for(int i = 0; i< numSpots; i++){
     for(int j = 0; j< numSpots; j++){
       if(i!=j && dist(spots[j].x,spots[j].y,spots[i].x,spots[i].y)<=(spots[i].rad+spots[j].rad)/2){
         fill(255,9,9);
         ellipse(spots[j].x,spots[j].y,30,30);        
       }
     }
   }  
 }

 void lines(){

   for(int i = 0; i< numSpots; i++){
     for(int j = i+1; j< numSpots; j++){
       stroke(255) ;
       line(spots[j].x,spots[j].y,spots[i].x,spots[i].y);    

     }
   }
 }  
}



Re: OOP Question
Reply #3 - Aug 16th, 2008, 8:39pm
 
Cedric wrote on Aug 16th, 2008, 4:17am:
I just started to learn OOP and worked through some examples in Bens Book "Processing". Btw, it was much easier to understand then the Burrito examples Ira Greenberg used in his book.

Oh, that's Casey's book. Or rather, it's Casey and I's book, but it's his baby. Actually it's not his baby because now he has a baby. Err, so he did the "hard" work on the book. How 'bout that. So if only one of us gets mentioned, it should be him.

Had it just been me, I mighta just stolen Ira's burrito examples. Mmmm, beans.

But as for your question, the problem is that for each object, you're testing collision between every single object over again. That is, inside the collision() method, you're checking for collisions between all objects, which is fine. But then you're calling the collision() method on each object, which means you're rechecking everything again! So with 100 points, that's 100x100x100 collision tests, which is, uh, a lot.

Point is, inside collision(), you should be testing that particular Spot's x and y position with a single for() loop that compares it against the others. Same goes for the lines() function. The fixed versions look like:
Quote:
 void collision() {
   for (int i = 0; i< numSpots; i++) {
     if (spots[i] != this) {
       if (dist(x, y, spots[i].x, spots[i].y) <= (rad + spots[i].rad) / 2) {
         fill(255,9,9);
         ellipse(spots[i].x, spots[i].y, 30, 30);    
       }
     }  
   }
 }

 void lines() {
   for (int i = 0; i< numSpots; i++) {
     if (spots[i] != this) {
       stroke(255) ;
       line(x, y, spots[i].x, spots[i].y);    
     }
   }
 }

Re: OOP Question
Reply #4 - Aug 18th, 2008, 1:52am
 
true... it says  Casey first and then Ben on the book cover... dont know why i confused it. Maybe because i was working through your other book "visualizing Data" at the same time... you guys see, alot of your books on my desk at the moment Smiley

Anyway, thanks for your help. I understand what the problem was. By putting the code from Ira into the class i called it several times...  "this"-command was new to me. i red about it on the reference page  a while ago but i couldnt tell it could be used in this case. Thanks again im learning something new everyday
Re: OOP Question
Reply #5 - Aug 19th, 2008, 12:41pm
 
While Ben's suggestion will very significantly reduce the number of comparisons you need to make from n*n*n to n*n, you can improve things still further.

Both collisions and line drawing in your example are symmetric. In other words, if spot A collides with spot B, then spot B must also have collided with spot A. You can therefore halve the number of line drawings and collision detections you need to make. In fact, it is slightly less than half since you never have to compare a spot with itself. An easy way to implement this is to pass an index number of the current spot being tested to your collision() and lines() methods. These methods need only compare with the remaining untested spots in your array.

A second, minor improvement, is to remove the stroke and fill settings from inside your for loops since they do not change in response to the loop iterations. While this will have a very minor effect in this case, it is good practice separate code that depends on each loop iteration from that which applies to all loop iterations.

There is also slightly more subtle bug introduced by Ben's more efficient drawing code. Because  the code for drawing an uncollided spot is in a different method to the code for drawing a collided spot, a spot that is drawn as collided, can get redrawn as uncollided as the for loop in draw proceeds. A more object-oriented solution would be to store as part of the state of each spot, whether or not it is in collision with anything, and then modify Spot's [tt]display/tt] method appropriately.

Code incorporating all of the above is shown below:

Quote:
int numSpots = 10;
Spot[]  spots = new Spot[numSpots];

void setup(){
 size(400,400);
 smooth();
 noStroke();
 for(int i = 0; i<spots.length; i++)
 {
   float x = random(0,width);
   float y= random(0,height);
   float rate = 1.5 + i*random(0.15,0.3);
   spots[i] = new Spot(x,y,40,rate);
 }
}


void draw(){

 fill(0,310);
 rect(0,0,width,height);
 fill(255);
 
 for(int i=0; i<spots.length; i++){
   spots[i].move();
   spots[i].collision(i);   // Test for collisions before drawing.
   spots[i].display();
   spots[i].lines(i);
 }

}

class Spot{
 float x, y;
 float rad;  
 int dirY=1;
 int dirX=1;
 float speed;
 boolean hasCollided;

 Spot(float xpos, float ypos,float radius, float sp){
   x=xpos;
   y=ypos;
   rad=radius;
   speed=sp;
   hasCollided = false;    // Records the spot's collision status
 }


 void move(){
   y= y+speed * dirY*0.15 ;
   x= x+speed * dirX ;

   if(y>=height || y<=0){
     dirY =dirY * -1;
   }
   if(x>=width || x<=0){
     dirX =dirX * -1;
   }
 }

 void display(){
   fill(255);
   ellipse(x,y,rad,rad);
   
   if (hasCollided){
      fill(255,9,9);
      ellipse(x, y, 30, 30);
      hasCollided = false;  // Reset collision status after drawing.
   }

 }

 void collision(int start)
 {
   for (int i = start+1; i< numSpots; i++){
     if (dist(x, y, spots[i].x, spots[i].y) <= (rad + spots[i].rad) / 2){
       // Label the pair of spots as having collided.
       this.hasCollided = true;
       spots[i].hasCollided = true;
     }  
   }
 }
 
 void lines(int start){
   strokeWeight(0.3);
   stroke(255) ;
 
   for (int i = start+1; i< numSpots; i++){
     if (spots[i] != this){
       line(x, y, spots[i].x, spots[i].y);    
     }
   }
 }
}



Re: OOP Question
Reply #6 - Aug 19th, 2008, 12:45pm
 
This didn't fit in my previous posting...

Finally, if all that makes sense, you can further improve the efficiency of collision detection significantly by using a HashGrid to store your spot locations. This would allow you potentially to draw many hundreds of spots in about the same time as you are currently drawing 10s of them (the example Ants sketch pointed to on that page contains 1000 objects with collision detection).
Re: OOP Question
Reply #7 - Aug 19th, 2008, 6:40pm
 
Thanks Jo,
This is great stuff!

If it's not on there yet, seems like an excellent example under Hacks

-Ira
Re: OOP Question
Reply #8 - Aug 19th, 2008, 9:39pm
 
Great, thank you Jo... I guess i have to take some time to totally understand the code. But at least i allready got the concept of it.
Page Index Toggle Pages: 1