Easy way to keeping angles between -179 and 180 degrees - java

Is there an easy way to convert an angle (in degrees) to be between -179 and 180? I'm sure I could use mod (%) and some if statements, but it gets ugly:
//Make angle between 0 and 360
angle%=360;
//Make angle between -179 and 180
if (angle>180) angle-=360;
It just seems like there should be a simple math operation that will do both statements at the same time. I may just have to create a static method for the conversion for now.

// reduce the angle
angle = angle % 360;
// force it to be the positive remainder, so that 0 <= angle < 360
angle = (angle + 360) % 360;
// force into the minimum absolute value residue class, so that -180 < angle <= 180
if (angle > 180)
angle -= 360;

Try this instead!
atan2(sin(angle), cos(angle))
atan2 has a range of [-π, π). This takes advantage of the fact that tan θ = sin θ / cos θ, and that atan2 is smart enough to know which quadrant θ is in.
Since you want degrees, you will want to convert your angle to and from radians:
atan2(sin(angle * PI/180.0), cos(angle * PI/180.0)) * 180.0/PI
Update
My previous example was perfectly legitimate, but restricted the range to ±90°. atan2's range is the desired value of -179° to 180°. Preserved below.
Try this:
asin(sin(angle)))
The domain of sin is the real line, the range is [-1, 1]. The domain of asin is [-1, 1], and the range is [-PI/2, PI/2]. Since asin is the inverse of sin, your input isn't changed (much, there's some drift because you're using floating point numbers). So you get your input value back, and you get the desired range as a side effect of the restricted range of the arcsine.
Since you want degrees, you will want to convert your angle to and from radians:
asin(sin(angle * PI/180.0)) * 180.0/PI
(Caveat: Trig functions are bazillions of times slower than simple divide and subtract operations, even if they are done in an FPU!)

This works with both negative and decimal numbers and doesn't require loops, nor trigonometric functions:
angle -= Math.floor(angle / 360 + 0.5) * 360
The result is in the [-180, 180) interval. For (-180, 180] interval, you can use this instead:
angle -= Math.ceil(angle / 360 - 0.5) * 360

Not that smart, too, but no if.
angle = (angle + 179) % 360 - 179;
But I am not sure how Java handles modulo for negative numbers. This works only if -1 modulo 360 equals 359.
UPDATE
Just checked the docs and a % b yields a value between -(|b| - 1) and +(|b| - 1) hence the code is broken. To account for negative values returned by the modulo operator one has to use the following.
angle = ((angle + 179) % 360 + 360) % 360 - 179;
But ... no ... never ... Use something similar to your initial solution, but fixed for values smaller then -179.

I know that years have passed, but still.
This solution contains no loops, no subtracting, no modulo (allows to normalize to radians interval). Works for any input, including negative values, big values, edge cases.
double normalizedAngle = angle - (ceil((angle + M_PI)/(2*M_PI))-1)*2*M_PI; // (-Pi;Pi]:
double normalizedAngle = angle - (ceil((angle + 180)/360)-1)*360; // (-180;180]:
double normalizedAngle = angle - (floor((angle + M_PI)/(2*M_PI)))*2*M_PI; // [-Pi;Pi):
double normalizedAngle = angle - (floor((angle + 180)/360))*360; // [-180;180):

I'm a little late to the party, I know, but...
Most of these answers are no good, because they try to be clever and concise and then don't take care of edge cases.
It's a little more verbose, but if you want to make it work, just put in the logic to make it work. Don't try to be clever.
int normalizeAngle(int angle)
{
int newAngle = angle;
while (newAngle <= -180) newAngle += 360;
while (newAngle > 180) newAngle -= 360;
return newAngle;
}
This works and is reasonably clean and simple, without trying to be fancy. Note that only zero or one of the while loops can ever be run.

