Drilling a square hole with a Reuleaux triangle

A Reuleaux triangle has the same width in every direction, so when it turns inside a square, it touches all four sides at once. A drill bit of that shape, guided to follow it, cuts an almost square hole. Only the corners stay slightly round, and the hole covers 2√3 + π/6 − 3, about 98.77 %, of the square. The example is a triangle of width 20, built from three arcs and turned through a third of a revolution, and every position is united into the hole.

A Reuleaux triangle turning inside a square and the almost square hole it drills

[1]:
import math
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
import planex as px

The triangle

The triangle consists of three arcs, each centered on the opposite corner of an equilateral triangle with the side s = 20 (Figure 1).

[3]:
s = 20.0
A = (0.0, 0.0)
B = (s, 0.0)
C = (s / 2, s * math.sqrt(3) / 2)
triangle = px.QuickSketch(B).Arc(center=A, sweep=60).Arc(center=B, sweep=60).Arc(center=C, sweep=60).Face()
center = (s / 2, s * math.sqrt(3) / 6)
Three arcs around the corners
Figure 1: A Reuleaux triangle of width 20

Check: its area is (π − √3) s² / 2:

[5]:
print("area", triangle.mass, "exact", (math.pi - math.sqrt(3)) / 2 * s**2)
area 281.90836920418315 exact 281.9083692041832

Constant width

Turned to any angle, the triangle’s bounding box is s by s. Moving it so that the box sits on the square’s lower left corner places it touching all four sides. Check: in 360 positions over a third of a turn, the box keeps its size:

[6]:
def placed(angle):
    t = triangle.Rotated(center, angle)
    (x0, y0), (x1, y1) = t.bounding_box
    return t.Translated((-x0, -y0)), (x1 - x0, y1 - y0)


boxes = [placed(120 * n / 360)[1] for n in range(360)]
print("bounding box differs from s by at most", max(max(abs(w - s), abs(h - s)) for w, h in boxes))
bounding box differs from s by at most 1.4210854715202004e-14

The hole

The hole is the union of all positions over a third of a turn, which by the triangle’s symmetry covers every position there is. Check: with more positions, the scallops between them shrink, and the area approaches the exact value:

[7]:
exact = (2 * math.sqrt(3) + math.pi / 6 - 3) * s**2
for count in (30, 120, 360, 1440):
    hole = px.Unify([placed(120 * n / count)[0] for n in range(count)])
    print(count, "positions:", round(hole.mass / s**2, 6), "of the square, exact", round(exact / s**2, 6))
30 positions: 0.985413 of the square, exact 0.9877
120 positions: 0.987215 of the square, exact 0.9877
360 positions: 0.987545 of the square, exact 0.9877
1440 positions: 0.987662 of the square, exact 0.9877

The hole of 1440 positions in the square, with some of the positions dashed, and one of its round corners:

The hole in the square
(a) The hole in the square
A corner
(b) A corner
Figure 2: The almost square hole

In motion

The triangle drilling the hole:

[9]: