Shuttles Game: Tapered Pegs

As is all too common with 3D printed replacement parts done remotely, the first Shuttles game pegs didn’t quite fit into the game board’s holes. Fortunately, living in the future means rapid prototyping and quick turnaround:

Shuttles Game pegs - tapered - solid model
Shuttles Game pegs – tapered – solid model

They’re slightly smaller, tapered toward the bottom, and take slightly less time to print.

The OpenSCAD code in the GitHub Gist now has has the tweaks.

Seedling Shelter Frame

Plant seedlings started in pots require some hardening off time outdoors before being transplanted. Veggie seedlings also require protection from critters regarding them as a buffet, so Mary covers them with a sheet of floating row cover, which must be both suspended over the plants to give them growing room and tucked under the tray to keep the bugs out. She asked for a frame to simplify the process:

Mesh Shelter Frame - assembled
Mesh Shelter Frame – assembled

The solid model shows the structure with no regard for proportion:

Mesh Shelter Frame - show view
Mesh Shelter Frame – show view

The 5 mm fiberglass rods come from our decommissioned six-passenger umbrella, cut to length in the Tiny Lathe™ by applying a Swiss Pattern knife file around the perimeter, over the ShopVac’s snout to catch the glass dust. I started with a pull saw (also over the vacuum) during the weekly Squidwrench v-meeting, whereupon Amber recommended either a Dremel slitting wheel or a file, so I mashed everything together and it worked wonderfully well, without producing any errant glass-fiber shards to impale my fingers.

The corners consist of three tubes stuck together at the origin:

Mesh Shelter Frame - non-hulled corner model
Mesh Shelter Frame – non-hulled corner model

Shrink-wrapping them with a hull() adds plenty of strength where it’s needed:

Mesh Shelter Frame - hulled corner model
Mesh Shelter Frame – hulled corner model

I decided putting the belly side (facing you in the picture) downward on the platform and the peak upward would distribute the distortion equally among the tubes and produce a nicely rounded outer surface for the mesh fabric:

Mesh Shelter Frame - build layout
Mesh Shelter Frame – build layout

Which led to some Wikipedia trawling to disturb the silt over my long-buried analytic geometry, plus some calculator work to help recall the process; back in the day I would have used a slipstick, but I was unwilling to go there. Although I could special-case this particular layout, the general method uses Euler’s Rotation Theorem, simplified because I need only one rotation.

Should you need concatenated rotations, you probably need quaternions, but, at this point, I don’t even remember forgetting quaternions.

Anyhow, the Euler rotation axis is the cross product of the [1,1,1] vector aimed through the middle of the corner’s belly with the [0,0,-1] target vector pointing downward toward the platform. The rotation amount is the acos() of the dot product of those two vectors divided by the product of their norms. With vector and angle in hand, dropping them into OpenSCAD’s rotate() transformation does exactly what’s needed:

rotate(acos((BaseVector*Nadir)/(norm(BaseVector)*norm(Nadir))),
       v=cross(BaseVector,Nadir))   // aim belly side downward
  Corner();

Dang, I was so happy when that worked!

Because the corner model rotates around the origin where all three tube centerlines meet, the result puts the belly below the platform, pointed downward. The next step applies a translation to haul the belly upward:

translate([ArmOAL,0,    // raise base to just below platform level
           ArmOC/sqrt(3) + (ArmRadius/cos(180/SocketSides))*cos(atan(sqrt(3)/2)) + Finagle])

This happens in a loop positioning the four corners for printing, so the first ArmOAL as the X axis parameter translates the shape far enough to let four of them coexist around the origin, as shown above.

The mess in the Z axis parameter has three terms:

  • Raise the centerline of the ends of the tubes to Z=0
  • Raise the rim of the tube to Z=0
  • Add a wee bit to make the answer come out right

The 0.18 mm Finagle constant fixes things having to do with the hull() applied to miscellaneous leftover angled-circles-as-polygons approximations and leaves just a skin below the platform to be sheared off by a huge cube below Z=0, matching the corner bellies with the bottoms of the feet.

