I am making a simple Asteroids-like game using Java that involves angles, but I'm not sure how I should format the angles and directions of the various objects.
I have three main questions:
Where should 0 radians be? (Should it be pointing upwards, should it point to the right, etc.)
Should turning clockwise increase or decrease the value?
Should the range of the value be from 0 to (2 * pi) or from -pi to pi?
In general, you should always follow the same conventions concerning angles in ANY language, as angles are really more mathematical than computer-related concepts and math is universal (e.g. the angle-related math functions in any reasonable language should, and in my experience do behave the same way).
Therefore, these conventions are not so much unique to java as they are a good set of rules to follow whenever you're dealing with angles:
0 radians is to the right.
increasing the angle means moving counterclockwise, so pi/2 radians is up, pi is left, ect.
The last one is a bit trickier. Mathematically speaking, the range of angles around the unit circle is [0, 2pi], and this how I prefer to use angles. In general, this makes things easier, but some java functions seem to utilize the other approach. The main trigonometric functions sin, cos, and tan can accept any number as a input (for instance sin(pi) == sin(3pi)), just as the domain of these functions in mathematics is All Real Numbers. The range of these functions does not relate to pi. However, the inverse trigonometric functions (asin, acos, and atan, but NOT atan2) have limited ranges that are defined in the javadoc:
the returned angle is in the range -pi/2 through pi/2.
Which is also consistent with mathematics. Therefore:
Most of time, you should use [0, 2pi]. It's easier to read and easier to operate on.
When dealing with the inverse trigonometric functions, keep in mind that their range is [pi/2, -pi/2] and make conversions as necessary.
Related
I'm using Math.sin to calculate trigonometry in Java with 3 decimal precision. However when I calculate values that should result in an Integer I get 1.0000000002 instead of 1.
I have tried using
System.out.printf(Locale.ROOT, "%.3f ", v);
which does solve the problem of 1.000000002 turning into 1.000.
However when I calculate numbers that should result in 0 and instead get -1.8369701987210297E-16 and use
System.out.printf(Locale.ROOT, "%.3f ", v);
prints out -0.000 when I need it to be 0.000.
Any ideas on how to get rid of that negative sign?
Lets start with this:
How do I avoid rounding errors with doubles?
Basically, you can't. They are inherent to numerical calculations using floating point types. Trust me ... or take the time to read this article:
What every Computer Scientist should know about floating-point arithmetic by David Goldberg.
In this case, the other thing that comes into play is that trigonometric functions are implemented by computing a finite number of steps of an infinite series with finite precision (i.e. floating point) arithmetic. The javadoc for the Math class leaves some "wiggle room" on the accuracy of the math functions. It is worth reading the javadocs to understand the expected error bounds.
Finally, if you are computing (for example) sin π/2 you need to consider how accurate your representation of π/2 is.
So what you should really be asking is how to deal with the rounding error that unavoidably happens.
In this case, you are asking is how to make it look like the user of your program as if there isn't any rounding error. There are two approaches to this:
Leave it alone! The rounding errors occur, so we should not lie to the users about it. It is better to educate them. (Honestly, this is high school maths, and even "the pointy haired boss" should understand that arithmetic is inexact.)
Routines like printf do a pretty good job. And the -0.000 displayed in this case is actually a truthful answer. It means that the computed answer rounds to zero to 3 decimal places but is actually negative. This is not actually hard for someone with high school maths to understand. If you explain it.
Lie. Fake it. Put in some special case code to explicitly convert numbers between -0.0005 and zero to exactly zero. The code suggested in a comment
System.out.printf(Locale.ROOT, "%.3f ", Math.round(v * 1000d) / 1000d);
is another way to do the job. But the risk of this is that the lie could be dangerous in some circumstances. On the other hand, you could say that real mistake problem is displaying the numbers to 3 decimal places.
Depends on accuracy you need, you can multiply by X and divide by X where X is X=10^y and y is required floating poing precision.
I'm making a simple 2d Java game similar to goemetry wars. I'm currently programming the player's shooting.
I have a target point specified by the mouse location. Now I want to add a bit of randomization, so that the shooting angle has a random offset. I am currently converting the point to a vector directly. My idea was to convert the vector to an angle, apply the randomization to that angle, and then, convert the angle back to a vector (given the length of the vector).
I can't figure out how to convert the angle back to a vector. I don't need code, it's basically just a question about the maths.
If there's a better way to randomize the shooting, I would love to hear that too! I can!t apply the randomization to the point because there are other things then the mouse that can set it.
Polar coordinate system
As everybody seems to have just answered in the comments, here goes the answer to your question as it is formulated : you need to use the polar coordinate system.
Let's call your angle a, the angle you want to add to it b, so the modified angle is a+b.
In the polar coordinate system, your point is represented by an angle a = Math.atan2(y, x) and a radius r = sqrt(x*x + y*y). If you just use the angle, you loose some information, which is at which distance the mouse is from your (0,0) point.
Converting back from your polar representation (after the angle has been modified) is now possible : x = r * Math.cos(a+b), y = r * Math.sin(a+b)
Without computing the angle
By using some cool trigonometry formulas, we can do better. We don't need to go to an angle and come back to a vector, we can modify the x and y values of the vector directly, still changing the angle like you want.
Remember that we want to find x'=r cos(a+b) and y'=r sin(a+b). We will obviously the following formulas :
Now let us multiply by r on both sides to get what whe want.
We now recognize x=r cos(a) and y=r sin(a), so we have the final expression :
If you come to think of it, for small values of b (which is what you want), sin(b) is close to 0, and cos(b) is close to 1, so the perturbation is small.
Not only do you reduce the number of trigonometry operations from 3 to 2, but you also have a cos and a sin of small values, which is nice if they are computed from Taylor series, makes convergence very fast. But that's implementation dependent.
NB: An alternative (and more elegant?) way to find the same result (and exact same formulas) is to use a rotation matrix.
I can haz cod
Whoopsie, you said you didn't want code. Well let's do it like this : I don't post it here, but if you want to compare with how I'd do it once you're done coding your implementation, you can see mine in action here : http://ideone.com/zRV4lL
Android uses the RectF structure for drawing bitmaps. I am working on my game structure, so for example, a Sprite will have and x/y coordinate, width, height, speed, and so on. This means every time in my render loop I have to cast those integers to floats when figuring out the source/target RectF's to use... alternatively, I can be far more universal and use floats everywhere so that when it comes time to simulate the physics and render, all of the types are already of the same type... even if it is unnecessary for what the property is (I don't need a float for "x position", but will have to cast it when rendering if not).
if floats generally are 2-10x's more ineffient (per http://developer.android.com/guide/practices/performance.html), what is the proper course to take?
TLDL: Should I cast int's to float on render, or just have all of the contributing variables be floats to begin with, even if floats are inefficient? How inefficient is a typecast?
The best way to do this is to do your calculations with the highest degree of precision required to produce the expected results within the specified tolerance. That means if you need to use doubles to do calculations and get the expected results with consistency, then use them. Figure out where less precision is acceptable, and only do the operations that require it with floats. Remember, your world doesn't have to approximate earth gravity for falling objects, you could simplify things and make the gravity 10 instead of 9.81, make pixels correspond to even units, etc. It'll also help if you define your constants in the same units and avoid doing unit conversion to do math (as this results in extra ops), it's better to add more final constants that have something like gravity in cm/s, m/s and km/h than it is to only have one and to convert it a thousand times. Oh and the cost of casting int to float isn't very high compared multiplying 2 floats, so think about that.
I'll also note that FPUs are becoming more and more common in modern android phones, and so the issue of using floating point math is becoming a little less important (although not entirely).
The other thing I want to note is Double/Float/Integer vs. double/float/int. The former require new objects to be created to use and shouldn't be used for math (whenever possible), whereas the latter are primitives and do not result in new object creation to use, they're less costly to create.
I have an arbitrary function or inequality (consisting of a number of trigonometrical, logarithmical, exponential, and arithmetic terms) that takes several arguments and I want to get its range knowing the domains of all the arguments. Are there any Java libraries that can help to solve a problem? What are the best practices to do that? Am I right that for an arbitrary function the only thing can be done is a brute-force approximation? Also, I'm interested in functions that can build intersections and complements for given domains.
Upd. The functions are entered by the user so the complexity cannot be predicted. However, if the library will treat at least simple cases (1-2 variables, 1-2 terms) it will be OK. I suggest the functions will mostly define the intervals and contain at most 2 independent variables. For instance, definitions like
y > (x+3), x ∈ [-7;8]
y <= 2x, x ∈ [-∞; ∞]
y = x, x ∈ {1,2,3}
will be treated in 99% of cases and covering them will be enough for now.
Well, maybe it's faster to write a simple brute-force for treating such cases. Probably it will be satisfactory for my case but if there are better options I would like to learn them.
Notational remark: I assume you want to find the range of the function, i.e. the set of values that the function can take.
I think this problem is not simple. I don't think that "brute force" is a solution at all, what does "brute force" even mean when we have continuous intervals (i.e infinitely many points!).
However, there might be some special cases where this is actually possible. For example, when you take a sin(F(x)) function, you know that its range is [-1,1], regardless of the inner function F(x) or when you take Exp(x) you know the range is (0,+inf).
You could try constructing a syntax tree with information about the ranges associated to each node. Then you could try going bottom-up through the tree to try to compute the information about the actual intervals in which the function values lie.
For example, for the function Sin(x)+Exp(x) and x in (-inf, +inf) you would get a tree
+ range: [left range] union [right range]
/ \
sin exp range [-1, 1] , range: (0,+inf)
| |
x x
so here the result would be [-1, 1] union (0, +inf) = [-1, +inf).
Of course there are many problems with this approach, for example the operation on ranges for + is not always union. Say you have two functions F(x) = Sin(x) and G(x) = 1-Sin(x). Both have ranges [-1,1], but their sum collapses to {1}. You need to detect and take care of such behaviour, otherwise you will get only an upper bound on the possible range (So sort of codomain).
If you provide more examples, maybe someone can propose a better solution, I guess a lot depends on the details of the functions.
#High Performance Mark: I had a look at JAS and it seems that its main purpose is to deal with multivariate polynomial rings, but the question mentioned trigonometric, logarithmic and other transcendental functions so pure polynomial arithmetic will not be sufficient.
Here's another approach and depending on how crazy your function can be (see EDIT) it might give you the universal solution to your problem.
Compose the final expression, which might be rather complex.
After that use numerical methods to find minimum and maximum of the function - this should give you the resulting range.
EDIT: Only in the case that your final expression is not continuous the above would not work and you would have to divide into continuous sections for each you would have to find min and max. At the end you would have to union those.
I would have thought that this is a natural problem to tackle with a Computer Algebra System. I googled around and JAS seems to be the most-cited Java CAS.
If I had to confine myelf to numeric approaches, then I'd probably tackle it with some variety of interval computations. So: the codomain of sin is [-1,1], the codomain of exp is (0,+Inf), and the codomain of exp(sin(expression)) is ...
over to you, this is where I'd reach for Mathematica (which is callable from Java).
What is the opposite of the Math.tan(double x) function of java?
I know that Tan(X) = oppositeSideLength/AdjacentSideLength
but I have the opposite and adjacent sides so I want to do the opposite operation.
ie: x = Tan^-1(oppositeSideLenght/AdjacentSideLength) (that is how I would enter it in a calculator.
I just looked in the Math class and I know that there is:
Math.atan(
Math.atan2
but I don't think that either of these is what I am looking for.
Yes, you do indeed want atan, or sometimes, atan2. The difference between the two is that atan will fail under some circumstances when one of the side lengths are zero. While that may be unlikely for triangles, it is a possibility for some other, more general uses of atan. In addition, the atan function gives you an angle limited to the interval [-pi/2,pi/2]. So if you think about the atan function as a function of two inputs, (x,y), atan(y/x) will yield the same result as atan((-y)/(-x)). This is a serious flaw in some circumstances.
To solve these problems, the atan2 is defined such that it yields the correct result for all values of x and y, in any quadrant. One would use it as
atan2(oppositesidelength,adjacentsidelength)
to yield a consistent result.
Of course, for use in a non-degenerate triangle, the simple call to atan(opposite/adjacent) should be entirely adequate for your purposes.
Yep, you want atan, the arc tangent. This is the inverse of tangent; same thing with sin and arc sine, cosine and arc cosine, etc. These are alternative mathematical terminology for these functions' inverses.
Notice how atan returns angles from -π/2 to π/2, by the way. That's a hint that it's an inverse function (tangent takes angles and spits out ratios, arc tangent takes ratios and spits out angles). It is also important to recognize the restricted range. You won't necessarily get back your original angle, since tangents repeat every π radians (every 180°) — tan(π) = 0, but atan(0) = 0, not π.
Math.atan and Math.atan2 should work just fine.
angle = Math.atan(Math.Tan(radians));
angle = Math.atan(oppositeSideLength/adjacentSideLength)
angle = Math.atan2(oppositeSideLength, adjacentSideLength)
Math.atan is indeed the opposite of tangent. It's called arctangent.
Basically x = arctan(tan(x)). Well, it's a tad more complex than that seeing that tangent is a repetitive function (it needs some adjustments by adding k*pi). You should check out the wikipedia article about the details.
Anyway, you can indeed compute x (the angle) by doing Math.atan(opposite/adjacent). Take note though that the angle will be in radians, so make sure you convert to other units if that's not what your using.