After a bit of trial fitting and tweaking, just three parameters cover the variations for the magazines in hand:
- Offset of screw from front-to-back center
- Height of spring retaining crimp
- Distance between screw and crimp
Collecting those numbers in a single array, with constants to select the entries, makes some sense:
//BlockData = [-0.5, 1.5, 11.5]; // Browning OEM BlockData = [-1.5, 2.0, 9.0]; // Generic 1 SCREWOFFSET = 0; CRIMPHEIGHT = 1; CRIMPDISTANCE = 2;
Although commenting out an undesired variable isn’t fashionable, OpenSCAD doesn’t have a practical mechanism to set specific values based on a control variable:
if-then-elsedeals with geometric objects(boolean)?when_true:when_false(the ternary operator) doesn’t scale well
You could, of course, depend on OpenSCAD’s behavior of using the last (in syntactic order) instance of a “variable”, but IMHO that’s like depending on semantic whitespace.
In any event, the rest of the block builds itself around those three values by recomputing all of its dimensions.
The Browning OEM block looks like this:

The Generic floorplate has a much larger spring retaining crimp, so the block has far more overhang:

As before, the yellow widgets are built-in support structures separated from the main object by one thread thickness and width. That seems to maintain good vertical tolerance and allow easy removal; the structures snap free with minimal force. A closeup look shows the gaps:

The main shape now has a 2 mm taper to ease the magazine spring past the upper edge of the block. The horn remains slightly inset from the side walls to ensure that the whole thing remains manifold:

The whole object looks about the same, though:

The shape descends from the geometry I used for the stainless steel block, with the additional internal channel (on the right in the models) to be filled with steel-loaded epoxy during assembly. That should make the whole block sufficiently robust that you must destroy the floorplate and distort the spring to get it out; wrecking the magazine’s innards should count as not “readily” modifiable.
Some destructive testing seems to be in order…
The OpenSCAD source code:
// Browning Hi-Power Magazine Plug
// Ed Nisley KE4ZNU December 2013
// February 2014 - easier customization for different magazine measurements
Layout = "Whole"; // Whole Show Split
// Whole = upright for steel or plastic
// Show = section view for demo, not for building
// Split = laid flat for plastic show-n-tell assembly
AlignPins = true && (Layout == "Split"); // pins only for split show-n-tell
Support = true && (Layout != "Split"); // no support for split, optional otherwise
// Define magazine measurements
//BlockData = [-0.5, 1.5, 11.5]; // Browning OEM
BlockData = [-1.5, 2.0, 9.0]; // Generic 1
SCREWOFFSET = 0;
CRIMPHEIGHT = 1;
CRIMPDISTANCE = 2;
//- Extrusion parameters must match reality!
// Print with 2 shells and 3 solid layers
ThreadThick = 0.20;
ThreadWidth = 0.40;
HoleWindage = 0.2;
Protrusion = 0.1; // make holes end cleanly
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
//----------------------
// Dimensions
Angle = 12.5; // from vertical
SpringID = 10.3; // magazine spring curvature (measure with drill shank)
SpringRadius = SpringID / 2;
Taper = 2.0; // total taper toward top
Length = 24.5; // front-to-back perpendicular to magazine shaft
Height = 17.0; // bottom-to-top, parallel to magazine shaft
RectLength = Length - SpringID; // block length between end radii
HornBaseOD = 8.0; // fits between follower pegs to prevent shortening
HornTipOD = 5.0;
HornAddTip = (HornTipOD/2)*tan(Angle);
HornAddBase = (HornBaseOD/2)*tan(Angle);
HornAddLength = HornAddTip + HornAddBase + 2*Protrusion;
HornLength = 12.0; // should recompute ODs, but *eh*
ScrewOD = 3.0 - 0.25; // screw hole dia - minimal thread engagement
ScrewLength = Height - 5.0;
ScrewOffset = BlockData[SCREWOFFSET]; // ... from centerline on XY plane
NutOD = 5.8; // hex nut dia across flats
NutThick = 2.4; // ... generous allowance for nut
NutTrapLength = 1.5*NutThick; // allow for epoxy buildup
NutTrapBaseHeight = 5.0; // ... base height from floor plate
CrimpHeight = IntegerMultiple(BlockData[CRIMPHEIGHT],ThreadThick); // vertical clearance for spring crimp tab on base plate
CrimpDistance = BlockData[CRIMPDISTANCE]; // ... clip to screw hole center
CrimpOffset = -(CrimpDistance - ScrewOffset); // ... horizontal from centerline
SupportLength = 4.0; // length of support struts under Trim
SupportWidth = IntegerMultiple(0.9*SpringID,4*ThreadWidth); // ... size needed for platform adhesion
SupportThick = CrimpHeight - ThreadThick; // ... clearance for EZ removal
VentDia = 2.5; // air vent from back of screw recess
//VentOffset = CrimpOffset + VentDia/2 + 5*ThreadWidth;
VentOffset = -(NutOD + 4*ThreadWidth);
VentLength = ScrewLength + VentDia;
RecessDia = 3.5; // additional air vent + weight reduction
RecessLength = ScrewLength + RecessDia/2; // ... internal length
RecessOffset = Length/2 - RecessDia/2 - 5*ThreadWidth; // ... offset from centerline
PinOD = 1.72; // alignment pins
PinLength = 4.0;
PinInset = 0.6*SpringRadius; // from outside edges
echo(str("Alignment pin length: ",PinLength));
NumSides = 8*4; // default cylinder sides
Offset = 5.0/2; // from centerline for build layout
//----------------------
// Useful routines
function Delta(a,l) = l*tan(a); // incremental length due to angle
// Locating pin hole with glue recess
// Default length is two pin diameters on each side of the split
module LocatingPin(Dia=PinOD,Len=0.0) {
PinLen = (Len != 0.0) ? Len : (4*Dia);
translate([0,0,-ThreadThick])
PolyCyl((Dia + 2*ThreadWidth),2*ThreadThick,4);
translate([0,0,-2*ThreadThick])
PolyCyl((Dia + 1*ThreadWidth),4*ThreadThick,4);
translate([0,0,-(Len/2 + ThreadThick)])
PolyCyl(Dia,(Len + 2*ThreadThick),4);
}
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);
}
module ShowPegGrid(Space = 10.0,Size = 1.0) {
Range = floor(50 / Space);
for (x=[-Range:Range])
for (y=[-Range:Range])
translate([x*Space,y*Space,Size/2])
%cube(Size,center=true);
}
//----------------------
// The magazine block
module Block(SectionSelect = 0) {
CropHeight = Height*cos(Angle); // block height perpendicular to base
echo(str("Perpendicular height: ",CropHeight));
difference() {
union() {
intersection() {
rotate([Angle,0,0])
hull() {
for (i=[-1,1])
translate([0,i*RectLength/2,-((Length/2)*sin(Angle) + Protrusion)])
cylinder(r1=SpringRadius,r2=(SpringRadius - Taper/2),
h=(Height + 2*(Length/2)*sin(Angle) + 2*Protrusion),
$fn=NumSides);
}
translate([0,0,CropHeight/2])
cube([2*SpringID,3*Length,CropHeight],center=true);
}
translate([0,-Height*sin(Angle),Height*cos(Angle)])
resize([(SpringID - Taper),0,0])
intersection() {
rotate([Angle,0,0])
translate([0,0,-(HornAddBase + Protrusion)])
cylinder(r1=HornBaseOD/2,
r2=HornTipOD/2,
h=(HornLength + HornAddLength + Protrusion),
$fn=NumSides);
cube([2*SpringID,Length,2*(HornLength*cos(Angle) + Protrusion)],center=true);
}
}
translate([0,ScrewOffset,-Protrusion]) // screw
rotate(180/6)
PolyCyl(ScrewOD,(ScrewLength + Protrusion),6);
translate([0,ScrewOffset,NutTrapBaseHeight]) // nut trap in center
rotate(180/6)
PolyCyl(NutOD,NutTrapLength,6);
translate([0,ScrewOffset,-Protrusion]) // nut clearance at base
rotate(180/6)
PolyCyl(NutOD,(1.1*NutThick + Protrusion),6);
translate([SpringID/2,CrimpOffset,-Protrusion])
rotate(180)
cube([SpringID,Length,(CrimpHeight + Protrusion)],center=false);
if (AlignPins) // alignment pins
if (true)
translate([0,-CropHeight*tan(Angle),CropHeight])
rotate([0,90,0]) rotate(45 + Angle)
LocatingPin(PinOD,PinLength);
else
for (i=[-1,1]) // cannot use these with additional vents * channels
rotate([Angle,0,0])
translate([0,
(i*((Length/2)*cos(Angle) - PinInset)),
(CropHeight/2 - i*2*PinInset)])
rotate([0,90,0]) rotate(45 - Angle)
LocatingPin(PinOD,PinLength);
translate([0,(ScrewOffset + 1.25*NutOD),ScrewLength]) // air vent
rotate([90,0,0]) rotate(180/8)
PolyCyl(VentDia,3*NutOD,8);
translate([0,VentOffset,-(VentDia/2)*tan(Angle)])
rotate([Angle,0,0]) rotate(180/8)
PolyCyl(VentDia,VentLength,8);
translate([0,RecessOffset,0]) // weight reduction recess
rotate([Angle,0,0]) rotate(180/8)
translate([0,0,-((RecessDia/2)*tan(Angle))])
PolyCyl(RecessDia,(RecessLength + (RecessDia/2)*tan(Angle)),8);
if (SectionSelect == 1)
translate([0*SpringID,-2*Length,-Protrusion])
cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
else if (SectionSelect == -1)
translate([-2*SpringID,-2*Length,-Protrusion])
cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
}
SupportSlots = (SupportWidth / (4*ThreadWidth)) / 2; // SupportWidth is multiple of 4*ThreadWidth
if (Support)
color("Yellow") {
translate([0,(CrimpOffset - SupportLength/2),SupportThick/2])
difference() {
translate([0,-ThreadWidth,0])
cube([(SupportWidth - Protrusion),SupportLength,SupportThick],center=true);
for (i=[-SupportSlots:SupportSlots])
translate([i*4*ThreadWidth + 0*ThreadWidth,ThreadWidth,0])
cube([(2*ThreadWidth),SupportLength,(SupportThick + 2*Protrusion)],center=true);
}
translate([0,ScrewOffset,0])
for (j=[0:5]) {
rotate(30 + 360*j/6)
translate([(NutOD/2 - ThreadWidth)/2,0,(1.1*NutThick - ThreadThick)/2])
color("Yellow")
cube([(NutOD/2 - ThreadWidth),
(2*ThreadWidth),
(1.1*NutThick - ThreadThick)],
center=true);
}
}
}
//-------------------
// Build it...
ShowPegGrid();
if (Layout == "Show")
Block(1);
if (Layout == "Whole")
Block(0);
if (Layout == "Split") {
translate([(Offset + Length/2),Height/2,0])
rotate(90) rotate([0,-90,-Angle])
Block(-1);
translate([-(Offset + Length/2),Height/2,0])
rotate(-90) rotate([0,90,Angle])
Block(1);
}
Comments
3 responses to “Browning Hi-Power Magazine Capacity Reduction: Blocks”
Fyi: http://www.thefederalistpapers.org/video-2/ny-state-police-refuse-to-enforce-safe-act-magazine-limits
I still don’t understand why a seven round limit is “unsupported”, but a ten round limit is perfectly fine. But these days there’s a whole lotta stuff I don’t understand.
You and me both, in this line of business…