Java: Perform mathematical operation while ignoring sign - java

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.

Related

Air Resistance In this simulation causes the velocity to rise drastically

The issue I have is that I'm attempting to add drag to an object in this basic physics simulation (Java [Processing]), but once I add the appropriate formula, it causes the objects velocity to increase drastically in the opposite direction. Of course the problem is that drag for some reason is being calculated too high but I'm not sure why thats happening as I'm using the real world equation.
void setup(){size(1280,720);}
class Circle{
float x,y,r,m,dx,dy,ax,ay,fx,fy;
Circle(float xPos, float yPos, float Radius, float Mass){
x = xPos;
y = yPos;
r = Radius;
m = Mass;
}
void ADD_DRAG(){
fx -= 0.5 * 1.225 * dx * dx * 0.5 * r * PI;
fy -= 0.5 * 1.225 * dy * dy * 0.5 * r * PI;
}
void update(){
ADD_DRAG();
ax = fx / m;
ay = fy / m;
dx += ax / frameRate;
dy += ay / frameRate;
x += dx / frameRate;
y += dy / frameRate;
}
}
Circle[] SceneObjects = {new Circle(50,50,20,20000),new Circle(50,50,2,20)};
void draw(){
background(51);
for (Circle c : SceneObjects){
c.update();
circle(c.x * 3,c.y * 3,c.r * 3);
}
}
void mouseClicked(){
if(SceneObjects[1].fx != 2000)
SceneObjects[1].fx = 2000;
else
SceneObjects[1].fx = 0;
}
This is the code, essentially there is a Circle class which stores the objects properties and then the forces applies are updated each draw loop. The mouseClicked void is just for testing by adding a force to the objects. All and any help is appreciated, thanks!
Maths I am Using:
Rearranged F=ma for ax = fx / m;
Acceleration * time = Speed for dx += ax / frameRate; (frameRate is 1/time)
Distance = Speed * time = for x += dx / frameRate; (same as above)
For drag im using this equation https://www.grc.nasa.gov/WWW/K-12/rocket/drageq.html with the constants eg air density etc added as seen.
There are a couple of things wrong here.
You haven't given us numbers (or a minimal complete example), but the vector algebra is off.
Yes, the acceleration is f = -kv2, and |v|2 = vx2 + vy2, but that doesn't mean that you can decompose f into fx=kvx2 and fy=kvy2. Not only is your magnitude off, but your acceleration is now not (in general) aligned with the motion; the path of your projectile will tend to curve toward a diagonal between the axes (e.g. x=y).
Also, your code always gives acceleration in the negative x and negative y directions. If your projectile happens to start out going that way, your version of air resistance will speed it up.
Finally, your time interval may simply be too large.
There is a better way. The differential equation is v' = -k v|v|, and the exact solution is v = (1/kt) z, (with appropriate choice of the starting time) where z is the unit direction vector. (I don't know how to put a caret over a letter.) This leads to v(t) = (1/t)v(t=1.0)
So you can either work out a fictional time t0 and calculate each new velocity using 1/(kt), or you can calculate the new velocity from the previous velocity: vn+1 =vn/(kd vn + 1), where d is the time interval. (And then of course you have to decompose v into vx and vy properly.)
If you're not familiar with vector algebra, this may seem confusing, but you can't get an air-resistance sim to work without learning the basics.

How to determine if two points do not have any obstructions between them

