The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Category: Software

General-purpose computers doing something specific

  • Craftsman Narrow Spring Clamp Jaws

    Craftsman Narrow Spring Clamp Jaws

    Quite some years ago I (ab)used several Craftsman (I’ve had them a long time) long narrow spring clamps while calibrating some thermocouples, much to the detriment of their rubbery jaws. The original gasket compound hackery didn’t work well, but eventually some of it stuck and I’ve been using them ever since:

    Craftsman Long Spring Clamp Jaw Pad - gasket compound
    Craftsman Long Spring Clamp Jaw Pad – gasket compound

    Now that I can 3D print TPU, however, it was time to fit better jaws in those things.

    The solid model eventually looked like this:

    Craftsman Long Spring Clamp Jaw Pads - solid model detail
    Craftsman Long Spring Clamp Jaw Pads – solid model detail

    The OpenSCAD code is replete with juicy magic numbers based on what few measurements I could make, then tweaked to fit the jaw where it had to go:

    <<< snippage >>>
    
    NumPads = 2 * 4;
    
    PadThick = 1.3;
    TabHeight = 1.5;
    
    module Tab() {
    
      difference() {
        linear_extrude(TabHeight)
          trapezoid(h=4.5,w1=1.4,w2=1.2,rounding=0.2,anchor=FRONT);
        back(5.5)
          xrot(60)
            cuboid([5,5,10],anchor=FRONT);
      }
    }
    <<< snippage >>>
    
    if (Layout == "Build") {
    nx = ceil(sqrt(NumPads));
    ny = nx;
    
    for (nx = [0:nx-1], ny = [0:ny-1])
      right(nx*Gap)
        back(ny*Gap)
          union() {
            linear_extrude(PadThick)
              trapezoid(h=7.5,w1=5.2,w2=4.2,rounding=0.2,anchor=FRONT);
            up(PadThick)
              back(0.5)
                Tab();
        }
    }
    
    

    The results is basically too small for the Prusa MK4 with the 0.8 mm nozzle laying down 0.4 mm layers, but the Makergear M2 with its 0.35 mm nozzle was in the midst of a heart attack at the time (about which, more later).

    The PrusaSlicer preview shows the chunky result:

    Craftsman Long Spring Clamp Jaw Pad - PrusaSlicer 0.8mm nozzle × 0.4mm layer detail
    Craftsman Long Spring Clamp Jaw Pad – PrusaSlicer 0.8mm nozzle × 0.4mm layer detail

    Because they’re so small, I printed nine at a time to get reasonable cooling and repair all four clamps.

    A few iterations produced a workable size & shape:

    Craftsman Long Spring Clamp Jaw Pad - size trials vs final install
    Craftsman Long Spring Clamp Jaw Pad – size trials vs final install

    The nicely curved profile comes from gluing them in place while gripping sturdy closed-cell foam:

    Craftsman Long Spring Clamp Jaw Pad - foam clamping
    Craftsman Long Spring Clamp Jaw Pad – foam clamping

    I used E6000+ adhesive, mostly because the tube didn’t scamper out of the way, but classic E6000 with nasty solvents might be Even Better. Neither would bond to the clamp’s plastic, so the eventual repair will once again require prying out debris with a dental pick.

    After resucitating the M2 , I resliced the model with 0.2 mm layers:

    Craftsman Long Spring Clamp Jaw Pad - PrusaSlicer 0.4mm nozzle × 0.2mm layer - detail
    Craftsman Long Spring Clamp Jaw Pad – PrusaSlicer 0.4mm nozzle × 0.2mm layer – detail

    Which looks much prettier in real life:

    Craftsman Long Spring Clamp Jaw Pad - detail - 0.8 vs 0.4 mm nozzle
    Craftsman Long Spring Clamp Jaw Pad – detail – 0.8 vs 0.4 mm nozzle

    A small ziplock bag with those nine pretty jaws now hangs from the rail supporting Too Many Clamps against future need.

  • Electrolux Dust Brush: Replacement Rubber

    Electrolux Dust Brush: Replacement Rubber

    The Electrolux Model G vacuum cleaner that Came With The Previous House™ has a dust brush attachment resembling a creature from a deep sea trench:

    Electroxlux Brush Ball - OEM assembled
    Electroxlux Brush Ball – OEM assembled

    Those bristles recently fell out during a cleaning session:

    Electroxlux Brush Ball - OEM with brush
    Electroxlux Brush Ball – OEM with brush

    I will never know their backstory, but the rough-cut metal ring strongly suggests they’re a replacement for the original Electrolux bristles. Given that the Model G dates back to the early sixties, that puppy has been around for six decades and counting.

    The rubber insert in the aluminum ball had long since fossilized:

    Electroxlux Brush Ball - OEM rubber
    Electroxlux Brush Ball – OEM rubber

    Not knowing if it would come out in approximately one piece, a doodle records whatever dimensions I could measure:

    Electroxlux Brush Ball - exterior measurements
    Electroxlux Brush Ball – exterior measurements

    Surprisingly, it remained just flexible enough:

    Electroxlux Brush Ball - OEM rubber removed
    Electroxlux Brush Ball – OEM rubber removed

    So I could measure a few more rubber dimensions:

    Electroxlux Brush Ball - rubber measurements
    Electroxlux Brush Ball – rubber measurements

    And ball dimensions:

    Electroxlux Brush Ball - metal measurements
    Electroxlux Brush Ball – metal measurements

    After screwing around drawing Beziér curves for maybe half an hour, I realized a crude polygonal approximation would work perfectly well when 3D printed in TPU with a 0.8 mm nozzle:

    Electroxlux Brush Ball - show 5 view
    Electroxlux Brush Ball – show 5 view

    Which had the compelling advantage I could exactly specify the points and tweak them for an exact fit:

    SplitHeight = 15;
    SplitCube = [100,100,SplitHeight - Protrusion];
    
    NozzleHeight = 15 + SplitHeight;          // above top of ball nozzle
    
    Pts = [
      [61,11],[62,6],[61,2],[55,0],[48,0],    // outside bottom
      [38,5],[33,11],
      [31,SplitHeight],[26,SplitHeight],      // atop ball nozzle rim
      [26,18],[27,19],[31,NozzleHeight],[33,NozzleHeight],    // bristle reinforcement
      [39,19],[40,18],[40,16],
      [35,SplitHeight],
      [36,11],[40,6],                         // inside bottom
      [45,3],[48,2],[52,1],[55,1],
      [59,3],[60,6],[58,9],[59,11],                  // brush metal rim seat
      [61,11],
    ];
    
    

    Turn those points into a polygon, spin it into a ring, and add a ghostly plane to show where the model separates into printable halves:

    if (Layout == "Show") {
        scale([0.5,0.5,1.0])
          rotate_extrude(angle=Slice,start=0,convexity=5) polygon(Pts);
        up(SplitHeight)
          color("Orange",0.2)
            cuboid([100,100,Protrusion],anchor=BOTTOM);
    }
    

    The scale() compensates for the list of points having easily measured real-life diameters, but the polygon() must be at the radius away from the origin; you could divide all the X axis coordinates in the list by two. Dimensions along the Z axis remain unchanged.

    Like this:

    Electroxlux Brush Ball - show 360 view
    Electroxlux Brush Ball – show 360 view

    Split it along the ghostly plane into printable parts:

    if (Layout == "Build") {
      fwd(50)
        intersection() {
          scale([0.5,0.5,1.0])
            rotate_extrude(convexity=5) polygon(Pts);
          cuboid(SplitCube,anchor=BOTTOM);
        }
      back(30)
        intersection() {
          down(SplitHeight)
            scale([0.5,0.5,1.0])
              rotate_extrude(convexity=5) polygon(Pts);
          cuboid(SplitCube,anchor=BOTTOM);
        }
    
    }
    

    Like this:

    Electroxlux Brush Ball - build view
    Electroxlux Brush Ball – build view

    Run the model through PrusaSlicer, install the 0.8 mm nozzle in the MK4, load TPU filament, let the magic happens, then dab JB PlasticBonder urethane adhesive around the mating rim of the bottom part:

    Electroxlux Brush Ball - gluing part
    Electroxlux Brush Ball – gluing part

    Yeah, black would be more better, but tan is fine.

    Flip it over, eyeballometrically center the gooey ring in the wide rim of the nozzle, and let it cure.

    Which eventually looks like it grew that way:

    Electroxlux Brush Ball - replacement TPU
    Electroxlux Brush Ball – replacement TPU

    See? Crude low-poly models FTW!

    As usual, however, it takes two to make a thing go right:

    Electroxlux Brush Ball - TPU versions
    Electroxlux Brush Ball – TPU versions

    The first pass in the center front showed the outer rim needed a deeper recess to grip the metal ring around the bristles; I’m sure I could pry the bristles out of the second version if lives depended on it.

    I added the elongated nozzle to stand halfway up inside the bristles to keep them from collapsing inward under suction:

    Electroxlux Brush Ball - nozzle in brush
    Electroxlux Brush Ball – nozzle in brush

    It’s definitely better than it was!

    Might last another half-century; the original Electrolux vacuums are survivors.

  • Cardboard Spiders: Nesting Assistance

    Cardboard Spiders: Nesting Assistance

    Scaling the spider layout to match the 6.8 mm dual-layer cardboard originally holding a monitor turned out well:

    Cardboard Spider - garage right
    Cardboard Spider – garage right

    The LightBurn nesting routine needed three equal-sized sheets for all the pieces, but I wanted a spider for each of the two garage door lights:

    Cardboard Spider - garage left
    Cardboard Spider – garage left

    So I grouped pairs of the abdomen shapes in the obvious (to me) arrangement, selected all but the body and legs, and LightBurn nested them on a single sheet:

    Spider - Nested sheet 1
    Spider – Nested sheet 1

    It easily nested the body and legs on a second sheet:

    Spider - Nested sheet 2
    Spider – Nested sheet 2

    However, I needed that second sheet for the second spider:

    Cardboard Spider - sheet 1 cutting
    Cardboard Spider – sheet 1 cutting

    So I manually arranged one big leg and one small leg to fit each end of the four narrow box flaps to get sixteen legs:

    Cardboard Spider - leg cutting
    Cardboard Spider – leg cutting

    The two bodies (the weird shape on Sheet 2) barely fit on two of the four small box-closing flaps, two spiders emerged from the pieces adorned with dollops of hot-melt glue, and the cardboard stockpile got the two remaining small flaps that will surely come in handy for something.

    The eyes are fluorescent green acrylic sized for a press fit.

    I love it when a plan comes together …

  • Outdoor Lamp Glass Retainers

    Outdoor Lamp Glass Retainers

    Tucking seasonal decorations into the lamps bracketing the garage door would be much easier if the rubber buttons retaining the glass panes were squishy cylinders, rather than fossilized pebbles:

    Outdoor Lamp Glass Retainers - old vs new
    Outdoor Lamp Glass Retainers – old vs new

    The five tidy buttons on the right are black TPU, hot off the MK4’s printbed:

    Outdoor Lamp Glass Retainers - PrusaSlicer preview
    Outdoor Lamp Glass Retainers – PrusaSlicer preview

    The flange allows for some, shall we say, dimensional variation in the installation:

    Outdoor Lamp Glass Retainers - old vs new - installed
    Outdoor Lamp Glass Retainers – old vs new – installed

    They’re just a tube atop a cylinder:

    nx = ceil(sqrt(NumButtons));
    ny = nx;
    
    union()
    for (nx = [0:nx-1], ny = [0:ny-1])
      right(nx*(FlangeOD + Gap))
        back(ny*(FlangeOD + Gap))
          render()
          union() {
            cyl(FlangeHeight,d=FlangeOD,circum=true,anchor=BOTTOM);
            tube(ButtonOA.z,od=ButtonOA[OD],id=ScrewOD,circum=true,anchor=BOTTOM);
          }
    
    

    Feel free to set up your own dimensions, in the unlikely event you have lamps in need of similar buttons.

    Each of the three lamps has sixteen buttons and, although PrusaSlicer allows you to set the number of instances, its Arrange tool doesn’t make a tidy / compact layout. Doing the layout in OpenSCAD saves a bunch of hassle, with the overall union() turning the array into a single object.

    It might not be the best decoration, but it should suffice:

    Outdoor Lamp Glass Retainers - with decoration
    Outdoor Lamp Glass Retainers – with decoration

    Now, to make critters for the driveway …

  • Worm Bin Vent Covers

    Worm Bin Vent Covers

    The current iteration of our Tumbleweed Can-O-Worms suffers from leakage down the legs:

    Worm Bin - leg leakage
    Worm Bin – leg leakage

    Perhaps surprisingly, the whole process is odorless. This is in the basement where ugly doesn’t count for much.

    The leakage comes through the air vent holes at the top of each leg socket:

    Worm Bin Vent Cover - vent
    Worm Bin Vent Cover – vent

    Unsurprisingly, the “worm tea” (it ain’t water!) dripping from the bottom of the compost tray goes directly through the vent holes and down the leg. It’s not clear to me what they expected.

    A simple 3D printed black TPU cap atop the leg socket should prevent most of the leakage:

    Worm Bin Vent Cover - installed
    Worm Bin Vent Cover – installed

    The odd pattern comes from Concentric infill around the notch, even though Concentric infill seems better suited to round things and fat extrusion threads with a 0.8 mm nozzle than straight lines. The weird stuff on the vertical side is a reflection of the basement and phone flash from the smooth TPU layer edges.

    The tray with the worms and compost sits atop the strut between the socket and the body, with the cap allowing room for the screen keeping the worms out of the sump.

    Ribs on the underside of the cap hold it above the leg socket to let the air through:

    Worm Bin Vent Covers - solid model
    Worm Bin Vent Covers – solid model

    Although five caps fit the MK4’s platform, I printed one or two at a time while tweaking improving the design:

    Worm Bin Vent Covers - PrusaSlicer preview
    Worm Bin Vent Covers – PrusaSlicer preview

    This design is for the original Can-O-Worms shown in the second picture and may not quite fit the new version. We’ll know more when we empty the bottom tray.

    The OpenSCAD source code as a GitHub Gist:

    // Worm Bin Leg Vent Cover
    // Ed Nisley – KE4ZNU
    // 2026-10-16
    include <BOSL2/std.scad>
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    HoleWindage = 0.2;
    Protrusion = 0.01;
    NumSides = 8*2*3;
    $fn=NumSides;
    LayerHeight = 0.4; // print with 0.8 mm nozzle
    LayerWidth = 0.9;
    WallThick = 1.5*LayerWidth;
    VentOD = 62.0; // at bottom of cover
    VentTopClear = 9.0; // to next layer bottom
    VentSideClear = 11.0; // to bin side
    VentStrutWidth = 3.0;
    CoverHeight = 15.0;
    CoverOD = VentOD + 2*VentSideClear – 2*WallThick;
    NumRibs = 6;
    RibHeight = VentTopClear – WallThick – 2.0;
    RibThick = 2*WallThick;
    //—–
    // Build things
    difference() {
    union() {
    cyl(WallThick,d=CoverOD,circum=true,anchor=BOTTOM) position(TOP) {
    tube(CoverHeight – WallThick,od=CoverOD,wall=WallThick,circum=true,
    anchor=BOTTOM,spin=180/NumSides);
    for (i=[0:NumRibs-1])
    rotate(i*(360/NumRibs) + 180/NumRibs)
    right(CoverOD/3 – RibThick/2)
    cube([CoverOD/3,RibThick,RibHeight],anchor=BOTTOM);
    }
    up(WallThick)
    right(CoverOD/2 – WallThick)
    cube([VentSideClear,VentStrutWidth + 2*WallThick,RibHeight],anchor=BOTTOM+RIGHT);
    right(CoverOD/2 – WallThick + Protrusion)
    cube([VentSideClear – WallThick,VentStrutWidth + 2*WallThick,CoverHeight],anchor=BOTTOM+RIGHT);
    }
    down(Protrusion)
    right(CoverOD/2 – VentSideClear)
    cube([CoverOD,VentStrutWidth,2*CoverHeight],anchor=BOTTOM+LEFT);
    }

  • HQ Sixteen: Improved Anchor

    HQ Sixteen: Improved Anchor

    The original anchor / weight keeping the HQ Sixteen from rolling away suffered a disastrous fall, so I conjured a much better-looking replacement from the vasty digital deep:

    HQ Sixteen - improved anchor
    HQ Sixteen – improved anchor

    The handle and fancy nut came from the deceased pepper mill, sitting on laser-cut acrylic in a 3D printed base, with a drawer liner disk peeking out from underneath.

    The orange disk in the solid model represents the steel slug harvested from the old anchor:

    HQ Sixteen Anchor - solid model - Show view
    HQ Sixteen Anchor – solid model – Show view

    A ring of hot-melt glue under the weight keeps it from rattling around in there, with the snug-fitting central post encouraging good positioning. I made the acrylic lid sit flush with the top of the base by the simple expedient of running another ring of glue around it, plunking the base + weight atop it on a flat surface, then taking a deep breath while the glue solidified. The nut & screw thus compress a solid stack of acrylic + glue + steel + glue + PETG-CF, rather than just parts flying in formation.

    The OpenSCAD source code also produces a pair of outlines for laser-cutting the adhesive sheet and acrylic, suitable for export to an SVG file:

    HQ Sixteen Anchor - solid model - cuts
    HQ Sixteen Anchor – solid model – cuts

    They’re offset inward by amounts appropriate for my CO₂ laser:

    if (Layout == "Cuts") {
      left(BaseOD/2 + Gap)
        offset(r=-1.0)
          circle(d=BaseOD);
      right(Cap[OD]/2 + Gap)
        offset(r=-0.2/2)
          projection()
            tube(1.0,id=Cap[ID],od=Cap[OD],anchor=BOTTOM);
    }
    

    It might be prudent to apply those offsets to the original outlines in LightBurn or whatever you use for lasering.

    The OpenSCAD source code as a GitHub Gist:

    // Anchor block for HQ Sixteen
    // Ed Nisley – KE4ZNU
    // 2026-09-29
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build,Cuts]
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Gap = 10.0/2; // build platform minimum spacing
    HoleWindage = 0.2;
    Protrusion = 0.1;
    NumSides = 8*3*2*4;
    $fn=NumSides;
    // Sizes
    Screw = [5.5,25.5,5.0]; // 10-24 screw + washer holding it together
    Weight = [11.0,80.0,11.5]; // stamped steel slug
    Cap = [26.0,Weight[OD],5.5]; // acrylic disk with handle hole
    BaseFloor = 10.0;
    BaseOD = 90.0; // printed anchor
    Spacing = 25.0; // Show view separation
    //———-
    // Model things
    module Housing() {
    tube(Screw[LENGTH],id=Screw[OD],od=BaseOD,anchor=BOTTOM) position(TOP) {
    tube(Weight[LENGTH],id=Screw[ID],od=Weight[ID],anchor=BOTTOM); // weight bushing
    tube(BaseFloor – Screw[LENGTH],id=Screw[ID],od=BaseOD,anchor=BOTTOM) position(TOP)
    tube(Cap[LENGTH] + Weight[LENGTH],id=Cap[OD],od=BaseOD,anchor=BOTTOM);
    }
    }
    //———-
    // Build things
    if (Layout == "Show") {
    Housing();
    color("Orange",0.7)
    up(BaseFloor + Spacing)
    tube(Weight[LENGTH],id=Weight[ID],od=Weight[OD],anchor=BOTTOM);
    color("Gray",0.8)
    up(BaseFloor + 2*Weight[LENGTH] + Spacing)
    tube(Cap[LENGTH],id=Cap[ID],od=Cap[OD],anchor=BOTTOM);
    }
    if (Layout == "Cuts") {
    left(BaseOD/2 + Gap)
    offset(r=-1.0)
    circle(d=BaseOD);
    right(Cap[OD]/2 + Gap)
    offset(r=0.2/2)
    projection()
    tube(1.0,id=Cap[ID],od=Cap[OD],anchor=BOTTOM);
    }
    if (Layout == "Build") {
    Housing();
    }

  • HQ Sixteen: Improved Fabric Rod Bushings

    HQ Sixteen: Improved Fabric Rod Bushings

    Descending from the previous bearings / sleeves / bushings, I added a rib to hold them in place as the rod slides back and forth while fiddling with fabric:

    HQ Sixteen - Rod Bushing - rib retainer - solid model - build view
    HQ Sixteen – Rod Bushing – rib retainer – solid model – build view

    An idealized serving suggestion:

    HQ Sixteen - Rod Bushing - rib retainer - solid model - show view
    HQ Sixteen – Rod Bushing – rib retainer – solid model – show view

    Which might count as the “better design” I expected it would need.

    They’re still almost invisible in use:

    HQ Sixteen - Rod Bushing - rib retainer
    HQ Sixteen – Rod Bushing – rib retainer

    Which is as it should be: inconspicuous, no more black dust, and E-Z rotation.

    The modified OpenSCAD source code is added to the original GitHub Gist:

    // Bushing for HQ Sixteen table rods
    // Ed Nisley – KE4ZNU
    // 2026-02-20
    // 2026-09-29 add retaining rib
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build]
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    HoleWindage = 0.2;
    Protrusion = 0.1;
    NumSides = 8*3*2*4;
    $fn=NumSides;
    Rod = [25.0,28.7,100.0]; // very short rod
    Sleeve = [Rod[OD] + 0.3,31.2 – 0.2,9.0]; // LENGTH = overall
    Rim = [Sleeve[ID],Sleeve[OD] + 6.0,0.6];
    LockRing = [Sleeve[ID],Sleeve[OD] + 2*(1.0),4.0]; // locks sleeve on plate
    IdlerLength = 15.0; // far right end of rods
    NumSlots = 2*4;
    Kerf = 1.0;
    Gap = 5.0;
    module Bushing(oal,lk=false) {
    difference() {
    union() {
    tube(oal,id=Sleeve[ID],od=Sleeve[OD],anchor=BOTTOM);
    tube(Rim[LENGTH],id=Rim[ID],od=Rim[OD],anchor=BOTTOM);
    if (lk)
    up(oal)
    tube(LockRing[LENGTH],id=LockRing[ID],od=LockRing[OD],chamfer=LockRing[LENGTH]/3,anchor=TOP);
    }
    for (a=[0:NumSlots–1])
    zrot(a*360/NumSlots)
    up(oal/4 + Rim[LENGTH])
    right(Sleeve[ID]/2)
    cuboid([Sleeve[OD],Kerf,oal],anchor=BOTTOM);
    }
    }
    //—–
    // Build things
    if (Layout == "Show") {
    color("Gray",0.5)
    xcyl(Rod[LENGTH],d=Rod[OD]);
    right(Rod[LENGTH]/3)
    zrot(180) yrot(90)
    Bushing(Sleeve[LENGTH]);
    left(Rod[LENGTH]/3)
    yrot(90)
    Bushing(IdlerLength,lk=true);
    }
    if (Layout == "Build") {
    right(Rim[OD]/2 + Gap/2)
    Bushing(Sleeve[LENGTH]);
    left(Rim[OD]/2 + Gap/2)
    Bushing(IdlerLength,lk=true);
    }
    // Bearing sleeve for HQ Sixteen table rods
    // Ed Nisley – KE4ZNU
    // 2026-02-20
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build]
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    HoleWindage = 0.2;
    Protrusion = 0.1;
    NumSides = 8*3*2*4;
    $fn=NumSides;
    Rod = [25.0,28.7,100.0]; // very short rod
    Sleeve = [Rod[OD] + 0.3,31.2 – 0.2,9.0]; // LENGTH = overall
    Rim = [Sleeve[ID],Sleeve[OD] + 6.0,0.6];
    IdlerLength = 15.0;
    NumSlots = 2*4;
    Kerf = 1.0;
    Gap = 5.0;
    module Bearing(oal) {
    difference() {
    union() {
    tube(oal,id=Sleeve[ID],od=Sleeve[OD],anchor=BOTTOM);
    tube(Rim[LENGTH],id=Rim[ID],od=Rim[OD],anchor=BOTTOM);
    }
    for (a=[0:NumSlots–1])
    zrot(a*360/NumSlots)
    up(oal/4 + Rim[LENGTH])
    right(Sleeve[ID]/2)
    cuboid([Sleeve[OD],Kerf,oal],anchor=BOTTOM);
    }
    }
    //—–
    // Build things
    if (Layout == "Show") {
    color("Gray",0.5)
    xcyl(Rod[LENGTH],d=Rod[OD]);
    right(Rod[LENGTH]/3)
    yrot(90)
    Bearing(Sleeve[LENGTH]);
    left(Rod[LENGTH]/3)
    yrot(90)
    Bearing(IdlerLength);
    }
    if (Layout == "Build") {
    right(Rim[OD]/2 + Gap/2)
    Bearing(Sleeve[LENGTH]);
    left(Rim[OD]/2 + Gap/2)
    Bearing(IdlerLength);
    }