A pair of Step2 rolling garden seats (they have a new version) served in Mary’s gardens long enough to give their seat panels precarious cracks:
The underside was giving way, too:
We agreed the new seat could be much simpler, although it must still hinge upward, so I conjured a pair of hinges from the vasty digital deep:
The woodpile disgorged a slab of 1/4 inch = 6 mm plywood (used in a defunct project) of just about the right size and we agreed a few holes wouldn’t be a problem for its projected ahem use case:
The screw holes on the hinge tops will let me run machine screws all the way through, should that be necessary. So far, a quartet of self-tapping sheet metal (!) screws are holding firm.
A closer look at the hinges in real life:
The solid model now caps the holes; I can drill them out should the need arise.
From the bottom:
Three coats of white exterior paint make it blindingly bright in the sun, although we expect a week or two in the garden will knock the shine right off:
After the first coat, I conjured a drying rack from a bamboo skewer, a cardboard flap, and some hot-melt glue:

Three small scars on the seat bottom were deemed acceptable.
The OpenSCAD source code as a GitHub Gist:
// Hinge brackets for rolling garden stool | |
// Ed Nisley - KE4ZNU - 2019-06 | |
Layout = "Build"; // [Block,Build,Show] | |
Support = true; | |
/* [Hidden] */ | |
ThreadThick = 0.20; | |
ThreadWidth = 0.40; | |
HoleWindage = 0.2; | |
Protrusion = 0.1; // make holes end cleanly | |
ID = 0; | |
OD = 1; | |
LENGTH = 2; | |
//---------------------- | |
// Dimensions | |
SeatThick = 6.0; // seat panel above cart body | |
HingePin = [11.5,12.0,7.0]; // ID = tip OD = base | |
HingeOffset = 8.0; // hinge axis above cart body (larger than radius!) | |
HingeBolster = [5.0,24.0,SeatThick]; // backing block below hinge | |
Block = [25.0,HingeOffset + 30.0,23.0]; // Z = above cart body | |
Screw = [3.8,11.0,2.5]; // self-tapping #8 OD=head LENGTH=head thickness | |
ScrewOC = 15.0; // spacing > greater than head OD | |
ScrewOffset = Block.y/2 - (ScrewOC/2 + Screw[OD]/2 + HingeOffset); // space for head behind hinge | |
BlockRadius = 7.0; // corner rounding | |
//---------------------- | |
// 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(r=(FixDia + HoleWindage)/2, | |
h=Height, | |
$fn=Sides); | |
} | |
// Basic block shape | |
// X axis collinear with hinge axes, hinge base at X=0 | |
module HingeBlock() { | |
PinSides = 3*4; | |
PinSupport = [HingePin[LENGTH] - 2*ThreadWidth,0.6*HingeOffset,HingePin[OD]]; // pre-rotated | |
union() { | |
translate([Protrusion,Block.y/2 - HingeOffset,HingeOffset]) | |
rotate([0,-90,0]) | |
rotate(180/PinSides) | |
cylinder(d=HingePin[OD],h=HingePin[LENGTH] + Protrusion,$fn=PinSides); | |
difference() { | |
hull() { | |
translate([Block.x - BlockRadius,-(Block.y/2 - BlockRadius),Block.z - BlockRadius]) | |
rotate(180/PinSides) | |
sphere(r=BlockRadius/cos(180/PinSides),$fn=PinSides); | |
translate([0,-(Block.y/2 - BlockRadius),Block.z - BlockRadius]) | |
rotate([0,90,0]) rotate(180/PinSides) | |
cylinder(r=BlockRadius/cos(180/PinSides),h=Block.x/2,$fn=PinSides); | |
translate([Block.x - BlockRadius,(Block.y/2 - BlockRadius),Block.z - BlockRadius]) | |
sphere(r=BlockRadius/cos(180/PinSides),$fn=PinSides); | |
translate([0,(Block.y/2 - BlockRadius),Block.z - BlockRadius]) | |
rotate([0,90,0]) rotate(180/PinSides) | |
cylinder(r=BlockRadius/cos(180/PinSides),h=Block.x/2,$fn=PinSides); | |
translate([0,-Block.y/2,0]) | |
cube([Block.x,Block.y - HingeOffset,Block.z/2],center=false); | |
translate([0,Block.y/2 - HingeOffset,HingeOffset]) | |
rotate([0,90,0]) rotate(180/PinSides) | |
cylinder(r=HingeOffset/cos(180/PinSides),h=Block.x,$fn=PinSides); | |
} | |
translate([Block.x/2 + HingeBolster.x,0,(SeatThick - Protrusion)/2]) | |
cube([Block.x,2*Block.y,SeatThick + Protrusion],center=true); | |
translate([0,-HingeBolster.y,(SeatThick - Protrusion)/2]) | |
cube([3*Block.x,Block.y,SeatThick + Protrusion],center=true); | |
for (j=[-1,1]) | |
translate([Block.x/2,j*ScrewOC/2 + ScrewOffset,-4*ThreadThick]) | |
rotate(180/8) | |
PolyCyl(Screw[ID],Block.z,8); | |
} | |
} | |
if (Support) { // totally ad-hoc | |
color("Yellow") render(convexity=4) | |
difference() { | |
translate([-(PinSupport.x/2 + 2*ThreadWidth),Block.y/2 - PinSupport.y/2,HingeOffset]) | |
cube(PinSupport,center=true); | |
translate([Protrusion,Block.y/2 - HingeOffset,HingeOffset]) | |
rotate([0,-90,0]) | |
rotate(180/PinSides) | |
cylinder(d=HingePin[OD] + 2*ThreadThick,h=2*HingePin[LENGTH],$fn=PinSides); | |
for (i=[-1:1]) | |
translate([i*4*ThreadWidth - HingePin[LENGTH]/2, | |
Block.y/2 - (PinSupport.y + 1*ThreadThick), | |
HingeOffset]) | |
cube([2*ThreadWidth,2*PinSupport.y,2*PinSupport.z],center=true); | |
} | |
} | |
} | |
module Blocks(Hand = "Left") { | |
if (Hand == "Left") | |
HingeBlock(); | |
else | |
mirror([1,0,0]) | |
HingeBlock(); | |
} | |
//- Build it | |
if (Layout == "Block") | |
HingeBlock(); | |
if (Layout == "Show") { | |
translate([1.5*HingePin[LENGTH],0,0]) | |
Blocks("Left"); | |
translate([-1.5*HingePin[LENGTH],0,0]) | |
Blocks("Right"); | |
} | |
if (Layout == "Build") { | |
translate([0,-Block.z/2,Block.y/2]) | |
rotate([-90,0,0]) { | |
translate([1.5*HingePin[LENGTH],0,0]) | |
Blocks("Left"); | |
translate([-1.5*HingePin[LENGTH],0,0]) | |
Blocks("Right"); | |
} | |
} |
This original doodle gives the key dimensions, apart from the rounded rear edge required so the seat can pivot vertically upward:
The second seat looks just like this one, so life is good …