raw Math
RAW Math Computer Graphics Coordinate Transformations

Mapping a Square to a Circle

Robert Eisele

Two independent input axes naturally fill a square, while many applications expect a circular domain. A gamepad stick is a common example: its horizontal and vertical readings lie in \([-1,1]\), but a direction and magnitude are easier to interpret inside the unit disk.

The elliptical grid mapping transforms the entire square continuously onto the disk:

\[ T(x,y)= \left( x\sqrt{1-\frac{y^2}{2}}, y\sqrt{1-\frac{x^2}{2}} \right). \]

Left: square coordinates. Right: corresponding coordinate lines and point in the unit disk.

Constructing the Mapping

Work in the square

\[ S=\{(x,y)\in\mathbb{R}^2:-1\leq x\leq1,\ -1\leq y\leq1\}. \]

We want every vertical line \(x=\text{constant}\) to become an ellipse in the disk. Its horizontal semiaxis is \(|x|\), while its vertical semiaxis is an unknown value \(c_x\). Therefore its image coordinates \((u,v)\) satisfy

\[ 1=\frac{u^2}{x^2}+\frac{v^2}{c_x^2}. \]

The image of the top edge must lie on the unit circle. Symmetry assigns the point at horizontal parameter \(x\) to

\[ (u,v)=\left(\frac{x}{\sqrt2},\sqrt{1-\frac{x^2}{2}}\right). \]

Substitution determines the missing semiaxis:

\[ \begin{aligned} 1 &=\frac{x^2/2}{x^2}+\frac{1-x^2/2}{c_x^2},\\ \frac12 &=\frac{1-x^2/2}{c_x^2},\\ c_x^2 &=2-x^2. \end{aligned} \]

Thus the image of a vertical grid line is

\[ 1=\frac{u^2}{x^2}+\frac{v^2}{2-x^2}. \]

By exchanging the coordinates, the image of a horizontal line \(y=\text{constant}\) is

\[ 1=\frac{u^2}{2-y^2}+\frac{v^2}{y^2}. \]

Solving the Ellipse Pair

Solving the first ellipse for \(u^2\) gives

\[ u^2=x^2\left(1-\frac{v^2}{2-x^2}\right). \]

Insert this expression into the horizontal-line ellipse:

\[ 1= \frac{x^2\left(1-v^2/(2-x^2)\right)}{2-y^2} +\frac{v^2}{y^2}. \]

After collecting the terms containing \(v^2\),

\[ v^2=y^2\left(1-\frac{x^2}{2}\right). \]

The sign of \(v\) must agree with the sign of \(y\), so

\[ v=y\sqrt{1-\frac{x^2}{2}}. \]

Symmetry gives the other coordinate and completes the map:

\[ \boxed{ u=x\sqrt{1-\frac{y^2}{2}}, \qquad v=y\sqrt{1-\frac{x^2}{2}} }. \]

Why the Square Boundary Becomes a Circle

On the right edge, where \(x=1\),

\[ u^2+v^2 =1-\frac{y^2}{2}+\frac{y^2}{2} =1. \]

The same argument applies to \(x=-1\) and, by symmetry, to \(y=\pm1\). Every side maps to one quarter of the unit circle, and each corner maps to a diagonal point such as \((1/\sqrt2,1/\sqrt2)\).

Inside the square, the Jacobian determinant is

\[ \det DT(x,y)= \frac{1-(x^2+y^2)/2} {\sqrt{1-x^2/2}\sqrt{1-y^2/2}}. \]

It is positive throughout the interior and vanishes only at the four corners. The map therefore preserves orientation and does not fold the interior over itself. It is not area-preserving: equal square cells become differently sized regions in the disk.

The Inverse Map

For a point \((u,v)\) in the unit disk, the corresponding square coordinates are

\[ \begin{aligned} x={}&\frac12\left( \sqrt{2+u^2-v^2+2\sqrt2\,u} -\sqrt{2+u^2-v^2-2\sqrt2\,u} \right),\\ y={}&\frac12\left( \sqrt{2-u^2+v^2+2\sqrt2\,v} -\sqrt{2-u^2+v^2-2\sqrt2\,v} \right). \end{aligned} \]

The difference of square roots automatically recovers the signs of \(u\) and \(v\). In floating-point code, clamp each radicand to zero before taking its square root; values near the circle boundary can become slightly negative through rounding.

JavaScript Implementation

function squareToDisk(x, y) {
    return {
        x: x * Math.sqrt(Math.max(0, 1 - y * y / 2)),
        y: y * Math.sqrt(Math.max(0, 1 - x * x / 2))
    };
}

function diskToSquare(u, v) {
    const rootTwo = Math.SQRT2;

    return {
        x: 0.5 * (
            Math.sqrt(Math.max(0, 2 + u * u - v * v + 2 * rootTwo * u)) -
            Math.sqrt(Math.max(0, 2 + u * u - v * v - 2 * rootTwo * u))
        ),
        y: 0.5 * (
            Math.sqrt(Math.max(0, 2 - u * u + v * v + 2 * rootTwo * v)) -
            Math.sqrt(Math.max(0, 2 - u * u + v * v - 2 * rootTwo * v))
        )
    };
}

Both functions assume normalized inputs. Clamp or reject coordinates outside the square before applying the map; the Math.max() calls only protect valid boundary values from floating-point roundoff. For a rectangle centered at \((c_x,c_y)\), first translate the point to the center and divide each axis by its half-size. Apply the square-to-disk map, then multiply both output coordinates by the desired disk radius and translate to the destination center.

For gamepad input, apply a dead zone before or after this mapping according to the device model. A circular dead zone usually belongs in disk coordinates because it then depends only on the mapped magnitude \(\sqrt{u^2+v^2}\).

References