3

Given a unit square of bounds (-1, -1), (-1, 1), (1, 1), (1, -1) how can I find a point on a circle of radius 1 inside the square such that each increment on the square represent an evenly spaced incremented point on the circle. I tried the following equation but it only covers 180 degrees and I can't figure out how to extend it to a full 360 degrees. Also bonus if you can explain how to convert in reverse from circle to square:

float2 SquareToCircle(float2 vertex)
{
    float bound = max(abs(vertex.x), abs(vertex.y));
    float theta = (vertex.x + bound + vertex.y + bound) * PI / 4;
    vertex.x = sin(theta) * bound;
    vertex.y = cos(theta) * bound;
    return vertex;
}          

enter image description here

When extended out to 3 dimensions, this is the result of my formula. It doesn't handle the right and left side coords very well: enter image description here

Bugbeeb
  • 133
  • "such that each increment on the square represent an evenly spaced incremented point on the circle": you may compute the "covered distance" on the square divided by the total perimeter, multiplied by $2\pi$. This way, even spaces on the perimeter on the square lead to even spaces on the circle. – Jean-Claude Arbaut Jul 13 '23 at 22:21

2 Answers2

3

Setup

Another possible approach is to consider the arc length parameterization of the two curves. This is a parameterization $\gamma : I \to \mathbb{R}^2$ (where $I$ is an interval) such that the distance a particle travels along the curve in $\mathbb{R}^2$ is equal to the amount of time it has traveled along the curve, i.e. the length of the curve from $\gamma(a)$ to $\gamma(b)$ is $b-a$.

An arc length parameterization of the circle, defined on the interval $[0,2\pi]$ is given by $$ \gamma_C(t) = (\cos(t), \sin(t)).$$ Note that this parameterization can be extended by periodicity to the entire real line, but we don't really want to do that. For the square, an arc length parameterization on $[0,8]$ (where $8$ is the perimeter of the square) is given by $$ \gamma_S(t) = \begin{cases} (1,t) & \text{if $t \in [0,1]$,} \\ (2-t, 1) & \text{if $t \in (1,3]$,} \\ (-1,4-t) & \text{if $t \in (3,5]$,} \\ (t-6,-1) & \text{if $t \in (5,7]$, and} \\ (1,t-8) & \text{if $t \in (7,8]$.} \\ \end{cases}$$ Again, this can be extended by periodicity, but we don't need that here.

From the Square to the Circle

Observe that both of these functions are injective, which means that they are invertible. So a reasonable approach to this problem is to take a point on the square, map it back to the interval $[0,8]$, scale that interval down to $[0,2\pi]$, then map that to the circle. That looks like $$ \gamma_C \circ \sigma_{\pi/4} \circ \gamma_S^{-1}, $$ where $\sigma_{a}$ denotes the scaling map $$ \sigma_{a}(t) = at. $$ The inverse of $\gamma_S$ can be given explicitly by $$\gamma_{S}^{-1}(x,y) = \begin{cases} y & \text{if $x=1$ and $y\ge 0$,} \\ 2-x & \text{if $x \in (-1,1)$ and $y=1$,} \\ 4-y & \text{if $x=-1$ and $y\in [-1,1]$,} \\ 6+x & \text{if $x \in (-1,1)$ and $y=-1$, and} \\ 8+y & \text{if $x = 1$ and $y \in [-1,0)$.} \end{cases}$$

As ugly as this is, this particular version of the mapping has the advantage of being relatively straightforward to implement in code.

From the Circle to the Square

The idea is similar: compute $$ \gamma_S \circ \sigma_{4/\pi} \circ \gamma_C^{-1}. $$ The happy thing about the given arc length parameterization of the circle is that the arc length is equal to angle (in radians). So it is sufficient to determine the angle from the origin to a given point on the circle. Note that the slope of a line from the origin to a point $(x,y)$ is $\tan(\theta)$, where $\theta$ is the angle that line makes to the $x$-axis. This implies that $$ \tan(\theta) = \frac{y}{x}. $$ It is tempting to then conclude that $$ \theta = \arctan\!\left( \frac{y}{x} \right), $$ but one needs to be a little careful about which quadrant things are living, and on which branch of the tangent function we want things to land. After a bit of work, $$\gamma_{C}^{-1}(x,y) = \begin{cases} \arctan(y/x) & \text{if $x \ge 0$ and $y \ge 0$, }\\ \arctan(y/x) + \pi/2 & \text{if $x < 0$, and} \\ \arctan(y/x) + 3\pi/4 & \text{if $x \ge 0$ and $y < 0$.} \end{cases}$$ Alternatively, many programming languages also include a two-argument arctangent function (atan2), which returns an angle between $(-\pi,\pi]$. If one uses this function, $\gamma_C^{-1}$ could be reduced to $$\gamma_C^{-1}(x,y) = \begin{cases} \operatorname{atan2}(y,x) & \text{if $y \ge 0$, and} \\ \operatorname{atan2}(y,x) + 2\pi & \text{if $y < 0$.} \end{cases}$$

  • nice! It makes sense to deconstruct the square into intervals. – Bugbeeb Jul 14 '23 at 01:09
  • FWIW, most math libraries provide a 2-argument arctan function, which handles the quadrant problem, and can handle $x=0$ https://en.wikipedia.org/wiki/Atan2 – PM 2Ring Jul 14 '23 at 07:06
  • @PM2Ring Nice. I didn't know that. Though it appears that atan2 returns an angle in $(-\pi, \pi]$, so either one has to account for the sign of $y$, or extend $\gamma_S$ by periodicity to the interval $[-4,4]$. But it does still simplify things a little. Thanks. – Xander Henderson Jul 14 '23 at 12:32
1

If $x$ and $y$ are both positive, the choice of $\theta$ that "lines up" the point on the circle with the point on the square (from the perspective of the center point) is $\theta = \arctan(y/x)$. Then the coordinates of the points on the circle can be computed from $\theta$ exactly as you did.

In general, you can do this process on the absolute values of $x$ and $y$, and then manually make sure at the end that the signs of the circle-point's coordinates are the same as the signs of the square-point's coordinates.

Greg Martin
  • 78,820
  • 2
    But this isn't what the question is about, as the projections equally spaced points on the square onto the circle will not be equally spaced. – Xander Henderson Jul 13 '23 at 22:41
  • What @XanderHenderson said, arctan will project the point through the circle to the origin and result in pinching near the corner of the square – Bugbeeb Jul 13 '23 at 22:59