views:

464

answers:

3

There is a special way of mapping a cube to a sphere described here: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html

It is not your basic "normalize the point and you're done" approach and gives a much more evenly spaced mapping.

I've tried to do the inverse of the mapping going from sphere coords to cube coords and have been unable to come up the working equations. It's a rather complex system of equations with lots of square roots.

Any math geniuses want to take a crack at it?

Here's the equations in c++ code:

sx = x * sqrtf(1.0f - y * y * 0.5f - z * z * 0.5f + y * y * z * z / 3.0f);

sy = y * sqrtf(1.0f - z * z * 0.5f - x * x * 0.5f + z * z * x * x / 3.0f);

sz = z * sqrtf(1.0f - x * x * 0.5f - y * y * 0.5f + x * x * y * y / 3.0f);

sx,sy,sz are the sphere coords and x,y,z are the cube coords.

+4  A: 

After some rearranging you can get the "nice" forms

(1)   1/2 z^2 = (alpha) / ( y^2 - x^2) + 1
(2)   1/2 y^2 = (beta)  / ( z^2 - x^2) + 1
(3)   1/2 x^2 = (gamma) / ( y^2 - z^2) + 1

where alpha = sx^2-sy^2 , beta = sx^2 - sz^2 and gamma = sz^2 - sy^2. Verify this yourself.

Now I neither have the motivation nor the time but from this point on its pretty straightforward to solve:

  1. Substitute (1) into (2). Rearrange (2) until you get a polynomial (root) equation of the form

    (4)    a(x) * y^4  + b(x) * y^2 + c(x) = 0
    

    this can be solved using the quadratic formula for y^2. Note that a(x),b(x),c(x) are some functions of x. The quadratic formula yields 2 roots for (4) which you will have to keep in mind.

  2. Using (1),(2),(4) figure out an expression for z^2 in terms of only x^2.

  3. Using (3) write out a polynomial root equation of the form:

    (5)    a * x^4  + b * x^2 + c = 0
    

    where a,b,c are not functions but constants. Solve this using the quadratic formula. In total you will have 2*2=4 possible solutions for x^2,y^2,z^2 pair meaning you will have 4*2=8 total solutions for possible x,y,z pairs satisfying these equations. Check conditions on each x,y,z pair and (hopefully) eliminate all but one (otherwise an inverse mapping does not exist.)

Good luck.

PS. It very well may be that the inverse mapping does not exist, think about the geometry: the sphere has surface area 4*pi*r^2 while the cube has surface area 6*d^2=6*(2r)^2=24r^2 so intuitively you have many more points on the cube that get mapped to the sphere. This means a many to one mapping, and any such mapping is not injective and hence is not bijective (i.e. the mapping has no inverse.) Sorry but I think you are out of luck.

----- edit --------------

if you follow the advice from MO, setting z=1 means you are looking at the solid square in the plane z=1.

Use your first two equations to solve for x,y, wolfram alpha gives the result:

x = (sqrt(6) s^2 sqrt(1/2 (sqrt((2 s^2-2 t^2-3)^2-24 t^2)+2 s^2-2 t^2-3)+3)-sqrt(6) t^2 sqrt(1/2 (sqrt((2 s^2-2 t^2-3)^2-24 t^2)+2 s^2-2 t^2-3)+3)-sqrt(3/2) sqrt((2 s^2-2 t^2-3)^2-24 t^2) sqrt(1/2 (sqrt((2 s^2-2 t^2-3)^2-24 t^2)+2 s^2-2 t^2-3)+3)+3 sqrt(3/2) sqrt(1/2 (sqrt((2 s^2-2 t^2-3)^2-24 t^2)+2 s^2-2 t^2-3)+3))/(6 s)

and

y = sqrt(-sqrt((2 s^2-2 t^2-3)^2-24 t^2)-2 s^2+2 t^2+3)/sqrt(2)

where above I use s=sx and t=sy, and I will use u=sz. Then you can use the third equation you have for u=sz. That is lets say that you want to map the top part of the sphere to the cube. Then for any 0 <= s,t <= 1 (where s,t are in the sphere's coordinate frame ) then the tuple (s,t,u) maps to (x,y,1) (here x,y are in the cubes coordinate frame.) The only thing left is for you to figure out what u is. You can figure this out by using s,t to solve for x,y then using x,y to solve for u.

Note that this will only map the top part of the cube to only the top plane of the cube z=1. You will have to do this for all 6 sides (x=1, y=1, z=0 ... etc ). I suggest using wolfram alpha to solve the resulting equations you get for each sub-case, because they will be as ugly or uglier as those above.

ldog
I don't know if the inverse mapping exists or not, but the idea that there are more points on the surface of a sphere than there are on the surface of a cube is bizarrely incorrect.
High Performance Mark
Thanks for the input gmatt - when I tried entering these equations into wolframalpha.com it did return several solutions (incorrect it seemed) and your answer gives me hope!
@HPM: not sure you understood me correctly, in the response I never say sphere surface has more points, I say the opposite. I think OP wants to find the mapping from sphere to cube.
ldog
@petrorocket: I put some more though into solving this in the morning today. I realized there may be a snag in step 2. The solution to eq (4) will give you y^2 in an "ugly" form with a square root. Some miracle would have to happen to get rid of that square root haha.
ldog
@gmatt: The very notion of more or fewer points doesn't work with infinites. Consider that there exist bijections between the integers and the rationals. With uncountable infinites, like the points in a surface area, the situation is even trickier. e.g., f(x) = 2x gives you a bijection between [0, 1] and [0, 2], even though the intervals have different lengths.
Rob Lachlan
@gmatt: I verified your initial equations and have been trying to work out the polynomial equation in step 1 but haven't gotten it in the form you suggest yet. Say, even if there are several possible solutions for the inverse mapping, shouldn't I be able to intuitively rule out the undesirable ones?
one of the math gurus at mathoverflow mentioned this approach: "As for the inverse, it's just a mess of radicals. Fix z=1 and solve for x and y squared if you insist on such a formula" – Leonid Kovalev
@Rob Lachlan: indeed I am not unaware of arguments such as those (my undergrad was math :) ). I was trying to construct an intuitive argument, but you rightly shot it down. Very well may be a bijection, something along the lines of the banach paradox http://en.wikipedia.org/wiki/Banach%E2%80%93Tarski_paradox
ldog
@petrocket: if you fix z = 1 then solving for x and y squared becomes much easier. what it means is that you are calulating the inverse mapping of the level set (x,y,1) for x,y reals and z = 1, that is to say (more concretely) a slice of your sphere where it intersects the plane z = 1 (i.e. a circle.) So then if you want to construct the full inverse you may be able to look to see what happnes as you move z around (instead of z=1, let z be any number between 0 and 1.) That may be an excellent starting point for solving for the inverse.
ldog
@petrocket: Disregard my comment about the intuitive argument why the inverse may not exist. As Rob pointed out its moot.As for the polynomial in step 1 plug in the `z^2` in (2). The simplify the fraction you get, then multiply by sides of the equation so you do not have fractions, then group the `y` terms, you should get only `y^4` `y^2` as your only `y` terms.If i have some time I will look into this problem more deeply but for now skiing is calling me:)
ldog
@petrocket: also pertaining your question about if there exists more than one viable solution, the problem that arises is that then you can not define a mapping, because a mapping requires 1 input to map to 1 output, whereas you would have 1 input mapping to N outputs. You wouldn't have a mapping per se, but you may still be able to deal with it on a point by point basis.
ldog
@gmatt: skiing!? I'm so jealous. Thanks for your invaluable input again! I did sub 1 in for z and then substituted equation 1 into equation 2 and took the resulting equation - which no longer had x or z terms - and put that into wolfram alpha and it gave me this:http://www.wolframalpha.com/input/?i=solve+y^4+%2B+y^2+-+2g+-+2a+%2B+2ay^2+-2+%3D+0+for+yThere are 4 solutions there to check which I haven't had time to check yet, but that's my next step.
@petrocket: I just realized I said that your level sets are circles, but in fact it should be the intersection of the cube with the plane z=1, i.e. a solid square. Sorry about that.
ldog
@gmatt: yeah i figured that's what you meant. unfortunately, the solution for even just x and y for one cube face seems pretty intense. I'm still working on it though!
@gmatt: I made some more progress. I went back and noticed that for the 2 dimensional mapping of a circle to a square for sx values from -1/sqrt(2) to 1/sqrt(2) - or basically 45 degrees left or right of the y axis the following equation maps from the circle to the square: x = sqrt(2) * sx for the bottom square the mapping is the same and for the left and right sides of the square you use y = sqrt(2) * sy since you know x is -1 or 1. For the cube/sphere mapping I've determined x = sqrt(2) * sx only works when y = 0 and z = 1 or -1. I must next determine how to work y into the 3d version.
@gmatt Think I got a solution finally. Will try to post it as a separate answer tomorrow. It's not pretty.
@gmatt Wow. Just finished testing a possible solution and saw your edit. My version is similar only my equation for x is different assuming face is +z or -zx = sqrt(-sqrt(-2s^2 + 2t^2 - 3)^2 - 24s^2) + 2s^2 - 2t^2 +3)/sqrt(2)and y = sqrt(-sqrt(-2s^2 + 2t^2 - 3)^2 - 24s^2) - 2s^2 + 2t^2 +3)/sqrt(2)Be sure to apply the sign of s and t b to x and y after the equations. So far in my testing this solution works - and it is encouraging to see that only one of our equations differs. It is late at night so my second equation might have a hole in it (I got used wolfram too)
This is what i plugged into wolfram to get the equations for the solution I tested:a^2 = x^2(1/2 - y^2/2 + y^2/3), b^2 = y^2(1/2 - x^2/2+x^2/3)Naturally, not all the solutions are valid.
+1  A: 

