Determine if angle lies between 2 other angles - c++

I am trying to figure out whether a angle lies between 2 other angles. I have been trying to create a simple function to perform this but none of my techniques will work for all possible values of the angles.
Can you help me edit my function to correctly determine if a angle lies between 2 other angles?
In the above picture; I use the green point as the central point, then I determine the angle of each line to the green point. I then calculate the angle of the black point to the green point. I am trying to check if the angle of the black dot is BETWEEN the 2 lines' angles.
NOTE: In my case; an angle(targetAngle) is said to lie between 2 other angles IF the difference between the 2 angles is < 180 degrees AND the targetAngle lies in the cavity made by those 2 angles.
The following code should work but it fails for these(which do lie between the angle):
- is_angle_between(150, 190, 110)
- is_angle_between(3, 41, 345)
bool is_angle_between(int target, int angle1, int angle2)
{
int rAngle1 = ((iTarget - iAngle1) % 360 + 360) % 360;
int rAngle2 = ((iAngle2 - iAngle1) % 360 + 360) % 360;
return (0 <= rAngle1 && rAngle1 <= rAngle2);
}
// Example usage
is_angle_between(3, 41, 345);
Another technique I attempted which also doesn't work:
int is_angle_between(int target, int angle1, int angle2)
{
int dif1 = angle1-angle2;
int dif2 = angle2-angle1;
int uDif1 = convert_to_positive_angle( dif1 ); // for eg; convert -15 to 345
int uDif2 = convert_to_positive_angle( dif2 );
if (uDif1 <= uDif2) {
if (dif1 < 0) {
return (target <= angle1 && target >= angle2);
}
else return (in_between_numbers(iTarget, iAngle1, iAngle2));
}
else {
if (dif2 < 0) {
return (target <= angle1 && target >= angle2);
}
else return (in_between_numbers(iTarget, iAngle1, iAngle2));
}
return -1;
}

bool is_angle_between(int target, int angle1, int angle2)
{
// make the angle from angle1 to angle2 to be <= 180 degrees
int rAngle = ((angle2 - angle1) % 360 + 360) % 360;
if (rAngle >= 180)
std::swap(angle1, angle2);
// check if it passes through zero
if (angle1 <= angle2)
return target >= angle1 && target <= angle2;
else
return target >= angle1 || target <= angle2;
}

Inspired by a post about Intervals in modular arithmetic:
static bool is_angle_between(int x, int a, int b) {
b = modN(b - a);
x = modN(x - a);
if (b < 180) {
return x < b;
} else {
return b < x;
}
}
where (in case of checking angles) modN() would be implemented as
// modN(x) is assumed to calculate Euclidean (=non-negative) x % N.
static int modN(int x) {
const int N = 360;
int m = x % N;
if (m < 0) {
m += N;
}
return m;
}

void normalize( float& angle )
{
while ( angle < -180 ) angle += 360;
while ( angle > 180 ) angle -= 360;
}
bool isWithinRange( float testAngle, float a, float b )
{
a -= testAngle;
b -= testAngle;
normalize( a );
normalize( b );
if ( a * b >= 0 )
return false;
return fabs( a - b ) < 180;
}

If angle2 were always 0, and angle1 were always between 0 and 180, this would be easy:
return angle1 < 180 && 0 < target && target < angle1;
if I'm reading the requirements correctly.
But it's not that hard to get there.
int reduced1 = (angle1 - angle2 + 360) % 360; // and imagine reduced2 = 0
if (180 < reduced1) { angle2 = angle1; reduced1 = 360 - reduced1; } // swap if backwards
int reducedTarget = (target - angle2 + 360) % 360;
return reduced1 < 180 && 0 < reducedTarget && reducedTarget < reduced1;

I've done this before by comparing angles.
In the sketch above vector AD will be between AB and AC if and only if
angle BAD + angle CAD == angle BAC
Because of floating point inaccuracies I compared the values after rounding them first to say 5 decimal places.
So it comes down to having an angle algorithm between two vectors p and q which is simply put like:
double a = p.DotProduct(q);
double b = p.Length() * q.Length();
return acos(a / b); // radians
I'll leave the vector DotProduct and Length calculations as a google search exercise. And you get vectors simply by subtracting the coordinates of one terminal from the other.
You should of course first check whether AB and AC are parallel or anti-parallel.

