Stencil lettering with bridges

Lettering cut out of a sheet makes a stencil, but the inside of a p, an a or an e would fall out, which is why a stencil font leaves bridges. The example is a stencil of the word “planex”, 40 high: the letters come from a font as outlines, the islands are found, and each gets a bridge through its middle. Lengths are in mm.

A stencil of the word planex2d with bridges holding the letters' islands

[1]:
import math
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from matplotlib.textpath import TextPath
import planex as px

The letters

Matplotlib turns text into outlines, and their polygons, drawn into one sketch, give the letters as faces, including the holes inside them. The outlines consist of short straight pieces, and FitArcs turns them back into arcs and lines, which a laser or a plotter follows more smoothly.

[3]:
outline = TextPath((0, 0), "planex", size=40.0, prop=FontProperties(family="DejaVu Sans", weight="bold"))
sketch = px.QuickSketch()
for polygon in outline.to_polygons():
    points = [tuple(p) for n, p in enumerate(polygon[:-1]) if n == 0 or math.dist(p, polygon[n - 1]) > 1e-9]
    sketch.Move(points[0])
    for p in points[1:]:
        sketch.Line(p)
    sketch.Close()
letters = sketch.Shape()
smooth = px.Unify([face.Copy().FitArcs(tol=0.02, max_edge_length=3) for face in letters.faces])
print(len(letters.faces), "letters with", sum(len(f.inner) for f in letters.faces), "holes")
print(len(letters.edges), "edges from the font,", len(smooth.edges), "after fitting arcs")
print("area", round(letters.mass, 3), "fitted", round(smooth.mass, 3))
6 letters with 3 holes
210 edges from the font, 130 after fitting arcs
area 1977.184 fitted 1976.042

Islands

The sheet reaches 10 beyond the letters. In the sheet minus the letters, every hole of a letter becomes a loose island:

[4]:
(x0, y0), (x1, y1) = smooth.bounding_box
sheet = px.QuickSketch((x0 - 10, y0 - 10)).Rectangle(x1 - x0 + 20, y1 - y0 + 20).Face()
plain = sheet - smooth
print("the cut sheet falls apart into", len(plain.faces), "pieces")
the cut sheet falls apart into 4 pieces

Bridges

Through the middle of each hole runs a vertical bridge, 2 mm wide, across the whole letter. The letter loses that strip, and the island stays joined to the sheet above and below it (Figure 1).

[5]:
bridges = []
for face in smooth.faces:
    (lx0, ly0), (lx1, ly1) = face.bounding_box
    for hole in face.inner:
        cx = hole.Face().center[0]
        bridges.append(px.QuickSketch((cx - 1, ly0 - 1)).Rectangle(2, ly1 - ly0 + 2).Face())
stencil = sheet - (smooth - px.Unify(bridges))
print(len(bridges), "bridges, the stencil is", len(stencil.faces), "piece")
px.WriteDXF(stencil, "stencil.dxf")
3 bridges, the stencil is 1 piece
Without bridges: the islands fall out
(a) Without bridges: the islands fall out
With bridges: one piece
(b) With bridges: one piece
Figure 1: The stencil without and with bridges

The stencil’s cut path: stencil.dxf