Here's one way you can think about it: for a given point P in the sphere, take the segment that starts at the origin, passes through P, and ends at the surface of the cube. Let L be the length of this segment. Now all you need to do is multiply P by L; this is equivalent to mapping ||P|| from the interval [0, 1] to the interval [0, L]. This mapping should be one-to-one - every point in the sphere goes to a unique point in the cube (and points on the surface stay on the surface). Note that this is assuming a unit sphere and cube; the idea should hold elsewhere, you'll just have a few scale factors involved.

I've glossed over the hard part (finding the segment), but this is a standard raycasting problem. There are some links here that explain how to compute this for an arbitrary ray versus axis-aligned bounding box; you can probably simplify things since your ray starts at the origin and goes to the unit cube. If you need help simplify the equations, let me know and I'll take a stab at it.

celion
+2  A: 

I want to give gmatt credit for this because he's done a lot of the work. The only difference in our answers is the equation for x.

To do the inverse mapping from sphere to cube first determine the cube face the sphere point projects to. This step is simple - just find the component of the sphere vector with the greatest length like so:

// map the given unit sphere position to a unit cube position
void cubizePoint(Vector3& position) {
    double x,y,z;
    x = position.x;
    y = position.y;
    z = position.z;

    double fx, fy, fz;
    fx = fabsf(x);
    fy = fabsf(y);
    fz = fabsf(z);

    if (fy >= fx && fy >= fz) {
        if (y > 0) {
            // top face
            position.y = 1.0;
        }
        else {
            // bottom face
            position.y = -1.0;
        }
    }
    else if (fx >= fy && fx >= fz) {
        if (x > 0) {
            // right face
            position.x = 1.0;
        }
        else {
            // left face
            position.x = -1.0;
        }
    }
    else {
        if (z > 0) {
            // front face
            position.z = 1.0;
        }
        else {
            // back face
            position.z = -1.0;
        }
    }
}

For each face - take the remaining cube vector components denoted as s and t and solve for them using these equations, which are based on the remaining sphere vector components denoted as a and b:

s = sqrt(-sqrt((2 a^2-2 b^2-3)^2-24 a^2)+2 a^2-2 b^2+3)/sqrt(2)
t = sqrt(-sqrt((2 a^2-2 b^2-3)^2-24 a^2)-2 a^2+2 b^2+3)/sqrt(2)

You should see that the inner square root is used in both equations so only do that part once.

Here's the final function with the equations thrown in and checks for 0.0 and -0.0 and the code to properly set the sign of the cube component - it should be equal to the sign of the sphere component.