Maybe not helpful, but I always liked using non-degree angles.
An angle range from 0 to 255 can be kept in bounds using bitwise operations, or for a single byte variable, simple allowed to overflow.
An angle range from -128 to 127 isn't quite so easy with bitwise ops, but again, for a single-byte variable, you can let it overflow.
I thought it was a great idea many years back for games, where you're probably using a lookup table for angles. These days, not so good - the angles are used differently, and are float anyway.
Still - maybe worth a mention.

A short way which handles negative numbers is
double mod = x - Math.floor((x + 179.0) / 360) * 360;
Cast to taste.
BTW: It appears that angles between (180.0, 181.0) are undefined. Shouldn't the range be (-180, 180] (exclusive, inclusive]

Here is an integer-only solution:
int normalize(int angle)
{
angle %= 360;
int fix = angle / 180; // Integer division!!
return (fix) ? angle - (360 * (fix)) : angle;
}
Sometimes being clever is just more fun, Platinum Azure.

I have made a formula for orientation of circular values
to keep angle between 0 and 359 is:
angle + Math.ceil( -angle / 360 ) * 360
but to keep between -179 to 180 formula can be:
angle + Math.ceil( (-angle-179) / 360 ) * 360
this will give its orientation shift about -179 keeping actual angle intact
generalized formula for shifting angle orientation can be:
angle + Math.ceil( (-angle+shift) / 360 ) * 360

Well, one more solution, this one with just one division and no loops.
static double normalizeAngle(double angle)
{
angle %= 360.0; // [0..360) if angle is positive, (-360..0] if negative
if (angle > 180.0) // was positive
return angle - 360.0; // was (180..360) => returning (-180..0)
if (angle <= -180.0) // was negative
return angle + 360.0; // was (-360..180] => returning (0..180]
return angle; // (-180..180]
}

