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 › Voronoi regions - circle polygon collision
Page Index Toggle Pages: 1
Voronoi regions - circle polygon collision (Read 4912 times)
Voronoi regions - circle polygon collision
Sep 7th, 2007, 5:27pm
 
I need to detect a circle vs polygon collision. I don't need to resolve the collision, just to detect it. I've read up on Voronoi regions but I can't seem to implement it right and nobody wants to explain how to do it properly.

Here's what I've got so far (ignore the P3D - that's just there because Processing keeps borking when I don't run it in P3D - I'm sure it's been reported)
Code:

Point [] p;
void setup(){
size(400, 400, P3D);
p = new Point[4];
p[0] = new Point(200, 100);
p[1] = new Point(200,200);
p[2] = new Point(80,180);
p[3] = new Point(50,100);
}
void draw(){
background(255);
quad(p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y);
ellipse(mouseX, mouseY, 50, 50);
if(circlePolyCollision(mouseX, mouseY, 25, p)){
fill(0);
} else {
fill(255);
}
}
boolean circlePolyCollision(float xc, float yc, float r, Point [] p){
Point c = new Point(xc,yc);
for(int i = 1; i < p.length+1; i++){
Point a = p[i-1];
Point b = p[(i%p.length)];
Line segment = new Line(a, b);
Line to_circle = new Line(a, c);
// vertex region check
float len = Line.dot(segment, segment);
float dp = Line.dot(to_circle, segment);
if(dp < 0){
// a is the closest vertex
if(proximity(a.x, a.y, c.x, c.y, r)){
return true;
} else {
//return false;
}
} else if(dp > len){
// b is the closest vertex
if(proximity(b.x, b.y, c.x, c.y, r)){
return true;
} else {
// return false;
}
}
// segment region check - check distance to line
float np = (to_circle.vx*segment.lx)+(to_circle.vy*segment.ly);
line(c.x, c.y, c.x+np*segment.lx, c.y+np*segment.ly);
float vx = np*segment.lx;
float vy = np*segment.ly;
float d = sqrt(vx*vx+vy*vy);
if(r-d >= 0){
return true;
}
}
return false;
}
boolean proximity(float x0, float y0, float x1, float y1, float len){
return (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0) <= len*len;
}
static class Point{
float x,y;
Point(float x, float y){
this.x = x;
this.y = y;
}
}
static class Line{
Point a, b;
float vx,vy,dx,dy,len,rx,ry,lx,ly;
Line(Point a, Point b){
this.a = a;
this.b = b;
updateLine();
}
void updateLine(){
vx = b.x-a.x;
vy = b.y-a.y;
len = sqrt(vx*vx+vy*vy);
// normalized unti-sized components
if (len>0) {
dx = vx/len;
dy = vy/len;
} else {
dx = 0;
dy = 0;
}
rx = -dy;
ry = dx;
lx = dy;
ly = -dx;
}
static float dot(Line a, Line b){
return a.vx*b.vx+a.vy*b.vy;
}
}

My trouble is that the code as I've presented it isn't dealing with the vertex regions right. I put in code to jump out when a vertex region fails but that just quits the whole loop.

All of the detections work - but I've got to jump out of the loop before I deal with the wrong area or else I get a false positive or negative. Can't for the life of me figure it out.
Re: Voronoi regions - circle polygon collision
Reply #1 - Sep 7th, 2007, 9:13pm
 
Ah right - I needed to bring the region check for the line segments into the if-else bit - nearly there now (added a quick optimisation too), just one final problem:
Code:

