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.

Tek Circuit Computer: Cursor Fixture Adhesion

After removing debris, flattening the top surface, and generally paying more attention to detail, the PETG sheet has much better adhesion to the fixture:

Tek CC - Milled cursor - cleaned fixture
Tek CC – Milled cursor – cleaned fixture

This time, I traced the inside of a drag-knife cut cursor to extract the blank from the stock and, yes, used new double-sided tape under the lower white protective film on the PETG.

Fewer air bubbles means better adhesion:

Tek CC - Milled cursor - fixture adhesion
Tek CC – Milled cursor – fixture adhesion

Spinning the 1/8 inch end mill at about 5000 RPM produced finer swarf at the Sherline’s maximum 609 mm/min = 24 inch/min pace, with less uplift. I suspect Moah RPMs! would be even better, constrained by melting the plastic into heartache & confusion.

Scribe the hairline with the diamond tool, ease the finished cursor off the fixture, scribble Sharpie into the scratch, and wipe

Tek CC - Milled cursor - second try
Tek CC – Milled cursor – second try

It’s Pretty Good™ when seen against an un-laminated bottom deck drawn with a Pilot V5RT pen:

Tek CC - Milled cursor - unlaminated bottom deck
Tek CC – Milled cursor – unlaminated bottom deck

The diamond point tears a slightly gritty path through the PETG, which then looks a bit more granular than a real hairline. I’ve been using four passes for emphasis; perhaps fewer would be better.

Tek Circuit Computer: Cursor Milling

The white separating film on the double-sided tape makes the cursor milling fixture look presentable:

Tek CC - Cursor milling fixture - 2-side tape applied
Tek CC – Cursor milling fixture – 2-side tape applied

Some deft X-acto knife work exposed the trench around what will be the cursor’s perimeter, in the hope of keeping tape stickiness out of the milling cutter.

Peeling off the white film and sticking a PETG cursor blank to the tape reveals I didn’t do a particularly good job of cleaning the rubble from the trench edges:

Tek CC - Milled cursor - bad tape application
Tek CC – Milled cursor – bad tape application

These PETG sheets arrive with a transparent film on one side and a white film on the other. The picture shows the white film on the bottom of the PETG sheet, with the dark areas corresponding to places where the film sticks to the tape and the tape sticks to the fixture. The lighter areas show an air gap in (at least) one of those interfaces; given the amount of clutter, I think it’s mostly between the tape and the fixture.

I milled the cursor with a 1/8 inch = 3.175 mm cutter:

Tek CC - Milled cursor - outline
Tek CC – Milled cursor – outline

The ball of swarf around the cutter wasn’t as threatening as it appears, because it had very little adhesive holding it together. The rows of swarf surrounding the PETG show why putting the tape all over the fixture isn’t a particularly good idea. ‘Nuff said.

Engraving the hairline with the diamond drag bit was entirely uneventful:

Tek CC - Milled cursor - hairline scribe
Tek CC – Milled cursor – hairline scribe

Four passes at Z=-2 mm = 300 g downforce put a delicate scratch across the surface. Run a fat black Sharpie along the hairline, wipe off the excess with denatured alcohol, and peel the white film from the other side:

Tek CC - Milled cursor - first try
Tek CC – Milled cursor – first try

It’s sitting atop the doodle giving the dimensions, such as they are, for the milling fixture.

The hairline came out so fine it makes the Pilot V5RT ballpoint pen lines look downright chunky:

Tek CC - Yellow Cardstock - Pilot V5RT - Milled Cursor
Tek CC – Yellow Cardstock – Pilot V5RT – Milled Cursor

Seen over the engraving test piece with scraped Testors paint, however, things look just the way they should:

Tek CC - Engraved - Testors Paint - Milled Cursor
Tek CC – Engraved – Testors Paint – Milled Cursor

In a techie kind of way, of course, which is the only way that matters on Planet Slipstick …

Fu Mask Cutting Templates

A local hospital contacted Mary’s quilting group to sew up cloth covers to prolong the life of their medical-grade N95 masks. Their recommended pattern, the Fu Face Mask from the FreeSewing group, comes in three sizes:

Freesewing - Fu Mask
Freesewing – Fu Mask

N.B.: Use their original PDF, because a JPG picture probably won’t come out at the right size.

Also N.B.: Used by itself, this is not a medical-grade filter mask.

