2
\$\begingroup\$

I am implementing a type for Ogre 3D rendering engine to provide spherical coordinates. So far everything is working fine, until I try to build spherical coordinates from a cartesian vector. Here is the code I am trying right now, but it's not working correctly (changing phi and theta result in only half a sphere).

/** @return a relative spherical coordinate from a cartesian vector. */
        static SphereVector from_cartesian( const Ogre::Vector3& cartesian )
        {
            using namespace Ogre;
            SphereVector result;
            result.radius = cartesian.length();
            result.phi    = cartesian.x   > Real(0) || cartesian.x   < Real(0) ? Math::ATan( cartesian.z / cartesian.x )   : Radian( Math::HALF_PI );
            result.theta  = result.radius > Real(0) || result.radius < Real(0) ? Math::ACos( cartesian.y / result.radius ) : Radian( 0 );
            return result;
        }

Here is the version currently in the repository:

static SphereVector from_cartesian( const Ogre::Vector3& cartesian )
                {
                        using namespace Ogre;
                        SphereVector result;
                        result.radius = cartesian.length();
                        result.phi    = cartesian.x   > Real(0) || cartesian.x   < Real(0) ? Math::ATan( cartesian.z / cartesian.x )   : Math::ATan( 0 );
                        result.theta  = result.radius > Real(0) || result.radius < Real(0) ? Math::ACos( cartesian.y / result.radius ) : Math::ACos( 0 );
                        return result;
                }

I have been searching for the right algorithm and I think I'm close but I can't figure out it correctly yet. My maths skills are not so good so I'm having trouble understanding what I'm doing wrong.

Any idea how it should be done?

\$\endgroup\$
3
\$\begingroup\$

phi can be gotten with atan2(x,z).

Then theta will be similarly atan2(hypot(x,z), y).

tan2 is a convenience function that will compute atan(x/y) also take into account the quadrant of where the point is and deals with y == 0 properly and generally goes from -pi to pi.

\$\endgroup\$
  • \$\begingroup\$ this is helpful, however "atan2(hypot(x,z), y)" don't seem to be correct. Did you mean acos? Because it seem to be the right equation. \$\endgroup\$ – Klaim Dec 6 '13 at 16:14
  • \$\begingroup\$ no it is because acos(y / radius)==atan(y/sqrt(radius^2-y^2)) and atan2(y,sqrt(radius^2-y^2)) accounts for quadrant \$\endgroup\$ – ratchet freak Dec 6 '13 at 16:17
  • \$\begingroup\$ My mistake, I assumed that radius was already the value of hypot(x,z). I'll try that right now. \$\endgroup\$ – Klaim Dec 6 '13 at 16:22
  • \$\begingroup\$ It don't seem to work either. I'm not sure where is the problem. \$\endgroup\$ – Klaim Dec 6 '13 at 16:26
  • \$\begingroup\$ It my mistake again, I did atan2( y, hypot(x,z)) instead of atan2( hypot(x,z), y). Now it seems to work almost perfectly. The problem I see is positions around negative Z being avoided (I'm using a moving cursor using the spherical coordinates to check visually). I think the issue might be related to a chain of transformations I'm doing, I'm checking that right now. \$\endgroup\$ – Klaim Dec 6 '13 at 16:39
2
\$\begingroup\$

Your problem is that arc-cos is only defined on specific interval. It has a domain of −1 ≤ x ≤ 1 and a range of 0 ≤ y ≤ π so it's only valid for those numbers.

This is formally defined as the following:

enter image description here

The theta interval of [0,180] is the reason why you are getting half a sphere. So in order to solve this problem you need to determine in which quarter the original vector's z is and shift it by pi accordingly.

\$\endgroup\$
  • \$\begingroup\$ I follow the explaination except the last line which contain the answer. What do you mean by which quarter? I assume that the "pole" is +Y, so what you seem to mean is that when theta is out of valid range I should add nPi, n being either 0.5, 1, 1.5 or 2? Not sure I understand correctly. \$\endgroup\$ – Klaim Dec 6 '13 at 12:16
  • \$\begingroup\$ Ok now I think I get it. I try the following code: SphereVector result = SphereVector::ZERO; result.radius = cartesian.length(); if( result.radius == 0 ) return result; result.phi = Math::ATan2( cartesian.z, cartesian.x ); result.theta = Math::ACos( cartesian.y / result.radius ); return result; It seems to be working except for positions close to negative Z which seems to be buggy, but it might be something else in my code that's wrong. \$\endgroup\$ – Klaim Dec 6 '13 at 16:16
2
\$\begingroup\$

Here's a basic unit circle:

enter image description here

It displays radians, degrees, and in the brackets are the x and y values, (or (cos, sin) of the respective angles). Should be familiar stuff.

Let's look at 120˚. The value of cos(120˚) is -1/2. Now look at cos(240˚). It's also -1/2. Thus, when you calculate arccos(-1/2), you might want the result to be 240˚, but it's going to give you 120˚. See the problem?

It's an easy fix, though. On the unit circle, what separates 120˚ and 240˚ is which side of the unit circle they are on. And the side they are on depends on if y is positive or negative. arccos(-1/2) only calculates the result for the positive side. So when you know it's on the other side, the result can be gotten with 360˚ - arccos(-1/2) = 240˚. Or, as radians, 2*pi - arccos(-1/2) = 4*pi/3.

I don't know how you have your axes set up, so I'm not going to give you the full solution, but this should explain the problem, and get you on the right track.

\$\endgroup\$
  • \$\begingroup\$ It helps understanding the issue, but not fully, because it was not clear to me how to work around the sign and pi thing. I learnt about atan2 which apparently does the work efficiently. \$\endgroup\$ – Klaim Dec 6 '13 at 16:17
  • \$\begingroup\$ Howver, my problem don't seem to be really fixed yet. \$\endgroup\$ – Klaim Dec 6 '13 at 16:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.