Point [] p;
void setup(){
size(400, 400, P3D);
p = new Point[4];
p[0] = new Point(200, 100);
p[1] = new Point(200,200);
p[2] = new Point(80,180);
p[3] = new Point(50,100);
}
void draw(){
background(255);
quad(p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y);
ellipse(mouseX, mouseY, 50, 50);
if(circlePolyCollision(mouseX, mouseY, 25, p)){
fill(0);
}
else {
fill(255);
}
}
boolean circlePolyCollision(float xc, float yc, float r, Point [] p){
Point c = new Point(xc,yc);
for(int i = 1; i < p.length+1; i++){
Point a = p[i-1];
Point b = p[(i%p.length)];
Line segment = new Line(a, b);
Line to_circle = new Line(a, c);
// vertex region check
float len = Line.dot(segment, segment);
float dp = Line.dot(to_circle, segment);
if(dp < 0){
// a is the closest vertex
if(proximity(a.x, a.y, c.x, c.y, r)) return true;
} else if(dp > len){
// b is the closest vertex
if(proximity(b.x, b.y, c.x, c.y, r)) return true;
} else if(dp >=0 && dp <=len){
// segment region check - check distance to line
float np = (to_circle.vx*-segment.lx)+(to_circle.vy*-segment.ly);
line(c.x, c.y, c.x+np*segment.lx, c.y+np*segment.ly);
float vx = np*segment.lx;
float vy = np*segment.ly;
float d = vx*vx+vy*vy;
if((r*r)-d >= 0){
return true;
}
}

}
return false;
}
boolean proximity(float x0, float y0, float x1, float y1, float len){
return (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0) <= len*len;
}
static class Point{
float x,y;
Point(float x, float y){
this.x = x;
this.y = y;
}
}
static class Line{
Point a, b;
float vx,vy,dx,dy,len,rx,ry,lx,ly;
Line(Point a, Point b){
this.a = a;
this.b = b;
updateLine();
}
void updateLine(){
vx = b.x-a.x;
vy = b.y-a.y;
len = sqrt(vx*vx+vy*vy);
// normalized unti-sized components
if (len>0) {
dx = vx/len;
dy = vy/len;
}
else {
dx = 0;
dy = 0;
}
rx = -dy;
ry = dx;
lx = dy;
ly = -dx;
}
static float dot(Line a, Line b){
return a.vx*b.vx+a.vy*b.vy;
}
}

How do I register a positive when the circle is inside the polygon?
Re: Voronoi regions - circle polygon collision
Reply #2 - Sep 8th, 2007, 12:08am
 
i used this in one of my sketches.. i think you can figure it out..


Code:


boolean InsidePolygon(Convex c,float x, float y)
{
// Convex is just a collection of points for the polygon
// just replace that code in the for-loop to loop through the points in your polygon
int i;
float angle=0;

float p1x = 0, p1y = 0, p2x = 0, p2y = 0;
if(c != null){
for (i=0;i<c.numPoints;i++) {
p1x = (c.vectors[i].m.p0.x - x); // point A X - X
p1y = (c.vectors[i].m.p0.y - y); // point A Y - Y
p2x = (c.vectors[i].m.p1.x - x); // point A's nearest neighbor X - X
p2y = (c.vectors[i].m.p1.y - y); // point A's nearest neighbor Y - Y
angle += Angle2D(p1x,p1y,p2x,p2y);
}

if (abs(angle) < PI)
return false;
else
return true;
}else{
return false;
}

}

float Angle2D(float x1, float y1, float x2, float y2)
{
float dtheta,theta1,theta2;

theta1 = atan2(y1,x1);
theta2 = atan2(y2,x2);
dtheta = theta2 - theta1;
while (dtheta > PI)
dtheta -= TWO_PI;
while (dtheta < -PI)
dtheta += TWO_PI;

return dtheta;
}


good luck

seltar
Re: Voronoi regions - circle polygon collision
Reply #3 - Sep 8th, 2007, 1:10am
 
Okay, I could test the center of the circle as to whether it is inside the poly - but thats adding a huge hurdle that I automatically have to hobble over to rule out if the circle is inside. I only get to skip that check if I get a collision! I'm pretty sure there must be a vector way of doing it. In fact this nifty page on polygon collision refers to voronoi regions inside the polygon. If that's true I can do away with the trig. I'm just a bit stumped at the moment - but you do have a valid point. I guess if I don't find an answer I can always rely on using a bounding box check against the circle and poly to cut overhead. But I'm damned sure there's another way...


