devised equations for mapping a square to a circle. I will provide the inverse to the mapping below.
For this blog entry, I use a slightly different notation from his equations; i.e.
u = x'
v = y'
(u,v) are circular coordinates in the domain {(u,v) | u² + v² ≤ 1}
(x,y) are square coordinates in the range [-1,1] x [-1,1]
Here are the inverse mapping equations:
x = ½ √( 2 + 2u√2 + u² - v² ) - ½ √( 2 - 2u√2 + u² - v² )
y = ½ √( 2 + 2v√2 - u² + v² ) - ½ √( 2 - 2v√2 - u² + v² )
or more legibly
The forward equations for the mapping are:
u = x √( 1 - ½ y² )
v = y √( 1 - ½ x² )
Here are some examples of the forward & inverse mappings at work.
For a proof/derivation of the inverse equations, see my paper in
arxiv: "Analytical Methods for Squaring the Disc"
In this blog, I shall refer to this mapping as the elliptical grid mapping. I chose this name because the mapping has a predilection to map a rectilinear grid into a grid of elliptical arcs in the circle. See Nowell's blog for a discussion and derivation of the elliptical arcs.
Here are more examples of the mapping:
Starbucks™ logo converted to a square |
Andy Warhol's pop art "Marilyn" as a circle |
[ C++ implementation source code below]
------------------------------------------------------------------------------------------
// sample code accompanying the paper:
// "Analytical Methods for Squaring the Disc"
// http://arxiv.org/abs/1509.06344
#include <stdio.h>
#include <math.h>
// Elliptical Grid mapping
// mapping a circular disc to a square region
// input: (u,v) coordinates in the circle
// output: (x,y) coordinates in the square
void ellipticalDiscToSquare(double u, double v, double& x, double& y)
{
double u2 = u * u;
double v2 = v * v;
double twosqrt2 = 2.0 * sqrt(2.0);
double subtermx = 2.0 + u2 - v2;
double subtermy = 2.0 - u2 + v2;
// Note: These next 4 terms should never be negative
// However, due to numerical imprecision in standard floating point libraries,
// it is possible to get a very small negative value
// when u = sqrt(2)/2 and v = sqrt(2)/2
// To remedy this, we impose an absolute value function
double termx1 = fabs(subtermx + u * twosqrt2);
double termx2 = fabs(subtermx - u * twosqrt2);
double termy1 = fabs(subtermy + v * twosqrt2);
double termy2 = fabs(subtermy - v * twosqrt2);
x = 0.5 * sqrt(termx1) - 0.5 * sqrt(termx2);
y = 0.5 * sqrt(termy1) - 0.5 * sqrt(termy2);
}
// Elliptical Grid mapping
// mapping a square region to a circular disc
// input: (x,y) coordinates in the square
// output: (u,v) coordinates in the circle
void ellipticalSquareToDisc(double x, double y, double& u, double& v)
{
u = x * sqrt(1.0 - y*y/2.0);
v = y * sqrt(1.0 - x*x/2.0);
}
int main()
{
double x,y;
double u,v;
ellipticalSquareToDisc(-0.789,0.654,u,v);
ellipticalDiscToSquare(u,v,x,y);
printf("%f %f\n",u,v);
printf("%f %f\n",x,y);
ellipticalDiscToSquare(0.31415, -0.926535,x,y);
ellipticalSquareToDisc(x,y,u,v);
printf("%f %f\n",x,y);
printf("%f %f\n",u,v);
return 0;
}
If u2 == v2, I find that termx2 and termy2 can be negative and result in errors.
ReplyDeleteThis comment has been removed by the author.
DeleteI modified the source code to include absolute value for all terms inside the square roots. This absolute value functions are unnecessary in a perfect world. However, in the real world with floating point imprecision , we can introduce absolute values in the equations to avoid the problems.
DeleteI'm assuming you mean when u = v = sqrt(2)/2 on the circle. The error you are getting with regards to a negative argument to a square root is because of numerical imprecision. If you observe the value inside the square roots in the source code for the case u=v=sqrt(2)/2, they are very small and close to zero. You can insert this code before the square root calls in order to avoid numerical imprecision of floating point numbers.
Deletedouble epsilon = 0.0000001;
if (fabs(termx1)< epsilon) {
termx1 = 0.0;
}
if (fabs(termx2)< epsilon) {
termx2 = 0.0;
}
if (fabs(termy1)< epsilon) {
termy1 = 0.0;
}
if (fabs(termy2)< epsilon) {
termy2 = 0.0;
}
In a perfect world of floating point numbers without numerical imprecision, the square roots of the terms should be okay. However, numerical imprecision introduces a very small negative number which causes havoc.
Another solution would be to put absolute value on each of the terms inside the square root.
This comment has been removed by the author.
ReplyDeletethanks for the write up.
ReplyDeleteMade a javascript/Canvas2D based version on codepen
https://codepen.io/mxfh/pen/gRLqXX#https%3A%2F%2F2.bp.blogspot.com%2F-GaCwOyqLmjw%2FVfN3yDNgGCI%2FAAAAAAAAASU%2FgBcnVj_ydGo%2Fs113%2Fchess.png
cool demo! Thanks for sharing your work!! It's nice to see an online implementation in javascript. I couldn't see the difference after changing the resampling factor but maybe it's just my poor eyes.
DeleteThis comment has been removed by the author.
ReplyDeleteAre there any good ways of interpolating values? So you get a slightly rounded square for instance?
ReplyDeleteFor the regular version I've already found a simple solution that works quite well.
u=x(1-0.5y^2)^(n/2)
where n is your interpolator.
But I can't find a very elegant way of doing the same for the inverse.
Actually, I found a similar solution for the inverse.
Deletex = ( √( 2 + 2un√2 + (un)² - (vn)² ) - √( 2 - 2un√2 + (un)² - (vn)² ) )/2n
Where n is the interpolator.
Also, by replacing each instance of n with n^0.48 in this function, you get nearly the original image back when using both formula's with the same value for n.
This comment has been removed by the author.
ReplyDeleteHow do I turn polar coordinates into u and v so I can use the forward mapping to solve for x and y?
ReplyDeleteGreat bblog I enjoyed reading
ReplyDelete