I am currently trying to put together an algorithm where I can know if there is an obstruction between two defined points in a plane.
Here is an example image.
We can see with the image that point 1, 2, 3, & 6 are all accessible from the origin point. Points 4 and 5 are not. You pass through the polygon.
The code I am using is the following. pStartPoint and pEndPoint is the line from the origin to the point in question. The function checks all edges to see if the line passes through the edge.
public double GetSlopeOfLine(Point a, Point b){
double x = b.y - a.y;
double y = b.x - a.x;
return (x / y);
}
public double GetOffsetOfLine(double x, double y, double slope){
return (y - (slope * x));
}
public boolean IsPointAccessable(Point pStartPoint, Point pEndPoint){
//Define the equation of the line for these points. Once we have slope and offset the equation is
//y = slope * x + offset;
double slopeOfLine = GetSlopeOfLine(pStartPoint, pEndPoint);
double offSet = GetOffsetOfLine(pStartPoint.x, pStartPoint.y, slopeOfLine);
//Collision detection for each side of each obstacle. Once we get the point of collision, does it lie on the
//line in between the two points? If so, collision, and I can't reach that point yet.
for (Iterator<Obstacles> ObstacleIt = AdjustedObstaclesList.iterator(); ObstacleIt.hasNext();) {
Obstacles pObstacle = ObstacleIt.next();
int NumberOfEdges = pObstacle.getPoints().size();
for(int i=0; i<NumberOfEdges; i++){
//Get Edge[i];
int index = i;
Point pFirstPoint = (Point)pObstacle.getPoints().get(index);
if(i >= NumberOfEdges - 1)
index = 0;
else
index = i+1;
Point pNextPoint = (Point)pObstacle.getPoints().get(index);
double slopeOfEdge = GetSlopeOfLine(pFirstPoint, pNextPoint);
double offsetEdge = GetOffsetOfLine(pNextPoint.x, pNextPoint.y, slopeOfEdge);
int x = Math.round((float) ((-offSet + offsetEdge) / (slopeOfLine - slopeOfEdge)));
int y = Math.round((float) ((slopeOfLine * x) + offSet));
//If it lies on either point I could be looking at two adjacent points. I can still reach that point.
if(x > pStartPoint.x && x < pEndPoint.x && y > pStartPoint.y && y < pEndPoint.y &&
x > pFirstPoint.x && x < pNextPoint.x && y > pFirstPoint.y && y < pNextPoint.y){
return false;
}
}
}
return true;
}
If the line passes through and the point where the lines cross is found between pStartPoint and pEndPoint I am assuming that pEndPoint cannot be reached.
This function is not working and I am wondering if it has something to do with the fact that the origin is not at the bottom left but at the top left and that (width, height) of my window is located in the bottom right. Therefore the coordinate plane is messed up.
My mind must be mush because I cannot think how to adjust for this and if that is truly my mistake as I cannot seem to fix the error. I thought adjusting the slope and offset by multiplying each by -1 might have been the solution but that doesn't seem to work.
Is my solution the right one? Does my code seem correct in checking for an intersect point? Is there a better solution to see if a point is accessible.
There is also going to be the next step after this where once I determine what points are accessible if I am now on one of the points of the polygon. For example, from point 1 what points are accessible without crossing into the polygon?
First, I would like to say that using slopes for this kind of task is do-able, but also difficult due to the fact that they are very volatile in the sense that they can go from negative infinity to infinity with a very small change in the point. Here's a slightly different algorithm, which relies on angles rather than slopes. Another advantage of using this is that the coordinate systems don't really matter here. It goes like this (I reused as much of your existing code as I could):
public boolean IsPointAccessable(Point pStartPoint, Point pEndPoint) {
//Collision detection for each side of each obstacle. Once we get the point of collision, does it lie on the
//line in between the two points? If so, collision, and I can't reach that point yet.
for (Iterator<Obstacles> ObstacleIt = AdjustedObstaclesList.iterator(); ObstacleIt.hasNext();) {
Obstacles pObstacle = ObstacleIt.next();
int NumberOfEdges = pObstacle.getPoints().size();
for(int i=0; i<NumberOfEdges; i++){
//Get Edge[i];
int index = i;
Point pFirstPoint = (Point)pObstacle.getPoints().get(index);
if(i >= NumberOfEdges - 1)
index = 0;
else
index = i+1;
Point pNextPoint = (Point)pObstacle.getPoints().get(index);
// Here is where we get a bunch of angles that encode in them important info on
// the problem we are trying to solve.
double angleWithStart = getAngle(pNextPoint, pFirstPoint, pStartPoint);
double angleWithEnd = getAngle(pNextPoint, pFirstPoint, pEndPoint);
double angleWithFirst = getAngle(pStartPoint, pEndPoint, pFirstPoint);
double angleWithNext = getAngle(pStartPoint, pEndPoint, pNextPoint);
// We have accumulated all the necessary angles, now we must decide what they mean.
// If the 'start' and 'end' angles are different signs, then the first and next points
// between them. However, for a point to be inaccessible, it also must be the case that
// the 'first' and 'next' angles are opposite sides, as then the start and end points
// Are between them so a blocking occurs. We check for that here using a creative approach
// This is a creative way of checking if two numbers are different signs.
if (angleWithStart * angleWithEnd <= 0 && angleWithFirst * angleWithNext <= 0) {
return false;
}
}
}
return true;
}
Now, all that is left to do is find a method that calculates the signed angle formed by three points. A quick google search yielded this method (from this SO question):
private double getAngle(Point previous, Point center, Point next) {
return Math.toDegrees(Math.atan2(center.x - next.x, center.y - next.y)-
Math.atan2(previous.x- center.x,previous.y- center.y));
}
Now, this method should work in theory (I am testing to be sure and will edit my answer if I find any issues with signs of angles or something like that). I hope you get the idea and that my comments explain the code well enough, but please leave a comment/question if you want me to elaborate further. If you don't understand the algorithm itself, I recommend getting a piece of paper out and following the algorithm to see what exactly is going on. Hope this helps!
EDIT: To hopefully aid in better understanding the solution using angles, I drew a picture with the four base cases of how the start, end, first, and next could be oriented, and have attached it to this question. Sorry for the sloppiness, I drew it rather quickly, but this should in theory make the idea clearer.
If you have a low segment count (for instance, your example only shows 12 segments for three shapes, two shapes of which we know we can ignore (because of bounding box checks), then I would recommend simply performing line/line intersection checking.
Point s = your selected point;
ArrayList<Point> points = polygon.getPoints();
ArrayList<Edge> edges = polygon.getEdges();
for(Point p: points) {
Line l = new Line(s, p);
for(Edge e: edges) {
Point i = e.intersects(l);
if (i != null) {
System.out.println("collision", i.toString());
}
}
}
With an intersects method that is pretty straight forward:
Point intersects(Line l) {
// boring variable aliassing:
double x1 = this.p1.x,
y1 = this.p1.y,
x2 = this.p2.x,
y2 = this.p2.y,
x3 = l.p1.x,
y2 = l.p1.y,
x3 = l.p2.x,
y2 = l.p2.y,
// actual line intersection algebra:
nx = (x1 * y2 - y1 * x2) * (x3 - x4) -
(x1 - x2) * (x3 * y4 - y3 * x4),
ny = (x1 * y2 - y1 * x2) * (y3 - y4) -
(y1 - y2) * (x3 * y4 - y3 * x4),
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0) return null;
return new Point(nx/d, ny/d);
}