On a related note - I found something on a similar page as your strip of code from here

I used to use the angle measuring method but I'm wondering if all of those atan2() calls are a bit expensive. It also lists doing a line-intersection check. Basically if you get an odd number casting out from your line then you have a point inside a polygon. Here's a sketch re-mashing that C stuff into something sensible (or not when you look at the if statement):
Code:

Point [] p;
void setup(){
size(400, 400, P3D);
p = new Point[4];
p[0] = new Point(200, 100);
p[1] = new Point(200,200);
p[2] = new Point(80,180);
p[3] = new Point(50,100);
}
void draw(){
background(255);
quad(p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y);
if(insidePolygon(mouseX, mouseY, p)) fill(0); else fill(255);
}
boolean insidePolygon(float x, float y, Point [] p){
int i, j, c = 0;
for (i = 0, j = p.length-1; i < p.length; j = i++) {
if ((((p[i].y <= y) && (y < p[j].y)) ||
((p[j].y <= y) && (y < p[i].y))) &&
(x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x))
c = (c+1)%2;
}
return c==1;
}
static class Point{
float x,y;
Point(float x, float y){
this.x = x;
this.y = y;
}
}

This also seems a bit long in the tooth too, but it works all the same. Might be faster, might not - needs a test I guess.

Re: Voronoi regions - circle polygon collision
Reply #4 - Sep 8th, 2007, 9:45am
 
The last piece of code seems to work perfectly even for complex examples. My students have been asking about collision code, do you mind if I borrow yours? I'll of course put a link inside a comment so it points back to you.
Re: Voronoi regions - circle polygon collision
Reply #5 - Sep 8th, 2007, 12:51pm
 
here's what i have so far.

P3D was giving the strange results with complex polys not insidePolygon() which works very good. anyway i leave the link to bourke:
http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/

Code:

Point[] polygon;
Circle circle; // x,y,r

void setup ()
{
   size( 200, 200, P3D ); // just for the per vertex coloring
   generateNewLines();
   circle = new Circle(0,0,0);
   frameRate( 24 );
}

void draw ()
{
   background(Colors.white);

   if (mousePressed ) {
       circle = new Circle(mouseX,mouseY,5);
   }

   fill( Colors.grey );
   stroke( Colors.black );

   beginShape(POLYGON);
   int i = 0;
   boolean inside = insidePolygon(circle.x, circle.y, polygon);
   while (true)
   {
       boolean intersect = inside || lineIntersectsCircle( polygon[i], polygon[(i+1)%polygon.length], circle);
       stroke( intersect ? Colors.blue : Colors.black );
       fill( intersect ? Colors.blue : Colors.grey );
       vertex( polygon[i].x, polygon[i].y );
       i ++;
       if ( i % polygon.length == 0 ) break;
   }
   endShape(CLOSE);

   circle.drawCircle();
}

void keyReleased ()
{
   generateNewLines();
}

void generateNewLines()
{
   int count = ((int)random(3,10));
   polygon = new Point[count];
   for( int i = 0; i<polygon.length; i++ )
   {
       polygon[i] = new Point(random(width),random(height));
   }    
}