Because the corners have awful overhangs, the results look a bit raggedy:

Mesh Shelter Frame - corner underside
Mesh Shelter Frame – corner underside

That’s after knocking off the high spots with a grubby sanding sponge and making a trial fit. They look somewhat less grotendous in person.

If we need another iteration, I’ll think hard about eliminating the overhangs by splitting the corner parallel to the belly, flipping the belly upward, and joining the pieces with a screw. What we have seems serviceable, though.

The OpenSCAD source code as a GitHub Gist:

// Mesh Shelter Frame for outdoor sprouts
// Ed Nisley KE4ZNU - July 2020
/* [Layout Options] */
Layout = "Show"; // [Build, Show, Corner, CornerSet, Base, BaseSet]
//-------
//- Extrusion parameters must match reality!
// Print with 2 shells
/* [Hidden] */
ThreadThick = 0.25;
ThreadWidth = 0.40;
HoleFinagle = 0.2;
HoleFudge = 1.00;
function HoleAdjust(Diameter) = HoleFudge*Diameter + HoleFinagle;
Protrusion = 0.1; // make holes end cleanly
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
inch = 25.4;
//-------
// Dimensions
RodOD = 5.0;
SocketDepth = 3*RodOD;
WallThick = 3.0;
ArmOD = RodOD + 2*WallThick;
ArmRadius = ArmOD / 2;
SocketSides = 3*4;
ArmOC = SocketDepth + ArmOD; // rod entry to corner centerline
ArmOAL = ArmOC + ArmRadius; // total arm length to outer edge
echo(str("ArmOC: ",ArmOC));
echo(str("ArmOAL: ",ArmOAL));
Nadir = [0,0,-1]; // vector toward print platform
RodLength = 100; // just for show
//-------
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=HoleAdjust(FixDia)/2,h=Height,$fn=Sides);
}
//-------
BaseVector = [1,1,1]; // vector through middle of base surface
module Corner() {
difference() {
hull() {
scale([1/cos(180/SocketSides),1/cos(180/SocketSides),1])
rotate(180/SocketSides)
sphere(d=ArmOD,$fn=SocketSides);
rotate(180/SocketSides)
cylinder(d=ArmOD,h=ArmOC,$fn=SocketSides);
rotate([-90,0,0]) rotate(180/SocketSides)
cylinder(d=ArmOD,h=ArmOC,$fn=SocketSides);
rotate([0,90,0]) rotate(180/SocketSides)
cylinder(d=ArmOD,h=ArmOC,$fn=SocketSides);
}
rotate(180/SocketSides)
translate([0,0,ArmOD])
PolyCyl(RodOD,SocketDepth + Protrusion,SocketSides);
rotate([-90,0,0]) rotate(180/SocketSides)
translate([0,0,ArmOD])
PolyCyl(RodOD,SocketDepth + Protrusion,SocketSides);
rotate([0,90,0]) rotate(180/SocketSides)
translate([0,0,ArmOD])
PolyCyl(RodOD,SocketDepth + Protrusion,SocketSides);
}
}
module CornerSet(s=RodLength) {
translate([-s/2,-s/2,s])
mirror([0,0,1])
Corner();
translate([s/2,-s/2,s])
rotate([0,0,90]) mirror([0,0,1])
Corner();
translate([s/2,s/2,s])
rotate([0,0,180]) mirror([0,0,1])
Corner();
translate([-s/2,s/2,s])
rotate([0,0,-90]) mirror([0,0,1])
Corner();
}
module Base() {
difference() {
union() {
cylinder(d=ArmOD,h=ArmOAL/2,$fn=SocketSides);
resize([0,0,ArmOC/2])
sphere(d=ArmOC,$fn=2*SocketSides);
}
translate([0,0,3*ThreadThick])
PolyCyl(RodOD,ArmOAL,SocketSides);
translate([0,0,-SocketDepth]) // cut sphere below platform
cube(2*SocketDepth,center=true);
}
}
module BaseSet(s=RodLength) {
for (i=[-1,1], j=[-1,1])
translate([i*s/2,j*s/2,0])
Base();
}
//-------
// Build it!
if (Layout == "Corner")
Corner();
if (Layout == "CornerSet")
CornerSet();
if (Layout == "Base")
Base();
if (Layout == "BaseSet")
BaseSet();
if (Layout == "Show") {
CornerSet();
for (i=[-1,1])
translate([i*RodLength/2,RodLength/2,RodLength])
rotate([90,0,0])
color("Green",0.5)
cylinder(d=RodOD,h=RodLength,$fn=SocketSides);
for (j=[-1,1])
translate([RodLength/2,j*RodLength/2,RodLength])
rotate([0,-90,0])
color("Green",0.5)
cylinder(d=RodOD,h=RodLength,$fn=SocketSides);
BaseSet();
for (i=[-1,1], j=[-1,1])
translate([i*RodLength/2,j*RodLength/2,0])
color("Green",0.5)
cylinder(d=RodOD,h=RodLength,$fn=SocketSides);
}
if (Layout == "Build") {
Finagle = 0.18; // hack for hull's angled round-to-polygon approximations, I think
difference() { // slice sliver from base to sit flat on platform
union()
for (a=[45:90:360])
rotate(a) // distribute around origin
translate([ArmOAL,0, // raise base to just below platform level
ArmOC/sqrt(3) + (ArmRadius/cos(180/SocketSides))*cos(atan(sqrt(3)/2)) + Finagle])
rotate(17) // arbitrary rotation for tidy arrangement
rotate(acos((BaseVector*Nadir)/(norm(BaseVector)*norm(Nadir))),
v=cross(BaseVector,Nadir)) // aim belly side downward
Corner();
translate([0,0,-ArmOD/2]) // slicing block below platform
cube([6*ArmOAL,6*ArmOAL,ArmOD],center=true);
}
rotate(45)
for (i=[-1,1], j=[-1,1])
translate([i*1.5*ArmOC,j*1.5*ArmOC,0])
Base();
}

