I have a function set to return a Value being used in a Quaternin Rotation. Howver when I have a reslt less than 1 or very close to 360 I recieve a result of -nan(ind). Is there a way to return a conclusive result betwen 1 & 360 to avoid any errors in calculaon. Thanks
const float ReturnAngle() const
{
return (acosf(angle) * 180.0f * 2) / PI;
}
Update:
These are some results as to wat I receive when checking the value after applying a transformaion rotation to my quatrnion.
angle: 359.598
angle: -nan(ind)
angle: 357.843
angle: -nan(ind)
angle: -nan(ind)
angle: 0.798189
angle: 2.16383
angle: 1.75475
angle: -nan(ind)
The value that you are passing to acosf is outside the allowed range -1 to 1. Review the code that sets the value for angle - perhaps you have a bug there. If it is just a rounding error and angle is outside the allowed range by a small value, "clamping" it to the valid range may be a good solution.
const float ReturnAngle() const
{
if (angle < -1f) angle = -1f;
if (angle > +1f) angle = +1f;
return (acosf(angle) * 180.0f * 2) / PI;
}
Related
I would like to make a table which can show sin,cos,tan,cot values of chosen degrees. As i said, i couldn't take the right answer of tan90 . I don't know how to change it to 'INFINITY'. I am waiting for your help ! :)
#include<math.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
main()
{
double table[10][4];
int i=0,j=0;
double val=0,PI=3.14159;
val = PI / 180.0;
printf("\tİstenen Tablo\n\t-------------\n");
printf("\t ACI\t\t\t SIN\t\t\t COS\t\t\t TAN\t\t\t COT\n\t------\t\t\t------\t\t\t------\t\t\t---
---\t\t\t------");
for(i=0;i<=90;i+=10)
{
for(j=0;j<5;j++)
{
switch (j)
{
case 0:table[i/10][j] = i; break;
case 1:table[i/10][j] = sin(i*val);break;
case 2:table[i/10][j] = cos(i*val);break;
case 3:table[i/10][j] = tan(i*val);break;
case 4:table[i/10][j] = 1/tan(i*val);break;
}
}
printf("\n\t %lf \t\t %lf\t\t %lf\t\t %lf\t\t %lf\n",table[i/10][0],table[i/10]
[1],table[i/10][2],table[i/10][3],table[i/10][4]);
}
return 0;
}
The reason you're getting incorrect values is that π is not 3.14159, that's just an approximation to its real value. In fact, any non-infinite series of decimal digits will be an approximation but, the more digits you have, the closer i * val will be to the correct value you should be passing to your trigonometric calls.
A far better value would be M_PI from math.h, which has far more precision than what you currently have.
You still may not get your expected values due to the limited precision of even the double type. If that happens, you will need to adjust what you get before working things out in floating point.
For example, the following functions could be used to get more acceptable values, forcing very specific values on quadrant boundaries so as to avoid even the smallest possibility of imprecision in those results:
double mySin(int degrees) {
int mod360 = degrees % 360;
if (mod360 == 0) return 0.0;
if (mod360 == 90) return 1.0;
if (mod360 == 180) return 0.0;
if (mod360 == 270) return -1.0;
return sin(degrees % 360 * M_PI / 180.0);
}
double myCos(int degrees) {
return mySin(degrees + 90);
}
double myTan(int degrees) {
int mod360 = degrees % 360;
if (mod360 == 0) return 0.0;
if (mod360 == 90) return 1.0 / 0.0;
if (mod360 == 180) return 0.0;
if (mod360 == 270) return -1.0 / 0.0;
return tan(mod360 * M_PI / 180.0);
}
double myCot(int degrees) {
// Now that tan() works properly for the quadrant
// boundaries, just use normal formula.
return 1.0 / myTan(degrees);
}
These are built from the ground up to use degree inputs rather than radians and, because you trap the infinite value cases before doing any floating point math (which is inherently imprecise when you're dealing with transcendentals), you can give the correct results for those cases.
A complete program, integrating these functions and getting rid of stuff you don't need is shown below. There's no point storing all those values in an array and then printing out the array (with no other use of it) when you can just print the values immediately:
#include <cmath>
#include <cstdio>
using namespace std;
double mySin(int degrees) {
int mod360 = degrees % 360;
if (mod360 == 0) return 0.0;
if (mod360 == 90) return 1.0;
if (mod360 == 180) return 0.0;
if (mod360 == 270) return -1.0;
return sin(mod360 * M_PI / 180.0);
}
double myCos(int degrees) {
return mySin(degrees + 90);
}
double myTan(int degrees) {
int mod360 = degrees % 360;
if (mod360 == 0) return 0.0;
if (mod360 == 90) return 1.0 / 0.0;
if (mod360 == 180) return 0.0;
if (mod360 == 270) return -1.0 / 0.0;
return tan(mod360 * M_PI / 180.0);
}
double myCot(int degrees) {
// Now that tan() works properly for the quadrant
// boundaries, just use normal formula.
return 1.0 / myTan(degrees);
}
int main()
{
printf("İstenen Tablo\n");
printf("-------------\n");
printf("\t ACI \t SIN \t COS \t TAN \t COT\n");
printf("\t------------\t-----------\t-----------\t-----------\t-----------\n");
for (int i = 0; i < 360; i += 10) {
printf("\t%12d\t%9.9lf\t%9.9lf\t%9.9lf\t%9.9lf\n",
i, mySin(i), myCos(i), myTan(i), myCot(i));
}
return 0;
}
The output of that is:
İstenen Tablo
-------------
ACI SIN COS TAN COT
------------ ----------- ----------- ----------- -----------
0 0.000000000 1.000000000 0.000000000 inf
10 0.173648178 0.984807753 0.176326981 5.671281820
20 0.342020143 0.939692621 0.363970234 2.747477419
30 0.500000000 0.866025404 0.577350269 1.732050808
40 0.642787610 0.766044443 0.839099631 1.191753593
50 0.766044443 0.642787610 1.191753593 0.839099631
60 0.866025404 0.500000000 1.732050808 0.577350269
70 0.939692621 0.342020143 2.747477419 0.363970234
80 0.984807753 0.173648178 5.671281820 0.176326981
90 1.000000000 0.000000000 inf 0.000000000
100 0.984807753 -0.173648178 -5.671281820 -0.176326981
110 0.939692621 -0.342020143 -2.747477419 -0.363970234
120 0.866025404 -0.500000000 -1.732050808 -0.577350269
130 0.766044443 -0.642787610 -1.191753593 -0.839099631
140 0.642787610 -0.766044443 -0.839099631 -1.191753593
150 0.500000000 -0.866025404 -0.577350269 -1.732050808
160 0.342020143 -0.939692621 -0.363970234 -2.747477419
170 0.173648178 -0.984807753 -0.176326981 -5.671281820
180 0.000000000 -1.000000000 0.000000000 inf
190 -0.173648178 -0.984807753 0.176326981 5.671281820
200 -0.342020143 -0.939692621 0.363970234 2.747477419
210 -0.500000000 -0.866025404 0.577350269 1.732050808
220 -0.642787610 -0.766044443 0.839099631 1.191753593
230 -0.766044443 -0.642787610 1.191753593 0.839099631
240 -0.866025404 -0.500000000 1.732050808 0.577350269
250 -0.939692621 -0.342020143 2.747477419 0.363970234
260 -0.984807753 -0.173648178 5.671281820 0.176326981
270 -1.000000000 0.000000000 -inf -0.000000000
280 -0.984807753 0.173648178 -5.671281820 -0.176326981
290 -0.939692621 0.342020143 -2.747477419 -0.363970234
300 -0.866025404 0.500000000 -1.732050808 -0.577350269
310 -0.766044443 0.642787610 -1.191753593 -0.839099631
320 -0.642787610 0.766044443 -0.839099631 -1.191753593
330 -0.500000000 0.866025404 -0.577350269 -1.732050808
340 -0.342020143 0.939692621 -0.363970234 -2.747477419
350 -0.173648178 0.984807753 -0.176326981 -5.671281820
Note the disparity between signs on the infinities and forced zeros. While both 0 / 1 and 0 / -1 can be treated as zero (there are no negative zeros despite the fact IEEE754 allows them), the values for 1 / 0 and -1 / 0 are given +inf and -inf respectively.
My more advanced mathematical buddies may disagree but I think I've got that right.
tan(x) is extremely sensitive to even tiny variations in the argument around odd multiples of pi/2 because its derivative 1/cos^2(x) is also unbounded around those points. This, along with the limited precision of the computer floating points, cause significant errors when evaluating tan around odd multiples of pi/2.
The better strategy is to first reduce the angle to the first octant [0, 45) (degrees) then use basic trig identities to derive the target value. This will provide better precision overall, and will also give an actual inf value at 90 degrees since 0 is an exactly representable number, and tan(0) = 0.
An example of such an implementation is below.
#include <math.h>
const double deg2rad = M_PI / 180.0;
double tan_degrees(int n)
{
bool neg = false, inv = false;
if(n < 0) { n = -n; neg = true; } // tan(-x) = -tan(x)
n %= 180; // tan(x + pi) = tan(x)
if(n > 90) { n = 180 - n; neg = !neg; } // tan(pi - x) = -tan(x)
if(n > 45) { n = 90 - n; inv = true; } // tan(pi/2 - x) = 1 / tan(x)
double val = tan(n * deg2rad);
if(neg) val = -val;
if(inv) val = 1. / val;
return val;
}
Common C implementations use the IEEE-754 “double” format (also called binary64) for their double type. The closest value to π/2 representable in this format is 884279719003555 / 562949953421312, which is 1.5707963267948965579989817342720925807952880859375. It differs from π/2 by about 6.12323399573676588613033•10−17. Its tangent is about 1.633123935319536975•1016. (The closest representable value to its tangent is 1.633123935319537•1016.) So the best result you can get by converting 90 degrees to radians and applying tan to the result is 1.633123935319536975•1016.
Because of this, it is not possible to get ∞ from tan(x). All numbers representable in this double format are rational, so none are exactly multiples of π/2, so none have tangents that are infinity.
(There are some numbers representable in the format that are closer to an odd multiple of π/2 than the value above. This is due to the irregularity of the irrational period of π with respect to the representable numbers. However, from memory, none of those numbers are sufficiently close to an odd multiple of π/2 that their tangent is outside the finite range of this double format, which would cause the tan function to overflow to an infinity.)
You might check whether the math library you are using provides a tanpi or tanPi function. This and related functions are recommended by IEEE 754-2008. Apple supplies tanPi with the name __tanPi. __tanPi(x) returns tan(πx), subject to calculation errors. __tanPi(.5) returns ∞.
I am trying to develop an alghoritm for controlling the rudder of the boat...
I got lost in geo algoritms...
Function Does not work corectly.
direction WhereToMove(double CurrentLatitude, double CurrentLongitude, double TargetLatitude, double TargetLongitude, double azimuth) {
double azimuthHysteresis = 5; //straight if the deviation is less than 5 degrees
double pi = 2 * asin(1.0);
double target = atan2(TargetLatitude - CurrentLatitude, TargetLongitude - CurrentLongitude) * 180 / pi;
double delta = azimuth - target;
if (delta > 180) delta -= 360;
if (delta < -180) delta += 360;
if (delta < -2) {
return right;
}
if (delta > 2) {
return left;
}
return straight; // go straight
}
A few points:
You could use the constant M_PI for pi
I would imagine you want your angles to be measured clockwise from north. atan2 gives an angle counter clockwise from the x axis. This is simple to fix, use
atan2( dLon, dLat)
instead of
atan2( dLat, dLon)
The distance represented by a degree of longitude is, roughly, cos(lat) times the distance represented by a degree of latitude. So you should scale your dlon in the above by cos( M_PI/180.0 * lat). (cos, like all the math functions that deal in angles, takes radians as an argument).
You could simplify computing the difference of azimuth and target by using the math library function remainder, as in
delta = remainder( azimuth-target, 360.0)
this will give a delta between -180 and 180
I don't know if your code will ever be used near 180E. I'd say you should compute the difference in longitudes as if it might, ie use
remainder( TargetLongitude - CurrentLongitude, 360.0)
instead of
TargetLongitude - CurrentLongitude
This might seem OTT, but I've found (the hard way) that its much easier to get into the habit of always computing the difference of longitudes this way than to track down everywhere in the code that such differences are taken when your code is used across 180E.
Final version
direction WhereToMove(double CurrentLatitude, double CurrentLongitude, double TargetLatitude, double TargetLongitude, double azimuth) {
double azimuthHysteresis = 2; //straight if the deviation is less than 2 degrees
double target = atan2(remainder(TargetLongitude - CurrentLongitude, 360.0), remainder(TargetLatitude - CurrentLatitude, 360.0)) * 180 / M_PI;
double delta = remainder(azimuth - target, 360.0);
double deltaModule = sqrt(delta * delta);
if (deltaModule <= azimuthHysteresis) //deviation is small go straight
{
return straight;
}
if (delta <= -azimuthHysteresis) {
return right;
}
if (delta >= azimuthHysteresis) {
return left;
}
return straight; // go straight
}
For a game I'm making, I want 3 ships which will all race around the map following a collection of points. It works perfectly fine, except for one point in the map, where the ships decide to rotate almost 360 degrees counter clockwise even though only 10 degrees clockwise should be enough.
The code for calculating the rotation:
vec2 distance = *desiredPosition - position;
float rot = atan2(distance.y, distance.x);
rot = rot * 180.f / PI + 90.f;
if (rot < angle)
{
angle -= dAngle;
boat->RotateImage(-dAngle);
}
if (rot > angle)
{
angle += dAngle;
boat->RotateImage(dAngle);
}
velocity += vec2(acceleration * cos((angle - 90) * PI / 180.0), acceleration * sin((angle - 90) * PI / 180.0));
How do I ensure it won't rotate in the wrong direction there?
Thanks to Richard Byron (accepted answer below), the problem is fixed. Taking the dot product is better than using degrees.
The final code:
vec2 distance = desiredPosition - position;
normal = vec2(sin((angle - 90) * PI / 180.0), cos((angle - 90) * PI / 180.0) * -1);
float dir = normal.x * distance.x + normal.y * distance.y;
//turn
if (dir > 0)
{
angle -= dAngle;
boat->RotateImage(-dAngle);
}
if (dir < 0)
{
angle += dAngle;
boat->RotateImage(dAngle);
}
velocity += vec2(acceleration * cos((angle - 90) * PI / 180.0), acceleration * sin((angle - 90) * PI / 180.0));
The angle the boat turns should be less than 180 degrees either CW or CCW. If it turns more than 180 degrees in one direction it would have been better to turn the other way.
A more general solution would be calculate the distance vector with respect to the boat's frame of reference.
There are a couple of problems with your updated code. Firstly, it should be rot2 = 360 - rot1; (rot1 + 360 is exactly the same angle as rot1).
The second issue is that you are not taking into account that 1 and 359 degrees are almost the same angle. So if abs(rot1 - angle) > 180, then you really want to use 360 - abs(rot1 - angle) in that case. Your later comparisons with rot and angle are a problem for the same reason, and you need to handle angle incrementing above 360 and decrementing below 0.
I could write out code for this, but there's actually a much simpler and faster way to do this. If you take the dot product of the vector (desiredPosition - position) and a vector at right angles to the ships current heading, then you can turn based on the sign of that result. If it's not clear how to do this, let me know and I can expand on it in the comments.
I have a weapon that bounces to the next enemy when it hits.
I first begin by calculating the delta and the getting the angle:
float deltaX = e->m_body->GetPosition().x - m_body->GetPosition().x;
float deltaY = e->m_body->GetPosition().y - m_body->GetPosition().y;
float angle = atan2((deltaY), deltaX) * 180 / M_PI;
Then I convert the angle to a vector and multiply it by 15 (the speed of the projectile):
b2Vec2 vec = b2Vec2(cos(angle*M_PI/180),sin(angle*M_PI/180));
vec *= 15.0f;
Finally, I apply the impulse to the body:
m_body->ApplyLinearImpulse(vec, m_body->GetPosition());
The problem is that the vector must be incorrect as the bullet does not go in the right direction. If I simply output the angle to the next enemy, it tends to output an angle that looks correct so the problem must be in the conversion to a vector.
I don't think you need to use any trigonometry functions here, because you already have the direction:
b2Vec2 direction = e->m_body->GetPosition() - m_body->GetPosition();
direction.Normalize(); // this vector now has length 1
float speed = ...;
m_body->ApplyLinearImpulse( speed * direction, m_body->GetWorldCenter() );
Is there a way to safety and simply deal with angle wrap with the minimum number of case statements.
Angle wrap occurs when using a particular representation for angle (either 0-360 deg or -180 - 180 deg (or equivalent in radians)) and you wrap over the angle. For example say you have an angle of -170, and you subtract 50 deg. You mathematically add up to -220 but should actually be +140 deg.
Obviously you can check for this using:
if (deg < -180) { 180 - abs(deg + 180); }
or similar. But firstly you need multitudes of checks and secondly it doesn't work if you wrap twice.
The second case where this is prevalent is in the interpolation between two angles.
For Example, say I have an angle of -170 deg and 160 deg and I want halfway in between them. A common way to do this is ang1 + 0.5(ang2-ang1) but in the example i have provided it will cause the angle to be -5 deg when it should be 175.
Is there a common way to handle angle wrap in these scenarios?
For completeness I'll include both [0, 360) and [-180, 180) normalizations.
You will need #include <math.h>.
Normalize to [0,360):
double constrainAngle(double x){
x = fmod(x,360);
if (x < 0)
x += 360;
return x;
}
Normalize to [-180,180):
double constrainAngle(double x){
x = fmod(x + 180,360);
if (x < 0)
x += 360;
return x - 180;
}
The pattern should be easy enough to recognize to generalize to radians.
Angle Bisection:
double angleDiff(double a,double b){
double dif = fmod(b - a + 180,360);
if (dif < 0)
dif += 360;
return dif - 180;
}
double bisectAngle(double a,double b){
return constrainAngle(a + angleDiff(a,b) * 0.5);
}
This should bisect an angle on the "smaller" side. (warning: not fully tested)
I find using remainder() from the math library is convenient. Given an angle a, to constrain it to -180, 180 you can just do:
remainder(a, 360.0);
and change the 360.0 to 2.0 * M_PI for radians
Normalise an angle to range [-180, 180)
deg -= 360. * std::floor((deg + 180.) * (1. / 360.));
Normalise an angle to range [0, 360)
deg -= 360. * std::floor(deg * (1. / 360.));
Examples:
deg = -90 -> [0, 360):
deg -= 360. * std::floor(-90 / 360.);
deg -= 360. * -1;
deg = 270
deg = 270 -> [-180, 180):
deg -= 360. * std::floor((deg + 180.) / 360.);
deg -= 360. * std::floor(480. / 360.);
deg -= 360. * 1.;
deg = -90;
See: http://en.cppreference.com/w/cpp/numeric/math/floor
So if figured out a way to effectively do what i want using Mystical's approach to constraining the Angle. Here it is:
This seems to work with any example i can think of.
I know this is an old thread, but try this on:
double angleRef(double thtIn, double thtRef){
tht = fmod(thtIn + (180-thtRef),360) + (thtRef-180);
return tht;
}
So as in your example, if A=-170 and B=160, then the angle halfway between them is
A + 0.5*(angleRef(B,A) - A) = -185
or if you prefer A=160 and B=-170
A + 0.5*(angleRef(B,A) - A) = 175
Please forgive any c++ format errors, it is not my native language.
auto new_angle = atan2(sin(old_angle), cos(old_angle));
Map angle(+PI ~ -PI) to signed int value (or short value):
angle_signed_short = angle_float / PI * 0x7FFFF;
Then you can add or sub value as normal. Then map back:
angle_float = angle_signed_short * PI / 0x7FFFF;