Jump to content
  • Advertisement
Sign in to follow this  
csuguy

Calculating Phi

This topic is 3591 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all, Ok, so I'm working on 3D rotations. Thanks to various online resources, I was able to understand these basic formulas: x = cos(phi)*sin(theta) y = sin(phi)*sin(theta) z = cos(theta) And then you times them by rho to scale of course. Anyways, I'm trying to take it a little further. I'm trying to do some rotations without phi and theta - and then calculate the phi and theta again. This is for rotating around the Y axis. Basically - I'm stuck when it comes to calculating phi. Using the above equations, I naturally got: phi = acos( x / sin(theta) ) Providing I'm not making some elementary mistake when reformatting the equation (wouldn't be the first time) that SHOULD calculate phi. HOWEVER, when I plug that into my c++ code - it, well... doesn't work. And I plug it in before I times it by Rho - so that's not the problem. Looking at it again, I thought that it obviously wouldn't work as it would only work well when sin(theta) was ~1 or ~-1. If its a small number, then its going to divide x multiple times - and thus the value would be greater than 1 (or less than -1) and thus acos wouldn't work. If sin(theta) returns 0 - I get a divide by zero error... So - how do I calculate this angle? Is there another equation - if so please describe in detail or provide good resource on the subject. Thank you, Ryan

Share this post


Link to post
Share on other sites
Advertisement
Just think of the sin(theta) as a factor that makes the circumference circle smaller the closer you get to the poles of the sphere. It doesn't actually affect the shape of the circle, just makes it bigger or smaller. So in essence, if you look at it from the top down you still have a circle shape:

x = cos(phi) * arbitrary_scalar
y = sin(phi) * arbitrary_scalar

We know that sin(phi)/cos(phi) = tan(phi), so it follows that phi = atan( sin(phi) / cos(phi) ), where we make substitutions from the above equations to get phi = atan(y / x). You should use the atan2 library function to handle all the edge cases, particular when one or both of the parameters is 0.

To get theta, take a look at the circle from the side. You essentially have a right triangle, where the opposite side is the radius of the circumference circle at theta. The radius of the circle is just √(x2 + y2) -- also derived from the first two equations. The adjacent side of the right triangle is simply z, since that's the height of the circle from the equator. The properties of right triangles tells us that tan(theta) = √(x2 + y2) / z, or theta = atan( √(x2 + y2) / z). Once again, atan2 will be your friend here and handle all the edge cases!

Share this post


Link to post
Share on other sites
Ok - so here's my c++ code for setting phi:

ret.setPhi( atan2f(y,x) );

ret is an instance of my vector class. Anyways - when I use this, it works good for all values where y is positive. Once y hits 0 or negative - it freaks out and moves it to a seemingly random positive spot - where it works fine again. So how do I make this work for the top and bottom of my rotation?

Thanks again,
Ryan

[Edited by - csuguy on September 17, 2008 1:55:14 AM]

Share this post


Link to post
Share on other sites
The atan2 function returns a value between -PI/2 and PI/2, and a negative Y value would make Phi negative. Is it possible your code doesn't handle that case?

Share this post


Link to post
Share on other sites
Here's the code for the function - I'm doing something wrong :/

Vector Vector::convertToVector(float x,float y,float z)
{
Vector ret;

//Calculate Distance from origin(0,0,0) and then divide x,y, and z by it
float mag = Vector::getDistance(x,y,z,0,0,0);
ret.setMagnitude(mag);

x = x/mag;
y = y/mag;
z = z/mag;

ret.setTheta( toDeg(atanf(getDistance(x,y,0,0,0,0),z)));
ret.setPhi( toDeg(atan2f(y,x)) );

return ret;
}

It is being called after this function:

static void axisRotation(float* d1,float* d2, float angle)
{
float r = sqrt((*d1)*(*d1)+(*d2)*(*d2));

float cur_ang = toDeg(acos(*d1/r));
cur_ang += angle;

*d1 = cos(toRad(cur_ang))*r;
*d2 = sin(toRad(cur_ang))*r;
}

I'm passing in the currentt X and Y floats for d1 and d2 respectively. I'm increasing the angle by +/- 1.0f, depending upon whether the user presses up/down.

