I have the following code...
public class Point {
private double x;
private double y;
static private final double RADTODEG = 180.0d / Math.PI ;
static private final double DEGTORAD = Math.PI / 180.0d;
/**
* Rotates the point by a specific number of radians about a specific origin point.
* #param origin The origin point about which to rotate the point
* #param degrees The number of radians to rotate the point
*/
public void rotateByRadians(Point origin, double radians) {
double cosVal = Math.cos(radians);
double sinVal = Math.sin(radians);
double ox = x - origin.x;
double oy = y - origin.y;
x = origin.x + ox * cosVal - oy * sinVal;
y = origin.y + ox * sinVal + oy * cosVal;
}
/**
* Rotates the point by a specific number of degrees about a specific origin point.
* #param origin The origin point about which to rotate the point
* #param degrees The number of degrees to rotate the point
*/
public void rotateByDegrees(Point origin, double degrees) {
rotateByRadians(origin, degrees * DEGTORAD);
}
/**
* Rotates the point by the specified number of radians about the axis' origin (0,0). To rotate about a specific origin point, see rotateByRadians(Point, double)
* #param radians Measure of radians to rotate the point
*/
public void rotateByRadians(double radians) {
if(isEmpty()) // Since we're rotating about 0,0, if the point is 0,0, don't do anything
return;
double cosVal = Math.cos(radians);
double sinVal = Math.sin(radians);
double newx = x * cosVal - y * sinVal;
double newy = x * sinVal + y * cosVal;
x = newx;
y = newy;
}
/**
* Rotates the point by the specified number of degrees about to the axis' origin (0,0). To rotate about a specific origin point, see rotateByDegrees(Point, double)
* #param degrees Measure of degrees to rotate the point
*/
public void rotateByDegrees(double degrees) {
rotateByRadians(degrees * DEGTORAD);
}
The problem arises when given a point, say 0,200. Calling the rotation (about axis origin 0,0) for 180 degrees it should be (0, -200). The x coordinate shouldn't have changed. However, it ends up being (-2.4492935982947064E-14, -200). I tried using strictfp but it doesn't make a difference. This only affects the result if the coordinate being rotated is zero. Nonzero values work fine. Any ideas why this is not accurate?
The code is below:
Point p = new Point(0.0d, 200.0d);
p.rotateByDegrees(180.0d);
System.out.println(p);
Gives output:
shapelib.Point Object {x: -2.4492935982947064E-14 y: -200.0}
For better or for worse, that's just the way it is with floating point math. From http://mindprod.com/jgloss/floatingpoint.html:
"Think of float and double as representing physical measurements. No
one would complain if their cabinet maker made a desk 6.000000000001
feet long. Analogously, don’t complain about the inevitable tiny
errors in floating point arithmetic results e.g. Math. cos(
Math.toRadians( 90 ) ) not coming out bang on zero. ( If you want
perfection, use int, long, BigInteger or BigDecimal. )"
Float arithmetic is not fully accurate. Error of 10^-14 power is enough in most cases.
If you calculate Math.sin(Math.PI) you'll get 1.2246467991473532E-16. Why do you need to get precisely 0 in your case?
Both the provided answers so far are correct, but they're missing a fundamental point.
The range of possible values a floating point number can take on is not continuous. Rather, it has holes in it. So you can imagine that from 0.1 to 0.2, instead of there being an infinite amount of numbers, there are only a finite number.
That's most of the reason why floating point arithmetic is inaccurate. Computers can't exactly represent every real number you'd like. Instead, they can get to only some small epsilon away from the actual value.
As an example, you can't exactly represent the fraction 2 / 10. If you print out all the decimal places to this number, you'll find it something like 0.20000000000000001.
See here for a more thorough writeup: http://floating-point-gui.de/
Related
I wrote a code that should turn a point around another point counterclockwise. But it does not work correctly.
public boolean contains(double x, double y) {
double ox = this.x.get() + (this.width.get()/2);
double oy = this.y.get() + (this.height.get()/2);
double theta = rotate.get() - (rotate.get() * 2);
double px1 = Math.cos(theta) * (x-ox) - Math.sin(theta) * (y-oy) + ox;
double py1 = Math.sin(theta) * (x-ox) + Math.cos(theta) * (y-oy) + oy;
return shape.contains(px1, py1);
}
x, y - are the coordinates of the point to be rotated.
ox,oy - is the coordinates of the point around which you want to rotate.
rotate.get() - angle to rotate
Update: Changes in the code that solved the problem, who can come in handy:
double px1 = Math.cos(Math.toRadians(theta)) * (x-ox) - Math.sin(Math.toRadians(theta)) * (y-oy) + ox;
double py1 = Math.sin(Math.toRadians(theta)) * (x-ox) + Math.cos(Math.toRadians(theta)) * (y-oy) + oy;
Please check, if your rotate.get() will give you a degrees value (e.g. 45°) or a radians value (e.g. 0.5*pi). Math.sin() and Math.cos() will only accept radians.
To convert them you could use something like angle = Math.toRadians(45)
Although this is answered, another simple way to get this done is using the built-in method of Rotate class. This way you dont need to worry about the Math stuff ;)
Rotate r = new Rotate();
r.setPivotX(ox);
r.setPivotY(oy);
r.setAngle(angleInDegrees);
Point2D point = r.transform(new Point2D(x, y));
I am trying to write a program that calculates the distance of a projectile, but the distance returned is not coming out correct. I am familiar with operator precedence in Java, but I am not sure why I am not getting the correct distance. For angle = 22, velocity = 35, and height = 10 I expect to get 75.54 but instead I am getting 42.03.
Are there obvious mistakes in my code that are causing this?
public class FootballDistanceCalculator {
public static final double GRAVITATIONAL_ACCELERATION = 32.174;
/**
* Calculates the distance a projectile travels
*
* #param angle angle at which projectile is thrown in degrees
* #param velocity initial velocity of projectile in miles/hour
* #param height initial height of projectile in feet
* #return distance traveled by projectile in feet
*/
public static double calculateDistance(double angle, double velocity, double height) {
double angleRadians = Math.toRadians(angle);
double vCosineThetaOverG = (velocity * (Math.cos(angleRadians))) / GRAVITATIONAL_ACCELERATION ;
double vSinTheta = velocity * (Math.sin(angleRadians));
double vSinThetaSquared = (Math.pow(vSinTheta, 2));
double twoGravHeight = (2 * GRAVITATIONAL_ACCELERATION * height);
double radical = Math.sqrt((vSinThetaSquared + twoGravHeight));
double distance = vCosineThetaOverG * (vSinTheta + radical);
return distance;
}
}
This is the equation I am basing this program off of:
d = (v cos(θ) / g)(v sin(θ) + √(v sin(θ)2 + 2 g h))
v = velocity
g = gravitational acceleration
h = height
The problem turned out to be a units conversion issue as indicated in the comments.
I had to take my velocity parameter and multiply by feet per mile (5280) and divide by seconds per hour (3600) to get my units to match.
You are calculating in metric but your g constant is in feet per second per second.
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.
I'm sorry if this question was asked before, I did search, and I did not find an answer.
My problem is, that I'd like to make movement on all 3 axes with the X and Y rotation of the camera being relevant.
This is what I did:
private static void fly(int addX, int addY){ //parameters are the direction change relative to the current rotation
float angleX = rotation.x + addX; //angle is basically the direction, into which we will be moving(when moving forward this is always the same as our actual rotation, therefore addX and addY would be 0, 0)
float angleY = rotation.y + addY;
float speed = (moveSpeed * 0.0002f) * delta;
float hypotenuse = speed; //the length that is SUPPOSED TO BE moved overall on all 3 axes
/* Y-Z side*/
//Hypotenuse, Adjacent and Opposite side lengths of a triangle on the Y-Z side
//The point where the Hypotenuse and the Adjacent meet is where the player currently is.
//OppYZ is the opposite of this triangle, which is the ammount that should be moved on the Y axis.
//the Adjacent is not used, don't get confused by it. I just put it there, so it looks nicer.
float HypYZ = speed;
float AdjYZ = (float) (HypYZ * Math.cos(Math.toRadians(angleX))); //adjacent is on the Z axis
float OppYZ = (float) (HypYZ * Math.sin(Math.toRadians(angleX))); //opposite is on the Y axis
/* X-Z side*/
//Side lengths of a triangle on the Y-Z side
//The point where the Hypotenuse and the Adjacent meet is where the player currently is.
float HypXZ = speed;
float AdjXZ = (float) (HypXZ * Math.cos(Math.toRadians(angleY))); //on X
float OppXZ = (float) (HypXZ * Math.sin(Math.toRadians(angleY))); //on Z
position.x += AdjXZ;
position.y += OppYZ;
position.z += OppXZ;
}
I only implement this method when moving forwards(parameters: 0, 90) or backwards(params: 180, 270), since movement can't happen on the Y axis while going sideways, since you don't rotate on the Z axis. ( the method for going sideways(strafing) works just fine, so I won't add that.)
the problem is that when I look 90 degrees up or -90 down and then move forward I should be moving only on the Y axis(vertically) but for some reason I also move forwards(which means on the Z axis, as the X axis is the strafing).
I do realize that movement speed this way is not constant. If you have a solution for that, I'd gladly accept it as well.
I think your error lies in the fact that you don't fully project your distance (your quantity of movement hypothenuse) on your horizontal plane and vertical one.
In other words, whatever the chosen direction, what you are doing right now is moving your point of hypothenuse in the horizontal plane X-Z, even though you already move it of a portion of hypothenuse in the vertical direction Y.
What you probably want to do is moving your point of a hypothenuse quantity as a total.
So you have to evaluate how much of the movement takes place in the horizontal plane and how much in the vertical axis. Your direction gives you the answer.
Now, it is not clear to me right now what your 2 angles represent. I highly recommend you to use Tait–Bryan angles in this situation (using only yawn and pitch, since you don't seem to need the rolling - what you call the Z-rotation), to simplify the calculations.
In this configuration, the yawn angle would be apparently similar to your definition of your angleY, while the pitch angle would be the angle between the horizontal plane and your hypothenuse vector (and not the angle of the projection in the plane Y-Z).
A schema to clarify:
With :
s your quantity of movement from your initial position P_0 to P_1 (hypothenuse)
a_y the yawn angle and a_p the pitch one
D_x, D_y, D_z the displacements for each axis (to be added to position, ie AdjXZ, OppYZ and OppXZ)
So if you look at this representation, you can see that your triangle in X-Z doesn't have s as hypotenuse but its projection s_xz. The evaluation of this distance is quite straightforward: if you place yourself in the triangle P_0 P_1 P_1xz, you can see that s_xz = s * cos(a_p). Which gives you:
float HypXZ = speed * Math.cos(Math.toRadians(angleP))); // s_xz
float AdjXZ = (float) (HypXZ * Math.cos(Math.toRadians(angleY))); // D_x
float OppXZ = (float) (HypXZ * Math.sin(Math.toRadians(angleY))); // D_z
As for D_y ie OppYZ, place yourself in the triangle P_0 P_1 P_1xz again, and you'll obtain:
float OppYZ = (float) (speed * Math.sin(Math.toRadians(angleP))); // D_y
Now, if by angleX you actually meant the angle of elevation as I suppose you did, then angleP = angleX and HypXZ = AdjYZ in your code.
With this correction, if angleX = 90 or angleX = -90, then
HypXZ = speed * cos(angleX) = speed * cos(90deg) = speed * 0;
... and thus AdjXZ = 0 and OppXZ = 0. No movement in the horizontal plane.
Note:
To check if your calculations are correct, you can verify if you actually move your point of the wanted quantity of movement (hypothenuse ie speed ie s). Using Pythagorean theorem:
s² = s_xz² + D_z² // Applied in the triangle P_0 P_1 P_1xz
= D_x² + D_y² + D_z² // Applied in the triangle P_0 P_1x P_1xz
With the definitions of the displacements given above:
D_x² + D_y² + D_z²
= (s * cos(a_p) * cos(a_y))² + (s * cos(a_p) * sin(a_y))² + (s * sin(a_p))²
= s² * (cos(a_p)² * cos(a_y)² + cos(a_p)² * sin(a_y)² + sin(a_p)²)
= s² * (cos(a_p)² * (cos(a_y)² + sin(a_y)²) + sin(a_p)²)
= s² * (cos(a_p)² * 1 + sin(a_p)²)
= s² * (cos(a_p)² + sin(a_p)²)
= s² * 1 // Correct
Hope it helped... Bye!
Let's say I have an (x,y) that is always the same for the start point of a line and an (x,y) that changes for the end point of that same line. The line is also always 40px long. At the start of the program the line originates in a vertical orientation (lets call it 0 degrees). Based on a user input I need the line to be redrawn a specific number of degrees from its origin by changing only the end (x,y).
SOME MORE FOOD FOR THOUGHT IF YOU NEED IT:
I'm in a rut trying to calculate this and make it work in Java. I can make the math work to calculate the point based on the arc length of a circle segment, but I don't know how to make Java do it.
I think it would work easier based off a triangle angles since I will always know the length of two sides of a triangle (one side formed by the 40px long line and the other side formed by the start point of that line and the border of the JPanel) and the angle those two lines form. Still, my brain is mush from trying to figure it out. Any help would be much appreciated.
UPDATE:
#casablanca got me on the right track. I brushed up on my trig functions and here is how I made it work.
First off, I didn't realize that 90 degrees was straight up, but once I did realize that I made my solution reflect that fact. I was drawing my line starting at the bottom center of the frame going out. Since the opposite side of the triangle is on the right side of the screen when the angle given by my user is less than 90 degrees and is on the left side of the screen when the angle given by my user is greater than 90 degrees I had to adjust the formula to account for that fact, thus I have four methods, one for the x coordinate on the left side of the screen (when the user given angle is greater than 90 degrees), one for the y coordinate on the left side of the screen (when the user given angle is greater than 90 degrees) and the same thing for the right side of the screen when the user given angle is less than 90 degrees. The int length in all methods is the length of the hypotenuse. Thanks again for your help #casablanca!
public double leftSideX(double angle, int length){
double x = frameWidth/2 - (length * Math.cos(Math.toRadians(90-(Math.toDegrees(angle)-90))));
return x;
}
public double leftSideY(double angle, int length){
double y = frameHeight - (length * Math.sin(Math.toRadians(90-(Math.toDegrees(angle)-90))));
return y;
}
public double rightSideX(double angle, int length){
double x = frameWidth/2 + (length * Math.cos(angle));
return x;
}
public double rightSideY(double angle, int length){
double y = frameHeight - (length * Math.sin(angle));
return y;
}
Is this what you're looking for?
startX = x;
startY = y;
endX = x + 40 * Math.sin(angle);
endY = y + 40 * Math.cos(angle);
And draw a line from (startX, startY) to (endX, endY) in whatever API you're using.
Also note that angle is in radians. If you had it in degrees, you need to convert it first:
angle = angle * Math.PI / 180;