Regular Polygons in the Poincaré Disk

A hyperbolic polygon inside the disk is a set of points outside it, and for a regular one that set is a regular polygon.

To draw a beautiful tiling of the hyperbolic plane by polygons, you first need to actually construct that polygon in some model (not just prove that one exists): an explicit list of coordinates, ready to hand to a computer.

For triangles this is a matter of trigonometry, and it’s especially easy when one of the angles is right. Lay down the vertical line and the unit circle in the upper half plane, meeting at right angles, and the third side is a semicircle centered at some cc on the real line with some radius RR. Write the two angles it makes with the sides already placed as functions of cc and RR, set them to the angles you want, and solve.

Right angles remain useful for larger polygons too: I’ve used them before to construct right-angled pentagons, hexagons and heptagons in the upper half plane, one geodesic at a time.

But what about other angles? This note records something I find particularly pleasing: if you ask for regular polygons, you can have any angle smaller than the Euclidean value, and the construction is simple, provided you do it in the Poincaré disk. We’ll use the symmetry of the polygon and the symmetry of the disk together to reduce the whole problem to finding a single real number.

The Disk and Its Geodesics

The Poincaré disk is a model of the hyperbolic plane on the open unit disk, with metric

ds2=4dx2(1x2)2ds^2=\frac{4\,\|dx\|^2}{(1-\|x\|^2)^2}

Its geodesics come in two kinds: the diameters, and the arcs of Euclidean circles that meet the boundary at right angles.

To build polygons out of geodesics we need a way to work with them, some coordinates on the set of geodesics itself. An arc-type geodesic is a circle, so it comes with a center cc and a radius rr, but these two aren’t independent: the circle must cross the unit circle at right angles. Orthogonality of circles is a Pythagorean condition (the two radii at a crossing point are the legs of a right triangle whose hypotenuse joins the centers), so it reads

r2+1=c2r^2+1=\|c\|^2

The radius isn’t really a second piece of data: r=c21r=\sqrt{\|c\|^2-1}, which is the length of the tangent segment from cc to the unit circle. So a geodesic is named by its center alone, and since r2=c21r^2=\|c\|^2-1 must be positive, that center lies outside the disk. This is a bijection: every point cc with c>1\|c\|>1 bounds exactly one such circle, swept out by swinging the tangent segment around cc.

What this misses are the diameters. They correspond to directions: sending cc off to infinity along a ray, the corresponding circle flattens out toward the diameter perpendicular to that ray1, and a point escaping in either of two opposite directions converges to the same geodesic. So the full space of geodesics is the exterior of the disk together with an RP1\RP^1 at infinity, one point per pair of opposite directions. (Glued in, that circle closes the exterior annulus into a Mobius strip, with the RP1\RP^1 as its core circle.)

Happily, we’ll never need the RP1\RP^1. We’re going to center our polygon at the origin of the disk, and then no side passes through the center: every side is an arc-type geodesic, and the polygon is a finite list of points outside the disk, one per side.

Thanks to my friend Gordon Kirby for wondering about what the points outisde the Poincare disk represent, which led me to think about this perspective!

Regular Polygons in the Space of Geodesics

A regular polygon is one where all pp sides have the same length and all pp interior angles are equal. Just like in the Euclidean plane, a regular hyperbolic polygon has a center: a point fixed by all of its symmetries2. We begin by moving our polygon so that this center is at the origin of the disk. This is exactly the situation we prepared for in the last section: the polygon is convex and contains its center, so no side passes through the origin, and each side is a circular arc with its own point cic_i outside the disk.

So, what are the symmetries of our polygon? In most models of hyperbolic space this would be a difficult question, but it’s easy in the Poincaré disk: the hyperbolic isometries fixing the origin are exactly the Euclidean rotations and reflections about it. In particular, the rotation carrying our polygon one step around itself is an ordinary Euclidean rotation by angle 2π/p2\pi/p.

This rotation takes each side of the polygon to the next one, so it takes each point cic_i to the next point ci+1c_{i+1}. That is, the points c1,,cpc_1,\dots,c_p form a single orbit of a Euclidean rotation of order pp. But we know what such orbits look like! They are the vertices of regular Euclidean polygons.

So the sides of our hyperbolic polygon, viewed as points in the space of geodesics, themselves form a regular Euclidean polygon outside the disk3. And a regular Euclidean pp-gon is completely determined (up to rotation) by its size, say its circumradius ρ\rho. Once we know ρ\rho we know the points cic_i, from the points we get the circles, and from the circles the polygon itself. All that remains of the construction is to find the one number ρ\rho.

