If statement not doing what I want it to - java

My if statement isn't working when I try to get the direction of a vector in my program.
The problem is when I input a vector of <-1, 1>
I get 45° instead of 125°
Source code
import java.util.*;
import java.math.*;
public class Main
{
public static void main(String[] args)
{
//Decloration
Scanner in = new Scanner(System.in);
int rty;
double v1;
double v2;
double dir;
double mag;
double V1;
double V2;
do {
//Input
System.out.println("Please input x");
v1 = in.nextDouble();
System.out.println("Please input y");
v2 = in.nextDouble();
//Calculating
dir = Math.toDegrees( Math.atan(v2/v1) );
if (v1 >= 0) {
if (v2 < 0) {
dir+=270;
}
} else {
if (v2 < 0) {
dir+=180;
} else {
dir+=90;
}
}
mag = Math.sqrt((v1*v1)+(v2*v2));
V1 = v1/mag;
V2 = v2/mag;
//Output
System.out.println("The unit vector is <" + V1 +", " + V2 + ">");
System.out.println("The magnitude is " + mag);
System.out.println("The direction is " + dir);
System.out.println("\n\nRty?");
//Quit
rty = in.nextInt();
System.out.println("\n");
dir = 0;
} while (rty == 1);
}
}
Does anyone know what's wrong?

Your logic is incorrect for all cases where at least one of v1 and v2 is negative. The atan method Javadocs state that:
the returned angle is in the range -pi/2 through pi/2
So if a value v1 is 0 or positive and v2 is negative, you'll wind up with -45 and you should add 360 degrees so that an input of (1, -1) is 325 degrees.
If a value v1 is negative and v2 is positive, you'll wind up with the same value as the above case, but you want the opposite direction. Add 180 degrees here.
If both v1 and v2 are negative, the degrees value you'll get is 45, so adding 180 here is correct.
Here is the updated logic:
if (v1 >= 0) {
if (v2 < 0) {
dir += 360;
}
} else {
dir += 180;
}
This will get you the correct value of 135 degrees, not 125, for (-1, 1).
However, getting the range of degrees (or originally radians) across the entire range of -pi to +pi is the job of Math.atan2. It takes polar coordinates, which is what v1 and v2 are.
... computing an arc tangent of y/x in the range of -pi to pi.
dir = Math.toDegrees(Math.atan2(v2, v1));
After converting to degrees, you will still want to add 360 degrees if the dir is negative. The logic is simpler:
if (dir < 0) {
dir += 360;
}

From docs, atan(double a)
Returns the arc tangent of a value; the returned angle is in the range -pi/2 through pi/2.
From docs, atan2(double a)
Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta). This method computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi.
The use of atan was scaling your answer down by pi to get it into the range of the values returned by the function. Switching to atan2 would solve that since the cartesian coordinates are converted to polar coordinates. You can obviously then use the toDegrees function.