All the top answers here are wrong. As such I feel it is necessary for me to post an answer.
I'm just reposting a portion of an answer which I posted here: https://stackoverflow.com/a/42424631/2642059 That answer also deals with the case where you already know which angle is the lefthand side and righthand side of the reflexive angle. But you also need to determine which side of the angle is which.
1st to find the leftmost angle if either of these statements are true angle1 is your leftmost angle:
angle1 <= angle2 && angle2 - angle1 <= PI
angle1 > angle2 && angle1 - angle2 >= PI
For simplicity let's say that your leftmost angle is l and your rightmost angle is r and you're trying to find if g is between them.
The problem here is the seem. There are essentially 3 positive cases that we're looking for:
l ≤ g ≤ r
l ≤ g ∧ r < l
g ≤ r ∧ r < l
Since you're calculating the lefthand and righthand sides of the angle, you'll notice there is an optimization opportunity here in doing both processes at once. Your function will look like:
if(angle1 <= angle2) {
if(angle2 - angle1 <= PI) {
return angle1 <= target && target <= angle2;
} else {
return angle2 <= target || target <= angle1;
}
} else {
if(angle1 - angle2 <= PI) {
return angle2 <= target && target <= angle1;
} else {
return angle1 <= target || target <= angle2;
}
}
Or if you need it you could expand into this nightmare condition:
angle1 <= angle2 ?
(angle2 - angle1 <= PI && angle1 <= target && target <= angle2) || (angle2 - angle1 > PI && (angle2 <= target || target <= angle1)) :
(angle1 - angle2 <= PI && angle2 <= target && target <= angle1) || (angle1 - angle2 > PI && (angle1 <= target || target <= angle2))
Note that all this math presumes that your input is in radians and in the range [0 : 2π].
Live Example

Is angle T between angles A and B, there are always two answers: true and false.
We need specify what we mean, and in this case we're looking for the normalized small sweep angles and whether our angle is between those values. Given any two angles, there is a reflex angle between them, is the normalized value of T within that reflex angle?
If we rotate A and B and T such that T = 0 and normalize A and B to within +-hemicircumference (180° or 2PI). Then our answer is whether A and B have different signs, and are within a hemicircumference of each other.
If we subtract the angle from test, then add 180° (so A is relative to T+180). Then we mod by 360 giving us a range between [-360°,360°] we add 360° and mod again (note, you could also just check if it's negative and add 360 if it is), giving us a value that is certain to be [0°,360°]. We subtract 180° giving us a value between [-180°,180°] relative to T+180°-180° aka, T. So T is now angle zero and all angles fall within the normalized range. Now we check to make sure the angles have a sign change and that they are not more than 180° apart, we have our answer.
Because the question asks in C++:
bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
int a_adjust = ((((a - test + 180)) % 360) + 360) % 360 - 180;
int b_adjust = ((((b - test + 180)) % 360) + 360) % 360 - 180;
return ((a_adjust ^ b_adjust) < 0) && ((a_adjust - b_adjust) < 180) && ((a_adjust - b_adjust) > -180);
}
We can also do some tricks to simplify out the code and avoid any unneeded modulo ops (see comments below). Normalize will move angle a into the range [-180°,180°] relative to angle t.
int normalized(int a, int test) {
int n = a - test + 180;
if ((n > 360) || (n < -360)) n %= 360;
return (n > 0)? n - 180: n + 180;
}
bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
int a_adjust = normalized(a,test);
int b_adjust = normalized(b,test);
return ((a_adjust ^ b_adjust) < 0) &&
((a_adjust > b_adjust)? a_adjust-b_adjust: b_adjust-a_adjust) < 180;
}
Also if we can be sure the range is [0,360], we can make do with a simpler if statement
bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
int dA = a - test + 180;
if (dA > 360) {
dA -= 360;
}
int a_adjust = (dA > 0) ? dA - 180 : dA + 180;
int dB = b - test + 180;
if (dB > 360) {
dB -= 360;
}
int b_adjust = (dB > 0) ? dB - 180 : dB + 180;
return ((a_adjust ^ b_adjust) < 0)
&& ((a_adjust > b_adjust) ? a_adjust - b_adjust : b_adjust - a_adjust) < 180;
}
JS Fiddle test of the code

I've found this quote from this thread:
if a point P is inside triangle ABC, then
Area PAB+Area PBC +Area PAC=Area ABC
notice that if P is on the edge of AB, BC, or CA, the above hold. But
effectively, one of the area PAB, PBC, PAC is 0 (so just make sure you
check that).
if P is outside, the above equality does NOT hold...
How to determine area? you have two options: 1) Heron's theorem,
involves sqrt, slower 2) the more perferred way is the cross products
(or effectively, the half of absolute value of (sum of the down
products minus the sum of up products))
for example, if A=(x1,y1) B=(x2,y2), C=(x3,y3) Area=
abs(x1*y2+x2*y3+x3*y1-x1*y3-x3*y2-x2*y1)/2
also you might want to be careful about floating point errors...
instead of checking for strict inequality, check for abs(b-a)
Hopefully that will help