My idea to do rotations around an axis is to simply isolate the two other dimensions and do 2D rotations with those 2 dimensions while leaving the third one be. The two dimensions would keep the same scale and thus it would remain a unit sphere. This maybe a bad way of doing it - but it SEEMS like it should work. At the very least I should be able to get the basic XY rotation down using this method. I did something wrong somewhere... If you can find it please tell me and explain.

Thank you!
Ryan

Share this post


Link to post
Share on other sites
A few things I noticed (in the comments). I believe your problem is in axisRotation on the acos call:
Vector Vector::convertToVector(float x,float y,float z)
{
/* Is this vector initialized? If not, then setting the magnitude below doesn't make sense */
Vector ret;

//Calculate Distance from origin(0,0,0) and then divide x,y, and z by it
/* The distance from the origin is just the length of the vector [x,y,z], so that should eliminate a subtraction (hopefully the compiler optimizes it anyway but it would be more clear if you just did it here) */
float mag = Vector::getDistance(x,y,z,0,0,0);
ret.setMagnitude(mag);

x = x/mag;
y = y/mag;
z = z/mag;

/* Again, probably more clear just to do sqrt(x*x + y*y) */
ret.setTheta( toDeg(atanf(getDistance(x,y,0,0,0,0),z)));
ret.setPhi( toDeg(atan2f(y,x)) );

return ret;
}

static void axisRotation(float* d1,float* d2, float angle)
{
float r = sqrt((*d1)*(*d1)+(*d2)*(*d2));

/* Here is likely your problem. acos is not a good function to use because it only returns a value in the range [0,PI], which is positive Y. atan2(*d2,*d1) should work a lot better since it disambiguates all quadrants and handles zeros properly */
float cur_ang = toDeg(acos(*d1/r));
cur_ang += angle;

*d1 = cos(toRad(cur_ang))*r;
*d2 = sin(toRad(cur_ang))*r;
}

Share this post


Link to post
Share on other sites
Your convertToVector function looks wrong. I don't quite get the goal of the axisRotation function so I'm not going to really look at it, plus I trust Zipster [smile]. Using the formula

α = atan2(|u×v|, uv)

where |u×v| is the cross product of u and v and uv is the dot product of u and v, and α is the angle between the vectors u and v.

To find φ, the angle between the z-axis and the vector (x, y, z), we simply do the following:

u = (0, 0, 1)
v = (x, y, z)

φ = atan2(|u×v|, uv)

φ = atan2(√(x2 + y2), z)

Of course, this is exactly what Zipster said in his original post. But we can also use this method to find θ, the angle between the x-axis and the vector (x, y, 0). Now we just have to do

u = (1, 0, 0)
v = (x, y, 0)

θ = atan2(|u×v|, uv)

θ = atan2(y, x)

So finally the function convertToVecotr becomes this:

Vector Vector::convertToVector(float x,float y,float z)
{
Vector vector;

vector.setMagnitude(std::sqrt(x * x + y * y + z * z));
vector.setPhi(std::atan2(std::sqrt(x * x + y * y), z));
vector.setTheta(std::atan2(y, x));

return vector;
}


Hopefully I didn't make any mistakes when writing this post... I'm not going to guarantee it's all perfect.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zipster
A few things I noticed (in the comments). I believe your problem is in axisRotation on the acos call:
*** Source Snippet Removed ***


Thank you Zipster - I made some of your recommended changes, particularly with the acos and atan. Can't believe I didn't catch that one myself. Unfortunately that didn't solve the problem :/ - I get some pretty random movements. I'm going to take what you and Mike have said and see if I can't solve it before posting my new code.

Again - thanks for all your help!
Ryan

Share this post


Link to post
Share on other sites
Ah! Lots of math lol. Anyways - I tried plugging in your formula (with a few corrections) but it didn't work :/. If I pass it a point like: (50,0,0) it turns it into (0,0,50)?! lol. I noticed that your equation for setPhi and setTheta were reversed so I switched them - but I just got the same results...

In order to work this out I'm going to take what you and Zipster have said and I'm going to make a sample program to test everything out. No custom classes - just the math.

If I can't get that to work then I'll post my sample code to see if guys can't solve it.

Thanks so much for your help!
Ryan

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!