Math.toDegrees( Math.atan(v2/v1) returns -45 and then you're adding 90, so it will give 45.
For your if clauses this should work:
Math.toDegrees(Math.atan(Math.abs(v2/v1))

Related

Calculating degrees to radians in terms of pi

I made a code converting degrees to radians following my teacher's format but I want to try to make it in terms of pi. Right now when I plug in 30 as my degrees the output is a loooong decimal but I want it to come out as pi/6. Is there a way to keep the pi symbol in the output?
This is my current code:
public static double convertDeg(double deg)
{
double rad = deg * (Math.PI/180);
return rad;
}
and
System.out.println("Degrees to radians: "+Calculate.convertDeg(30));
The output is: "Degrees to radians: 0.5235987755982988"
"but I want it to come out as pi/6."
To get this format; Try this.
public static String convertDeg(double deg)
{
String rad = "Math.PI/"+(180/deg);
return rad;
}
It returns a string as the method return type is string.
It does'nt exactly return "pi/6" but "Math.PI/6".
So get the idea for its use from this;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
class HelloWorld {
public static String convertDeg(double deg)
{
String rad = "Math.PI/"+(180/deg);
return rad;
}
public static void main(String[] args) {
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
try {
Object result = engine.eval(convertDeg(30));
System.out.println("\nDegree to Radian = "+result);
}
catch (ScriptException e) {
// Something went wrong
e.printStackTrace();
}
}
}
Its answer is as follows,
Degree to Radian = 0.5235987755982988
You can't set formatting up to convert degrees to radians with pi out of the box in java, but you can write your own function to do this.
We know that
360 degrees = 2 * PI radians =>
180 degrees = PI radians =>
1 degree = PI / 180 radians =>
Therefore
X degrees = PI * (X / 180) radians
In case degrees is an integer value
we can simplify a fraction X / 180
if gcd(X, 180) > 1, gcd -- the greater common divider.
X / 180 = (X / gcd(X, 180)) / (180 / gcd(X, 180))
The code is something like this (don't forget to check corner cases):
String formatDegreesAsFractionWithPI(int degrees) {
int gcd = gcd(degrees, 180);
return "(" + (degrees / gcd) + " / " + (180 / gcd) + ") * PI"
}
int gcd(int a, int b) = { ... }
In case degrees is a floating point number,
the problem is more complicated and my advice
is to read about 'converting decimal floating
point number to integers fraction'.
Related questions: gcd in java, convert float to fraction (maybe works)

I am trying to calculate sine of an angle without using the Math.sin() in java

I am trying to calculate sine of an angle without using the Math.sin(). I got stuck in it's equation as I keep getting the wrong results
note I have a method that changes the angle from degrees to radians
public static double sin(double x, int precision) {
//this method is simply the sine function
double answer = 1, power = 1;
int n = 2,factorial = 1;
while (n<=precision) {
power = (power * x * x *-1) +1 ;
factorial = (factorial * (n +1))* (n-1);
answer = answer + ((power/factorial ));
n = n + 2;
}
return answer;
}
It looks like you're attempting to calculate the sine of angle given in radians using the Maclaurin series, a special case of Taylor series.
sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ...
Your initial answer is 1 when it should be x. Your initial power is 1 when it should be x also.
double answer = x, power = x;
For some reason you're adding one to the power part of the result when you shouldn't be.
power = (power * x * x * -1);
You'll also need to fix your factorial calculation. Multiply by n + 1 and n, not n + 1 and n - 1.
factorial = (factorial * (n + 1)) * (n);
With these fixes, testing:
for (double angle = 0; angle <= Math.PI; angle += Math.PI / 4)
{
System.out.println("sin(" + angle + ") = " + sin(angle, 10));
}
The results are pretty good considering the limitations of precision for floating point arithmetic.
sin(0.0) = 0.0
sin(0.7853981633974483) = 0.7071067811796194
sin(1.5707963267948966) = 0.999999943741051
sin(2.356194490192345) = 0.7070959900908971
sin(3.141592653589793) = -4.4516023820965686E-4
Note that this will get more inaccurate as the values of x get larger, not just because of the inaccuracy to represent pi, but also because of the floating point calculations for adding and subtracting large values.

Decide if a circle intersects with an infinite line

So, I need to check if a circle is intersected by a line algebraically. I've attempted to do this by taking a perpendicular line to the infinite that passes through the center of the circle. I then measure the perpendicular against the radius of the circle, and it states that the line does not intersect if d > r.
import java.util.Scanner;
public class LineCircle_Intersection {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
double p1x, p2x, p1y, p2y, cx, cy, r;
System.out.print("Enter p1x: ");
p1x = in.nextDouble();
System.out.print("Enter p1y: ");
p1y = in.nextDouble();
System.out.print("Enter p2x: ");
p2x = in.nextDouble();
System.out.print("Enter p2y: ");
p2y = in.nextDouble();
System.out.print("Enter cx: ");
cx = in.nextDouble();
System.out.print("Enter cy: ");
cy = in.nextDouble();
System.out.print("Enter r: ");
r = in.nextDouble();
double m = (p2y - p1y) / (p2x - p1x);
double pem = -1 / m;
double pey = pem + p1y; // pe = perpendicular line (used E instead of L because lowercase l looks too much like 1)
double pex = (pey - p1y) / pem;
double d = Math.sqrt((pex - cx) * (pex - cx) + (pey - cy) * (pey - cy));
if (d <= r) {
if (d == r) {
System.out.println("Line intersects the circle at one point.");
} else {
System.out.println("Line intersects the circle at two points.");
}
} else if (m == 1) {
if (d <= r) // There's a problem in this area. I'm not sure what, or how to fix it.
{
if (d == r) {
System.out.println("The line intersects the circle at one point.");
} else {
System.out.println("Line intersects the circle at two points.");
}
} else {
System.out.println("Line does not intersect the circle.");
}
} else {
System.out.println("Else."); //This says "Else" for testing purposes.
}
}
}
Here's where things start to go wrong. There are several points that can be input that clearly should intersect or not intersect, but the program frequently says otherwise.
Will be working on this for a few hours, so if I solve it before someone else I'll post an update and how I solved it.
The principle is correct, but I won't verify your computations.
In short:
boolean intersects=false, is_tangent=false;
double p2p1x=p2x-p1x, p2p1y=p2y-p1y;
double p1p2DistSq=p2p1x*p2p1x+p2p1y*p2p1y;
if(p1p2DistSq > 1e-12) { // well-behaved line
double p1cx=p1x-cx, p1cy=p1y-cy;
double crossprod=p2p1x*p1cy-p2p1y*p1cx;
double distCenterToLineSquare=crossprod*crossprod/p1p2DistSq;
double rSquare=r*r;
intersects = (distCenterToLineSquare <= rSquare); // r is radius
// for practical purposes, if the relative error of
// (r-dist) is 1e-6 of r, we might consider the line as tangent.
is_tangent = Math.abs(distCenterToLineSquare - rSquare)/rSquare < 1e-12;
} // cowardly refusing to deal with ill-configured lines
In details:
Preliminary (and take this as an easy way to deduce the distance between a point and a line if you don't have internet at the moment)
The cross-product between two vectors
{ax, ay} x {bx, by} = |a|*|b|*sin(angle_between_dir_a_and_b)
Also this cross-product is (ax*by-ay*bx)
Now, assume a line passing through P1 with direction defined by the unitary vector {ux, uy}. The distance of a point {cx. cy} to this line will be
dist=sin(alpha)*|P1-C|
where |P1-C| is the distance between C and P1 and alpha is the angle between the direction {ux, uy} and the direction of the line {P1,C}. Let's denote with {vx,vy} the unitary direction of {P1,C} line. In this case, since u and v are unitary (|u|=|v|=1)
sin(alpha)=ux*vy-uy*vx
Thus
dist=(ux*vy-uy*vx)*|P1-C|
Plugging in the vx, vy using
vx=(P1x-Cx)/|P1-C| // it's a unitary vector
vy=(P1y-Cy)/|P1-C|
results in
dist=ux*(P1y-Cy)-uy*(P1x-Cx)
Now, the only thing that remains, is ux and uy. Since your line is defined by P1 and P2
ux=(P2x-P1x)/|P1-P2|
uy=(P2y-P1y)/|P1-P2|
(with |P1-P2| again, the distance between P1 and P2)
dist=( (P2x-P1x)*(P1y-Cy)-(P2y-P1y)*(P1x-Cx) )/ |P1-P2|
Ok, |P1-P2| requires an sqrt evaluation and we can get rid of it by comparing your dist^2 with the radius^2
The 'line intersect circle' then becomes
double p1cx=p1x-cx, p1cy=p1y-cy;
double p2p1x=p2x-p1x, p2p1y=p2y-p1y;
double crossprod=p2p1x*p1cy-p2p1y*p1cx;
double distCenterToLineSquare=crossprod*crossprod/(p2p1x*p2p1x+p2p1y*p2p1y);
boolean intersects= (distCenterToLineSquare <= r*r); // r is radius

How to find the nearest angle from 0 to 180 and -180 to 0

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;
}
}

Calculating circular rotation required to match new angle

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;
}
}

Categories