Dealing with Angle Wrap in c++ code - c++

Is there a way to safety and simply deal with angle wrap with the minimum number of case statements.
Angle wrap occurs when using a particular representation for angle (either 0-360 deg or -180 - 180 deg (or equivalent in radians)) and you wrap over the angle. For example say you have an angle of -170, and you subtract 50 deg. You mathematically add up to -220 but should actually be +140 deg.
Obviously you can check for this using:
if (deg < -180) { 180 - abs(deg + 180); }
or similar. But firstly you need multitudes of checks and secondly it doesn't work if you wrap twice.
The second case where this is prevalent is in the interpolation between two angles.
For Example, say I have an angle of -170 deg and 160 deg and I want halfway in between them. A common way to do this is ang1 + 0.5(ang2-ang1) but in the example i have provided it will cause the angle to be -5 deg when it should be 175.
Is there a common way to handle angle wrap in these scenarios?

For completeness I'll include both [0, 360) and [-180, 180) normalizations.
You will need #include <math.h>.
Normalize to [0,360):
double constrainAngle(double x){
x = fmod(x,360);
if (x < 0)
x += 360;
return x;
}
Normalize to [-180,180):
double constrainAngle(double x){
x = fmod(x + 180,360);
if (x < 0)
x += 360;
return x - 180;
}
The pattern should be easy enough to recognize to generalize to radians.
Angle Bisection:
double angleDiff(double a,double b){
double dif = fmod(b - a + 180,360);
if (dif < 0)
dif += 360;
return dif - 180;
}
double bisectAngle(double a,double b){
return constrainAngle(a + angleDiff(a,b) * 0.5);
}
This should bisect an angle on the "smaller" side. (warning: not fully tested)

I find using remainder() from the math library is convenient. Given an angle a, to constrain it to -180, 180 you can just do:
remainder(a, 360.0);
and change the 360.0 to 2.0 * M_PI for radians

Normalise an angle to range [-180, 180)
deg -= 360. * std::floor((deg + 180.) * (1. / 360.));
Normalise an angle to range [0, 360)
deg -= 360. * std::floor(deg * (1. / 360.));
Examples:
deg = -90 -> [0, 360):
deg -= 360. * std::floor(-90 / 360.);
deg -= 360. * -1;
deg = 270
deg = 270 -> [-180, 180):
deg -= 360. * std::floor((deg + 180.) / 360.);
deg -= 360. * std::floor(480. / 360.);
deg -= 360. * 1.;
deg = -90;
See: http://en.cppreference.com/w/cpp/numeric/math/floor

So if figured out a way to effectively do what i want using Mystical's approach to constraining the Angle. Here it is:
This seems to work with any example i can think of.

I know this is an old thread, but try this on:
double angleRef(double thtIn, double thtRef){
tht = fmod(thtIn + (180-thtRef),360) + (thtRef-180);
return tht;
}
So as in your example, if A=-170 and B=160, then the angle halfway between them is
A + 0.5*(angleRef(B,A) - A) = -185
or if you prefer A=160 and B=-170
A + 0.5*(angleRef(B,A) - A) = 175
Please forgive any c++ format errors, it is not my native language.

auto new_angle = atan2(sin(old_angle), cos(old_angle));

Map angle(+PI ~ -PI) to signed int value (or short value):
angle_signed_short = angle_float / PI * 0x7FFFF;
Then you can add or sub value as normal. Then map back:
angle_float = angle_signed_short * PI / 0x7FFFF;

Related

How to develop to-point moving algorithm using Gps and compass