The patterns do not include the usual 1/4 inch seam allowance around the outside, so I cranked out 3D printed plastic cutting templates.

If you’re not interested in 3D printing, 2D print the PDF file on cardboard, sketch a seam allowance, and cut it out, as quilters have been doing since slightly after home printers happened.

The plan of attack:

  • Convert mask outlines into a bitmap image (GIMP)
  • Create Bezier curves by tracing outlines (Inkscape)
  • Save curves as SVG files
  • Convert SVG into solid model (OpenSCAD)
  • Add stiffening ribs &c
  • Save as STL solid model
  • Slice into G-Code file (Slic3r)
  • Fire the M2!

So, we begin …

Import the PDF into The GIMP, delete the text & suchlike, convert to monochrome, and save the pattern outlines as a PNG file:

Fu Facemask - outlines
Fu Facemask – outlines

It turns out Inkscape can directly import the PDF, but it valiantly tries to convert all the text and the incidental graphic elements, none of which will be useful in this situation. It’s easier to delete them in The GIMP and make a bank shot off a PNG file.

Update: Scruss’s comment provides a much simpler workflow!

Import the PNG into Inkscape and trace one outline with the Bezier curve tool:

Fu Mask - Inkscape Bezier trace
Fu Mask – Inkscape Bezier trace

If you squint really carefully, you’ll see Bezier control handles sticking out of the nodes. I laid three nodes along the top arc and four along the right side, but do what’cha like; the Insert key or Shift+I inserts and Delete removes nodes. It’s easier to center a node in the middle of the PNG line with snapping turned off: Shift+drag while mousing or globally with #.

You could unleash the bitmap auto-tracer, but it generates a bazillion uselessly tiny Bezier curves.

When you’re happy, select and copy the path with Ctrl+C, paste it into a shiny new Inkscape document (Ctrl+N) with Ctrl-V, save it with a catchy file name like Fu Mask - Small - nominal.svg, and close that document to return to the document with the PNG outlines and the original path.

Select the original path again, create a dynamic offset with Ctrl+J, open the XML editor with Ctrl+Shift+X (which automagically selects the proper SVG element), and change the inkscape:radius value from 0 to 6.35 (mm, which everyone should use) to get a 1/4 inch seam allowance:

Fu Mask - Inkscape XML Editor - Offset radius
Fu Mask – Inkscape XML Editor – Offset radius

The path will puff out with curved corners:

Fu Mask - Inkscape offset
Fu Mask – Inkscape offset

Copy into a new document, save as Fu Mask - Small - seam allowance.svg, and close.

Repeat that process for each of the three mask sizes to create three pairs of SVG files: the nominal mask outline and the corresponding seam allowance outline for each size.

The OpenSCAD program imports the SVG files, removes the nominal outline from within the seam allowance to leave the outline, adds stiffening ribs, and stamps an ID letter on both sides of the central button:

Fu Mask Cutting Template - Small - solid model
Fu Mask Cutting Template – Small – solid model

Choose one of the three sizes with the OpenSCAD customizer, save the resulting model as an STL file, repeat for the three sizes, and you’re done.

This process can convert any outline paths in SVG files into cutting templates, so, should the Fu Mask not suit your fancy, Use The Source.

For convenience, the STL files are on Thingiverse.

From the comments, a Washington hospital uses a similar pattern: their PDF with assembly instructions.

The OpenSCAD source code as a GitHub Gist:

// Fu Mask cutting templates
// Ed Nisley - KE4ZNU - 2020-03
// Mask patterns from:
// More info on my blog:
/* [Mask Size] */
Name = "Small"; // [Small, Medium, Large, Test]
/* [Hidden] */
Templates = [ // center ID letter and file name
["T","Test"], // for whatever you like
T_ID = 0; // Template indexes
T_NAME = 1;
BarThick = 4.0; // template thickness
HubOD = 20.0; // center button diameter
// These should match slicer values
ThreadThick = 0.25;
ThreadWidth = 0.40;
Protrusion = 0.1; // make clean holes
//--- Build it
t = Templates[search([Name],Templates,1,1)[0]]; // find template index
Dir = "./";
FnOuter = str(Dir,"Fu Facemask - ",t[T_NAME]," - seam allowance.svg");
FnInner = str(Dir,"Fu Facemask - ",t[T_NAME]," - nominal.svg");
difference() {
linear_extrude(BarThick,convexity=5) {
intersection() {
union() {
square([200.0,5.0],center=true); // horizontal bar
square([5.0,200.0],center=true); // vertical bar
circle(d=HubOD); // central button
difference() { // cutting template!
translate([0,0,BarThick - ThreadThick]) // top ID recess
cylinder(d=HubOD - 6*ThreadWidth,h=ThreadThick + Protrusion);
translate([0,0,-Protrusion]) // bottom ID recess
cylinder(d=HubOD - 6*ThreadWidth,h=ThreadThick + Protrusion);
translate([0,0,2*BarThick/3]) // top ID
mirror([1,0,0]) // bottom ID

Verily, there’s nothing like a good new problem to take your mind off all your old problems …

Drag Knife Calibration: Downforce and Speed

The drag knife faceplant suggested I must pay a bit more attention to fundamentals, so, with a 60° drag knife blade sticking out a reasonable amount, the next step is to see what effect the cutting “depth” (a.k.a. downforce) and speed have on the outcome.

A smidge of GCMC code later:

Drag Knife Cal - depth - overview - Camotics sim
Drag Knife Cal – depth – overview – Camotics sim

It’s not obvious, but each pattern steps downward by 0.5 mm from left to right. With the spring force equal to 375 g + 57 g/mm, the downforce ranges from 400 to 520 g over the five patterns.

Laminated scrap, meet drag knife:

Drag Knife Cal - Depth - as cut
Drag Knife Cal – Depth – as cut

Pulling up on the surrounding scrap left the patterns on the sticky mat:

Drag Knife Cal - Depth - extracted
Drag Knife Cal – Depth – extracted

Which suggested any cutting force would work just fine.

Flushed with success, I cut some speed variations at the minimum depth of Z=-0.5 mm = 400 g:

Drag Knife Cal - Speed - 0.5 mm - as cut
Drag Knife Cal – Speed – 0.5 mm – as cut

The blade cut through the top laminating film, the paper, and some sections of the bottom film, but mostly just scored the latter.

Repeating at Z=-1.5 mm = 460 g didn’t look much different:

Drag Knife Cal - Speed - 1.5 mm - as cut
Drag Knife Cal – Speed – 1.5 mm – as cut

However, the knife completely cut all the patterns:

Drag Knife Cal - Speed - 1.5 mm - extracted
Drag Knife Cal – Speed – 1.5 mm – extracted

As far as I can tell, the cutting speed doesn’t make much difference, although the test pattern is (deliberately) smooth & flowy like the Tek CC deck outlines. I’d been using 1000 mm/min and 2000 mm/min seems scary-fast, so 1500 mm/min may be a good compromise.

The GCMC source code as a GitHub Gist:

// Calibrate Drag Knife - speed & feed
// Ed Nisley - KE4ZNU
// 2020-03 values for MPCNC
// Dimensions
CutIncr = -0.5mm;
BottomCutZ = -2.5mm;
SpeedRatio = 2.0;
MaxSpeed = 2000mm;
MinSpeed = MaxSpeed / 8;
StripWidth = 10mm;
CornerRadius = StripWidth/2;
PatternSize = StripWidth * [3,3];
PatternSpace = 1.25;
SafeZ = 10.0mm; // above all obstructions
TravelZ = 2.0mm; // within engraving / milling area
FALSE = 0;
if (!isdefined("TestSelect")) {
TestSelect = "Depth";
comment("Test Selection: ",TestSelect);
// One complete pattern
// Centered at ctr, ctr.z=cut depth
function Pattern(ctr) {
local d1 = CornerRadius; // useful relative distances
local d2 = 2*d1;
local d3 = 3*d1;
local d4 = 4*d1;
goto([-,-,TravelZ]); // set up for entry move
goto(head(ctr,2) + [-d2,d3]);
move([ctr.x + d2,-,ctr.z]); // enter to cut depth
move_r([d4,0]); // re-cut entire entry path
goto([-,-,TravelZ]); // exit to surface
// goto(head(ctr,2));
// Start cutting!
if (TestSelect == "Depth") {
comment("Depth variations");
s = MaxSpeed / 2;
c = [0,0,-]; // initial center at origin
for (c.z = CutIncr; c.z >= BottomCutZ; c.z += CutIncr) {
comment("At: ",c," speed:",s);
c.x += PatternSpace * PatternSize.x;
if (TestSelect == "Speed") {
comment("Speed variations");
c = [0,0,-2mm]; // initial center at origin
for (s = MinSpeed; s <= MaxSpeed; s *= SpeedRatio) {
comment("At: ",c," speed: ",s);
c.x += PatternSpace * PatternSize.x;

Drag Knife Blade Extension

The battered corner of my bench scale shows it’s been knocking around for quite a while, but the drag knife blade tip seems pretty close to the first 0.5 mm division:

Drag Knife Blade - 0.5 mm
Drag Knife Blade – 0.5 mm

The blade extends from the LM12UU holder for the MPCNC.

Scribbling the blade across a scrap of laminated yellow card stock (about 0.4 mm thick) showed it didn’t cut all the way through the bottom plastic layer, even with the spring mashed flat.

So I screwed it out to 0.7 mm:

Drag Knife Blade - 0.7 mm
Drag Knife Blade – 0.7 mm

The scale isn’t quite parallel to the blade axis and maybe it’s sticking out 0.8 mm; setting a drag knife’s blade extension obviously isn’t an exact science.

In any event, another scribble slashed all the way through the laminated deck without gashing the sacrificial cardboard atop my desk, which seems good enough.

Round Soaker Hose Splint

One of two new round rubber soaker hoses arrived with a slight crimp, enough to suggest it would crumble at an inopportune moment. Rather than return the hose for something that’s not an obvious failure, I clamped the crimp:

Round Soaker Hose Splice - top
Round Soaker Hose Splice – top

Unlike the clamps for the punctured flat soaker hoses, this one doesn’t need to withstand much pressure and hold back a major leak, so I made the pieces a bit thicker and dispensed with the aluminum backing plates:

Round Soaker Hose Splice - bottom
Round Soaker Hose Splice – bottom

The solid model is basically the same as for the flat hoses, with a slightly oval cylinder replacing the three channels:

Round Soaker Hose Splice - OpenSCAD model
Round Soaker Hose Splice – OpenSCAD model

The OpenSCAD source code as a GitHub Gist:

// Rubber Soaker Hose Splice
// Ed Nisley KE4ZNU 2020-03
Layout = "Build"; // [Hose,Block,Show,Build]
TestFit = false; // true to build test fit slice from center
//- 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;
// Dimensions
// Hose lies along X axis
Hose = [200,14.5,13.6]; // X = longer than anything else
// 8-32 stainless screws
Screw = [4.1,8.0,3.0]; // OD = head LENGTH = head thickness
Washer = [4.4,9.5,1.0];
Nut = [4.1,9.7,6.0];
Block = [50.0,Hose.y + 2*Washer[OD],4.0 + 1.5*Hose.z]; // overall splice block size
echo(str("Block: ",Block));
Kerf = 1.0; // cut through middle to apply compression
CornerRadius = Washer[OD]/2;
NumScrews = 3; // screws along each side of cable
ScrewOC = [(Block.x - 2*CornerRadius) / (NumScrews - 1),
Block.y - 2*CornerRadius,
2*Block.z // ensure complete holes
echo(str("Screw OC: x=",ScrewOC.x," y=",ScrewOC.y));
// Useful routines
module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
FixDia = Dia / cos(180/Sides);
cylinder(d=(FixDia + HoleWindage),h=Height,$fn=Sides);
// Hose shape
// This includes magic numbers measured from reality
module HoseProfile() {
NumSides = 12*4;
// Outside shape of splice Block
// Z centered on hose rim circles, not overall thickness through center ridge
module SpliceBlock() {
difference() {
for (i=[-1,1], j=[-1,1]) // rounded block
translate([i*(Block.x/2 - CornerRadius),j*(Block.y/2 - CornerRadius),-Block.z/2])
for (i = [0:NumScrews - 1], j=[-1,1]) // screw holes
translate([-(Block.x/2 - CornerRadius) + i*ScrewOC.x,
-(Block.z/2 + Protrusion)])
PolyCyl(Screw[ID],Block.z + 2*Protrusion,6);
cube([2*Block.x,2*Block.y,Kerf],center=true); // slice through center
// Splice block less hose
module ShapedBlock() {
difference() {
// Build them
if (Layout == "Hose")
if (Layout == "Block")
if (Layout == "Show") {
difference() {
if (Layout == "Build") {
SliceOffset = TestFit && !NumScrews%2 ? ScrewOC.x/2 : 0;
intersection() {
if (TestFit)
union() {