Using a similar style of function as in your question, I have had good luck with the following methods:
public static bool IsInsideRange(double testAngle, double startAngle, double endAngle)
{
var a1 = System.Math.Abs(AngleBetween(startAngle, testAngle));
var a2 = System.Math.Abs(AngleBetween(testAngle, endAngle));
var a3 = System.Math.Abs(AngleBetween(startAngle, endAngle));
return a1 + a2 == a3;
}
public static double AngleBetween(double start, double end)
{
return (end - start) % 360;
}

I know this post is old, but there doesn't seem to be an accepted answer and I have found the following approach to be quite reliable. Although it might be more than what you need. It supports angle ranges larger than 180 degrees (as well as larger than 360 degrees and negative angles). It also supports decimal accuracy.
The method uses this normalize() helper function to convert angles into the right space:
float normalize( float degrees )
{
//-- Converts the specified angle to an angle between 0 and 360 degrees
float circleCount = (degrees / 360.0f);
degrees -= (int)circleCount * 360;
if( 0.0f > degrees )
{
degrees += 360.0f;
}
return degrees;
}
Here's the solution:
bool isWithinRange( float start, float end, float angle )
{
if( fabsf( end - start ) >= 360.0f )
{
//-- Ranges greater or equal to 360 degrees cover everything
return true;
}
//-- Put our angle between 0 and 360 degrees
float degrees = normalize( angle );
//-- Resolve degree value for the start angle; make sure it's
// smaller than our angle.
float startDegrees = normalize( start );
if( startDegrees > degrees )
{
startDegrees -= 360.0f;
}
//-- Resolve degree value for the end angle to be within the
// same 360 degree range as the start angle and make sure it
// comes after the start angle.
float endDegrees = normalize( end );
if( endDegrees < startDegrees )
{
endDegrees += 360.0f;
}
else if( (endDegrees - startDegrees) >= 360.0f )
{
endDegrees -= 360.0f;
}
//-- All that remains is to validate that our angle is between
// the start and the end.
if( (degrees < startDegrees) || (degrees > endDegrees) )
{
return false;
}
return true;
}
Hope this helps someone.

If you have angles $$a$ and $b$, and wan't to see if angle x is between these angles.
You can calculate the angle between a->x and a->b.
If ∠a->x is less than ∠a->b, x must be between a and b.
The distance between to angles, a and b
function distanceBetweenAngles(a, b) {
distance = b - a;
if (a > b) {
distance += 2*pi;
}
return distance;
}
Then you can do
// Checks if angle 'x' is between angle 'a' and 'b'
function isAngleBetween(x, a, b) {
return distanceBetweenAngles(a, b) >= distanceBetweenAngles(a, x);
}
This assumes you are using Radians, and not Degrees, as one should. It removes a lot of unnecessary code.

Related

Cubic Interpolation with the official formula fails