I am trying to develop an alghoritm for controlling the rudder of the boat...
I got lost in geo algoritms...
Function Does not work corectly.
direction WhereToMove(double CurrentLatitude, double CurrentLongitude, double TargetLatitude, double TargetLongitude, double azimuth) {
double azimuthHysteresis = 5; //straight if the deviation is less than 5 degrees
double pi = 2 * asin(1.0);
double target = atan2(TargetLatitude - CurrentLatitude, TargetLongitude - CurrentLongitude) * 180 / pi;
double delta = azimuth - target;
if (delta > 180) delta -= 360;
if (delta < -180) delta += 360;
if (delta < -2) {
return right;
}
if (delta > 2) {
return left;
}
return straight; // go straight
}
A few points:
You could use the constant M_PI for pi
I would imagine you want your angles to be measured clockwise from north. atan2 gives an angle counter clockwise from the x axis. This is simple to fix, use
atan2( dLon, dLat)
instead of
atan2( dLat, dLon)
The distance represented by a degree of longitude is, roughly, cos(lat) times the distance represented by a degree of latitude. So you should scale your dlon in the above by cos( M_PI/180.0 * lat). (cos, like all the math functions that deal in angles, takes radians as an argument).
You could simplify computing the difference of azimuth and target by using the math library function remainder, as in
delta = remainder( azimuth-target, 360.0)
this will give a delta between -180 and 180
I don't know if your code will ever be used near 180E. I'd say you should compute the difference in longitudes as if it might, ie use
remainder( TargetLongitude - CurrentLongitude, 360.0)
instead of
TargetLongitude - CurrentLongitude
This might seem OTT, but I've found (the hard way) that its much easier to get into the habit of always computing the difference of longitudes this way than to track down everywhere in the code that such differences are taken when your code is used across 180E.
Final version
direction WhereToMove(double CurrentLatitude, double CurrentLongitude, double TargetLatitude, double TargetLongitude, double azimuth) {
double azimuthHysteresis = 2; //straight if the deviation is less than 2 degrees
double target = atan2(remainder(TargetLongitude - CurrentLongitude, 360.0), remainder(TargetLatitude - CurrentLatitude, 360.0)) * 180 / M_PI;
double delta = remainder(azimuth - target, 360.0);
double deltaModule = sqrt(delta * delta);
if (deltaModule <= azimuthHysteresis) //deviation is small go straight
{
return straight;
}
if (delta <= -azimuthHysteresis) {
return right;
}
if (delta >= azimuthHysteresis) {
return left;
}
return straight; // go straight
}

Find a new angle of the ball when it bounces (using coordinates)

I have a game where a ball is bouncing off walls. It's on a coordinate plane. I want there to be some small amount of randomness when it bounces to keep the game more interesting. How would I do this while keeping the ball at a constant speed the whole time? Right now my code means it only bounces at right angles.
The top left corner of the window is 0,0 and the bottom right is winW,winH (set at 800,800 right now).
ball.cpp snippet
pos.x = start.x;
pos.y = start.y;
speed.x = .4f; // the f indicates that it's per frame.
speed.y = .4f;
void Ball::hitLeftRight() {
speed.x = -speed.x;
}
void Ball::hitTopBottom() {
speed.y = -speed.y;
}
void Ball::reset() {
// for a new level in game
pos.x = start.x;
pos.y = start.y;
}
void Ball::update() {
// called every frame
pos.y += speed.y;
pos.x += speed.x;
ballShape.setPosition(pos);
}
You could add a random angle to the ball in addition to just flipping the velocities. By using the <random> header and generating values between 0 and 2 * pi you can add a random velocity in any direction. Or as in the example below, limiting it to -22.5 to 22.5 degrees. Or pi / 8.0 radians.
Random Angle
You could of course tweak those values based on the angle of impact, but that is implementation specific. Below is an example on how you could generate such numbers:
#include <random>
#include <cmath>
#include <iostream>
int main() {
constexpr double pi = 3.14159;
constexpr double bounceSpeed = 5.0;
std::random_device seed;
std::mt19937 generator(seed());
// Numbers between - pi / 2 and pi / 8 gives angles between -22.5 and 22.5
std::uniform_real_distribution<double> random(-pi / 8.0, pi / 8.0);
double deltaX = cos(random(generator)) * bounceSpeed;
double deltaY = sin(random(generator)) * bounceSpeed;
std::cout << deltaX << "\n" << deltaY << "\n";
return 0;
}
Afterwards, you could add deltaX and deltaY to your respective x and y velocities.
Plain Random
Or if you're satisfied with just any purely random velocity:
// Generate random double in range [min, max]
double uniform(double min, double max) {
static std::random_device seed;
static std::mt19937 generator(seed());
std::uniform_real_distribution<double> random(min, max);
return random(generator);
}
Call that function twice with the velocity range you desire, and add that to the x and y of your ball.
Keeping Speed
To keep the speed of the ball you could normalize the velocity vector and then multiply it by the desired speed of you ball after adding the random velocity.
To normalize, divide by length:
#include <cmath>
#include <iostream>
int main() {
constexpr double speed = 4.0;
double x = 3.0;
double y = 4.0;
double length = sqrt(x * x + y * y);
x /= length;
y /= length;
x *= speed;
y *= speed;
std::cout << x << "\n" << y << "\n";
return 0;
}
Then multiply by speed to keep it consistent.
To start, have a set speed for the ball as a float. Then have a velocity which you move by every update with x and y members like what you have now. Calculate how far you move x and y using your speed, your angle of movement, and some trigonometry. (Remember to convert degrees to radians)
float Speed = 10;
float Angle = 45;
Velocity.x = cos(Angle * 3.14159 / 180) * Speed;
Velocity.y = sin(Angle * 3.14159 / 180) * Speed;
Whenever you encounter a collision, you can then recalculate your angle add your new random angle and recalculate your velocity. (Again converting degrees to radians).
//if (collision)
Angle = atan2(Velocity.y * 3.14159 / 180, Velocity.x * 3.14159 / 180);
Angle += rand() % 90 + 1; // Could also be subtracting here
Velocity.x = cos(Angle * 3.14159 / 180) * Speed;
Velocity.y = sin(Angle * 3.14159 / 180) * Speed;
Add or subtract from the angle based on what side you have struck and from what angle.
void HitTop()
{
if (Velocity.x > 0)
//Subtract random angle
else
//Add random angle
}
Do this for all sides.
Let your border has arbitrary form (not only rectangle). If border normal (unit) vector in bouncing points is
n = (n.x, n.y)
then speed vector after bouncing changes:
dot = speed.x * n.x + speed.y * n.y
//after reflection
newspeed.x = speed.x - 2 * dot * n.x
newspeed.y = speed.y - 2 * dot * n.y
To add some randomness, just rotate normal by small random angle:
df = GaussianRandom(Mean = 0, Sigma = Pi / 30) //arbitrary parameter
//seems in c++ it is std::normal_distribution
n'.x = n.x * Cos(df) - n.y * Sin(df)
n'.y = n.x * Sin(df) + n.y * Cos(df)
and use n' to calculate reflection. Note that this approach preserves speed magnitude.

