Error, Implementing Winding Number Algorithm, (OpenGL, C++) - c++

I am trying to implement the Winding Number Algorithm to test if a point is within another polygon. Although the results from my algorithm are wrong and not consistent. I have been working on this for ages now and it has become a bit of a pain!
I have basically converted pseudo code from notes and websites, such as, softsurfer.com
I successfully detect if my player and building object bounding boxes overlap. I return the result to a struct, (BoxResult) which lets me know if there has been a collision and returns the box which it has collided with (Below)
struct BoxResult{
bool collide;
Building returned;
};
void buildingCollision(){
int wn = 0; //winding number count
BoxResult detect = boxDetection(); //detect if any bounding boxes intersect
if(detect.collide){ //If a bounding box has collided, excute Winding Number Algorithm.
for(int i = 0; i < player.getXSize(); i++){
Point p;
p.x = player.getXi(i);
p.y = player.getYi(i);
wn = windingNum(detect.returned,p);
cout << wn << endl;
//Continue code to figure out rebound reaction
}
}
}
I then test for a collision between the building and the player (Below). I have tried 5 different attempts and hours of debugging to understand where the error is occuring, however I am implementing the most ineffienct method which just uses maths (Below).
int windingNum(Building & b, Point & p){
int result = 0; //Winding number is one, if point is in poly
float total; //Counts the total angle between different vertexs
double wn;
for(int i = 0; i <= b.getXSize()-1;i++){
float acs, nom, modPV, modPV1, denom, angle;
if(i == 3){
//Create the different points PVi . PVi+1
Point PV, PV1;
PV.x = (b.getXi(i) + wx) * p.x;
PV.y = (b.getYi(i) + wy) * p.y;
PV1.x = (b.getXi(0) + wx) * p.x;
PV1.y = (b.getYi(0) + wy) * p.y;
modPV = sqrt( (PV.x * PV.x) + (PV.y * PV.y)); //Get the modulus of PV
modPV1 = sqrt( (PV1.x * PV1.x) + (PV1.y * PV1.y)); //Get modulus of PV1
nom = (PV1.x * PV.x) + (PV1.y * PV.y); //Dot product of PV and PV1
denom = modPV * modPV1; //denomintor of winding number equation
angle = nom / denom;
acs = acos(angle) * 180/PI; //find the angle between the different points
total = total + acs; //add this angle, to the total angle count
}
if(i < 3){
//Create the different points PVi . PVi+1
Point PV, PV1;
PV.x = (b.getXi(i) + wx) * p.x;
PV.y = (b.getYi(i) + wy) * p.y;
PV1.x = (b.getXi(i+1) +wx) * p.x;
PV1.y = (b.getYi(i+1) +wy) * p.y;
modPV = sqrt((PV.x * PV.x) + (PV.y * PV.y)); //Get the modulus of PV
modPV1 = sqrt((PV1.x * PV1.x) + (PV1.y * PV1.y)); //Get modulus of PV1
nom = (PV1.x * PV.x) + (PV1.y * PV.y); //Dot product of PV and PV1
denom = modPV * modPV1; //denomintor of winding number equation
angle = nom / denom;
acs = acos(angle) * 180/PI; //find the angle between the different points
total = total + acs; //add this angle, to the total angle count
}
}
wn = total;
if(wn < 360){
result = 0;}
if(wn == 360){
result = 1;}
return result;
}
For reasons I do not understand acs = acos(angle) always returns 1.#IND000.
Btw so you know, I am just testing the algorithm against another square, hence the two if statements if i == 3 and if i < 3.
Also incase you need to know these, wy and wx are the world co-ordinates which are translated. Thus moving the player around the world e.g. to move the player forward everything is translated by a minus number for wy.
Further, a Building object would look something like the following struct below:
struct Building {
vector<float> x; //vector storing x co-ords
vector<float> y; //vector storing y co-ords
float ymax, ymin, xmax, xmin //values for bounding box
vector<int> polygons; //stores the number points per polygon (not relevant to the problem)
}
If anyone can help I would amazingly grateful! I just wish I could see where it is all going wrong! (Something I am sure all programmers have said in there time lol) Thanks for readings...

The two lines calculating the modulus of PV and PV1 are incorrect. They should be
modPV = sqrt(PV.x * PV.x + PV.y * PV.y );
modPV1 = sqrt(PV1.x * PV1.x + PV1.y * PV1.y);
Does that fix the problem?

I probably don't understand your problem/question, but there's a simple and robust point in polygon test available here: PNPOLY.

