Laser-Engraved Bentley Snowflakes

Algorithmic snowflakes make for interesting coasters and decorations:

Snowflake Hangers - frosted
Snowflake Hangers – frosted

But they lack the complexity of real snowflakes:

Wilson Bentley Photomicrograph of Dendrite Star Snowflake No. 842 - SIA-SIA2013-09114 - rescaled
Wilson Bentley Photomicrograph of Dendrite Star Snowflake No. 842 – SIA-SIA2013-09114 – rescaled

That’s from the Smithsonian collection of the Wilson Bentley snowflake photos from back in the 1890s, all of which are CC0 = Public Domain images.

So pick a nice image, say #842, clean it up a bit, and isolate the flake from the background:

Snowflake No. 842 - SIA-SIA2013-09114 - isolated
Snowflake No. 842 – SIA-SIA2013-09114 – isolated

Pick a threshold level to prettify the result:

Snowflake No. 842 - SIA-SIA2013-09114 - Threshold
Snowflake No. 842 – SIA-SIA2013-09114 – Threshold

Then engrave it into the back of an acrylic mirror scrap, so the darkest parts become most transparent:

Bentley 842 - engraved mirror - white background
Bentley 842 – engraved mirror – white background

Which looks better when seen against an illuminated background:

Bentley 842 - engraved mirror - color background A
Bentley 842 – engraved mirror – color background A

Well, I think it does:

Bentley 842 - engraved mirror - color background B
Bentley 842 – engraved mirror – color background B

Maybe four different snowflakes atop those squares?

Gotta get this ready for the next snow season …

Tailor’s Clapper: 3D Printed Finger Grips

With the pockets milled into the oak blocks, the next step is to insert a pair of comfy 3D printed finger grips:

Ironing weight - prototype grip
Ironing weight – prototype grip

Getting comfy required a bank shot off the familiar chord equation to find the radius of a much larger circle producing the proper depth between the known width. The recess then comes from subtracting a hotdog from a lozenge exactly filling the wood pocket.

Ironing Weight Finger Grip - recess chord
Ironing Weight Finger Grip – recess chord

A pair of grips takes just under two hours to print while requiring no attention, which I vastly prefer to tending the Sherline.

The wood pocket is 7 mm deep and the grips stand 6.5 mm tall, leaving just enough room for three blobs of acrylic adhesive to hold them together. After squishing the grips into their pockets, a pair of right angles aligned everything while the adhesive cured:

Ironing weight - grip adhesive curing
Ironing weight – grip adhesive curing

Mary asked for a longer weight for a place mat project, with a slightly narrower block to compensate for the additional length:

Ironing weight - seam ironing B
Ironing weight – seam ironing B

The grip and pocket were the same size, so it was just a matter of tweaking the block size and cutting more wood.

All in all, a quick project with satisfying results!

The OpenSCAD source code as a GitHub Gist:

// Oak ironing weight finger grips
// Ed Nisley KE4ZNU 2023-01
Layout = "Show"; // [Block,Grip,Show,Build]
//- Extrusion parameters must match reality!
/* [Hidden] */
ThreadThick = 0.25;
ThreadWidth = 0.40;
HoleWindage = 0.2;
Protrusion = 0.1; // make holes end cleanly
inch = 25.4;
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
ID = 0;
OD = 1;
LENGTH = 2;
//----------
// Dimensions
// Length along X axis
Block = [250.0,50.0,39.0]; // overall wood block
BlockRadius = 10.0;
CornerRadius = 10.0;
Kerf = 0.2;
Socket = [160.0,25.0,6.5]; // raw recess into block
SocketRadius = Socket.y/2;
echo(Socket=Socket,SocketRadius=SocketRadius);
WallThick = ThreadWidth; // Thinnest printed wall
Clearance = 0.5; // between grip and recess
GripBlock = Socket - [2*Clearance,2*Clearance,Clearance];
GripBlockRadius = SocketRadius - Clearance;
echo(GripBlock=GripBlock);
GripDepth = 5.0; // finger grip recess
GripRecess = [GripBlock.x - 2*WallThick,GripBlock.y - 2*WallThick,GripDepth];
GripRecessRadius = GripBlockRadius - WallThick;
echo(GripRecess=GripRecess,GripRecessRadius=GripRecessRadius);
GripChordRadius = (pow(GripDepth,2) + pow(GripRecess.y,2)/4) / (2*GripDepth);
echo(GripChordRadius=GripChordRadius);
NumSides = 4*8;
//----------
// Shapes
module WoodBlock() {
difference() {
hull()
for (i=[-1,1], j=[-1,1]) // rounded block
translate([i*(Block.x/2 - BlockRadius),j*(Block.y/2 - BlockRadius),-Block.z/2])
cylinder(r=BlockRadius,h=Block.z,$fn=NumSides);
for (j=[-1,1]) // grip socket
translate([0,j*(Block.y/2 + Protrusion),0])
rotate([j*90,0,0])
hull() {
for (i=[-1,1])
translate([i*(Socket.x/2 - SocketRadius),(Socket.y/2 - SocketRadius),0])
cylinder(r=SocketRadius,h=Socket.z + Protrusion,$fn=NumSides);
}
cube([2*Block.x,2*Block.y,Kerf],center=true);
}
}
module Grip() {
difference() {
hull()
for (i=[-1,1]) // overall grip block
translate([i*(GripBlock.x/2 - GripBlockRadius),0,0])
cylinder(r=GripBlockRadius,h=GripBlock.z,$fn=NumSides);
hull() {
for (i=[-1,1]) // grip recess
translate([i*(GripBlock.x/2 - GripRecessRadius - WallThick),
0,
GripChordRadius + GripBlock.z - GripDepth])
sphere(r=GripChordRadius,$fn=NumSides);
}
}
}
//----------
// Build them
if (Layout == "Block")
WoodBlock();
if (Layout == "Grip")
Grip();
if (Layout == "Show") {
color("Brown")
WoodBlock();
color("Silver")
for (j=[-1,1])
translate([0,j*(Block.y/2 - GripBlock.z),0])
rotate([j*-90,0,0])
Grip();
}
if (Layout == "Build") {
for (i=[-1,1])
translate([i*(Block.y/2 - GripBlock.z),0,0])
rotate([0,0,90])
Grip();
}

Tailor’s Clapper: CNC Pocketing

Separating the interior contour of the finger grip from its overall shape let me reduce the woodworking to a simple pocketing operation:

Ironing Weight Finger Grip
Ironing Weight Finger Grip

Start by aligning the finished block to put the joint between the pieces parallel to the X axis, then touch off at the center:

Ironing Weight - alignment
Ironing Weight – alignment

A pair of clamps screwed to the tooling plate act as fixtures to align the block when it’s flipped over to mill the other pocket.

Just to see how it worked, I set up a GCMC program to produce a trochoidal milling pattern using the sample program:

Tailors Clapper - Pocket Milling Path
Tailors Clapper – Pocket Milling Path

Now, most folks would say the Sherline lacks enough speed and stiffness for trochoidal milling:

Ironing weight - trochoidal milling
Ironing weight – trochoidal milling

Aaaand I would agree with them: chugging along at 24 in/min = 600 mm/min doesn’t put the 10 k RPM spindle speed to good use. Fortunately, oak doesn’t require much in the way of machine stiffness and the trochoid path does ensure good chip clearance, so there’s that.

If I had to do a lot of trochoid milling, I’d tweak the GCMC sample code to short-cut the return path across the circle diameter, rather than air-cut the last half of every circumference.

The code starts by emptying a circular pocket so the trochoid path begins in clear air, rather than trenching into solid wood.

Eventually it finishes the pocket:

Ironing weight - grip pocket
Ironing weight – grip pocket

After the trochoid finishes, one climb-milling pass around the perimeter clears the little ripple between each trochoid orbit.

Flip it over, clamp it down, touch off the middle, and do it all again.

The next step is filling those pockets with a pair of comfy grips.

The GCMC source code as a GitHub Gist:

// Ironing weight pocketing
// Ed Nisley KE4ZNU - 2023-01
//-----
// Library routines
include("/opt/gcmc/example/cc_hole.inc.gcmc");
include("varcs.inc.gcmc");
include("tracepath_comp.inc.gcmc");
include("trochoidal.inc.gcmc");
/*
include("tracepath.inc.gcmc");
include("engrave.inc.gcmc");
*/
//-----
// Useful constants
SafeZ = 10.0mm; // above all obstructions
TravelZ = 2.0mm; // within engraving / milling area
BlockHome = [0.0mm,0.0mm,TravelZ]; // Origin on surface at center of pocket
FALSE = 0;
TRUE = !FALSE;
//-----
// Overall values
Socket = [160.0mm,25.0mm,7.0mm]; // raw grip recess into block
RoundEnds = TRUE; // TRUE for smooth rounded endcaps
SocketRadius = RoundEnds ? Socket.y/2 : 10.0mm;
comment("SocketRadius: ",SocketRadius);
CutterDia = 6.32mm - 0.15; // actual cutter diameter - windage
MillStep = 0.25 * CutterDia; // stepover in XY plane
comment("CutterDia: ",CutterDia," MillStep: ",MillStep);
MillClean = MillStep/2;
PlungeSpeed = 150.0mm; // cutter Z plunge into work
MillSpeed = 600.0mm; // XY speed
if (CutterDia > SocketRadius) {
error("Cutter too large for corner radius");
}
CornerOC = head(Socket,2) - 2*[SocketRadius,SocketRadius];
comment("CornerOC: ",CornerOC);
Corners = RoundEnds ? // rear left CCW around slot
{-CornerOC/2, CornerOC/2} :
{[-CornerOC.x,CornerOC.y]/2, [-CornerOC.x,-CornerOC.y]/2, [CornerOC.x,-CornerOC.y]/2, CornerOC/2};
comment("Corners: ", Corners);
if (RoundEnds) {
SlotPerimeter = {[0.0mm,Socket.y/2,-Socket.z]}; // entry point at center rear
SlotPerimeter += {Corners[0] + [0.0mm,SocketRadius]};
SlotPerimeter += varc_ccw([-SocketRadius,-SocketRadius],SocketRadius) + SlotPerimeter[-1];
SlotPerimeter += varc_ccw([+SocketRadius,-SocketRadius],SocketRadius) + (Corners[0] + [-SocketRadius,0.0mm]);
SlotPerimeter += {Corners[1] + [0.0mm,-SocketRadius]}; // across front
SlotPerimeter += varc_ccw([+SocketRadius,+SocketRadius],SocketRadius) + SlotPerimeter[-1];
SlotPerimeter += varc_ccw([-SocketRadius,+SocketRadius],SocketRadius) + (Corners[1] + [+SocketRadius,0.0mm]);
}
else {
SlotPerimeter = {[0.0mm,Socket.y/2,-Socket.z]}; // entry point at center rear
SlotPerimeter += {Corners[0] + [0.0mm,SocketRadius]};
SlotPerimeter += varc_ccw([-SocketRadius,-SocketRadius],SocketRadius) + SlotPerimeter[-1];
SlotPerimeter += {Corners[1] + [-SocketRadius,0.0mm]};
SlotPerimeter += varc_ccw([+SocketRadius,-SocketRadius],SocketRadius) + SlotPerimeter[-1];
SlotPerimeter += {Corners[2] + [0.0mm,-SocketRadius]}; // across front
SlotPerimeter += varc_ccw([SocketRadius,SocketRadius],SocketRadius) + SlotPerimeter[-1];
SlotPerimeter += {Corners[3] + [SocketRadius,0.0mm]};
SlotPerimeter += varc_ccw([-SocketRadius,SocketRadius],SocketRadius) + SlotPerimeter[-1];
}
//--- Begin cutting
goto([-,-,TravelZ]);
goto(BlockHome);
if (!RoundEnds) { // clear corners outward of main pocket
foreach(Corners; xy) {
comment("Plunge corner at: ",xy);
feedrate(PlungeSpeed);
goto(xy);
move([-,-,-Socket.z]);
comment(" pocket");
feedrate(MillSpeed);
cc_hole(xy,(SocketRadius - MillClean),CutterDia/2,MillStep,-Socket.z);
goto([-,-,TravelZ]);
comment(" done!");
}
}
comment("Open slot");
TrochRadius = (Socket.y - CutterDia)/2 - MillClean;
TrochPath = {[-(Socket.x/2 - TrochRadius - CutterDia/2 - MillStep),TrochRadius],
[ (Socket.x/2 - TrochRadius - CutterDia/2 - MillStep),TrochRadius]};
comment(" clear landing zone");
xy = [TrochPath[0].x,0.0mm];
feedrate(PlungeSpeed);
goto(xy);
move([-,-,-Socket.z]);
feedrate(MillSpeed);
cc_hole(xy,Socket.y/2 - MillClean,CutterDia/2,MillStep,-Socket.z);
goto([-,-,TravelZ]);
comment(" trochoid pocket milling");
feedrate(MillSpeed);
trochoid_move(TrochPath[0],TrochPath[1],
-Socket.z, TrochRadius, MillStep);
goto([-,-,TravelZ]);
comment("Clean slot perimeter");
feedrate(MillSpeed);
goto([-,-,-Socket.z]);
tracepath_comp(SlotPerimeter,CutterDia/2,TPC_CLOSED + TPC_LEFT + TPC_ARCIN + TPC_ARCOUT);
goto([-,-,TravelZ]);
goto(BlockHome);
#!/bin/bash
# Ironing weight finger grip pocketing
# Ed Nisley KE4ZNU - 2023-01
Flags='-P 4 --pedantic' # quote to avoid leading hyphen gotcha
# Set these to match your file layout
LibPath='/opt/gcmc/library'
Prolog='prolog.gcmc'
Epilog='epilog.gcmc'
#-----
gcmc $Flags \
--include "$LibPath" --prologue "$Prolog" --epilogue "$Epilog" \
"Ironing weight grip pocket.gcmc" > "Grip pocket.ngc"
view raw pocket.sh hosted with ❤ by GitHub

