Browning Hi-Power Magazine Capacity Reduction: Blocks

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-else deals 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:

Browning Hi-Power Magazine Block - solid model - BHP OEM
Browning Hi-Power Magazine Block – solid model – BHP OEM

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

Browning Hi-Power Magazine Block - solid model - Generic 1
Browning Hi-Power Magazine Block – solid model – Generic 1

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:

Browning Hi-Power Magazine Block - solid model - Generic 1 - support detail
Browning Hi-Power Magazine Block – solid model – Generic 1 – support detail

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:

Browning Hi-Power Magazine Block - solid model - Generic 1 - whole end
Browning Hi-Power Magazine Block – solid model – Generic 1 – whole end

The whole object looks about the same, though:

Browning Hi-Power Magazine Block - solid model - Generic 1 - whole side
Browning Hi-Power Magazine Block – solid model – Generic 1 – whole side

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