Shuttles Board Game: Replacement Pegs

For reasons not relevant here, I made replacement pegs for the Shuttles board game:

Shuttles Game - solid model - Slic3r
Shuttles Game – solid model – Slic3r

Not the most challenging solid model I’ve ever conjured from the vasty digital deep, but 3D printing is really good for stuff like this.

The OEM pegs have a hollow center, most likely to simplify stripping them from the injection mold, which I dutifully duplicated:

Shuttles Game pegs - hollow - solid model
Shuttles Game pegs – hollow – solid model

It turns out the additional perimeter length inside the pegs requires 50% more printing time, far offsetting the reduced 10% infill. Given that each solid set takes just under an hour, I decided to lose half an hour of verisimilitude.

I plunked a nice round cap atop the OEM peg’s flat end, but stopped short of printing & installing a round plug for the butt end.

While the 3D printer’s hot, ya may as well make a bunch:

Shuttles game pegs
Shuttles game pegs

Game on …

The OpenSCAD source code as a GitHub Gist:

Update: They’re a bit too large, so the Gist now produces tapered pegs.

// Shuttles game pegs
// Ed Nisley KE4ZNU - July 2020
/* [Layout Options] */
Layout = "Peg"; // [Build, Peg]
Hollow = false;
//-------
//- Extrusion parameters must match reality!
/* [Hidden] */
ThreadThick = 0.25;
ThreadWidth = 0.40;
HoleWindage = 0.2;
Protrusion = 0.1; // make holes end cleanly
ID = 0;
OD = 1;
LENGTH = 2;
//-------
// Dimensions
/* [Dimensions] */
Peg = [4.0,7.5,26.0]; // overall length, including the rounded Cap
Taper = 1.0;
CapRadius = Peg[OD]/2;
PegBaseLength = Peg[LENGTH] - CapRadius;
NumPegs = [1,6]; // lay out in array
ArrayCenter = [NumPegs[0] - 1,NumPegs[1] - 1] / 2;
NumSides = 6*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/2 + HoleWindage,h=Height,$fn=Sides);
}
//-------
// One peg
module Peg() {
union() {
translate([0,0,PegBaseLength])
difference() {
sphere(d=Peg[OD],$fn=NumSides);
translate([0,0,-Peg[OD]/2])
cube([2*Peg[OD],2*Peg[OD],Peg[OD]],center=true);
}
difference() {
cylinder(d1=Peg[OD] - Taper,d2=Peg[OD],h=PegBaseLength,$fn=NumSides);
if (Hollow)
translate([0,0,-Protrusion])
PolyCyl(Peg[ID],PegBaseLength+Protrusion,NumSides);
}
}
}
//-------
// Build it!
if (Layout == "Peg")
Peg();
if (Layout == "Build")
for (i=[0:NumPegs[0] - 1], j=[0:NumPegs[1] - 1])
translate([(i - ArrayCenter.x)*1.5*Peg[OD],(j - ArrayCenter.y)*1.5*Peg[OD],0])
Peg();
view raw Shuttles Game.scad hosted with ❤ by GitHub