I am trying to implement the Cubic Interpolation method using the next formula when a=-0.5 as usual.
My Linear Interpolation and Nearest Neighbor interpolation is working great but for some reason the Cubic interpolation fails with white pixels and turn them sometimes to turquoise color and sometimes messing around with another colors.
for example using rotation: (NOTE: please look carefully on the right image and you will notice the problems)
Another Example with much more black pixels. It almost seems to work perfectly but look on the dog's tongue. (strong white pixels turn to turquoise again)
you can see that my implementation of the Linear Interpolation is working great:
Since the actual rotation worked, I think I have a small mistake in the code that I did not notice, or maybe it's a numeric error or a double / float error.
It is important to note that I read the image normally and store the destination image as follows:
cv::Mat img = cv::imread("../dogfails.jpeg");
cv::Mat rotatedImageCubic(img.rows,img.cols,CV_8UC3);
Clarifications:
Inside my cubic interpolation function, srcPoint (newX and newY) is the "landing point" from the inverse transformation.
In my inverse transformations I am not using matrix multiplication with the pixels, right now I am just using the formulas for rotation. It might be important for the "numerical errors". For example:
rotatedX = x * cos(angle * toRadian) + y * sin(angle * toRadian);
rotatedY = x * (-sin(angle * toRadian)) + y * cos(angle * toRadian);
Here is my code for the Cubic Interpolation
double cubicEquationSolver(double d,double a) {
d = abs(d);
if( 0.0 <= d && d <= 1.0) {
double score = (a + 2.0) * pow(d, 3.0) - ((a + 3.0) * pow(d, 2.0)) + 1.0;
return score;
}
else if(1 < d && d <= 2) {
double score = a * pow(d, 3.0) - 5.0*a * pow(d, 2.0) + 8.0*a * d - 4.0*a;
return score;
}
else
return 0.0;
}
void Cubic_Interpolation_Helper(const cv::Mat& src, cv::Mat& dst, const cv::Point2d& srcPoint, cv::Point2i& dstPixel) {
double newX = srcPoint.x;
double newY = srcPoint.y;
double dx = abs(newX - round(newX));
double dy = abs(newY - round(newY));
double sumCubicBValue = 0;
double sumCubicGValue = 0;
double sumCubicRValue = 0;
double sumCubicGrayValue = 0;
double uX = 0;
double uY = 0;
if (floor(newX) - 1 < 0 || floor(newX) + 2 > src.cols - 1 || floor(newY) < 0 || floor(newY) > src.rows - 1) {
if (dst.channels() > 1)
dst.at<cv::Vec3b>(dstPixel) = cv::Vec3b(0, 0,0);
else
dst.at<uchar>(dstPixel) = 0;
}
else {
for (int cNeighbor = -1; cNeighbor <= 2; cNeighbor++) {
for (int rNeighbor = -1; rNeighbor <= 2; rNeighbor++) {
uX = cubicEquationSolver(rNeighbor + dx, -0.5);
uY = cubicEquationSolver(cNeighbor + dy, -0.5);
if (src.channels() > 1) {
sumCubicBValue = sumCubicBValue + (double) src.at<cv::Vec3b>(
cv::Point2i(round(newX) + rNeighbor, cNeighbor + round(newY)))[0] * uX * uY;
sumCubicGValue = sumCubicGValue + (double) src.at<cv::Vec3b>(
cv::Point2i(round(newX) + rNeighbor, cNeighbor + round(newY)))[1] * uX * uY;
sumCubicRValue = sumCubicRValue + (double) src.at<cv::Vec3b>(
cv::Point2i(round(newX) + rNeighbor, cNeighbor + round(newY)))[2] * uX * uY;
} else {
sumCubicGrayValue = sumCubicGrayValue + (double) src.at<uchar>(
cv::Point2i(round(newX) + rNeighbor, cNeighbor + round(newY))) * uX * uY;
}
}
}
if (dst.channels() > 1)
dst.at<cv::Vec3b>(dstPixel) = cv::Vec3b((int) round(sumCubicBValue), (int) round(sumCubicGValue),
(int) round(sumCubicRValue));
else
dst.at<uchar>(dstPixel) = sumCubicGrayValue;
}
I hope someone here will be able to help me, Thanks!

Algorithm to detect if two lines intersect does not give accurate/wanted results in SFML

I've been scratching my head for a couple of days on this one.
In my 2D top down game I have an array of FloatRects which represents walls.
I want to shoot a bullet represented by a point at X distance from the player (X being the weapon's range).
What I want to do is to check if there is a wall on the bullet's trajectory and if there is set the new bullet target to the collision point.
To do this I've tried to check if a side of a rectangle intersects with my bullet's trajectory line using this solution:
https://stackoverflow.com/a/1968345/10546991
I've used the program linked above but it gives me strange results, when the target is higher than the player the bullet just shoot behind.
Take a look at the following example :
Note : In SFML the Y axis points down
A is the player
B is the bullet's impact point or target, call it however you want.
C is the rectangle's Top Left side
D is the rectangle's Top Right side
(2500,2000) = a
(2500, 1300) = b
(2400, 2500) = c
(2600, 2500) = d
S1 = (0, -700)
S2 = (200, 0)
numeratorS = 700 * -100
numeratorT = 200 * -500
denominator = -200 * 700
s = 700 * - 100 / -200 * 700 = -70 000 / -140 000 = 7/14 = 1/2
t = -100 000 / -140 000 = 10 / 14 = 5 / 7
intersectionPoint.x = 2500 + (1/2 * 0) = 2500
intersectionPoint.y = 2000 + (1/2 * -700) = 1650
intersectionPoint (2500,1650)
So as you can see when testing if there is an intersection between the line between the player and the bullet's impact point and the top side of the rectangle (which is below the player) the program finds an intersection in between the player and the bullet!
I'm also providing a part of the code I use to detect collisions
Vector2f intersectionPoint = target;
//Check if bullet hits a wall and calculate new target
for (int i = 0; i < arraySize; i++)
{
float intersectionPointMagnitude = sqrt(intersectionPoint.x * intersectionPoint.x + intersectionPoint.y * intersectionPoint.y);
//Storing rect corner positions
Vector2f rectTopLeft;
rectTopLeft.x = collisions[i].left;
rectTopLeft.y = collisions[i].top;
Vector2f rectBottomLeft;
rectBottomLeft.x = collisions[i].left;
rectBottomLeft.y = collisions[i].top + collisions[i].height;
Vector2f rectBottomRight;
rectBottomRight.x = collisions[i].left + collisions[i].width;
rectBottomRight.y = collisions[i].top + collisions[i].height;
Vector2f rectTopRight;
rectTopRight.x = collisions[i].left + collisions[i].width;
rectTopRight.y = collisions[i].top;
Vector2f intersectionLeft = intersectionPoint;
if (Maths::vectorsIntersect(start, target, rectTopLeft, rectBottomLeft, intersectionLeft))
{
//We want to set a new target only if the detected collision is closer than the previous one
intersectionLeft = intersectionLeft - start;
float intersectionLeftMagnitude = sqrt(intersectionLeft.x * intersectionLeft.x + intersectionLeft.y * intersectionLeft.y);
if (intersectionLeftMagnitude < intersectionPointMagnitude)
{
intersectionPoint = intersectionLeft;
target = start + intersectionPoint;
}
}
I just can't understand where the issue is coming from so if anyone could help me out it'll be greatly appreciated.
Edit : Here is Maths::vectorsIntersect which is really similar to the function in the link above
bool vectorsIntersect(Vector2f A, Vector2f B, Vector2f C, Vector2f D, Vector2f& intersectionPoint)
{
Vector2f S1 = B - A;
Vector2f S2 = D - C;
//Calculate scalar parameters
float denominator = (S1.x * S2.y - S1.y * S2.x);
//We can't divide by 0!
if (denominator == 0.0f)
return false;
//S & T have the same denominator
float numeratorS = (S1.x * (A.y - C.y) - S1.y * (A.x - C.x));
float numeratorT = (S2.x * (A.y - C.y) - S2.y * (A.x - C.x));
float s, t;
s = numeratorS / denominator;
t = numeratorT / denominator;
//Check for intersection point
if (abs(s) > 0.0f && abs(s) < 1.0f && abs(t) > 0.0f && abs(t) < 1.0f)
{
//Return intersection point
intersectionPoint.x = A.x + (t * S1.x);
intersectionPoint.y = A.y + (t * S1.y);
return true;
}
return false;
}
EDIT 2 :
Alright I feel stupid now, in my Maths::vectorIntersect function I was checking if the absolute values of both s and t are between 0 and 1, which is why when the scalar parameters where between -0 and -1 the function returned true and led to unexpected behaviors.
Problem solved thank you for the help

how is the quadratic formula used in game coding?

hello guys i am game programmer from korea.
and just today i found a some code that use the QUADRATIC formula for calculating something. here is code
hduVector3Dd p = startPoint;
hduVector3Dd v = endPoint - startPoint;
// Solve the intersection implicitly using the quadratic formula.
double a = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
double b = 2 * (p[0]*v[0] + p[1]*v[1] + p[2]*v[2]);
double c = p[0]*p[0] + p[1]*p[1] + p[2]*p[2] - m_radius * m_radius;
double disc = b*b - 4*a*c;
// The scale factor that must be applied to v so that p + nv is
// on the sphere.
double n;
if(disc == 0.0)
{
n = (-b)/(2*a);
}
else if(disc > 0.0)
{
double posN = (-b + sqrt(disc))/(2*a);
double negN = (-b - sqrt(disc))/(2*a);
n = posN < negN ? posN : negN;
}
else
{
return false;
}
// n greater than one means that the ray defined by the two points
// intersects the sphere, but beyond the end point of the segment.
// n less than zero means that the intersection is 'behind' the
// start point.
if(n > 1.0 || n < 0.0)
{
return false;
}
this is the part of function that checking SOMETHING for sphere shape.
and i couldn't figure out why use QUADRATIC FORMULA for calculating SOMETHING.
any idea, will be preciated
i just really wanna know, understand and reuse it for my code in the future^^
It looks like it is doing a line-sphere collision check.
I'm not exactly sure where one would use this though(I could be dumb xD)

Why is my character spawning at a 45 degree angle when I've coded it to be set to 0 degrees?

The title describes my problem, as you can see here my character is spawning at a 45 degree angle. The character always spawns at (0, 0)
I have created some code for animations that uses the character's position and desired position to calculate at what angle the character should be at. This angle is determined by using the percentage of distance the character has travelled to its destination (these destinations are only Y values. X is increased by 1 every tick). I have made an equation to determine the angle based on the percentage travelled. The equation: -(9/500)X^2 + (9/5)X. This is a parabola that intersects point (0, 0), (50, 45) ← 45 degree angle and (100, 0).
The code I made to use this equation is as follows:
float GetRotationDegrees(int Current, int Target, int LaneWidth) {
int numerator = Current - LaneWidth;
int denominator = Target - LaneWidth;
float X;
if (denominator == 0) X = 0;
else X = abs(numerator / denominator * 100);
return -(9/500)*pow(X,2)+(9/5)*X; // function: -(9/500)X^2 + (9/5)X.
}
The LaneWidth is always 400, Current is the character's current Y coordinate and Target is the target Y coordinate. At Y = 0 the equation should be -400 / -400 * 100 = 100. And when you put 100 in the equation you get 0, this value of 0 is then used like this:
SetActorLocation(FVector(1, sin(angle * PI / 180), 0).Rotation());
I am really confused right now because whenever I manually try this it won't work.
It is probably something stupid I did, I tend to look over things a lot of the time and am not that good at maths (I am also new to C++, so the mistake might be there too). If someone could help me I would really appreciate it!
(By the way, I know that the way that GetRotationDegrees decides X the angle will only return positive angles, I want to get this to work first)
Fixed it with this:
float GetRotationDegrees(int Current, int Target, int LaneWidth) {
if (Current == Target) return 0;
int MovingFrom;
if (Target != 0) MovingFrom = 0;
else if (Target > Current) MovingFrom = -LaneWidth;
else MovingFrom = LaneWidth;
float Percentage = float(Current - MovingFrom) / float(Target - MovingFrom) * 100;
if (Target < Current) Percentage = -Percentage;
bool isNegative = Percentage < 0;
if (isNegative) return -(-9 * pow(abs(Percentage), 2) / 500 + 9 * abs(Percentage) / 5);
return -9 * pow(Percentage, 2) / 500 + 9 * Percentage / 5;
}

Check point within polygon

int pnpoly(int npol, float *xp, float *yp, float x, float y)
{
int i, j, c = 0;
for (i = 0, j = npol-1; i < npol; j = i++) {
if ((((yp[i] <= y) && (y < yp[j])) ||
((yp[j] <= y) && (y < yp[i]))) &&
(x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
c = !c;
}
return c;
}
This function checks if a point is within a polygon. How do I deal with a polygon coordinate that is negative? For example,
float x[3] = { 0.16, 1.2, -10 };
float y[3] = { 1.8, 10, -5.5 };
I tried checking a valid point within the polygon and it returns 0.
There are pretty good implementations from the iSurfer
The two methods used in most cases (and the two I know of) are crossing number and winding number. Both of them are not affected by the signs of the polygon/point coordinates. So it must be a bug in your code.
For completeness I'm placing the code for a crossing number test which seems to be what you're trying to do in your code
// a Point is defined by its coordinates {int x, y;}
// isLeft(): tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and P1
// =0 for P2 on the line
// <0 for P2 right of the line
// See: Algorithm 1 "Area of Triangles and Polygons"
inline int isLeft( Point P0, Point P1, Point P2 )
{
return ( (P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y) );
}
//===================================================================
// cn_PnPoly(): crossing number test for a point in a polygon
// Input: P = a point,
// V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
// Return: 0 = outside, 1 = inside
// This code is patterned after [Franklin, 2000]
int cn_PnPoly( Point P, Point* V, int n )
{
int cn = 0; // the crossing number counter
// 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)
}
//===================================================================
A special case that can arise with the crossing number number test, is when the ray overlaps an edge of the polygon. In that case it becomes somewhat fuzzy how to count intersections. That's why it's not the actual number of intersections we count, but the number we crossed over semiplanes defined by the ray.
The winding number test is more robust to this respect
Note that if your polygon N and N+1 have the same y value then this test can fail computing vt. For example: determining if a point is inside an axis aligned square.
You need to deal with lines parallel on y