As regards your implementation of the crossing number algorithm the first obvious mistake is that you are not looping over all the sides. You are one short. You should loop up to i < n and then define i plus one as
int ip1 = ( i + 1 ) % n;
This applies to the code in your original question too of course to save you having to have two copies of the code.
The second one is that
rem = cn % 1;
has no effect. The code on softsurfer is fine i.e.
rem = (cn&1);
It is trying to detect if cn is odd or even by testing if the last bit is set. If you want to the same test using the modulo operator % then you should write it as
rem = cn % 2;
as that assigns the remainder on division by two of cn to rem.
I haven't looked beyond that to see if there are any more issues.

I have given up with the winding number code, it really has got me! If anyone does find the solution I would still be amazingly grateful. I am now trying with point in poly detection using the crossing number algorithm. I kept the pesudo code in the comments, again from softsurfer....
int cn_PnPoly( Point P, Building & b, int n )
{
int cn = 0; // the crossing number counter
int rem = 0;
vector<float>x;
vector<float>y;
x.swap(b.getX());
y.swap(b.getY());
//// loop through all edges of the polygon
//for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
// if (((V[i].y <= P.y) && (V[i+1].y > P.y)) // an upward crossing
// || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
// // compute the actual edge-ray intersect x-coordinate
// float vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
// if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
// ++cn; // a valid crossing of y=P.y right of P.x
// }
//}
//return (cn&1); // 0 if even (out), and 1 if odd (in)
// loop through all edges of the polygon
for (int i=0; i<n-1; i++) { // edge from V[i] to V[i+1]
if (((y.at(i) <= P.y) && (y.at(i+1) > P.y)) // an upward crossing
|| ((y.at(i) > P.y) && (y.at(i+1) <= P.y))) { // a downward crossing
// compute the actual edge-ray intersect x-coordinate
float vt = (float)(P.y - y.at(i)) / (y.at(i+1) - y.at(i));
if (P.x < x.at(i) + vt * (x.at(i+1) - x.at(i))) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
rem = cn % 1;
return (rem); // 0 if even (out), and 1 if odd (in)
}
Again this always returns zero, I am unsure why!?! Have I converted the algorithm incorrectly? Does it matter which direction the points are tested (i.e. clockwise, anti-clockwise)?

I have tried implementing PNPOLY as audris suggests. However this gives some funny results.
Below is the orginal C code, then below that is my conversion of that for my app...
Original C code...
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
My code....
Where wx and wy are the global co-ordinates.
int pnpoly(int nvert, vector<float> vertx, vector<float> verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( (( (verty.at(i)+wy) > testy) != ( (verty.at(j)+wy) >testy)) &&
(testx < ((vertx.at(j)+wx) - (vertx.at(i)+wx) ) * (testy- (verty.at(i)+wy) ) / ( (verty.at(j)+wy) - (verty.at(i)+wy)) + (vertx.at(i)+wx)) )
c++;
}
return c;
}
I am testing the player object, against a 2D square building. This also returns strange results, when I hit bottom line (xmin,ymin to xmax,ymin) it works fine. If I hit ethier of the sides (xmin,ymin to xmin,ymax or xmax,ymin to xmax,ymax) it returns 1 only if the player is so far in its past the orgin point. Also on side (xmin,ymin to xmin,ymax) where the player enters the bounding box the algorithm returns 2 despite to hitting the polygon. On the top side, (xmin,ymax to xmax,ymax) it returns 1 only if the player is totally in the polygon.
Also i pass two vectors x and y which are from the Building object, and the vector size as int nvert. Could any of this be to do with the heading of the player object? How is the accounted for within the algorithm?

Hi have done as Troubadour has suggested concerning the crossing number algorithm and made several changes, however the if statement never returns true for some reason. I post of the new code is below. Btw thanks again for everyones replies :-)
int cn_PnPoly( Point P, Building & b, int n )
{
int cn = 0; // the crossing number counter
int rem = 0;
vector<float>x;
vector<float>y;
x.swap(b.getX());
y.swap(b.getY());
//// loop through all edges of the polygon
//for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
// if (((V[i].y <= P.y) && (V[i+1].y > P.y)) // an upward crossing
// || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { // a downward crossing
// // compute the actual edge-ray intersect x-coordinate
// float vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
// if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
// ++cn; // a valid crossing of y=P.y right of P.x
// }
//}
//return (cn&1); // 0 if even (out), and 1 if odd (in)
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
int ip1 = (i +1) %n;
if (((y.at(i) <= P.y) && (y.at(ip1) > P.y)) // an upward crossing
|| ((y.at(i) > P.y) && (y.at(ip1) <= P.y))) { // a downward crossing
// compute the actual edge-ray intersect x-coordinate
float vt = (float)(P.y - y.at(i)) / (y.at(ip1) - y.at(i));
if (P.x < x.at(i) + vt * (x.at(ip1) - x.at(i))) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
rem = (cn&1);
return (rem); // 0 if even (out), and 1 if odd (in)
}

Below I corrected the code, I forgot to add the world co-ords into account. Yet another silly silly error...
int cn_PnPoly( Point P, Building & b, int n )
{
int cn = 0; // the crossing number counter
int rem = 0;
vector<float>x;
vector<float>y;
x.swap(b.getX());
y.swap(b.getY());
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge from V[i] to V[i+1]
int ip1 = (i +1) %n;
if ((( (y.at(i)+wy) <= P.y) && ( (y.at(ip1)+wy) > P.y)) // an upward crossing
|| (( (y.at(i)+wy) > P.y) && ( (y.at(ip1)+wy) <= P.y))) { // a downward crossing
// compute the actual edge-ray intersect x-coordinate
float vt = (float)(P.y - (y.at(i)+wy) ) / ( (y.at(ip1)+wy) - (y.at(i)+wy) );
if (P.x < (x.at(i)+wx) + vt * ( (x.at(ip1)+wx) - (x.at(i)+wx) )) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
rem = (cn&1);
return (rem); // 0 if even (out), and 1 if odd (in)
}
Although this works to detect when a point is in a polygon, it does not take into account the current heading of the player.
If this doesn't make sense, in the 2D game I move the world map around the player by translating all the polygons by the world co-ordinates. These are wx and wy in the game.
Also I rotate the player about a heading varriable.
These are figured out within the draw function, however the collision detection function does not take the heading into account. To do this do I symply multiply the x and y co-ord given by the Building object by the heading? Unfortunately I am not very good at geometry.

Related

How to get the left chain of points of a polygon?

I am trying to get the left polygonal chain given a set of consecutive points. (NOTE: edges are non-intersecting.)
Image 1. Sample polygon and its bound.
What I did was:
Get the minY, maxY and minX. (Bound.)
Find the point that contains minY (or maxY) then save it as the first point.
Save any points until point with minY or maxY is found while checking for point with minX.
If the same Y is found first, save it as the new first point and repeat from #3.
If other Y is found first and the saved points has minX, this is the chain. Otherwise, save as the new first point and repeat from #3.
Image 2. The left chain of points.
But using this steps might give wrong result for some polygon, like this:
Since one point is (minX, maxY), either of the side will be returned.
EDIT:
With the idea of the left-bottom- and left-top-most points, here is the current code that I am using:
Get the min (left-bottom-most) and max (left-top-most) point.
std::vector<Coord> ret;
size_t i = 0;
Coord minCoord = poly[i];
Coord maxCoord = poly[i];
size_t minIdx = -1;
size_t maxIdx = -1;
size_t cnt = poly.size();
i++;
for (; i < cnt; i++)
{
Coord c = poly[i];
if (c.y < minCoord.y) // new bottom
{
minCoord = c;
minIdx = i;
}
else if (c.y == minCoord.y) // same bottom
{
if (c.x < minCoord.x) // left most
{
minCoord = c;
minIdx = i;
}
}
if (c.y > maxCoord.y) // new top
{
maxCoord = c;
maxIdx = i;
}
else if (c.y == maxCoord.y) // same top
{
if (c.x < maxCoord.x) // left most
{
maxCoord = c;
maxIdx = i;
}
}
}
Get the points connected to the max point.
i = maxIdx;
Coord mid = poly[i];
Coord ray1 = poly[(i + cnt - 1) % cnt];
Coord ray2 = poly[(i + 1) % cnt];
Get which has smallest angle. This will be the path we will follow.
double rad1 = Pts2Rad(mid, ray1);
double rad2 = Pts2Rad(mid, ray2);
int step = 1;
if (rad1 < rad2)
step = cnt - 1;
Save the points.
while (i != minIdx)
{
ret.push_back(poly[i]);
i = (i + step) % cnt;
}
ret.push_back(poly[minIdx]);
To be specific, I am assuming that no vertex is duplicated and define the "left chain" as the sequence of vertices from the original polygon loop that goes from the leftmost vertex in the top side of the bounding box, to the leftmost vertex in the bottom side of the bounding box. [In case the top and bottom sides coincide, these two vertices also coincide; I leave it to you what to return in this case.]
To obtain these, you can scan all vertices and keep the left-topmost so far and left-bottommost so far. Then compare to the next vertex. If above the left-topmost, becomes the new lef-topmost. If at the same level and to the left, becomes the new left-topmost. Similarly for the left-bottommost.

Implementing De Boors algorithm for finding points on a B-spline

I've been working on this for several weeks but have been unable to get my algorithm working properly and i'm at my wits end. Here's an illustration of what i have achieved:
If everything was working i would expect a perfect circle/oval at the end.
My sample points (in white) are recalculated every time a new control point (in yellow) is added. At 4 control points everything looks perfect, again as i add a 5th on top of the 1st things look alright, but then on the 6th it starts to go off too the side and on the 7th it jumps up to the origin!
Below I'll post my code, where calculateWeightForPointI contains the actual algorithm. And for reference- here is the information i'm trying to follow. I'd be so greatful if someone could take a look for me.
void updateCurve(const std::vector<glm::vec3>& controls, std::vector<glm::vec3>& samples)
{
int subCurveOrder = 4; // = k = I want to break my curve into to cubics
// De boor 1st attempt
if(controls.size() >= subCurveOrder)
{
createKnotVector(subCurveOrder, controls.size());
samples.clear();
for(int steps=0; steps<=20; steps++)
{
// use steps to get a 0-1 range value for progression along the curve
// then get that value into the range [k-1, n+1]
// k-1 = subCurveOrder-1
// n+1 = always the number of total control points
float t = ( steps / 20.0f ) * ( controls.size() - (subCurveOrder-1) ) + subCurveOrder-1;
glm::vec3 newPoint(0,0,0);
for(int i=1; i <= controls.size(); i++)
{
float weightForControl = calculateWeightForPointI(i, subCurveOrder, controls.size(), t);
newPoint += weightForControl * controls.at(i-1);
}
samples.push_back(newPoint);
}
}
}
//i = the weight we're looking for, i should go from 1 to n+1, where n+1 is equal to the total number of control points.
//k = curve order = power/degree +1. eg, to break whole curve into cubics use a curve order of 4
//cps = number of total control points
//t = current step/interp value
float calculateWeightForPointI( int i, int k, int cps, float t )
{
//test if we've reached the bottom of the recursive call
if( k == 1 )
{
if( t >= knot(i) && t < knot(i+1) )
return 1;
else
return 0;
}
float numeratorA = ( t - knot(i) );
float denominatorA = ( knot(i + k-1) - knot(i) );
float numeratorB = ( knot(i + k) - t );
float denominatorB = ( knot(i + k) - knot(i + 1) );
float subweightA = 0;
float subweightB = 0;
if( denominatorA != 0 )
subweightA = numeratorA / denominatorA * calculateWeightForPointI(i, k-1, cps, t);
if( denominatorB != 0 )
subweightB = numeratorB / denominatorB * calculateWeightForPointI(i+1, k-1, cps, t);
return subweightA + subweightB;
}
//returns the knot value at the passed in index
//if i = 1 and we want Xi then we have to remember to index with i-1
float knot(int indexForKnot)
{
// When getting the index for the knot function i remember to subtract 1 from i because of the difference caused by us counting from i=1 to n+1 and indexing a vector from 0
return knotVector.at(indexForKnot-1);
}
//calculate the whole knot vector
void createKnotVector(int curveOrderK, int numControlPoints)
{
int knotSize = curveOrderK + numControlPoints;
for(int count = 0; count < knotSize; count++)
{
knotVector.push_back(count);
}
}
Your algorithm seems to work for any inputs I tried it on. Your problem might be a that a control point is not where it is supposed to be, or that they haven't been initialized properly. It looks like there are two control-points, half the height below the bottom left corner.

Optimizing / simplifying a path where many points are close together?

I have a path of points that represent the outline of a polygon. The path is constructed from pixels.
This means all points are very very close to each other, but I've ensured they are all unique.
Right now I'm checking if 3 points are collinear, and if they are, I remove the middle one.
I check if they are collinear using dot product. I observed however that many of my dot products are 0.0f. What could be wrong?
void ImagePolygon::computeOptimized()
{
m_optimized = m_hull;
m_optimized.erase(
std::unique(m_optimized.begin(),
m_optimized.end()),
m_optimized.end());
int first = 0;
int second = 1;
std::vector<int> removeList;
for(int i = 2; i < m_optimized.size(); ++i)
{
second = i - 1;
first = i - 2;
if(isColinear(m_optimized[i - 2],m_optimized[i - 1],m_optimized[i]))
{
m_optimized.erase(m_optimized.begin() + i - 1);
removeList.push_back(i - 1);
}
}
std::sort(removeList.rbegin(),removeList.rend());
for(int i = 0; i < removeList.size(); ++i)
{
m_optimized.erase(m_optimized.begin() + removeList[i]);
}
}
bool ImagePolygon::isColinear( const b2Vec2& a, const b2Vec2& b, const b2Vec2& c ) const
{
b2Vec2 vec1 = b2Vec2(b.x - a.x, b.y - a.y);
vec1.Normalize();
b2Vec2 vec2 = b2Vec2(c.x - b.x, c.y - b.y);
vec2.Normalize();
float dotProduct = vec1.x * vec2.x + vec1.y * vec2.y;
//test value
return abs(dotProduct) > 0.00001f;
}
The major problem is that I'm getting a lot of 0 dot products when I should not so therefore no matter where I set the threshold the path is not optimized as much as it should be.
Thanks
float32 Normalize()
{
float32 length = Length();
if (length < b2_epsilon)
{
return 0.0f;
}
float32 invLength = 1.0f / length;
x *= invLength;
y *= invLength;
return length;
}
You want the 2x2 determinant vec1.x * vec2.y - vec1.y * vec2.x instead of the dot product. The determinant is zero iff the points are collinear, whereas the dot product is zero iff the points form a right angle.
This:
return abs(dotProduct) > 0.00001f;
is actually telling you whether your vectors are (not) perpendicular, not whether they are parallel. Check if it's close to 1 rather than close to 0 for parallel.
You should not increment index in case the element is deleted. You are skipping some values. Try the following:
for(int i = 2; i < m_optimized.size();) {
second = i - 1;
first = i - 2;
if (isColinear(m_optimized[i - 2],m_optimized[i - 1],m_optimized[i])) {
m_optimized.erase(m_optimized.begin() + i - 1);
removeList.push_back(i - 1);
} else i++;
}
Also I can not understand the purpose of the removeList. You erase some points inside of the main loop and try to erase the same points in the subsidiary loop. It seems to be an error. BTW, there is no reason to sort removeList due to the way it was constructed.

Implementing seek behaviour of objects on a plane in OpenSceneGraph?

I have created an open plane area with thin cylinders on it like pucks, they bounce around the area and have collision detection for some larger cylinders also placed on the plane. I am trying to get them to now head towards a set point on the plane using a steering method.
The steering works for works for avoiding the obstacles by calculating distance from obstacle then calculating angle between direction travelling and the direction of the obstacle, using the calculation of the distance from obstacle when the puck is too close it steers left or right based on the calculated angle. The same technique reversed fails to work for steering towards a point, I have tried using both acos and atan2 to calculate the angle between direction travelling and target direction and from outputs believe this bit is right but when I try to use that calculation to determine when to steer towards the target I get unexpected results. Sometimes random turning?
#include "Puck.h"
#include <iostream>
#include <fstream>
using namespace std;
#include <math.h>
ofstream fout("danna.txt");
#ifndef M_PI
#define M_PI 3.1415
#endif
class TranslateCB : public osg::NodeCallback
{
public:
TranslateCB() : _dx( 0. ), _dy( 0. ), _dirx(1), _diry(0), _inc(0.1), _theta(0) {}
TranslateCB(Puck** pp, Obstacle** ob, int count, double r, double x, double y) : _dx( 0. ), _dy( 0. ),
_dirx(2.0*rand()/RAND_MAX-1), _diry(2.0*rand()/RAND_MAX-1), _inc(0.3), _theta(0)
{
obstacles = ob;
ob_count = count;
_radius = r;
_x = x;
_y = y;
puckH = pp;
}
virtual void operator()( osg::Node* node,osg::NodeVisitor* nv )
{
osg::MatrixTransform* mt =
dynamic_cast<osg::MatrixTransform*>( node );
osg::Matrix mR, mT;
mT.makeTranslate( _dx , _dy, 0. );
mt->setMatrix( mT );
double ob_dirx;
double ob_diry;
double ob_dist;
double centerX=0, centerY =0;
_theta = 0;
double min = 4;
// location that I am trying to get the pucks to head towards
centerX = 1;
centerY = 5;
double tDirx = (_x+_dx) - centerX;
double tDiry = (_y+_dy) - centerY;
double tDist = sqrt(tDirx*tDirx+tDiry*tDiry); //distance to target location
// normalizing my target direction
tDirx = tDirx/tDist;
tDiry = tDiry/tDist;
double hDist = sqrt(_dirx*_dirx + _diry*_diry); //distance to next heading
_dirx= _dirx/hDist;
_diry= _diry/hDist;
double cAngle = acos(_dirx*tDirx+_diry*tDiry); //using inverse of cos to calculate angle between directions
double tAngle = atan2(centerY - (_y+_dy),centerX - (_x+_dx)); // using inverse of tan to calculate angle between directions
double tMin = tDist*sin(cAngle);
//if statement used to define when to apply steering direction
if(tMin > 3)
{
if(tDist < 1){ _theta = 0; } //puck is inside target location, so keep travelling straight
if(cAngle > M_PI/2){ _theta = -0.1; } //turn left
else{ _theta = 0.1; } //turn right
}
else{ _theta = 0; }
////// The collision detection for the obstacles that works on the same princables that I am using above
for(int i = 0; i < ob_count; i++)
{
ob_dirx = (_x+_dx) - obstacles[i]->x;
ob_diry = (_y+_dy) - obstacles[i]->y;
ob_dist = sqrt(ob_dirx*ob_dirx+ob_diry*ob_diry);
if (ob_dist < 3) {
//normalise directions
double ob_norm = sqrt(ob_dirx*ob_dirx+ob_diry*ob_diry);
ob_dirx = (ob_dirx)/ob_norm;
ob_diry = (ob_diry)/ob_norm;
double norm = sqrt(_dirx*_dirx+_diry*_diry);
_dirx = (_dirx)/norm;
_diry = (_diry)/norm;
//calculate angle between direction travelling, and direction to obstacle
double angle = acos(_dirx*ob_dirx + _diry*ob_diry);
//calculate closest distance between puck and obstacle if continues on same path
double min_dist = ob_dist*sin(angle);
if(min_dist < _radius + obstacles[i]->radius && ob_dist < min+obstacles[i]->radius)
{
min = ob_dist;
if(ob_dist < _radius + obstacles[i]->radius){ _theta = 0; }
else if(angle <= M_PI/2){ _theta = -0.3; }
else{ _theta = 0.3; }
}
}
}
//change direction accordingly
_dirx = _dirx*cos(_theta) + _diry*sin(_theta);
_diry = _diry*cos(_theta) - _dirx*sin(_theta);
_dx += _inc*_dirx;
if((_x+_dx > 20 && _dirx > 0) || (_x+_dx < -20 && _dirx < 0))
{
_dirx = -_dirx;
_diry += (0.2*rand()/RAND_MAX-0.1); //add randomness to bounce
}
_dy += _inc*_diry;
if((_y+_dy > 20 && _diry > 0) || (_y+_dy < -20 && _diry < 0))
{
_diry = -_diry;
_dirx += (0.2*rand()/RAND_MAX-0.1); //add randomness to bounce
}
traverse( node, nv );
}
private:
double _dx,_dy;
double _dirx,_diry;
double _inc;
double _theta;
double _radius;
double _x,_y;
Obstacle** obstacles;
Puck** puckH;
int ob_count;
};
Puck::Puck()
{
}
void Puck::createBoids (Puck** pucks, Group *root, Obstacle** obstacles, int count, double xx, double yy)
{
// geometry
radius = 0.2;
x = xx;
y = yy;
ob_count = count;
Cylinder *shape=new Cylinder(Vec3(x,y,0),radius,0.1);
ShapeDrawable *draw=new ShapeDrawable(shape);
draw->setColor(Vec4(1,0,0,1));
Geode *geode=new Geode();
geode->addDrawable(draw);
// transformation
MatrixTransform *T=new MatrixTransform();
TranslateCB *tcb = new TranslateCB(pucks, obstacles,ob_count,radius,x,y);
T->setUpdateCallback(tcb);
T->addChild(geode);
root->addChild(T);
}
any help would be amazing!
The problem here is that the technique that "works" for avoiding obstacles will always occur when the puck is heading towards the obstacle. This special condition makes both the direction of the puck and the direction of the obstacle in adjacent quadrants.
When attempting to make the pucks steer towards the obstacle however, the technique breaks down because the puck most likely will be heading away from the obstacle, no longer having the condition that the target and direction vectors are in adjacent quadrants.
The correct way to determine the steering direction is to rotate the target vector by an angle that would make the the direction vector point straight up in the quadrants (0, 1). Now that the target vector is relative to the direction vector (0, 1) looking at the x component of the target vector will determine the steering direction. If the x component of the target vector is negative, the puck must turn left to steer towards the target (increase the angle). If the x component of the target vector is positive, the puck must turn right to steer towards the target (decrease the angle).
Consider the following snippet written in python to verify this, it should still be easy to read for you to grasp the concept:
from math import *
dirX = 0.0
dirY = 0.0
targX = 1.0
targY = 0.0
def dir():
global dirX, dirY, targX, targY
# get magnitiude of direction
mag1 = sqrt(dirX*dirX + dirY*dirY)
if mag1 != 0:
# normalize direction vector
normX = dirX / mag1
normY = dirY / mag1
# get magnitude of target vector
mag2 = sqrt(targX*targX + targY*targY)
if mag2 != 0:
# normalize target vector
targX = targX / mag2
targY = targY / mag2
# find the angle need to rotate the dir vector to (0, 1)
rotateAngle = (pi/2.0) - atan2(normY, normX)
# rotate targ vector by that angle (we only care about the x component)
relTargX = cos(rotateAngle) * normX + sin(rotateAngle) * normY
# if the target vector's x is negative
if relTargX < 0:
# turn left
print "Left!"
# otherwise the target vector is 0 or positive
else:
# turn right
print "Right!"
def out():
global dirX, dirY, targX, targY
# function just prints values to the screen
print "dir(%f, %f) targ(%f, %f)" % (dirX, dirY, targX, targY)
# for values 0 to 360
for i in range(360):
# pretend this is the pucks direction
dirX = sin(radians(i))
dirY = cos(radians(i))
# print dir and target vectors to screen
out()
# print the direction to turn
dir()
I suppose I could've written this in C++, but compared to running a python prompt it's a royal pain. It is as readable as any pseudo code I could've written and the concepts will work regardless of language.

Trying to optimize line vs cylinder intersection

My brain has been melting over a line segment-vs-cylinder intersection routine I've been working on.
/// Line segment VS <cylinder>
// - cylinder (A, B, r) (start point, end point, radius)
// - line has starting point (x0, y0, z0) and ending point (x0+ux, y0+uy, z0+uz) ((ux, uy, uz) is "direction")
// => start = (x0, y0, z0)
// dir = (ux, uy, uz)
// A
// B
// r
// optimize? (= don't care for t > 1)
// <= t = "time" of intersection
// norm = surface normal of intersection point
void CollisionExecuter::cylinderVSline(const Ogre::Vector3& start, const Ogre::Vector3& dir, const Ogre::Vector3& A, const Ogre::Vector3& B, const double r,
const bool optimize, double& t, Ogre::Vector3& normal) {
t = NaN;
// Solution : http://www.gamedev.net/community/forums/topic.asp?topic_id=467789
double cxmin, cymin, czmin, cxmax, cymax, czmax;
if (A.z < B.z) { czmin = A.z - r; czmax = B.z + r; } else { czmin = B.z - r; czmax = A.z + r; }
if (A.y < B.y) { cymin = A.y - r; cymax = B.y + r; } else { cymin = B.y - r; cymax = A.y + r; }
if (A.x < B.x) { cxmin = A.x - r; cxmax = B.x + r; } else { cxmin = B.x - r; cxmax = A.x + r; }
if (optimize) {
if (start.z >= czmax && (start.z + dir.z) > czmax) return;
if (start.z <= czmin && (start.z + dir.z) < czmin) return;
if (start.y >= cymax && (start.y + dir.y) > cymax) return;
if (start.y <= cymin && (start.y + dir.y) < cymin) return;
if (start.x >= cxmax && (start.x + dir.x) > cxmax) return;
if (start.x <= cxmin && (start.x + dir.x) < cxmin) return;
}
Ogre::Vector3 AB = B - A;
Ogre::Vector3 AO = start - A;
Ogre::Vector3 AOxAB = AO.crossProduct(AB);
Ogre::Vector3 VxAB = dir.crossProduct(AB);
double ab2 = AB.dotProduct(AB);
double a = VxAB.dotProduct(VxAB);
double b = 2 * VxAB.dotProduct(AOxAB);
double c = AOxAB.dotProduct(AOxAB) - (r*r * ab2);
double d = b * b - 4 * a * c;
if (d < 0) return;
double time = (-b - sqrt(d)) / (2 * a);
if (time < 0) return;
Ogre::Vector3 intersection = start + dir * time; /// intersection point
Ogre::Vector3 projection = A + (AB.dotProduct(intersection - A) / ab2) * AB; /// intersection projected onto cylinder axis
if ((projection - A).length() + (B - projection).length() > AB.length()) return; /// THIS IS THE SLOW SAFE WAY
//if (projection.z > czmax - r || projection.z < czmin + r ||
// projection.y > cymax - r || projection.y < cymin + r ||
// projection.x > cxmax - r || projection.x < cxmin + r ) return; /// THIS IS THE FASTER BUGGY WAY
normal = (intersection - projection);
normal.normalise();
t = time; /// at last
}
I have thought of this way to speed up the discovery of whether the projection of the intersection point lies inside the cylinder's length. However, it doesn't work and I can't really get it because it seems so logical :
if the projected point's x, y or z co-ordinates are not within the cylinder's limits, it should be considered outside. It seems though that this doesn't work in practice.
Any help would be greatly appreciated!
Cheers,
Bill Kotsias
Edit : It seems that the problems rise with boundary-cases, i.e when the cylinder is parallel to one of the axis. Rounding errors come into the equation and the "optimization" stops working correctly.
Maybe, if the logic is correct, the problems will go away by inserting a bit of tolerance like :
if (projection.z > czmax - r + 0.001 || projection.z < czmin + r - 0.001 || ... etc...
The cylinder is circular, right? You could transform coordinates so that the center line of the cylinder functions as the Z axis. Then you have a 2D problem of intersecting a line with a circle. The intersection points will be in terms of a parameter going from 0 to 1 along the length of the line, so you can calculate their positions in that coordinate system and compare to the top and bottom of the cylinder.
You should be able to do it all in closed form. No tolerances. And sure, you will get singularities and imaginary solutions. You seem to have thought of all this, so I guess I'm not sure what the question is.
This is what I use, it may help:
bool d3RayCylinderIntersection(const DCylinder &cylinder,const DVector3 &org,const DVector3 &dir,float &lambda,DVector3 &normal,DVector3 &newPosition)
// Ray and cylinder intersection
// If hit, returns true and the intersection point in 'newPosition' with a normal and distance along
// the ray ('lambda')
{
DVector3 RC;
float d;
float t,s;
DVector3 n,D,O;
float ln;
float in,out;
RC=org; RC.Subtract(&cylinder.position);
n.Cross(&dir,&cylinder.axis);
ln=n.Length();
// Parallel? (?)
if((ln<D3_EPSILON)&&(ln>-D3_EPSILON))
return false;
n.Normalize();
d=fabs(RC.Dot(n));
if (d<=cylinder.radius)
{
O.Cross(&RC,&cylinder.axis);
//TVector::cross(RC,cylinder._Axis,O);
t=-O.Dot(n)/ln;
//TVector::cross(n,cylinder._Axis,O);
O.Cross(&n,&cylinder.axis);
O.Normalize();
s=fabs( sqrtf(cylinder.radius*cylinder.radius-d*d) / dir.Dot(O) );
in=t-s;
out=t+s;
if (in<-D3_EPSILON)
{
if(out<-D3_EPSILON)
return false;
else lambda=out;
} else if(out<-D3_EPSILON)
{
lambda=in;
} else if(in<out)
{
lambda=in;
} else
{
lambda=out;
}
// Calculate intersection point
newPosition=org;
newPosition.x+=dir.x*lambda;
newPosition.y+=dir.y*lambda;
newPosition.z+=dir.z*lambda;
DVector3 HB;
HB=newPosition;
HB.Subtract(&cylinder.position);
float scale=HB.Dot(&cylinder.axis);
normal.x=HB.x-cylinder.axis.x*scale;
normal.y=HB.y-cylinder.axis.y*scale;
normal.z=HB.z-cylinder.axis.z*scale;
normal.Normalize();
return true;
}
return false;
}
Have you thought about it this way?
A cylinder is essentially a "fat" line segment so a way to do this would be to find the closest point on line segment (the cylinder's center line) to line segment (the line segment you are testing for intersection).
From there, you check the distance between this closest point and the other line segment, and compare it to the radius.
At this point, you have a "Pill vs Line Segment" test, but you could probably do some plane tests to "chop off" the caps on the pill to make a cylinder.
Shooting from the hip a bit though so hope it helps (:
Mike's answer is good. For any tricky shape you're best off finding the transformation matrix T that maps it into a nice upright version, in this case an outright cylinder with radius 1. height 1, would do the job nicely. Figure out your new line in this new space, perform the calculation, convert back.
However, if you are looking to optimise (and it sounds like you are), there is probably loads you can do.
For example, you can calculate the shortest distance between two lines -- probably using the dot product rule -- imagine joining two lines by a thread. Then if this thread is the shortest of all possible threads, then it will be perpendicular to both lines, so Thread.LineA = Thread.LineB = 0
If the shortest distance is greater than the radius of the cylinder, it is a miss.
You could define the locus of the cylinder using x,y,z, and thrash the whole thing out as some horrible quadratic equation, and optimise by calculating the discriminant first, and returning no-hit if this is negative.
To define the locus, take any point P=(x,y,z). drop it as a perpendicular on to the centre line of your cylinder, and look at its magnitude squared. if that equals R^2 that point is in.
Then you throw your line {s = U + lamda*V} into that mess, and you would end up with some butt ugly quadratic in lamda. but that would probably be faster than fiddling matrices, unless you can get the hardware to do it (I'm guessing OpenGL has some function to get the hardware to do this superfast).
It all depends on how much optimisation you want; personally I would go with Mike's answer unless there was a really good reason not to.
PS You might get more help if you explain the technique you use rather than just dumping code, leaving it to the reader to figure out what you're doing.