Quilting Hexagon Template Generator

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:

Quilting Hexagon Cutting Template
Quilting Hexagon Cutting Template

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:

Hex Quilting Template - geometry doodles
Hex Quilting Template – geometry doodles

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

Solidifying those relations:

Quilting Hex Template - build layout
Quilting Hex Template – build layout

Then math got real:

Hex Quilting Templates - on strips
Hex Quilting Templates – on strips

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

Hex Quilting Template - grip strips
Hex Quilting Template – grip strips

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:

Quilting Hex Template - half-triangle
Quilting Hex Template – half-triangle

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:

Quilting Hex Template - family
Quilting Hex Template – family

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();
}

Comments are closed.