Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping A Sphere To A Cube

Tags:

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.

like image 925
petrocket Avatar asked Apr 17 '10 01:04

petrocket


People also ask

What is a cubed sphere?

Abstract. A new gridding technique for the solution of partial differential equations in spherical geometry is presented. The method is based on a decomposition of the sphere into six identical regions, obtained by projecting the sides of a circumscribed cube onto a spherical surface.

What is a quad sphere?

In mapmaking, a quadrilateralized spherical cube, or quad sphere for short, is an equal-area polyhedral map projection and discrete global grid scheme for data collected on a spherical surface (either that of the Earth or the celestial sphere).


2 Answers

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.

--- edit --- Here's an optimized glsl fragment shader version by Daniel to show that it doesn't have to be such a big scary function. Daniel uses this to filter sampling on cube maps! Great idea!

const float isqrt2 = 0.70710676908493042;  vec3 cubify(const in vec3 s) { float xx2 = s.x * s.x * 2.0; float yy2 = s.y * s.y * 2.0;  vec2 v = vec2(xx2 – yy2, yy2 – xx2);  float ii = v.y – 3.0; ii *= ii;  float isqrt = -sqrt(ii – 12.0 * xx2) + 3.0;  v = sqrt(v + isqrt); v *= isqrt2;  return sign(s) * vec3(v, 1.0); }  vec3 sphere2cube(const in vec3 sphere) { vec3 f = abs(sphere);  bool a = f.y >= f.x && f.y >= f.z; bool b = f.x >= f.z;  return a ? cubify(sphere.xzy).xzy : b ? cubify(sphere.yzx).zxy : cubify(sphere); } 
like image 157
petrocket Avatar answered Sep 22 '22 21:09

petrocket


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.

like image 33
ldog Avatar answered Sep 25 '22 21:09

ldog