-
Miniature Chain Mail: Handouts
I ran off a few patches of miniature chain mail for holiday handouts to a few folks who’d appreciate them:

Chain Mail Armor – 6×6 9.6 mm – top view A little patch like that makes a fondletoy that’s easier to pocket than, say, a planetary gear bearing and should be robust enough to withstand quite a bit of abuse.
Alas, it turned out that recent Slic3r development versions suffered a bridging regression. The stable 1.2.9 version does the right thing:

Slic3r 1.2.9 – good bridging The hot-from-Github version goes diagonally, producing a pattern like an internal layer that normally sits atop the (omitted) bridge layer:

Slic3r 7c8b710 – diagonal bridging While that might barely work, the little bitty link bars will certainly fall into the abyss:

Slic3r 7c8b710 – diagonal bridging on links Given the complexity of slicing algorithms, I definitely can’t track down the problem; using the stable version for a while should suffice.
The OpenSCAD source code as a GitHub gist:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters// Chain Mail Armor Buttons // Ed Nisley KE4ZNU – December 2014 Layout = "Build"; // Link Button LB Joiner Joiners Build PillarMod //——- //- Extrusion parameters must match reality! // Print with 1 shell and 2+2 solid layers ThreadThick = 0.25; ThreadWidth = 0.40; HoleWindage = 0.2; Protrusion = 0.1; // make holes end cleanly function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); //——- // Dimensions //- Set maximum sheet size SheetSizeX = 125; // 170 for full sheet on M2 SheetSizeY = 125; // 230 … //- Diamond or rectangular sheet? Diamond = false; // true = rotate 45 degrees, false = 0 degrees for square BendAround = "X"; // X or Y = maximum flexibility *around* designated axis Cap = true; // true = build bridge layers over links CapThick = 4 * ThreadThick; // flat cap on link: >= 3 layers for solid bridging Armor = true && Cap; // true = build armor button atop (required) cap ArmorThick = IntegerMultiple(2.0,ThreadThick); // height above cap surface ArmorSides = 4; ArmorAngle = true ? 180/ArmorSides : 0; // true -> rotate half a side for best alignment //- Link bar sizes BarThick = 3 * ThreadThick; BarWidth = 3.3 * ThreadWidth; BarClearance = 3 * ThreadThick; // vertical clearance above & below bars VertexHack = false; // true to slightly reduce openings to avoid coincident vertices //- Compute link sizes from those values //- Absolute minimum base link: bar width + corner angle + build clearance around bars // rounded up to multiple of thread width to ensure clean filling BaseSide = IntegerMultiple((4*BarWidth + 2*BarWidth/sqrt(2) + 3*(2*ThreadWidth)),ThreadWidth); BaseHeight = 2*BarThick + BarClearance; // both bars + clearance echo(str("BaseSide: ",BaseSide," BaseHeight: ",BaseHeight)); //echo(str(" Base elements: ",4*BarWidth,", ",2*BarWidth/sqrt(2),", ",3*(2*ThreadWidth))); //echo(str(" total: ",(4*BarWidth + 2*BarWidth/sqrt(2) + 3*(2*ThreadWidth)))); BaseOutDiagonal = BaseSide*sqrt(2) – BarWidth; BaseInDiagonal = BaseSide*sqrt(2) – 2*(BarWidth/2 + BarWidth*sqrt(2)); echo(str("Outside diagonal: ",BaseOutDiagonal)); //- On-center distance measured along coordinate axis // the links are interlaced, so this is half of what you think it should be… LinkOC = BaseSide/2 + ThreadWidth; LinkSpacing = Diamond ? (sqrt(2)*LinkOC) : LinkOC; echo(str("Base spacing: ",LinkSpacing)); //- Compute how many links fit in sheet MinLinksX = ceil((SheetSizeX – (Diamond ? BaseOutDiagonal : BaseSide)) / LinkSpacing); MinLinksY = ceil((SheetSizeY – (Diamond ? BaseOutDiagonal : BaseSide)) / LinkSpacing); echo(str("MinLinks X: ",MinLinksX," Y: ",MinLinksY)); NumLinksX = ((0 == (MinLinksX % 2)) && !Diamond) ? MinLinksX + 1 : MinLinksX; NumLinksY = ((0 == (MinLinksY % 2) && !Diamond)) ? MinLinksY + 1 : MinLinksY; echo(str("Links X: ",NumLinksX," Y: ",NumLinksY)); //- Armor button base ButtonHeight = BaseHeight + BarClearance + CapThick; echo(str("ButtonHeight: ",ButtonHeight)); //- Armor ornament size & shape // Fine-tune OD & ID to suit the number of sides… TotalHeight = ButtonHeight + ArmorThick; echo(str("Overall Armor Height: ",TotalHeight)); ArmorOD = 1.0 * BaseSide; // tune for best base fit ArmorID = 10 * ThreadWidth; // make the tip blunt & strong //——- module ShowPegGrid(Space = 10.0,Size = 1.0) { RangeX = floor(95 / Space); RangeY = floor(125 / Space); for (x=[-RangeX:RangeX]) for (y=[-RangeY:RangeY]) translate([x*Space,y*Space,Size/2]) %cube(Size,center=true); } //——- // Create link with armor button as needed module Link(Topping = false) { LinkHeight = (Topping && Cap) ? ButtonHeight : BaseHeight; render(convexity=3) rotate((BendAround == "X") ? 90 : 0) rotate(Diamond ? 45 : 0) union() { difference() { translate([0,0,LinkHeight/2]) // outside shape intersection() { cube([BaseSide,BaseSide,LinkHeight],center=true); rotate(45) cube([BaseOutDiagonal,BaseOutDiagonal,(LinkHeight + 2*Protrusion)],center=true); } translate([0,0,(BaseHeight + BarClearance + 0*ThreadThick – Protrusion)/2]) intersection() { // inside shape cube([(BaseSide – 2*BarWidth), (BaseSide – 2*BarWidth), (BaseHeight + BarClearance + 0*ThreadThick + (VertexHack ? Protrusion/2 : 0))], center=true); rotate(45) cube([BaseInDiagonal, BaseInDiagonal, (BaseHeight + BarClearance + 0*ThreadThick + (VertexHack ? Protrusion/2 : 0))], center=true); } translate([0,0,((BarThick + 2*BarClearance)/2 + BarThick)]) // openings for bars cube([(BaseSide – 2*BarWidth – 2*BarWidth/sqrt(2) – (VertexHack ? Protrusion/2 : 0)), (2*BaseSide), BarThick + 2*BarClearance – Protrusion], center=true); translate([0,0,(BaseHeight/2 – BarThick)]) cube([(2*BaseSide), (BaseSide – 2*BarWidth – 2*BarWidth/sqrt(2) – (VertexHack ? Protrusion/2 : 0)), BaseHeight], center=true); } if (Topping && Armor) translate([0,0,(ButtonHeight – Protrusion)]) // sink slightly into the cap rotate(ArmorAngle) cylinder(d1=ArmorOD,d2=ArmorID,h=(ArmorThick + Protrusion), $fn=ArmorSides); } } //——- // Create split buttons to join sheets module Joiner() { translate([-LinkSpacing,0,0]) difference() { Link(false); translate([0,0,BarThick + BarClearance + TotalHeight/2 – Protrusion]) cube([2*LinkSpacing,2*LinkSpacing,TotalHeight],center=true); } translate([LinkSpacing,0,0]) intersection() { translate([0,0,-(BarThick + BarClearance)]) Link(true); translate([0,0,TotalHeight/2]) cube([2*LinkSpacing,2*LinkSpacing,TotalHeight],center=true); } } //——- // Build it! //ShowPegGrid(); if (Layout == "Link") { Link(false); } if (Layout == "Button") { Link(true); } if (Layout == "LB") { color("Brown") Link(true); translate([LinkSpacing,LinkSpacing,0]) color("Orange") Link(false); } if (Layout == "Build") for (ix = [0:(NumLinksX – 1)], iy = [0:(NumLinksY – 1)]) { x = (ix – (NumLinksX – 1)/2)*LinkSpacing; y = (iy – (NumLinksY – 1)/2)*LinkSpacing; translate([x,y,0]) color([(ix/(NumLinksX – 1)),(iy/(NumLinksY – 1)),1.0]) if (Diamond) Link((ix + iy) % 2); // armor at odd,odd & even,even points else if ((iy % 2) && (ix % 2)) // armor at odd,odd points Link(true); else if (!(iy % 2) && !(ix % 2)) // connectors at even,even points Link(false); } if (Layout == "Joiner") Joiner(); if (Layout == "Joiners") { NumJoiners = max(MinLinksX,MinLinksY)/2; for (iy = [0:(NumJoiners – 1)]) { y = (iy – (NumJoiners – 1)/2)*2*LinkSpacing + LinkSpacing/2; translate([0,y,0]) color([0.5,(iy/(NumJoiners – 1)),1.0]) Joiner(); } } if (Layout == "PillarMod") // Slic3r modification volume to eliminate pillar infill translate([0,0,(BaseHeight + BarClearance)/2]) cube([1.5*SheetSizeX,1.5*SheetSizeY,BaseHeight + BarClearance],center=true);