Garden Soaker Hose Repairs In Use

Just for completeness, here’s what the various soaker hose clamps look like in the garden, as solid models only let you visualize the ideal situation:

Soaker Hose Connector Clamp - Show view
Soaker Hose Connector Clamp – Show view

This one prevents a puddle in the path to the right:

Soaker hose repairs in situ - clamp
Soaker hose repairs in situ – clamp

Bending the hoses around the end of a bed puts them on edge, with this clamp suppressing a shin-soaking spray to the left:

Soaker hose repairs in situ - end-on clamp
Soaker hose repairs in situ – end-on clamp

The clamp at the connector closes a leak around the crimped brass fitting, with the other two preventing gouges from direct sprays into the path along the bottom of the picture:

Soaker hose repairs in situ - clamps and connector fix
Soaker hose repairs in situ – clamps and connector fix

All in all, a definite UI improvement!

As far as I can tell, we have the only soaker hose repairs & spritz stoppers in existence. Hooray for 3D printing!

Maximum 3D Printing Speed

With everybody 3D printing masks these days, the question of “how fast can you print” came up on the Makergear forum.

Here’s my opinion:

The fundamental limit comes from the heater’s ability to bring cold plastic up to extrusion temperature inside the 20 mm hot zone.

Using airscape’s example, the extruded thread is 0.5 mm thick × 0.8 mm wide = 0.4 mm², so laying down that thread at 50 mm/s means the extruder is heating plastic at 20 mm³/s and is “pushing it with PLA”.

In round numbers, normal printing speeds with a normal nozzle and normal plastics runs around 10 mm³/s, so a practical upper limit is probably around 15 mm³/s.

As far as thread size goes, the diameter of the flat area around the nozzle orifice sets the maximum thread width, because the nozzle must compress the thread against the previous layer. If the thread is wider than the nozzle, the gooey plastic curls up around the sides of the nozzle and doesn’t bond well. The rule of thumb is to round up the orifice diameter to the next convenient number:

  • 0.35 mm nozzle → 0.4 mm thread
  • 0.75 mm nozzle → 0.8 mm thread

The maximum thread (= layer) thickness should be about 60% of the thread width, which is why a 0.8 mm wide thread calls for a 0.5 mm layer thickness.

Assuming the extruder can heat 15 mm³/s of plastic, the maximum printing speed will be 15 mm³/s / 0.4 mm² = 37.5 mm/s: comfortably under airscape’s “pushing it” 50 mm/s.

A visualization may be helpful:

Extrusion Dimensions
Extrusion Dimensions