Ironing Weight, a.k.a. Tailor’s Clapper: Overview

Mary wanted some ironing weights, formally known as tailor’s clappers, to produce flatter seams as she pieced fabric together:

Ironing weight - flattened seam
Ironing weight – flattened seam

The weights are blocks of dense, hard, unfinished wood:

Ironing weight - seam ironing A
Ironing weight – seam ironing A

One can buy commercial versions ranging from cheap Amazon blocks to exotic handmade creations, but a comfortable grip on a block sized to Mary’s hands were important. My lack of woodworking equipment constrained the project, but the picture shows what we settled on.

The general idea is a rounded wood block with 3D printed grips:

Ironing Weight Finger Grip
Ironing Weight Finger Grip

All other clappers seem to have a simple slot routed along the long sides, presumably using a round-end or ball cutter, which means the cutter determines the shape. This being the age of rapid prototyping, I decided to put the complex geometry in an easy-to-make printed part inserted into a simple CNC-milled pocket.

The first pass at the grip models:

Ironing Weight Finger Grip - slicer preview
Ironing Weight Finger Grip – slicer preview

Both recesses came from spheres sunk to their equators with their XY radii scaled appropriately, then hulled into the final shape. Customer feedback quickly reported uncomfortably abrupt edges along the top and bottom:

Ironing Weight - maple prototype
Ironing Weight – maple prototype

We also decided the straight-end design didn’t really matter, so all subsequent grips have rounded ends to simplify milling the pocket into the block.

With the goal in mind, the next few posts will describe the various pieces required to make a nice tailor’s clapper customized to fit the user’s hand.

Linux Where You Least Expect It

A price / coupon scanner in a nearby CVS evidently woke up dead:

CVS Price Scanner - Linux boot screen
CVS Price Scanner – Linux boot screen

Yup, it’s a Linux console boot log, with the last line suggesting something horrible happened inside the device mapper:

A start job is running for dev-mapper-cryptswap1.device

The systemd timing status shows it’s been stuck for a while and has no hope of rescue:

(2d 1h 41min 10s / no limit)

I’d reboot that sucker if it had a keyboard …

Holly Coaster: Improved Mirror Setup

Other than demonstrating that it’s possible to laser-engrave a 3 mm deep pocket in a ¼ inch thick piece of scrap paneling, the process didn’t have much to recommend it:

Holly Coaster - mirror flaws
Holly Coaster – mirror flaws

So I re-did the layout to put the 3 mm mirror in 3 mm thick plywood:

Holly Coaster - overview
Holly Coaster – overview

The coaster has a self-adhesive cork pad on the bottom, which required an intermediate adhesive layer holding the aluminized Mylar reflector on the bottom of the mirror to brighten the colored areas.

The LightBurn layout shows all the pieces:

Holly Mirror Coaster - LB layout
Holly Mirror Coaster – LB layout

The plywood cuts with the good side down, although “good” is certainly a judgement call with B/BB grade plywood. I cover the good side with blue painter’s tape to reduce scorch marks. In a real application, you’d do some sanding and finishing, probably before cutting; in this case, I want to see what happens to bare wood in coaster duty.

Engrave and cut the mirror with the backing upward:

Holly Coaster - removing mirror layer
Holly Coaster – removing mirror layer

The tracer rounds may be burning aluminum.

I colored the engraved areas with fat-tip permanent markers, despite knowing the alcohol will crack the acrylic. In real life, you’d use spray paint, probably with laser-cut tape masks.

The adhesive layer extends 2 mm beyond the mirror perimeter to stick onto the bottom face of the plywood:

Holly Coaster - adhesive placement
Holly Coaster – adhesive placement

Peeling off the paper reveals the adhesive tape stuck to the back side of the mirror:

Holly Coaster - adhesive exposed
Holly Coaster – adhesive exposed

Apply the similarly embiggened aluminized Mylar to the adhesive:

Holly Coaster - mylar placed
Holly Coaster – mylar placed

Cutting the holly shape directly from the original foot-square adhesive sheet lets me tuck smaller shapes into the remaining uncut areas. In a production environment, however, joining the Mylar and adhesive (perhaps using pre-cut squares), then cutting them as one sheet would definitely simplify the process.

Then peel-n-stick a cork disk (thus explaining why the plywood is exactly 4 inch OD) on the bottom:

Holly Coaster - edge view
Holly Coaster – edge view

I’ve been aligning the cork by feel, which explains the half-millimeter overhang along the right side. Inexplicably, I have yet to justify an alignment fixture.

The LightBurn SVG layout as a GitHub Gist:

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Frosted Snowflakes

After the first two snowflake coasters, it finally penetrated my thick skill that putting a 1 mm hole in the flake cut from the center of the plywood would convert it into a decorative window hanging:

Snowflake Hanger - plywood
Snowflake Hanger – plywood

Admittedly, I may be using the word “decorative” in a manner you had not previously encountered, but work with me on this.

Cutting a similar flake from transparent acrylic looks better:

Snowflake Hanger - blue acrylic
Snowflake Hanger – blue acrylic

Transparent acrylic turned out to be, well, too transparent, so I set up a LightBurn layout to “engrave” a light frosting on the flake before cutting it out:

Snowflake Hangers - engraving in situ
Snowflake Hangers – engraving in situ

That worked for all subsequent flakes, but I had to do something about the first few flakes. After realizing that the time to engrave an object depends only on its width, I set up a rectangle with the proper parameters, snugged two forlorn flakes next to each other, and fired the laser:

Snowflake Hangers - retroactive engraving
Snowflake Hangers – retroactive engraving

I thought using cardboard was a Good Idea™ for a stable backing, but lightly vaporizing the top layer produced an unbelievable amount of filth:

Snowflake Hangers - frosted
Snowflake Hangers – frosted

I had to scrub those poor flakes with dish detergent and a toothbrush to get them even close to their former pristine state; the blue one may never recover.

Anyhow, frosted flakes look good if you don’t look closely:

Snowflake Hangers - frosted
Snowflake Hangers – frosted

The grid pattern comes from the window screen in direct sunlight; the vertical bars are DIY BirdSavers.

The LightBurn layout produces 120 mm coasters to fit my 20 ounce mugs:

Snowflake Coaster 120 mm - LB Layout
Snowflake Coaster 120 mm – LB Layout

You get two hanging flakes: one plain plywood and one frosted acrylic!

The LightBurn SVG layout as a GitHub Gist:

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.