Mary took on the task of finishing a hexagonal quilt from pieced strips, only to discover she’ll need several more strips and the myriad triangles required to turn hexagons into strips. The as-built strips do not match any of the standard pattern sizes, which meant ordinary templates were unavailing. I offered to build a template matching the (average) as-built hexagons, plus a triangle template based on those dimensions.

Wikipedia has useful summaries of hexagon and equilateral triangle geometry and equations.

Quilters measure hexes based on their finished side length, so a “1 inch hex” has sides measuring 1 inch, with the seam allowance extending ¼ inch beyond the sides. It’s difficult to measure finished sides with sufficient accuracy, so we averaged the side-to-side distance across several hexes.

Some thrashing around produced a quick-and-dirty check piece that matched (most of) the stack of un-sewn hexes:

That one came from a knockoff of the circle template, after some cleanup & tweakage, but failed user testing for not withstanding the side force from the rotary cutter blade. The inside and outside dimensions were correct, however, so I could proceed with some confidence I understood the geometry.

Both the pattern width (the side-to-side distance across the inside of the hex) and the seam allowance appearing in the Customizer appear *in inches*, because that’s how things get measured outside the Basement Laboratory & Fabrication Facility:

```
FinishedWidthInch = 2.75;
FinishedWidth = FinishedWidthInch * inch;
SeamAllowanceInch = 0.25;
SeamAllowance = SeamAllowanceInch * inch;
```

You feed in one side-to-side measurement and all other hex dimensions get calculated from that number; quilters default to a ¼ inch seam allowance. Remember, standard quilt hexes are measured by their side length, so just buy some standard templates.

This is one of the few times I’ve needed triangle graph paper:

After I gave up trying to get it right on square-grid paper, of course.

Solidifying those relations:

Both templates have non-skid strips to keep the fabric in place while cutting:

I should have embossed the size on each template, but this feels like a one-off project and YAGNI. Of course, that’s how I felt about the circle templates, so maybe next time I’ll get it right.

As it turned out, Mary realized she needed a template for the two half-triangles at the end of each row:

It’s half of the finished size of the equilateral triangle on the right, with seam allowance added all around. The test scrap of fabric on the left shows the stitching along the hypotenuse of the half-triangle, where it joins to the end-of-row hexagon. Ideally, you need two half-triangle templates, but Mary says it’s easier to cut the fabric from the back side than to keep track of two templates.

The family portrait now has three members:

The OpenSCAD source code as a GitHub Gist:

// Quilting - Hexagon Templates | |

// Ed Nisley KE4ZNU - July 2020 | |

// Reverse-engineered to repair a not-quite-standard hexagon quilt | |

// Useful geometry: | |

// https://en.wikipedia.org/wiki/Hexagon | |

/* [Layout Options] */ | |

Layout = "Build"; // [Build, HexBuild, HexPlate, TriBuild, TriPlate, EndBuild, EndPlate] | |

//------- | |

//- Extrusion parameters must match reality! | |

// Print with 2 shells | |

/* [Hidden] */ | |

ThreadThick = 0.25; | |

ThreadWidth = 0.40; | |

HoleFinagle = 0.2; | |

HoleFudge = 1.00; | |

function HoleAdjust(Diameter) = HoleFudge*Diameter + HoleFinagle; | |

Protrusion = 0.1; // make holes end cleanly | |

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); | |

inch = 25.4; | |

//------- | |

// Dimensions | |

/* [Layout Options] */ | |

FinishedWidthInch = 2.75; | |

FinishedWidth = FinishedWidthInch * inch; | |

SeamAllowanceInch = 0.25; | |

SeamAllowance = SeamAllowanceInch * inch; | |

TemplateThick = 3.0; | |

TriKnob = true; | |

EndKnob = false; | |

/* [Hidden] */ | |

FinishedSideInch = FinishedWidthInch/sqrt(3); | |

FinishedSide = FinishedSideInch * inch; | |

echo(str("Finished side: ",FinishedSideInch," inch")); | |

CutWidth = FinishedWidth + 2*SeamAllowance; | |

CutSide = CutWidth/sqrt(3); | |

echo(str("Cut side: ",CutSide / inch," inch")); | |

// Make polygon-circles circumscribe the target widths | |

TemplateID = FinishedWidth / cos(180/6); | |

TemplateOD = CutWidth / cos(180/6); | |

/* [Hidden] */ | |

TriRadius = FinishedSide/sqrt(3); | |

TriPoints = [[TriRadius,0], | |

[TriRadius*cos(120),TriRadius*sin(120)], | |

[TriRadius*cos(240),TriRadius*sin(240)] | |

]; | |

echo(str("TriPoints: ",TriPoints)); | |