Constructing Regular Polygons with a Given Angle

So, which value of ρ\rho gives which polygon? The one geometric quantity we care about is the interior angle θ\theta, since that’s what a tiling will constrain, so what we need is the relationship between ρ\rho and θ\theta.

The interior angle is the angle at which two adjacent sides meet at a vertex, and in our picture those two sides are two circles crossing. So the question becomes: at what angle do two circles meet, given their radii and the distance dd between their centers? We answered a special case of this already, when we found that two circles meet at right angles exactly when r12+r22=d2r_1^2+r_2^2=d^2. That was the Pythagorean theorem applied to the triangle with the two radii as legs, and for a general angle the same triangle is there, with Pythagoras upgraded to the law of cosines.

But we must be careful about which angle is which! The angle between two circles is measured between their tangent lines at the crossing point, while our triangle is built out of radii, and each radius is perpendicular to its own tangent. Rotating both tangents a quarter turn to line up with the radii replaces the angle between them with its supplement. So the apex angle of the triangle is not θ\theta but χ=πθ\chi=\pi-\theta, and the law of cosines r12+r222r1r2cosχ=d2r_1^2+r_2^2-2r_1r_2\cos\chi=d^2 reads

r12+r22+2r1r2cosθ=d2r_1^2+r_2^2+2r_1r_2\cos\theta=d^2

As a sanity check, at θ=π/2\theta=\pi/2 this recovers the orthogonality relation, as it must.

Now we apply this at a vertex of our polygon, where everything is determined by ρ\rho. The two crossing circles are congruent sides of the polygon, so they share a single radius rr, and orthogonality with the boundary gives r2=ρ21r^2=\rho^2-1. Their centers are adjacent vertices of the outer polygon: two points at distance ρ\rho from the origin, separated by the angle δ=2π/p\delta=2\pi/p, so d2=ρ2(22cosδ)d^2=\rho^2(2-2\cos\delta). Substituting all of this into the angle relation gives

2(ρ21)(1+cosθ)=ρ2(22cosδ)2(\rho^2-1)(1+\cos\theta)=\rho^2(2-2\cos\delta)

and collecting the ρ2\rho^2 terms solves the problem:

TheoremBounding Circles of a Regular Polygon

A regular hyperbolic pp-gon with interior angle θ\theta, centered at the origin of the Poincaré disk, is bounded by pp circles whose centers form a regular Euclidean pp-gon of circumradius ρ\rho, where ρ2=1+cosθcosθ+cosδ,δ=2πp\rho^2=\frac{1+\cos\theta}{\cos\theta+\cos\delta},\qquad \delta=\frac{2\pi}{p}

Let’s try it out on the right-angled hexagon: p=6p=6 and θ=π/2\theta=\pi/2, so cosθ=0\cos\theta=0 and cosδ=1/2\cos\delta=1/2, giving ρ2=2\rho^2=2 and r2=ρ21=1r^2=\rho^2-1=1. The right-angled hexagon is cut out by six unit circles, centered at the vertices of a regular hexagon of circumradius 2\sqrt2. This is the kind of concrete answer we were looking for!

The formula also tells us exactly which angles are possible, and proves the claim from the introduction. For ρ2\rho^2 to be positive we need cosθ+cosδ>0\cos\theta+\cos\delta>0, which happens exactly when θ+δ<π\theta+\delta<\pi, that is, when

θ<(p2)πp\theta<\frac{(p-2)\pi}{p}

the interior angle of the regular Euclidean pp-gon. And the formula shows how the two extremes are approached. As θ\theta climbs toward the Euclidean value, ρ\rho\to\infty: the bounding circles grow huge and their arcs flatten into straight lines, cutting out a tiny polygon near the origin. This is just the familiar fact that small hyperbolic polygons are nearly Euclidean. At the other end θ=0\theta=0, the circumradius reaches its minimum ρ2=2/(1+cosδ)\rho^2=2/(1+\cos\delta), the vertices reach the boundary circle, and adjacent bounding circles become exactly tangent: this is the ideal polygon, the largest regular pp-gon there is. In between, ρ2\rho^2 increases with θ\theta, so every allowed angle occurs for exactly one polygon. We can now specify a regular polygon by its angle and read off the circles that draw it:

Which Ones Tile

A polygon tiles the hyperbolic plane face-to-face when some whole number of copies fit together perfectly around each vertex. If qq copies meet at each vertex, they divide the 2π2\pi of angle there equally, so the polygon we need is the regular pp-gon with

θ=2πq\theta=\frac{2\pi}{q}

