Given two bearing, how do I find the smallest angle between them?
So for example if 1 heading is 340 degrees and the second is 10 degrees the smallest angle will be 30 degrees.
I've attached a picture to show what I mean. I've tried subtracting one from the other but that didn't work because of the wrap around effect of a circle. I've also tried using negative degrees (180 - 359 being -180 to 0) but that got messed up when trying to calculate the angle between positive and negative number.
I'm sure there must be an easier way that having lots of if statements.
Thank for your help.
Adam
BTW. This is a navigation question so the radius of the circle is unknown.
I ended up using the following formula found on this message board because I needed the result to be signed based on the direction (clockwise or counterclockwise). It has a good explanation of exactly what's going on.
((((bearing - heading) % 360) + 540) % 360) - 180
float getDifference(float a1, float a2) {
return Math.min((a1-a2)<0?a1-a2+360:a1-a2, (a2-a1)<0?a2-a1+360:a2-a1)
}
What about:
angle = Math.abs(a1-a2);
if (angle > 180)
angle = 360 - angle;
You mention an issue regarding positive and negative numbers, so perhaps there is something I'm not considering here...
For navigation, you might want to know if b1 is left or right of b2, so here it is in a nice function. (Assumes exactly 0 is not a use case)
function isBearing1LeftOrRightOfBearing2 (b1, b2) {
if (Math.sign(((b1 - b2 + 540) % 360) - 180) > 0) {
return 'left'
} else {
return 'right'
}
}
If angle direction is needed, then this will work:
int maxBearing = Math.max(bearing0, bearing1);
int minBearing = Math.min(bearing0, bearing1);
int firstDir = maxBearing - minBearing;
int secondDir = minBearing + 360 - maxBearing;
int diff = Math.min(firstDir, secondDir);
boolean anticlock_dir = false;
int anticlock = bearing1 + diff;
if (anticlock >= 360)
anticlock = anticlock - 360;
if (anticlock == bearing0)
anticlock_dir = true;
You need to consider the difference in both directions.
public static double bearingDiff(double a, double b) {
double maxBearing = Math.max(a, b);
double minBearing = Math.min(a, b);
double antiClockwiseDiff = maxBearing - minBearing;
double clockwiseDiff = minBearing + 360 - maxBearing;
return Math.min(antiClockwiseDiff, clockwiseDiff);
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have solved this problem but im not sure if its correct..
User should give the coordinates of a point and I should check if that point is within,outside or on the circle. I used the distance formula to solve this .
The given information about the circle are:
Circle is centered at ( 0,0 )
and radius is 10
public static void main(String[] strings) {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter a point with two coordinates");
double y1 = scanner.nextDouble();
double x1 = scanner.nextDouble();
// circle is centered at 0,0
double y2 = 0;
double x2 = 0;
double radius = 10;
double distance;
// using the distance formula to calculate the distance
// from the point given from the user and point where circle is centered
/**
* distance formula
* d = √ ( x2 - x1 )^2 + (y2 - y1 )^2
*/
distance = Math.pow( x2 - x1,2) + Math.pow(y2-y1,2);
// find square root
distance = Math.sqrt(distance);
String result="";
if(distance < radius) {
result = "("+y1+" , "+x1+")" +" is within the circle";
}
else if(distance > radius) {
result = y1+" , "+x1 +" is outside the circle";
}
else if(distance == radius) {
result =y1+" , "+x1 +" is on the circle";
}
System.out.println(result);
}
It's fine but sloppy.
There's no need to compute the square root. Work in units of distance squared.
Then compare using distance < radius * radius etc., perhaps renaming distance for the sake of clarity. Computing square roots is costly and imprecision can creep in which can be difficult to control. This is particularly important in your case where you want to test for a point being on the circle's edge.
Also consider writing (x2 - x1) * (x2 - x1) rather than using pow for the second power. Although Java possibly (I never remember for sure which is certainly a good enough reason for my not using it) optimises to the longer form, other languages (such as C) don't and imprecision can creep in there too.
Are you sure this question requires doubles as input? The examples given are integers. With integers you can be sure of a points location, with real numbers (doubles) you cannot be sure about the "on circle" or not which is another reason I think the question expects you to use integers.
And the trick for performance and accuracy is to not use Math.sqrt and only work with integers.
int x;
int y;
int centreX;
int centreY;
int deltaX = x - centreX;
int deltaY = y - centreY;
int distanceSquared = deltaX * deltaX + deltaY * deltaY;
int radiusSquared = radius * radius;
if (distanceSquared == radiusSquared) { //distance == radius
//on circle
} else if (distanceSquared < radiusSquared) { //distance < radius
//in circle
} else {
//out of circle
}
Consider using Math.hypot() to calculate a distance and compare double values using some small threshold:
static final double DELTA = 1E-5; // not necessarily 1E-5; see comments
//...
distance = Math.hypot(x2 - x1, y2 - y1);
if (Math.abs(distance - radius) < DELTA)
// on the circle
else if (distance > radius)
// outside
else
// inside
The reason for using DELTA is that there is a very small chance to get equal double values by calculations. If they differ in at least one bit, direct comparison will return false.
By appliying threshold, you check not whether the point lays exactly on the circle, but whether it lays inside a ring between radius - DELTA and radius + DELTA. So DELTA is a kind of tolerance limit which value should be chosen particularily for application, e. g. depending on absolute or relative input inaccuracy.
for example my current angle position is 170, I click on the object to rotate it to -170 degrees, here the problem is that after 178, 179, 180, the next number is -180, -179, -178 and so on...
even tough numerically 170 degrees is far from -170 degrees, but visually they look near, an object is rotating the longest way in order to reach that number, for example:
if(currentAngle < targetAngle)
{
currentAngle += 1;
}
if(currentAngle > targetAngle)
{
currentAngle -= 1;
}
this way I can reach the target angle, but again how to transpass this barrier between 180 and -180, maybe there are a formula for this?
update:
onclick() {
double angle = Math.atan2(dy, dx);
targetAngle = (int)Math.toDegrees(angle); //180 to //-179
}
onFrame() {
//here happens the animation
if(currentAngle < targetAngle)
{
currentAngle +=1;
}
if(currentAngle > targetAngle)
{
currentAngle -= 1;
}
}
now what if I'am currently on -179 angle degree, and I clicked on 160 angle degree, it should rotate to left to reach that angle as fast as posible, but in my case it is rotating to the right(which is longer), and thats because it is limited from -180 to 179, thus how to break the limits and go from 179 to -180, -181, -182...if you understand what I mean
1) my click handler:
onClick() {
double angle = Math.atan2(dy, dx);
angle = (int)Math.toDegrees(angle);
Log.d("test", "angler:" + angle);
if(angle < 0)
angle += 360;
}
so here I convert the degrees to positive using angle += 360, then:
2) my onFrame handler:
onFrame() {
if(currentAngle != angle)
{
if(angle < currentAngle)
{
currentAngle -= 5;
}
else
{
currentAngle += 5;
}
int diff = Math.abs(angle - currentAngle);
if(diff <= 5)
{
currentAngle = angle; // if its near we put it exact to that angle
}
invalidate(); //update the view
}
}
thats all I have
Note that I am assuming your angles are between 0 and 359, rather than having negatives.
There are two separate problems here.
Given a currentAngle and a targetAngle, how does one determine the direction of rotation which will result in completing the rotation in the shortest number of frames?
How do we handle the boundary condition where the angle crosses from 359 to 0/360?
Solution
Problem 1 is mostly addressed in #ellitron's answer, but you have to separate out the pieces a little bit to determine the direction to move your currentAngle. Problem 2 requires a mod by 360 which handles negative numbers after each update to currentAngle, and this answer gives us a nice trick for that.
if (currentAngle - targetAngle == 0) return;
if (Math.abs(currentAngle - targetAngle) < 180) {
// Rotate current directly towards target.
if (currentAngle < targetAngle) currentAngle++;
else currentAngle--;
} else {
// Rotate the other direction towards target.
if (currentAngle < targetAngle) currentAngle--;
else currentAngle++;
}
currentAngle = ((currentAngle % 360) + 360) % 360;
For future reference, here is how one might test this code outside of a rendering environment. We can simply pass in two command line arguments and determine from the output whether we rotated the right way or not.
public class Angle {
public static void main(String[] args) {
int currentAngle = Integer.parseInt(args[0]);
int targetAngle = Integer.parseInt(args[1]);
while (currentAngle - targetAngle != 0) {
if (Math.abs(currentAngle - targetAngle) < 180) {
// Rotate current directly towards target.
if (currentAngle < targetAngle) currentAngle++;
else currentAngle--;
} else {
// Rotate the other direction towards target.
if (currentAngle < targetAngle) currentAngle--;
else currentAngle++;
}
currentAngle = ((currentAngle % 360) + 360) % 360;
System.out.printf("CurrentAngle = %d, targetAngle = %d\n",
currentAngle, targetAngle);
}
}
}
As you have defined currentAngle in your question, its values in ascending order are:
0,1,2,...,180,-179,-178,...,0
Which means that for you, -179 is greater than 179, and therefore arithmetic comparison will not work for you. First you must convert these numbers to a range that looks like:
0,1,2,...,180,181,182,...,359
Which you can do with the following formula:
if(angle < 0)
angle += 360
Now you can find the difference between the two angles (say angle1 and angle2) like this:
abs(angle1 - angle2)
or if you want to cross over 0, then do this:
360 - abs(angle1 - angle2)
To give you the shortest distance between these two angles, you would take the minimum like this:
min(abs(angle1 - angle2), 360 - abs(angle1 - angle2))
I had the same problem for rotation animation. From 1 to -1 it goes through the large arc, with length 358 but the small arc is with length 2. So does the opposite. But imagine it gets values 0, 100, 200, 300, 0, 100, 200, 300, and so on... in order animation to be smooth it has to go through values 0, 100, 200, 300, 360, 460, 560, 660, .. so the angle will rise if it turns only clockwise.
Check out this algorithm:
class RotAngle
{
double oldAngle = 0; //old angle modulo 360
int rot = 0; //this is current spin count (times 360 degrees)
public double Rotate(double angle)
{
double currAngle = ((angle % 360) + 360) % 360;
//if you enter only positive angles (angle % 360) will do
//((angle % 360) + 360) % 360 is needed for negative angles
double diff_2 = 2 * (oldAngle - currAngle);
/* mathematically is equal to: old + 360 - current < current - old */
/* so closer is one rotation less */
if (diff_2 < -360) rot--;
/* opposite: 360 + current - old < old - current -> one rotation more is closer */
if (diff_2 > 360) rot++;
oldAngle = currAngle;
return rot * 360 + currAngle;
}
}
I started learning java just over a year ago so i'm still fairly new.
I'm trying to make an object travel from one point to another at a constant net velocity no matter where the second point is in the frame. Currently it's working fairly well as long as I run the method every few frames.
The only problem is that it it will only move horizontally unless the second point is approximately between 45 and 135 degrees or between 225 and 315 degrees (1/2π and 3/2π or 5/2π and 7/2π).
It may be because of the 'if' statements meant to stop it from dividing by 0 but it doesn't seem like it. Also if there is any way to simplify those equations or remove 'if' statements I wouldn't mind some advice there too.
Note: vel is the net velocity the objects travel at and Prime.mx and Prime.my is the location of the target point.
public void target()
{
if (Prime.mx > x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x + (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.mx < x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x - (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my > y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y + (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my < y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y - (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
}
I use Math.round twice because the first brings it to a float from a double and the second makes it an int. I need the x and y as ints so the paint method can draw the objects.
I found a few simillar problems on the site but the closest one was in python and and the anwer didn't seem applicable to my problem.
I believe you are overcomplicating this. If your starting point is (sx, sy) and your destination is (dx, dy) then you can easily calculate any point (x, y) that is p distance along the line (0.0 <= p <= 1.0). You can use this to move at velocity v. So I suggest finding your end point and then using simple arithmetic to move on the x and y axis.
float dx = dist * Math.cos(angle);
float dy = dist * Math.sin(angle);
for (float p = 0.0; p <= 1.0; p = Math.min(1.0, p + dist / v) {
x = sx + p * (dx - sx);
y = sy + p * (dy - sy);
}
The Math.min expression in the for loop ensures that you end up exactly at the destination point.
If you already have the destination point then it's just as easy. Instead of finding dx and dy from dist and angle you find dist from dx and dy using pythagoras.
More than solution these are some advices.
First, implement all you coordinate variables as floats to prevent rounding precision loss errors and round only right before painting.
Second, define float dx = Prime.mx - x; float dy = Prime.my - y; distance to target from current point (to use later). I would use Math.atan2(dy,dx) to compute angle between current point and target. Then use that angle to increment coordinates like this:
x += Math.cos(angle)*vel;
y += Math.sin(angle)*vel;
Third, check if your object is at target using (dx*dx + dy*dy <= radius*radius) for suitable radius (can be 1).
Also note that if the y axis goes down, then the angle will be CW (clock-wise) instead of CCW (counter-clock-wise).
I have encountered some issues regarding angles. I have an angle A and another angle B, I want to animate A the shortest way so that it reaches B. The first confusion for me is that angles go from 0 to 180, and 0 to -180. Not sure what the pros of that is. Anyway, I will give a for instance:
float a = -35;
float b = 90;
For each update I want to either add 1 or subtract 1 degree from a, until it reaches b, and I want to make sure it goes the shortest way.
Here's my code, which seems to be working. But it does not seem very efficient:
b += 360;
if (b > a) {
if (b - a < 180) {
a += 1;
} else {
a -= 1;
}
} else {
if (a - b < 180) {
a -= 1;
} else {
a += 1;
}
}
Is there a better/easier way to do it?
So you want the shortest route from a to b.
Since we are looking at a difference lets subtract:
float d = a-b;
If the value of the result is greater than 180 then we want to subtract 360.
if (d > 180) {
d -= 360;
} else if (d<-180) {
d += 360;
}
Now d is the total distance to travel. You can compare that with 0 to see which way to go. You can also do nice things like move further the larger d is. For example to make it always move 10% of the way (note that this series will never end as it will constantly approach by smaller and smaller amounts so you need to cope with that scenario):
a += d/10;
You also need to consider frame rate if you want a smooth animation.
If you work out tpf (time per frame) as a floating point fraction of a second.
long frameTook = lastFrame - System.currentTimeMillis();
long lastFrame = System.currentTimeMillis();
float tpf = frameTook / 1000;
You can now do a constant animation (where degreesPerFrame is the speed of animation) using:
float move = degreesPerFrame * tpf;
Check we aren't going to move past the destination, if we are just move to it.
if (move > FastMath.abs(d)) {
a = b;
} else {
if (d>0) {
a+=move;
} else {
a-=move;
}
}
I have a data values which vary from +PI to -PI radian.
I need to get minimum rotation (in radians) required to go from old value to new value like:
float rotationRequired(float oldValue, float newValue){
return newValue - oldValue;
}
but simply subtracting won't do, because to go from -179 degree to +179 degree one does not need to rotate full circle around, just 2 degree clockwise. Because -PI = +PI in a circle are technically same rotation. Also the values can be in any range, i.e 740 = 360 + 360 + 20, hence only 20.
I'm thinking about breaking values into sin and cos values, subtract and then atan :
double oldY = Math.sin(oldValue);
double oldX = Math.cos(oldValue);
double newY = Math.sin(newValue);
double newX = Math.cos(newValue);
float delta = (float) Math.atan2( (newY - oldY),(newX - oldX) );
But still its not giving correct results, can anyone suggest another method ?
Just do the subtraction, and then limit the result to +/-180 by adding or subtracting 360 as necessary (the % operator may help here...)
I converted angles to degrees and used this method to suggest what minimum rotation is required and in what direction:
public static int suggestRotation(int o, int n){
//--convert to +0 to +360 range--
o = normalize(o);
n = normalize(n);
//-- required angle change --
int d1 = n - o;
//---other (360 - abs d1 ) angle change in reverse (opp to d1) direction--
int d2 = d1 == 0 ? 0 : Math.abs(360 - Math.abs(d1))*(d1/Math.abs(d1))*-1;
//--give whichever has minimum rotation--
if(Math.abs(d1) < Math.abs(d2)){
return d1;
}else {
return d2;
}
}
private static int normalize(int i){
//--find effective angle--
int d = Math.abs(i) % 360;
if(i < 0){
//--return positive equivalent--
return 360 - d;
}else {
return d;
}
}