How about
(angle % 360) - 179
This will actually return different results than the naive approach presented in the question, but it will keep the angle between the bounds specified. (I suppose that might make this the wrong answer, but I will leave it here in case it solves another persons' similar problem).

int angle = -394;
// shortest
angle %= 360;
angle = angle < -170 ? angle + 360 : (angle > 180 ? angle - 380 : angle);
// cleanest
angle %= 360;
if (angle < -179) angle += 360;
else if (angle > 180) angle -= 360;

It is better to use library functions. They handle special cases like NaN and infinities.
public static double normalizeAngleDegrees(double angle) {
return Math.toDegrees(Math.atan2(Math.sin(Math.toRadians(angle)), Math.cos(Math.toRadians(angle))));
}

Here is my contribution. It seems to work for all angles with no edge issues. It is fast. It can do n180[360000359] = -1 almost instantaneously. Notice how the Sign function helps select the correct logic path and allows the same code to be used for different angles.
Ratch
n180[a_] :=
If[Abs[Mod[a, If[Sign[a] == 0, 360, Sign[a] 360]]] <= 180,
Mod[a, If[Sign[a] == 0, 360, Sign[a] 360]],
Mod[a, If[Sign[a] == 0, 360, -Sign[a] 360]]]

I don't know much Java, but I came across the same problem in Python. Most of the answers here were either for integers so I figured I'd add one that allows for floats.
def half_angle(degree):
return -((180 - degree) % 360) + 180
Based off the other answers I'm guessing the function would looks something like this in Java (feel free to correct me)
int halfAngle(int degree) {
return -Math.floorMod(180 - degree, 360) + 180
}
double halfAngle(double degree) {
// Java doesn't have a built-in modulus operator
// And Math.floorMod only works on integers and longs
// But we can use ((x%n) + n)%n to obtain the modulus for float
return -(((180 - degree) % 360 + 360) % 360) + 180
}
Replace int with float to your liking.
This works for any degree, both positive and negative and floats and integers:
half_angle(180) == 180
half_angle(180.1) == -179.9 // actually -179.89999999999998
half_angle(-179.9) == -179.9
half_angle(-180) = 180
half_angle(1) = 1
half_angle(0) = 0
half_angle(-1) = -1
Math explanation:
Because the modulo operator is open at the upper end, the value x % 360 is in the range [0, 360), so by using the negative angle, the upper end becomes the lower end. So -(-x%360) is in (-360, 0], and -(-x%360)+360 is in (0, 360].
Shifting this by 180 gives us the answer.

Related

How do I find all the angles between two points on the circumference of a circle

I'm currently working on my computer science culminating and I'm stuck and cannot seem to find any answers on similar questions asked on stack overflow.
The problem I am trying to solve is there are three given points in the form (x, y) on a screen (1280 by 720 where the top left corner is (0, 0)) and you have to find a curve that passes through all three points.
So, knowing there are three points, you know that they form a triangle. After finding the circumcentre, you now have:
The radius,
The origin,
And three angles
The curve must start at the first given point, pass through the second given point, and end at the third given point.
int pointX = Math.round(circumcentreX + radius * Math.cos(angle));
int pointY = Math.round(circumcentreY + radius * Math.sin(angle));
Above are the equations I found to find the points on the curve given the angle.
I also know how to find the angles of the three given points with Math.atan2(y, x);
The main problem I have is finding the angles that lie on the curve. So far I have
float angle = (float)Math.toRadians(beginningAngle - (beginningAngle - endAngle) * t);
, where t is the percent of the curve it is at, but it doesn't work half the time.
The angles of the circle would be where North is 270, East is 0, South is 90, West is 180.
Here is what the curve should look like
Angles: 183.57921 : 264.60202 : 358.09354
X coordinates: [473, 597, 747]
Y coordinates: [376, 248, 380]
Here is a case of where it fails
Angles: 179.00537 : 270.5897 : 2.6373901
X coordinates: [473, 614, 752]
Y coordinates: [376, 234, 380]
Please let me know if you found a way to find the angles or would like some source code. Thank you in advance!
After fiddling around with what causes errors and what works, I ended up with four if, else-if, and else's to fix my problem.
float change;
float tba = (ba / 100 < 1) ? ba + 360 : ba;
float tma = (ma / 100 < 1) ? ma + 360 : ma;
float tea = (ea / 100 < 1) ? ea + 360 : ea;
if(tba > tma && tma > tea && ea > ba)
change = ea - ba - 360;
else if(tba < tma && tma < tea && ea < ba)
change = tea - ba;
else if(ea < ba && ma < ba)
change = ea - ba;
else
change = (ea - ba < 0f) ? ea - ba + 360 : ea - ba;
change %= 360;
the float change is the difference between the first angle and the third angle. The floats tba, tma, and tea are only temporary versions of ba, ma, and ea (which are the angles of the given points) which are used so that comparing the angles are easier. For example, when the ba is 350° and ma is 10° and ea is 15°, instead, they could be 350°, 370°, and 375°, respectively. (change would be 25° in this case) I'm not sure if the last line is necessary but I'm keeping it there just in case.
By the way, the reason I'm using floats instead of doubles is that slick2D usually have floats as arguments and not doubles.

Char movement, getting x/y speeds from 2 points

So i'm trying to make a program in java and i need to figure out how much I should add to x and y to make the player movement a constant speed at any angle. The information i have is 2 points, a starting point and an and and I need the char to move from a to b at a constant speed. I'm currently trying to figure out a bunch of different Math methods but I just cant get it right. Thanks in advance.
It's actually quite simple. You have points A (A.x, A.y) and B (B.x, B.y) and need the update for your character position.
Start by calculating the direction vector dir = B - A (subtract component-wise, such that dir.x = B.x - A.x; dir.y = B.y - A.y).
If you add this entire vector to your character's position, you will move it by sqrt(dir.x^2 + dir.y^2) (Pythagorean theorem). Hence, the speed will be: speed = sqrt(dir.x^2 + dir.y^2) / frameTime.
So if you want a constant speed, you have to find a multiple of the direction vector. This will be:
update = dir * speed / sqrt(dir.x^2 + dir.y^2) * frameTime
characterPosition = characterPosition + update
Don't bother with angle calculations. Vector arithmetic is usually way more robust and expressive.
Mathematically (I won't provide code, sorry), assuming you can move in any direction on a two dimensional plane, from the top of my head it could be something like this (taken from old school geometry):
Having a speed, let's say 20 pixels per second (it could also be any other unit you chose, including in-game distance)
Having a polling system, where you have 2 main variables: the last known coords for the char (point A: Ax, Ay), the time of the last known update.
Having the time of the current update.
Having the coords for the destination (point B: Bx, By).
Figure out the current position of your character, which could be done like this (without converting from cartesian to polar coord system):
Figure out angle of movement: find the deltas for X and Y (Dx=Bx-Ax and Dy=By-Ay respectively), and use tangent to find the angle where Angle = tan-1(Dy/Dx).
Figure out the travelled distance (TD) from last poll to current poll where TD = speed * elapsed time
Figure out the coords for the new position (Cx and Cy) using sine and cosine, where travelled X is Tx=TD*sin(Angle) and travelled Y is Ty=TD*cos(Angle).
Now add the travelled distances to your original coords, and you get the current position. Where Cx=Ax+Tx and Cy=Ay+Ty.
How "smooth" movement is depends highly on the quality of your polling and somehow also on rounding for small distances.
Since u didn't provide us code I can just guess what exactly you are trying.
So I'll start with some basics.
I guess you are not just trying to make a program
but the basic movement of an 2d based game engine.
However a constant moving is based of an update method
that reads the player input about 30 times while rendering.
Example from my Projects:
public void run() {
boolean isRunning = true;
long lastNanoTime = System.nanoTime(),nowNanoTime = System.nanoTime();
final double secondAsNano = 1000000000.0;
final double sixtiethOfSecondAsNano = secondAsNano / 30.0;
int delta = 1;
while(isRunning){
nowNanoTime = System.nanoTime();
if(delta * sixtiethOfSecondAsNano + lastNanoTime < nowNanoTime){
update();
delta++;
if(delta == 30){
delta = 1;
lastNanoTime = nowNanoTime;
}
}
render();
}
}
public update(){
if(Input.moveUP) setPlayerY(getPlayerY() - 0.2);
if(Input.moveDOWN) setPlayerY(getPlayerY() + 0.2);
if(Input.moveRIGHT) setPlayerX(getPlayerX() + 0.2);
if(Input.moveLEFT) setPlayerX(getPlayerX() - 0.2);
}
I just cut it down to be easy readable so it might not work correct but it should explain you how its basically done.
I ended up figuring this out almost an hour after posting this. Sorry that my question was so vague, what I was looking for was the math behind what i was trying to do not the code itself but thanks to people who answered. Here is the solution I came up with in code :
if (q.get(0)[0][0] > q.get(0)[1][0]) {
if(q.get(0)[0][0] == q.get(0)[1][0]) {
currentLocation[0] -= 5 * Math.cos((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
currentLocation[1] += 5 * Math.sin((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
}
else{
currentLocation[0] -= 5 * Math.cos((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
currentLocation[1] -= 5 * Math.sin((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
}
} else {
if(q.get(0)[0][0] == q.get(0)[1][0]) {
currentLocation[0] += 5 * Math.cos((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
currentLocation[1] -= 5 * Math.sin((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
}
else{
currentLocation[0] += 5 * Math.cos((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
currentLocation[1] += 5 * Math.sin((Math.atan(((double) q.get(0)[0][1] - (double) q.get(0)[1][1]) / ((double) q.get(0)[0][0] - (double) q.get(0)[1][0]))));
}
}
I figured out a way to get the result I wanted though I probably over complicated it. q is an ArrayList that holds 2d arrays that are 2x2 [a/b][x/y]. and currentLocation a 2 index array that's just [x/y]. The result is the affect I wanted where it draws a line in (X units) a pass from point a to b in any direction at the same speed. This question was poorly worded and i'm sorry for wasting peoples time.

Java: Perform mathematical operation while ignoring sign

I am trying to make a game involving the calcualtion of forces needed to perform some simple mathematical operations. I have a dx variable that represents both the speed and direction along the X-axis. Now I want to subtract friction but it would have to be an addition if the direction of dx is negative. Is there any way to subtract friction from dx using absolute values while returning a signed value? I was looking in to bitwise operators but I have no idea what the possibilities are...
What I could do (i think):
sign = ( dx >> 31 ) << 31;
dx = (dx << 1) >> 1;
dx = ( dx - friction ) & sign;
Not sure if this would work, just the idea... Any improvements?
Edit: this is what I did:
dxSign = Integer.signum(dx) * -1;
dySign = Integer.signum(dy) * -1;
dy = Math.abs(dy);
dx = Math.abs(dx);
fric = body.friction * mass;
dx = ( dx > fric ) ? dx - fric : 0;
dy = ( dy > fric ) ? dy - fric : 0;
dx = dx * absorption;
dy = dy * absorption;
dx *= dxSign;
dy *= dySign;
The safest option is to use a condition. You don't want fraction to change direction
dx = dx < -friction ? dx + friction : dx > friction ? dx - friction : 0
Use Math.abs when you want to ignore the sign. Be aware that optimization might result in your code becoming unreadable and difficult to maintain. So, for starters try to make your code as clean and possible and when you encounter performance issues, determine the source of your problem by benchmarking and optimize only the bottleneck of your code.
Your code is incorrect, for two reasons:
dx = (dx << 1) >> 1; does not eliminate the sign. Moreover, it may incorrectly produce a "false positive" (i.e. a falsely negative number) when bit 31 is set to 1. Even if you replace >> with >>> you would not get an absolute value, because ints are stored in two's complement, not as a sign + value representation
sign will be either a 10000...00 or 00000..00 in binary representation, so ( dx - friction ) & sign will not merge the sign with the results of the other calculation.
You can change the addition for subtraction with the Integer.signum function, like this:
dx -= Integer.signum(dx) * friction;
Just do it the obvious way: don't try to be clever unless this has performance issues.
if(dx >0 ) {
dx -= friction;
} else {
dx += friction;
}
If that seems a bit verbose, hide it in a method:
dx = applyFriction(dx,friction);
int applyFriction(int pos, int friction) {
if(pos > 0) {
return pos - friction;
} else {
return pos + friction;
}
}
This has issues when abs(pos) < friction, and if you look into your physics, you'll find it's more likely that friction is simulated by multiplying - which removes your concern about sign.
In physics, the sign of your direction doesn't really matter. I suggest using the fact that the kinetic friction is a force which is proportional to absolute velocity of the object in motion. That is, the object has some coefficient of friction μF in the range [0, 1]. The change in velocity is just δV = μF∙V∙δt where V is the initial velocity vector of your object, and δt is some span of time.
The new velocity V' is given by
V' = V - δV
= V - μF∙V∙δt
= V(1 - μF∙δt)
In this way, you don't need to deal with the sign at all, and it extends easily into higher dimensional cases.
As an aside, make sure δt is small enough that it doesn't introduce an oscillation in your model.

Bullet not traveling correct way?

I've spent a pretty long time trying to figure out how to move a bullet based on an angle (In degrees)
I define radians as:
public static double toRadians(int angle) {
return (90.0 - angle) * (Math.PI / 180);
}
Here is the code where I actually move the bullet. xMov and yMov can be negative and are added onto the current position every tick.
I've never really worked with Radians, but found the above code to make 0 north, 90 east, etc, which is what I want. I just can't figure out why the bullet isn't going to correct direction. It appears that the X is correct but the Y isn't.
double x= Math.cos(Utilities.toRadians(mvmtAngle));
double y= Math.sin(Utilities.toRadians(mvmtAngle));
xMov += x* 4;
yMov += y* 4;
Thanks!
- tips
EDIT: The degree is being set fine (From the input)
EDIT 2:
Rendering code:
((Graphics2D)g).rotate(Utilities.toRadians(barrelAngle), xStart, yStart);
g.fillRect(xStart - 1, yStart, 2, BARREL_SIZE);
((Graphics2D)g).rotate(-(Utilities.toRadians(barrelAngle)), xStart, yStart);
The problem is probably that the +y direction on the screen is down, while I suspect that you want +y to be up. Your toRadians function converts 0 degrees to PI/2 radians, which on the screen is going down. Try using this conversion instead:
public static double toRadians(int angle) {
return (angle - 90.0) * (Math.PI / 180);
}
This just reverses the sign of the angle. Since Math.cos is an even function, it won't affect the computed x coordinate but will reverse the sense of the y coordinate.

Java 2D - Drag Mouse to rotate Image Smoothly

What is the logic that goes behind rotating an image via mouse movement. I know how to rotate using graphics2d.rotate...but having difficulty doing it with the mouse as the source for rotation. Here is basic steps:
get mouse x(dx) and mouse y(dy) distances from anchoring point( in this case that would be
the center of the image we want to rotate).
use this point in Math.arcTan2(dy,dx) to obtain the angle or rotation.
use the value from step to for Graphics2D.rotate method.
With that strategy, everytime i rotate the image, the image starts rotating from -pi and after 90 degrees of rotation it go goes back to -pi. I don't understand what I'm doing wrong here, it should be pretty basic.
Here is a part of the code :
// mouse dragged events get sent here.
public void mouseDragged( MouseEvent e ) {
int mx = e.getX( ), my = e.getY( );
// just checking if it falls within bounds of the image we
// want to rotate.
if( mx > speedKX || mx < speedKX + speedK.getWidth( ) || my > speedKY || my < speedKY + speedK.getHeight( )/2 )
{
theta += getTheta( e.getX( ), e.getY( ) );
}
}
As I understand you should look for the initial angle (when you clicked, the line between the anchor and your click) and the current angle (when you are dragging, same line). That angle (independent from the current distance to anchor point) will give you the rotation.
So you have to:
rotate(anglenow - angle0)
How to find it:
In both cases (initial click and mouse move event) you have to find angle between anchor and mouse point thinking the anchor as the origin.
I would use a method (getAngle(x1,y1,x2,y2). That method (except for race conditions like same x or same y, easily detectable) should calculate arctan(dy/dx).
Sign
But when you are dividing dy/dx it can be:
+ / + -> +
+ / - -> -
- / + -> -
- / - -> +
It is, four posibilities give you two kind of results. So, you have to look some condition to detect them.
I should review arctan doc or source to see what values it gives (between 0 and pi, or -pi/2 and +pi/2) and check then sign of the dx or the dy (depending on what range arctan returns) and use it to add/decrement pi to then resulting angle.
Then you'll get a getAngle method that return correctly the 360º space.
Edit
Javadoc says:
Math.atan retuns the arc tangent of an angle, in the range of -pi/2 through pi/2.
So, assuming your angle of value 0 is for the X axis as I assumed, the range it returns is the right hemisphere. So you have to distiguish the right hemisphere from the left one.
If you calculate dx = xtarget - xorigin (as you did for the division) it will be positive if the correct hemisphere is the right one, and negative if it's not.
So if dy < 0 then you have to add pi to your resulting angle. It will be between -pi/2 and 3pi/2. You also can correct the result by passing all to (-pi,pi) range or (0,2pi) range.
Edit: pseudocode, please double check!
onmousedown {
startpoint = (x,y);
startangle = getAngle(origin, startpoint);
}
onmousemove {
currentpoint = (x,y);
currentangle = getAngle(origin, currentpoint);
originalimage.rotate(currentangle - startangle);
}
getAngle(origin, other) {
dy = other.y - origin.y;
dx = other.x - origin.x;
if (dx == 0) // special case
angle = dy >= 0? PI/2: -PI/2;
else
{
angle = Math.atan(dy/dx);
if (dx < 0) // hemisphere correction
angle += PI;
}
// all between 0 and 2PI
if (angle < 0) // between -PI/2 and 0
angle += 2*PI;
return angle;
}

Categories

Resources