Aaaaand, as always, calibrate the Extrusion Multiplier for whatever conditions you’re using to ensure the slicer and the hardware agree on how much plastic is coming out of the nozzle.

Tek Circuit Computer: Cursor Hairline Filling

Some cleanup and a fresh layer of double-sided tape gives the cursor milling fixture plenty of adhesion:

Tek CC - Cursor blank on fixture
Tek CC – Cursor blank on fixture

This time, I diamond-scribed three PETG cursors through the transparent protective film, with two / four / six passes:

Tek CC - Cursor hairline filling
Tek CC – Cursor hairline filling

It’s not a Purple Crayon, but it suffices for my simple needs.

Scribbling a (soft!) lacquer crayon over transparent plastic still scuffs the pristine surface around the engraved line, so I tried scribbling the six-pass cursor before peeling the film, as shown above. Unfortunately, the film shreds left around the line either prevent a clean fill or pull the paint out of the ditch as the film peels back:

Tek CC - Cursor lacquer fill
Tek CC – Cursor lacquer fill

Peeling the film and scribbling ever-so-gently left a more complete line, but, if you look very closely (perhaps opening the image in a new tab for more dots), you can see the scuffs left by the scribbles on either side of the line:

Tek CC - Cursor 2 4 6 scribes
Tek CC – Cursor 2 4 6 scribes

When seen from the other side against laminated decks, though, the scuffs pretty much vanish:

Tek CC - Classic Tek Logo vectorized - red hairline
Tek CC – Classic Tek Logo vectorized – red hairline

The red hairline isn’t historically accurate, but I like the way it looks.

Give me some (heavyweight matte) paper and a (lacquer) crayon, put me in a basement (shop), and I’ll be happy for days

Vectorized Classic Tektronix Logo

The Tektronix Circuit Computer sports the most ancient of many Tektronix logos:

Tek CC Logo - scanned
Tek CC Logo – scanned

It’s a bitty thing, with the CRT about 0.7 inch long, scanned directly from my original Tek CC.

Import the PNG image into FreeCAD at 0.2 mm below the XY plane, resize it upward a smidge so the CRT is maybe 0.8 inch long, then trace “wires” all over it:

Tek Logo - FreeCAD tracing - overlay
Tek Logo – FreeCAD tracing – overlay

Given FreeCAD’s default gradient background, the wires definitely don’t stand out by themselves:

Tek Logo - FreeCAD tracing - vectors
Tek Logo – FreeCAD tracing – vectors

Several iterations later, the vectorized logo sits at the correct angle and distance from the origin at the center:

Tek Logo - FreeCAD tracing - rotated
Tek Logo – FreeCAD tracing – rotated

The cheerful colors correspond to various “groups” and make it easier to find errant vectors.

Rather than figure out how to coerce FreeCAD into converting wires into proper G-Code, export the vectors into a DXF file and slam it into DXF2GCODE:

Tek Logo - DXF2GCODE vectors
Tek Logo – DXF2GCODE vectors

Export as G-Code, iterate around the whole loop a few times to wring out the obvious mistakes, indulge in vigorous yak shaving, eventually decide it’s Good Enough™ for the moment.

Protip: set DFX2GCODE to put “0” digits before the decimal point to eliminate spaces between the coordinate axes and the numeric values which should not matter in the least, but which confuse NCViewer into ignoring the entire file.

Tinker the script running the GCMC source code to prepend the logo G-Code to the main file and it all comes out in one run:

Tek CC - with vectorized logo - cutting
Tek CC – with vectorized logo – cutting

That’s the top deck, laminated in plastic, affixed to a Cricut sticky mat on the MPCNC platform, ready for drag-knife cutting.

Assembled with a snappy red hairline:

Tek CC - Classic Tek Logo vectorized - red hairline
Tek CC – Classic Tek Logo vectorized – red hairline

Isn’t it just the cutest thing you’ve seen in a while?

It needs more work, but it’s pretty close to right.