and the resulting tiling is the regular tiling with Schläfli symbol {p,q}\{p,q\}. Which pairs (p,q)(p,q) actually occur? We just determined the possible angles of a regular pp-gon, so we only need to check whether 2π/q2\pi/q is on the list:

2πq<(p2)πp    2p<q(p2)    1p+1q<12\frac{2\pi}{q}<\frac{(p-2)\pi}{p} \iff 2p<q(p-2) \iff \frac1p+\frac1q<\frac12

This is the classical existence condition for hyperbolic tilings, and it has infinitely many solutions: for every p3p\geq 3 all sufficiently large qq work, and for every q3q\geq3 all sufficiently large pp do.

But now we have more than existence. For each solution, the theorem hands us the tile itself: substituting θ=2π/q\theta=2\pi/q,

ρ=1+cos2πqcos2πq+cos2πp,r=ρ21\rho=\sqrt{\frac{1+\cos\frac{2\pi}{q}}{\cos\frac{2\pi}{q}+\cos\frac{2\pi}{p}}},\qquad r=\sqrt{\rho^2-1}

and the tile is the region of the disk outside the pp circles of radius rr centered at the points ρ(cos2πkp,sin2πkp)\rho\left(\cos\frac{2\pi k}{p},\sin\frac{2\pi k}{p}\right). Some small cases come out cleanly:

tilingρ2\rho^2r2r^2
{6,4}\{6,4\}2211
{4,6}\{4,6\}3322
{5,4}\{5,4\}1+51+\sqrt55\sqrt5
{8,3}\{8,3\}1+21+\sqrt22\sqrt2
{7,3}\{7,3\}4.048924.04892\ldots3.048923.04892\ldots

The right-angled pentagon {5,4}\{5,4\} is one I constructed in an earlier note by placing geodesics in the upper half plane one at a time. Here it arrives all at once, as five circles of radius 51/45^{1/4}.

Here is how the condition plays out for small pp: the table shows the smallest qq that works, and every larger qq works too.

pp345678\cdots
smallest qq7544333

Triangles are the hardest to tile with, needing at least seven around each vertex, and from p=7p=7 onward every q3q\geq3 works. Choose any pair from this menu, and the formula draws it:

Reflection Groups and Code

With the sides in hand, one polygon is easy to draw. But we’re after the whole tiling, and here reflections do all the work: reflecting the tile across its own sides, then reflecting the copies across theirs, and so on, fills the entire disk. For a computer this is wonderful news, because reflecting across one of our geodesics is an operation it already knows. Reflection across a circle of center cc and radius rr is just inversion in that circle:

vec2 reflectIn(vec2 z, vec2 c, float r2) {
    vec2 d = z - c;
    return c + r2 * d / dot(d, d);
}

There’s one condition to look at first. The tiling is drawn by reflections, but for the tile itself to be a fundamental chamber, so that every tile is reached from ours by a unique symmetry and the reflection count means something, the tile must be a Coxeter polygon: every interior angle of the form π/m\pi/m for a whole number mm. Our angle is θ=2π/q=π/(q/2)\theta=2\pi/q=\pi/(q/2), so this happens exactly when qq is even. That shrinks the menu a little:

pp34567\cdots
smallest even qq86444\cdots

For now we take qq even, and at the end we’ll feed the program an odd qq anyway and see what it does.

So, how should we draw the tiling? The naive plan, actually performing the reflections copy by copy, works badly on a screen: after a dozen generations the disk is still visibly empty near its boundary, which is exactly where the tiles are smallest and most numerous, and each further generation costs exponentially more for less and less visible progress. So we run the whole thing backwards: instead of unfolding the polygon out to every pixel, take each pixel and fold it back into the polygon. A point is inside the polygon exactly when it’s outside all pp walls, so the fold is a loop: while some wall-circle contains the point, reflect it out, and count the reflections as you go.

int fold(inout vec2 z, float rho, float r2) {
    int word = 0;
    for (int step = 0; step < 120; step++) {
        bool moved = false;
        for (int i = 0; i < MAXP; i++) {
            if (float(i) >= pCount) break;
            vec2 c = wallCenter(i, rho);
            if (inside(z, c, r2)) {
                z = reflectIn(z, c, r2);
                word++;
                moved = true;
            }
        }
        if (!moved) break;
    }
    return word;
}

Every pixel costs about the same, and the tiling is drawn all the way to the horizon. The reflection count word is a bonus: its parity two-colors the tiling like a checkerboard, coloring each tile by whether it takes an even or odd number of reflections to reach.

The one remaining detail is drawing the edges. A tile near the boundary is only a few pixels wide, so edges of fixed Euclidean width would swallow it whole. The fix is to measure the distance to the nearest wall hyperbolically4, which gives every edge the same weight no matter where it sits:

float wallDist(vec2 z, vec2 c, float r2) {
    vec2 d = z - c;
    return abs(dot(d, d) - r2) / (sqrt(r2) * (1.0 - dot(z, z)));
}

Here is the whole program. Set pp and qq at the top and it draws that tiling; on the page above, those two are the buttons.

// The {p, q} tiling of the Poincaré disk, one pixel at a time.
// Choose the tiling here: q even, and 1/p + 1/q < 1/2.
const float pCount = 5.0;   // sides of the polygon
const float qCount = 4.0;   // copies meeting at each vertex

const int MAXP = 12;
const float PI = 3.14159265359;
const float TAU = 6.28318530718;

// rho^2 = (1 + cos theta)/(cos theta + cos delta), with theta = 2pi/q the
// interior angle and delta = 2pi/p the angle between adjacent centers. Every
// wall is orthogonal to the boundary, so r^2 = rho^2 - 1.
float outerRadius2() {
    float theta = TAU / qCount;
    float delta = TAU / pCount;
    return (1.0 + cos(theta)) / (cos(theta) + cos(delta));
}

// The p exterior points, turned half a step so a vertex points along the x axis.
vec2 wallCenter(int i, float rho) {
    float a = PI / pCount + TAU * float(i) / pCount;
    return rho * vec2(cos(a), sin(a));
}

bool inside(vec2 z, vec2 c, float r2) {
    vec2 d = z - c;
    return dot(d, d) < r2;
}

vec2 reflectIn(vec2 z, vec2 c, float r2) {
    vec2 d = z - c;
    return c + r2 * d / dot(d, d);
}

int fold(inout vec2 z, float rho, float r2) {
    int word = 0;
    for (int step = 0; step < 120; step++) {
        bool moved = false;
        for (int i = 0; i < MAXP; i++) {
            if (float(i) >= pCount) break;
            vec2 c = wallCenter(i, rho);
            if (inside(z, c, r2)) {
                z = reflectIn(z, c, r2);
                word++;
                moved = true;
            }
        }
        if (!moved) break;
    }
    return word;
}

// sinh of the hyperbolic distance from z to the wall: monotone in the
// distance, so fine for edge thresholds.
float wallDist(vec2 z, vec2 c, float r2) {
    vec2 d = z - c;
    return abs(dot(d, d) - r2) / (sqrt(r2) * (1.0 - dot(z, z)));
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 z = (2.0 * fragCoord - iResolution.xy) / min(iResolution.x, iResolution.y) * 1.06;
    if (dot(z, z) >= 1.0) { fragColor = vec4(0.96, 0.95, 0.92, 1.0); return; }

    float rho2 = outerRadius2();
    float rho = sqrt(rho2);
    float r2 = rho2 - 1.0;

    int word = fold(z, rho, r2);

    float s = 1e9;
    for (int i = 0; i < MAXP; i++) {
        if (float(i) >= pCount) break;
        s = min(s, wallDist(z, wallCenter(i, rho), r2));
    }

    vec3 tile = (word % 2 == 0) ? vec3(0.20, 0.45, 0.70) : vec3(0.80, 0.62, 0.18);
    vec3 col = mix(vec3(0.96, 0.95, 0.92), tile, word == 0 ? 0.55 : 0.26);
    col = mix(vec3(0.15, 0.15, 0.16), col, smoothstep(0.0, 2.0 * fwidth(s) + 0.010, s));

    fragColor = vec4(col, 1.0);
}

Nothing in this program knows any hyperbolic geometry beyond two facts from the start of this note: a geodesic is a circle orthogonal to the boundary, and reflecting across it is inversion. Everything else is the formula.

Footnotes

  1. The circle centered at Ru^R\hat u of the right radius is the set xu^=(x2+1)/2Rx\cdot\hat u=(\|x\|^2+1)/2R, and as RR\to\infty this becomes xu^=0x\cdot\hat u=0.

  2. The symmetries of a polygon form a finite group, as each symmetry is determined by how it permutes the vertices. To see a finite group of isometries has a fixed point, look at the smallest disk containing the vertices: this disk is unique, so every symmetry must carry it to itself, and therefore fix its center.

  3. The vertices of the outer polygon each face a side of the inner one, which is exactly the relationship of a polygon to its dual.

  4. Precisely, this computes sinh\sinh of the hyperbolic distance from zz to the wall, which is monotone in the distance and so just as good for a threshold, while saving the GPU an arcsinh\operatorname{arcsinh} per pixel per wall.

← All notes