Test if point is within line range on 2D space

This question is a bit hard to formulate so I'll start by showing this image:
I want to test if points (such as p1 and p2 on the image) are within the dotted lines that are perpendicular to the line at its limits. I know the coordinates of the points to test and the line's limits.
So for p1 it would be false, and for p2 it would be true.
What would be the most computationaly efficient way to calculate this?
I'm working with floats in Java.
This can be very efficiently done with the dot product:
This is positive if A has a component parallel to B, and negative if anti-parallel.
Therefore if you have a line segment defined by points A and B, and a test point P, you only need two dot product operations to test:
dot(A - B, P - B) >= 0 && dot(B - A, P - A) >= 0
EDIT: a graphical explanation:
The dot product can be shown to be:
Thus if θ > 90 then dot(A, B) < 0, and vice versa. Now for your problem:
In case 1, when dot(A - B, P - B) > 0 we say that P is on the correct side of the dotted line at B, and vice versa in case 2. By symmetry we can then do the same operation at A, by swapping A and B.
If the point to test (tp), together with start point (sp) and end point (ep) of the range, form an obtuse triangle and you can conclude that it is out of range.
https://en.wikipedia.org/wiki/Acute_and_obtuse_triangles
A special case would be tp is same slope as sp and ep, then you calculate the 2 distances:
distSpAndTp - distance between sp and tp
distEpAndTp - distance between ep and tp
If the sum of these 2 distances = distance between sp and ep, then tp is in range, otherwise it is out of range.
The best approach would be to first create a rectangle Rect (of android.graphics package), with x and width being the beginning and end of line, and y and height being the beginning and end of screen height.
Then use the Rect method Rect.contains(int x, int y) to check if the point coordinates lie within.
For floats, use RectF class instead.
This approach involves using 2D vectors (which you should have been using anyway, makes working in any coordinate system a lot easier) and the dot (scalar) product. It does not require any relatively expensive operations like the square root or trignometic functions, and therefore is very performant.
Let A and B be the start and end points (by point I mean a vector representing a position in 2D space) of the line segment, in any order. If P is the point to test, then:
If dot(PA, AB) and dot(PB, AB) have the same sign, the point lies outside the region (like p1 in your example).
If the dot products above have opposite signs, the point lies inside the region (like p2).
Where PA = A - P, PB = B - P and AB = B - A.
This condition can be surmised as follows:
if dot(PA, AB) * dot(PB, AB) <= 0 {
// Opposite signs, inside region
// If the product is equal to zero, the point is on one of the dotted lines
}
else {
// Same signs, outside region
}
Let's say, x1 is the beginning of the line and x2 is the end. Then ax is the dot in an horizontal plan and; y1, y2 and ay in vertical plan.
if((ax >= x1 && ax <= x2) && (ay >= y1 && ay <= y2)){ // do your work
}
There are a few answers already but there is another solution that will give you the unit distance on the line segment that the points is perpendicular to.
Where then line is x1,y1 to x2,y2 and the point is px,py
Find the vector from line start to end
vx = x2 - x1;
vy = y2 - y1;
And the vector from the line start (or end) to the point
vpx = px - x1;
vpy = py - y1;
And then divide the dot product of the two vectors by the length squared of the line.
unitDist = (vx * vpx + vy * vpy) / (vx * vx + vy * vy);
If the perpendicular intercept is on the line segment then unitDist will be 0 <= unitDist <= 1
In shortened form
x2 -= x1;
y2 -= y1;
unitDist = (x2 * (px - x1) + y2 * (py - y1)) / (x2^2 + y2^2);
if (unitDist >= 0 && unitDist <= 1) {
// point px,py perpendicular to line segment x1,y1,x2,y2
}
You also get the benefit of being able to get the point on the line where the intercept is.
p2x = vx * unitDist + x1;
p2y = vy * unitDist + y1;
And thus the distance that the point is from the line (note not line segment but line)
dist = hypot(p2x - px, p2y - py);
Which is handy if you are using a point to select from many lines by using the minimum distance to determine the selected line.
This can be solved nicely using complex numbers.
Let a and b the endpoints of the segment. We use the transformation that maps the origin 0 to a and the point 1 to b. This transformation is a similarity and its expresion is simply
p = (b - a) q + a
as you can verify by substituting q=0 or q=1. It maps the whole stripe perpendicular to the given segment to the stripe perpendicular to the segment 0-1.
Now the inverse transform is obviously
q = (p - a) / (b - a),
and the desired condition is
0 <= Re((p - a) / (b - a)) <= 1.
To avoid the division, you can rewrite
0 <= Re((p - a) (b - a)*) <= |b-a|²
or
0 <= (px - ax)(bx - ax) + (py - ay)(by - ay) <= (bx - ax)² + (by - ay)².

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.

Easy way to keeping angles between -179 and 180 degrees

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.

Categories

Resources