Terminology I had to look up:
- Window: something in a wall you can see through
- Sash: a sliding panel in a window
- Mullion: vertical post separating two windows
- Muntin: strips separating glass panes in a sash
TIL: Muntin, which I’d always known was called a Mullion.
With that as preface, one of Mary’s quilting cronies lives in a very old house updated with vinyl windows sporting wood muntins arranged in a grille. The wood strips forming the grille end in plastic clips that snap into the sash, thereby holding the grill in place to make the window look more-or-less historically correct, while not being a dead loss as far as winter heating goes.
Time passed, sun-drenched plastic became brittle, and eventually enough clips broke that the grilles fell out. An afternoon quilting bee produced a question about the possibility of making a 3D printed clip, as the original manufacturer is either defunct or no longer offers that particular style of clip as a replacement part.
Well, I can do that:

The original is (obviously) the transparent injection-molded part in the upper left. The other two come hot off the M2’s platform, with the one on the right showing the support material under the sash pin.
The solid model looks about like you’d expect:

There is obviously no way to build it without support material, so I painted the bottom facet of the sash pin with a PrusaSlicer support enforcer:

The pin comes out slightly elongated top-to-bottom, but it’s still within the tolerances of the original part and ought to pop right into the sash. We’ll know how well it works shortly after the next quilting bee.
The doodle with useful measurements amid some ideas that did not work out:

The OpenSCAD source code as a GitHub Gist:
// Window Muntin Clips | |
// Ed Nisley KE4ZNU June 2022 | |
Layout = "Show"; // [Build, Show] | |
/* [Hidden] */ | |
ThreadThick = 0.25; | |
ThreadWidth = 0.40; | |
HoleWindage = 0.2; | |
Protrusion = 0.1; // make holes end cleanly | |
inch = 25.4; | |
ID = 0; | |
OD = 1; | |
LENGTH = 2; | |
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); | |
//---------------------- | |
// Dimensions | |
ClipOA = [13.0,18.7,8.0]; | |
TongueAngle = 70; | |
TongueOA = [14.0,10.0,1.8 - 0.2]; // minus Z windage for angular slices | |
BuildGap = 5.0; | |
//---------------------- | |
// Useful routines | |
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=(FixDia + HoleWindage)/2, | |
h=Height, | |
$fn=Sides); | |
} | |
//---------------------- | |
// Pieces | |
module Shell() { | |
// Clip base as 2D polygon | |
// Magic numbers from measurements | |
cx = ClipOA.x; | |
cy = ClipOA.y; | |
cz = ClipOA.z; | |
ClipPts = [ | |
[0,0],[0,cz],[0.3,cz], | |
[1.0,cz-1.0],[2.0,cz-2.3],[2.0,cz-3.0],[1.3,cz-3.5], | |
[1.3,1.6],[17.4,1.6], | |
[17.4,cz-3.5],[16.7,cz-3.0],[16.7,cz-2.3],[17.7,cz-1.0], | |
[18.4,cz],[18.7,cz],[18.7,0.0],[0,0] | |
]; | |
difference() { | |
translate([-ClipOA.x,-ClipOA.y/2,0]) | |
rotate([90,0,90]) | |
linear_extrude(height=ClipOA.x,convexity=3) | |
polygon(convexity=3,points=ClipPts); | |
translate([-(ClipOA.x - 3.0/2 + Protrusion),0,0]) | |
cube([3.0 + Protrusion,ClipOA.y - 2*1.3,4*1.6],center=true); | |
} | |
} | |
module Tongue() { | |
tx = TongueOA.x; | |
ty = TongueOA.y; | |
tz = TongueOA.z; | |
tt = ty - 2*sqrt(2)*tz; // width at top of tapers | |
td = ThreadWidth; // min size of features | |
intersection() { | |
rotate([0,-TongueAngle,0]) { | |
difference() { | |
union() { | |
hull() { | |
for (j=[-1,1]) { | |
translate([td/2,j*(ty - td)/2,td/2]) | |
cube(td,center=true); | |
translate([td/2,j*(tt - td)/2,tz - td/2]) | |
cube(td,center=true); | |
} | |
translate([10.0,0,0]) | |
rotate(180/12) | |
cylinder(d=ty,h=td,center=false,$fn=12); | |
translate([10.0,0,tz - td/2]) | |
rotate(180/12) | |
cylinder(d=tt,h=td,center=false,$fn=12); | |
}; | |
translate([10.0,0,-5.2]) | |
rotate(180/12) | |
cylinder(d=5.0,h=5.2,center=false,$fn=12); | |
translate([10.0,0,-5.2]) | |
rotate(180/12) | |
resize([0,0,2.0]) | |
sphere(d=5.0/cos(180/12),$fn=12); | |
} | |
if (false) | |
translate([10.0,0,-10]) // stiffening hole | |
rotate(180/6) | |
PolyCyl(0.1,20,6); | |
} | |
} | |
cube([2*ClipOA.x,2*ClipOA.y,2*IntegerMultiple(13.0,ThreadThick)],center=true); | |
} | |
} | |
module Clip() { | |
Shell(); | |
Tongue(); | |
} | |
//---------------------- | |
// Build it | |
if (Layout == "Show") { | |
Clip(); | |
} | |
if (Layout == "Build") { | |
Clip(); | |
} |
One thought on “Replacement Muntin Clips”
Comments are closed.