boolean lineIntersectsCircle ( Point p1, Point p2, Circle c )
{
   float ldx = p2.x-p1.x;
   float ldy = p2.y-p1.y;
   float lk = ldy / ldx;
   float ld = p1.y - p1.x * lk;
   /*
  // draw parallel line
    float cd = c.y - c.x * lk; // parallel line thru circle center
    stroke( Colors.blue );
    float cyy = (c.x+ldx) * lk + cd;
    line (c.x,c.y,c.x+ldx,cyy);
    */
   float cd2 = c.y - c.x * (-1.0/lk); // parallel line thru circle center

   /*
  // draw perpendicular line
    stroke( Colors.green );
    float cyyy =  (c.x+ldx) * (-1.0/lk) + cd2;
    line (c.x,c.y,c.x+ldx,cyyy);
    */

   // how this is solved:
   // left side: c.y = c.x * (-1.0/lk) + cd2
   // right side: p1.y = p1.x * lk + ld
   // where c.y = p1.y = y and c.x = p1.x = x
   // x * (-1.0/lk) + cd2 = x * lk + ld
   // x * ((-1.0/lk)-lk) = ld - cd2
   // x = (ld - cd2) / ((-1.0/lk)-lk)

   float xx = (ld - cd2) / ((-1.0/lk)-lk);
   float yy = xx * lk + ld;

   /*
  // draw connection
    stroke( Colors.red );
    line( xx,yy,c.x,c.y );
    */

   if ( !(xx >= min( p1.x, p2.x ) & xx <= max( p1.x, p2.x ) & yy >= min( p1.y, p2.y ) & yy <= max( p1.y, p2.y )) ) return false;

   float rr = sqrt((c.x-xx)*(c.x-xx)+(c.y-yy)*(c.y-yy));

   return c.rad >= rr;
}

boolean insidePolygon ( float x, float y, Point [] p)
{
   // aaron steed
   // http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Programs;action=display;num=1189178826;start=4#4
   int i, j, c = 0;
   for (i = 0, j = p.length-1; i < p.length; j = i++) {
       if ((((p[i].y <= y) && (y < p[j].y)) ||
           ((p[j].y <= y) && (y < p[i].y))) &&
           (x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x))
           c = (c+1)%2;
   }
   return c==1;
}

class Circle
{
   float x,y,rad, dia;
   
   Circle ( float _x, float _y, float _rad )
   {
       x = _x;
       y = _y;
       rad = _rad;
       dia = 2.0 * _rad;
   }

   void drawCircle ()
   {
       fill( Colors.white );
       stroke( Colors.red );
       ellipse( x, y, dia, dia );
   }
}

static class Point {
   float x,y;
   Point(float x, float y){
       this.x = x;
       this.y = y;
   }
}

class Colors
{
   final static int red   = 0xFFFF0000;
   final static int green = 0xFF00FF00;
   final static int blue  = 0xFF0000FF;  
   final static int black = 0xFF000000;    
   final static int grey  = 0xFFAAAAAA;    
   final static int white = 0xFFFFFFFF;
}
Re: Voronoi regions - circle polygon collision
Reply #6 - Sep 19th, 2007, 12:36am
 
I'm a bit stuck on my Line and Point classes to grasp your example fjen. I need to sit down at some point and get all of that division out of your equations to see how fast I can make it.

New boy at work came up with a solution to the problem though:

Just stick to convex polygons and then you can use the right hand normals as you walk around the poly to detect being inside the box.

Very cheap - no division needed. And if you needed concave polygons you could get around it by making cluster constructions - slap a lot of bounding box checks in there and you've solved the overhead issue.

You're welcome to use the code Watz Wink I'm only cobbling together other people's work.

On the matter of collision, these have been proving really useful in Flash 8 at the moment as a part of a Utility class that stores my "widget" code (such as recursive movieclip pausing for pause functions in games):
Code:

boolean circleOverlap(float x0, float y0, float r0, float x1, float y1, float r1){
return (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0) <= (r0+r1)*(r0+r1);
}
boolean circleInside(float x0, float y0, float r0, float x1, float y1, float r1){
return (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0) <= (r1-r0)*(r1-r0);
}
boolean proximity(float x0, float y0, float x1, float y1, float len){
return (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0) <= len*len;
}
boolean rectContains(float xr float yr, float width, float height, float x, float y):
Boolean{
return x >= xr && y >= yr && x < xr+width && y < yr+height;
}

The proximity function I should have been using in games design from the very start - it's super fast.
Page Index Toggle Pages: 1