Although I’d put the same knob on the half-triangle end piece template as on the equilateral triangle template for piecing hexagons into strips, Mary decided a flat chip would be easier to use:

Bonus: you can now flip it over to cut the other half-triangles, if you haven’t already figured out how to cut two layers of fabric folded wrong sides together.
While I was at it, the knob on the triangle became optional, too. Flipping that one doesn’t buy you much, though.
The OpenSCAD source as a GitHub Gist has been ever so slightly tweaked:
| // 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(); | |
| } |

