EndPoints = [[TriRadius,0], | |

[TriRadius*cos(120),TriRadius*sin(120)], | |

[TriRadius*cos(120),0] | |

]; | |

echo(str("EndPoints: ",EndPoints)); | |

TipCutRadius = 2*(TriRadius + SeamAllowance); // circumscribing radius of tip cutter | |

TipPoints = [[TipCutRadius,0], | |

[TipCutRadius*cos(120),TipCutRadius*sin(120)], | |

[TipCutRadius*cos(240),TipCutRadius*sin(240)] | |

]; | |

HandleHeight = 1 * inch; | |

HandleLength = (TemplateID + TemplateOD)/2; | |

HandleThick = IntegerMultiple(3.0,ThreadWidth); | |

HandleSides = 12*4; | |

StringDia = 4.0; | |

StringHeight = 0.6*HandleHeight; | |

DentDepth = HandleThick/4; | |

DentDia = 15 * DentDepth; | |

DentSphereRadius = (pow(DentDepth,2) + pow(DentDia,2)/4)/(2*DentDepth); | |

KnobOD = 15.0; // Triangle handle | |

KnobHeight = 20.0; | |

//------- | |

module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes | |

Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); | |

FixDia = Dia / cos(180/Sides); | |

cylinder(r=HoleAdjust(FixDia)/2,h=Height,$fn=Sides); | |

} | |

//------- | |

// Hex template | |

module HexPlate() { | |

difference() { | |

cylinder(r=TemplateOD/2,h=TemplateThick,$fn=6); | |

translate([0,0,-Protrusion]) | |

cylinder(r=TemplateID/2,h=(TemplateThick + 2*Protrusion),$fn=6); | |

} | |

for (i=[1:6/2]) | |

rotate(i*60) | |

translate([0,0,TemplateThick/2]) | |

cube([HandleLength,HandleThick,TemplateThick],center=true); | |

} | |

module HexHandle() { | |

difference() { | |

rotate([90,0,0]) | |

scale([1,HandleHeight/(TemplateOD/2),1]) | |

rotate(180/HandleSides) | |

cylinder(d=HandleLength,h=HandleThick,center=true,$fn=HandleSides); | |

translate([0,0,-HandleHeight]) | |

cube([2*TemplateOD,2*TemplateOD,2*HandleHeight],center=true); | |

translate([0,HandleThick,StringHeight]) | |

rotate([90,090,0]) | |

rotate(180/8) | |

PolyCyl(StringDia,2*HandleThick,8); | |

for (j=[-1,1]) { | |

translate([0,j*(DentSphereRadius + HandleThick/2 - DentDepth),StringHeight]) | |

rotate(180/48) | |

sphere(r=DentSphereRadius,$fn=48); | |

} | |

} | |

} | |

module HexTemplate() { | |

HexPlate(); | |

HexHandle(); | |

} | |

//------- | |

// Triangle template | |

module TriPlate() { | |

linear_extrude(height=TemplateThick) | |

intersection() { | |

offset(delta=SeamAllowance) // basic cutting outline | |

polygon(points=TriPoints); | |

rotate(180) | |

polygon(points=TipPoints); | |

} | |

} | |

module TriTemplate() { | |

union() { | |

if (TriKnob) | |

cylinder(d=KnobOD,h=KnobHeight,$fn=HandleSides); | |

TriPlate(); | |

} | |

} | |

//------- | |

// End piece template | |

module EndPlate() { | |

linear_extrude(height=TemplateThick) | |

intersection() { | |

offset(delta=SeamAllowance) // basic cutting outline | |

polygon(points=EndPoints); | |

rotate(180) | |

polygon(points=TipPoints); | |

} | |

} | |

module EndTemplate() { | |

union() { | |

if (EndKnob) | |

translate([0,(TriRadius/2)*sin(30),0]) | |

cylinder(d=KnobOD,h=KnobHeight,$fn=HandleSides); | |

EndPlate(); | |

} | |

} | |

//------- | |

// Build it! | |

if (Layout == "HexPlate") | |

HexPlate(); | |

if (Layout == "HexBuild") | |

HexTemplate(); | |

if (Layout == "TriPlate") | |

TriPlate(); | |

if (Layout == "TriBuild") | |

TriTemplate(); | |

if (Layout == "EndPlate") | |

EndPlate(); | |

if (Layout == "EndBuild") | |

EndTemplate(); | |

if (Layout == "Build") { | |

translate([1.5*TriRadius,-TriRadius,0]) | |

rotate(180/6) | |

TriTemplate(); | |

translate([-1.5*TriRadius,-TriRadius,0]) | |

rotate(180/6) | |

EndTemplate(); | |

translate([0,TemplateOD/2,0]) | |

HexTemplate(); | |

} |

## One thought on “Quilting Hexagon Template Generator”