C++ AI Rotation Issue

For a game I'm making, I want 3 ships which will all race around the map following a collection of points. It works perfectly fine, except for one point in the map, where the ships decide to rotate almost 360 degrees counter clockwise even though only 10 degrees clockwise should be enough.
The code for calculating the rotation:
vec2 distance = *desiredPosition - position;
float rot = atan2(distance.y, distance.x);
rot = rot * 180.f / PI + 90.f;
if (rot < angle)
{
angle -= dAngle;
boat->RotateImage(-dAngle);
}
if (rot > angle)
{
angle += dAngle;
boat->RotateImage(dAngle);
}
velocity += vec2(acceleration * cos((angle - 90) * PI / 180.0), acceleration * sin((angle - 90) * PI / 180.0));
How do I ensure it won't rotate in the wrong direction there?
Thanks to Richard Byron (accepted answer below), the problem is fixed. Taking the dot product is better than using degrees.
The final code:
vec2 distance = desiredPosition - position;
normal = vec2(sin((angle - 90) * PI / 180.0), cos((angle - 90) * PI / 180.0) * -1);
float dir = normal.x * distance.x + normal.y * distance.y;
//turn
if (dir > 0)
{
angle -= dAngle;
boat->RotateImage(-dAngle);
}
if (dir < 0)
{
angle += dAngle;
boat->RotateImage(dAngle);
}
velocity += vec2(acceleration * cos((angle - 90) * PI / 180.0), acceleration * sin((angle - 90) * PI / 180.0));
The angle the boat turns should be less than 180 degrees either CW or CCW. If it turns more than 180 degrees in one direction it would have been better to turn the other way.
A more general solution would be calculate the distance vector with respect to the boat's frame of reference.
There are a couple of problems with your updated code. Firstly, it should be rot2 = 360 - rot1; (rot1 + 360 is exactly the same angle as rot1).
The second issue is that you are not taking into account that 1 and 359 degrees are almost the same angle. So if abs(rot1 - angle) > 180, then you really want to use 360 - abs(rot1 - angle) in that case. Your later comparisons with rot and angle are a problem for the same reason, and you need to handle angle incrementing above 360 and decrementing below 0.
I could write out code for this, but there's actually a much simpler and faster way to do this. If you take the dot product of the vector (desiredPosition - position) and a vector at right angles to the ships current heading, then you can turn based on the sign of that result. If it's not clear how to do this, let me know and I can expand on it in the comments.

Given two points, find a third point on the line

I have two points A (x1,y1) and B (x2,y2) that are given as an input to the program. I have to find a third point C that lies on the line AB and is at a distance 10 away from the point A.
I can easily get the slope of the line but that doesn't give me the full equation for the line. Even if I get the full equation, I am not sure using this equation, how would I find out a point that is x distance away from A.
Any suggestions on how to approach this?
There are always two points on each line:
get the vector from A to B (subtract the coordinates)
normalize the vector (divide by its length; pythagorean theorem)
multiply the vector by 10 or -10
add the vector to A to get C
Note that if A==B, the line is not defined, and this algorithm causes a division by zero. You may want to add a test for equality at the beginning.
You can use the sine or the cosine (times 10) of the angle of the line to get the horizontal or vertical distance of the point that is a distance of 10 from a given point. A shortcut is to use the horizontal or vertical distance divided by the direct distance between the points to get the sine or cosine.
You can do it using vectors like this:
Let D = the difference between B and A (D = B - A)
Then any point on the line can be described by this formula:
point = A + Dt
where t is a real number.
So just plug in any value for t to get another point. For example if you let t == 1 then the equation above reduces to point = B. If you let t = 0 then it reduces to point = A. So you can see that you can use this to find a point between A and B simply by let t range from 0 to 1. Additionally if you let t > 1, you will find a point past B.
You can see from the image that your given points are x1,y1 and x2,y2. You need to find an intermediate point at a distance 'R' from point x1,y1.
All you need to do is to find θ using
Tan θ = (y2-y1)/(x2-x1)
Then you can get the intermediate point as (R * cos θ),(R * Sin θ)
I have drawn this assuming positive slope.
Going on similar lines you can seek a solution for other special cases lile:
i. Horizontal line
ii. Vertical line
iii. Negative slope
Hope it clarifies.
I have done the calculation in Andengine using a Sprite object. I have two Array List x coordinates and y coordinates. Here i am just calculating using the last two values from these two array list to calculate the third point 800 pixel distant from Your point B. you can modify it using different values other than 800. Hope it will work.The coordinate system here is a little different where (0,0) on the top left corner of the screen. Thanks
private void addExtraCoordinate(CarSprite s) {
int x0, y0, x1, y1;
float x = 0f, y = 0f;
x0 = Math.round(xCoordinates.get(xCoordinates.size() - 2));
x1 = Math.round(xCoordinates.get(xCoordinates.size() - 1));
y0 = Math.round(yCoordinates.get(yCoordinates.size() - 2)) * (-1);
y1 = Math.round(yCoordinates.get(yCoordinates.size() - 1)) * (-1);
if (x1 == x0 && y1 == y0) {
return;
} else if (y1 == y0 && x1 != x0) {
if (x1 > x0) {
x = (float) x1 + 800f;
} else
x = (float) x1 - 800f;
y = Math.round(yCoordinates.get(yCoordinates.size() - 1));
} else if (y1 != y0 && x1 == x0) {
if (y1 > y0) {
y = (float) Math.abs(y1) - 800f;
} else
y = (float) Math.abs(y1) + 800f;
x = Math.round(xCoordinates.get(xCoordinates.size() - 1));
} else {
float m = (float) (yCoordinates.get(yCoordinates.size() - 1) * (-1) - yCoordinates
.get(yCoordinates.size() - 2) * (-1))
/ (float) (xCoordinates.get(xCoordinates.size() - 1) - xCoordinates
.get(xCoordinates.size() - 2));
if (x1 > x0) {
x = (float) ((float) x1 + 800f / (float) Math
.sqrt((double) ((double) 1f + (double) (m * m))));
} else
x = (float) ((float) x1 - 800f / (float) Math
.sqrt((double) ((double) 1f + (double) (m * m))));
if (y0 > y1) {
y = (float) ((float) Math.abs(y1) + 800f / (float) Math
.sqrt((double) (((double) 1f / (double) (m * m)) + (double) 1f)));
} else
y = (float) ((float) Math.abs(y1) - 800f / (float) Math
.sqrt((double) (((double) 1f / (double) (m * m)) + (double) 1f)));
}
xCoordinates.add(x);
yCoordinates.add(y);
}

Determine if angle lies between 2 other angles

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.