Miniature PETG Printed Chain Mail

The small patch of chain mail early in the M2’s PETG conversion had links with four threads along each bar:

Chain Mail - PETG patches atop PLA patch

Chain Mail – PETG patches atop PLA patch

Dropping the bars to 3.3 threads wide produced a slightly smaller patch:

Chain mail - 6 and 4 thread - detail

Chain mail – 6 and 4 thread – detail

The bars on the platform are 1.6 mm = 4 threads wide, because I’ve forced the thread width to 0.40 for that layer:

Chain Mail - 3.3 wide - Slic3r preview - bottom layer

Chain Mail – 3.3 wide – Slic3r preview – bottom layer

The remainder are closer to 1.4 mm = 3.3 threads, with the preview showing Slic3r allowed a narrow gap that doesn’t appear in real life:

Chain Mail - 3.3 wide - Slic3r preview - link bridge layer

Chain Mail – 3.3 wide – Slic3r preview – link bridge layer

What’s important about this is that the bridging worked perfectly: all the links emerged free of their neighbors and the patch flexed along both axes.

Chain mail - 6 and 4 thread

Chain mail – 6 and 4 thread

I tried this on one layer of Elmer’s White Glue, diluted 1:3 with water, and the links bonded firmly. I’d had some trouble with a few links popping off the usual hairspray after the first few layers, so I decided to try something different.

The fine hair strands have mostly Gone Away, perhaps due to using Concentric infill.

All in all, PETG looks pretty good, even if it’s just as hard to photograph as red PLA.

Update: You may prefer the source code as a GitHub gist.

The OpenSCAD source code:

// 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 = 55;	// 170 for full sheet on M2
SheetSizeY = 55;	// 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 = 4 * ThreadThick;		// vertical clearance above & below bars

VertexHack = 0;						// 0 = no, 1 = 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)],
								center=true);
						rotate(45)
							cube([BaseInDiagonal,
									BaseInDiagonal,
									(BaseHeight + BarClearance + 0*ThreadThick + VertexHack*Protrusion/2)],
									center=true);
					}

				translate([0,0,((BarThick + 2*BarClearance)/2 + BarThick)])		// openings for bars
					cube([(BaseSide - 2*BarWidth - 2*BarWidth/sqrt(2) - VertexHack*Protrusion/2),
						(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),
						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);
Advertisements

,

  1. #1 by Josh on 2015-04-02 - 20:16

    I’ve found that desaturating pictures of red PLA works best – yes, you lose the color obviously, but you can see a lot more detail, which, when showing off sample prints or print defects/artifacts, is more important than the color anyway.

    • #2 by Ed on 2015-04-02 - 20:25

      Aye! I finally tried some variations on the desaturation technique I used for those pix on the MG forum a while ago; it’ll show up next week.

      For critical pix, the future looks downright monochrome!