void cubizePoint2(Vector3& position)
{
    double x,y,z;
    x = position.x;
    y = position.y;
    z = position.z;

    double fx, fy, fz;
    fx = fabsf(x);
    fy = fabsf(y);
    fz = fabsf(z);

    const double inverseSqrt2 = 0.70710676908493042;

    if (fy >= fx && fy >= fz) {
        double a2 = x * x * 2.0;
        double b2 = z * z * 2.0;
        double inner = -a2 + b2 -3;
        double innersqrt = -sqrtf((inner * inner) - 12.0 * a2);

        if(x == 0.0 || x == -0.0) { 
            position.x = 0.0; 
        }
        else {
            position.x = sqrtf(innersqrt + a2 - b2 + 3.0) * inverseSqrt2;
        }

        if(z == 0.0 || z == -0.0) {
            position.z = 0.0;
        }
        else {
            position.z = sqrtf(innersqrt - a2 + b2 + 3.0) * inverseSqrt2;
        }

        if(position.x > 1.0) position.x = 1.0;
        if(position.z > 1.0) position.z = 1.0;

        if(x < 0) position.x = -position.x;
        if(z < 0) position.z = -position.z;

        if (y > 0) {
            // top face
            position.y = 1.0;
        }
        else {
            // bottom face
            position.y = -1.0;
        }
    }
    else if (fx >= fy && fx >= fz) {
        double a2 = y * y * 2.0;
        double b2 = z * z * 2.0;
        double inner = -a2 + b2 -3;
        double innersqrt = -sqrtf((inner * inner) - 12.0 * a2);

        if(y == 0.0 || y == -0.0) { 
            position.y = 0.0; 
        }
        else {
            position.y = sqrtf(innersqrt + a2 - b2 + 3.0) * inverseSqrt2;
        }

        if(z == 0.0 || z == -0.0) {
            position.z = 0.0;
        }
        else {
            position.z = sqrtf(innersqrt - a2 + b2 + 3.0) * inverseSqrt2;
        }

        if(position.y > 1.0) position.y = 1.0;
        if(position.z > 1.0) position.z = 1.0;

        if(y < 0) position.y = -position.y;
        if(z < 0) position.z = -position.z;

        if (x > 0) {
            // right face
            position.x = 1.0;
        }
        else {
            // left face
            position.x = -1.0;
        }
    }
    else {
        double a2 = x * x * 2.0;
        double b2 = y * y * 2.0;
        double inner = -a2 + b2 -3;
        double innersqrt = -sqrtf((inner * inner) - 12.0 * a2);

        if(x == 0.0 || x == -0.0) { 
            position.x = 0.0; 
        }
        else {
            position.x = sqrtf(innersqrt + a2 - b2 + 3.0) * inverseSqrt2;
        }

        if(y == 0.0 || y == -0.0) {
            position.y = 0.0;
        }
        else {
            position.y = sqrtf(innersqrt - a2 + b2 + 3.0) * inverseSqrt2;
        }

        if(position.x > 1.0) position.x = 1.0;
        if(position.y > 1.0) position.y = 1.0;

        if(x < 0) position.x = -position.x;
        if(y < 0) position.y = -position.y;

        if (z > 0) {
            // front face
            position.z = 1.0;
        }
        else {
            // back face
            position.z = -1.0;
        }
    }

So, this solution isn't nearly as pretty as the cube to sphere mapping, but it gets the job done!

Any suggestions to improve the efficiency or read ability of the code above are appreciated!

--- edit --- I should mention that I have tested this and so far in my tests the code appears correct with the results being accurate to at least the 7th decimal place. And that was from when I was using floats, it's probably more accurate now with doubles.

very nice, nice to see you got it working :)
ldog
btw, the formula for `x` did you guess that it works and try it? Because Wolfram alpha spits out that ugly thing that I posted above.
ldog
actually, when I entered the system of 2 equations into wolfram alpha it gave me the two solutions for x and y that i posted. i also could recognize that the formula looked correct because i first solved the two dimensional version of the mapping so i had an idea for what to look for in the 3d version. thanks again for your help! i'm using it to generate light maps for my planet and do collision detection (vids here: http://www.youtube.com/user/petrocket)