We decided a few more inches of hose would be useful, so the shower head holder now has a 90° rotation in the middle to straighten the hose from plumbing to the side clip holding it out of the way:

The original rectangular connection becomes a simple square:

Unlike the original version, however, building this one requires a support structure:

Which turned out to be a Good Thing™, because a new cheap textured PEI steel printing plate seems to have poor adhesion despite being scrupulously clean:

Despite obvious defects due to the shifts while printing, the holder sufficed for acceptance testing.
I’m not sure about that steel plate, though.
The OpenSCAD source code as a GitHub Gist, which also contains the non-rotated version:
| // Delta shower head holder extension | |
| // Ed Nisley – KE4ZNU | |
| // 2025-05-02 | |
| // 2026-11-09 add rotation for hose repositioning | |
| include <BOSL2/std.scad> | |
| Layout = "Show"; // [Show,Build,Plug,Socket,Connector,Retainer,Latch,Test] | |
| MountAngle = 30; // between OEM and new holder | |
| MountOffset = 40.0; | |
| BaseAngle = 90; // [90,0,-90] | |
| /* [Hidden] */ | |
| HoleWindage = 0.2; | |
| Protrusion = 0.1; | |
| NumSides = 3*3*4; | |
| Gap = 5.0; | |
| ID = 0; | |
| OD = 1; | |
| LENGTH = 2; | |
| WallThick = 8.0; // holder wall thickness | |
| Radius = 3.0; // shapely rounding | |
| Insert = [21.0,24.5,35.0]; // hose connector taper | |
| PlugSize = Insert + [-0.3,-0.3,0]; // … for better fit in OEM holder | |
| Slot = [Insert[OD],14.6,3*Insert[LENGTH]]; // slot on OEM holder, arbitrary length | |
| Latch = [Insert[OD] – 0.25,Insert[OD] + 2*WallThick,1.5]; // ID = latch engagement | |
| RetainerSize = [14.5,25.0,6.0]; | |
| RetainerPegThick = 3.0; | |
| $fn=NumSides; | |
| //———- | |
| // Define Shapes | |
| module Plug() { | |
| up(Slot.y/2) | |
| xrot(BaseAngle) | |
| down(Slot.y/2) | |
| difference() { | |
| cyl(l=Insert[LENGTH],d1=PlugSize[ID],d2=PlugSize[OD],anchor=BOTTOM); | |
| down(Protrusion) | |
| cyl(10.0 + 5,d=5.5,anchor=BOTTOM); // M4 burly insert | |
| } | |
| } | |
| module LatchRing() { | |
| difference() { | |
| union() { | |
| tube(Latch[LENGTH],id=Insert[OD],od=Latch[OD],anchor=BOTTOM); | |
| difference() { | |
| tube(Latch[LENGTH]/2,id1=Insert[OD],id2=Latch[ID],od=Insert[OD],anchor=BOTTOM) | |
| position(TOP) | |
| tube(Latch[LENGTH]/2,id1=Latch[ID],id2=Insert[OD],od=Insert[OD],anchor=BOTTOM); | |
| cuboid(Slot + [0,1.0,0],anchor=RIGHT+CENTER); | |
| } | |
| } | |
| cuboid(Slot + [0,1.0,0],anchor=LEFT+CENTER); | |
| right(Insert[OD]/2) | |
| cube([Insert[OD],Insert[OD] + 2*WallThick,3*Insert[LENGTH]],anchor=LEFT+CENTER); | |
| } | |
| } | |
| module Socket() { | |
| difference() { | |
| tube(l=Insert[LENGTH],id1=Insert[ID],id2=Insert[OD],wall=WallThick,anchor=BOTTOM) | |
| position(TOP) LatchRing(); | |
| cuboid(Slot + [0,1.0,0],anchor=LEFT+CENTER); | |
| right(Insert[OD]/2) | |
| cube([Insert[OD],Insert[OD] + 2*WallThick,3*Insert[LENGTH]],anchor=LEFT+CENTER); | |
| } | |
| } | |
| module Connector() { | |
| difference() { | |
| left(MountOffset – Insert[ID]/4) | |
| cuboid([MountOffset + Insert[LENGTH]*sin(MountAngle),Slot.y,Slot.y + 0*Insert[LENGTH]*cos(MountAngle)], | |
| anchor=LEFT+BOTTOM); | |
| left(MountOffset) | |
| down(Protrusion) | |
| cyl(10.0 + 5,d=5.5,anchor=BOTTOM); // M4 burly insert | |
| yrot(MountAngle) right(Insert[ID]/2 + WallThick) | |
| cyl(l=Insert[LENGTH],d1=Insert[ID] + 2*WallThick,d2=Insert[OD] + 2*WallThick,anchor=BOTTOM); | |
| } | |
| } | |
| module Retainer() { | |
| difference() { | |
| cyl(RetainerSize[LENGTH] – RetainerPegThick,d=RetainerSize[OD],anchor=BOTTOM) position(TOP) | |
| cyl(RetainerPegThick,d=RetainerSize[ID],anchor=BOTTOM); | |
| down(Protrusion) | |
| cyl(2*RetainerSize[LENGTH],d=4.0,anchor=BOTTOM); | |
| } | |
| } | |
| module Adapter() { | |
| union() { | |
| left(MountOffset) | |
| Plug(); | |
| yrot(MountAngle) right((Insert[ID] + 2*WallThick)/2) | |
| Socket(); | |
| Connector(); | |
| } | |
| } | |
| //———- | |
| // Build things | |
| if (Layout == "Plug") | |
| Plug(); | |
| if (Layout == "Socket") | |
| Socket(); | |
| if (Layout == "Retainer") | |
| Retainer(); | |
| if (Layout == "Connector") | |
| Connector(); | |
| if (Layout == "Latch") | |
| LatchRing(); | |
| if (Layout == "Test") | |
| render() | |
| difference() { | |
| left(0.75*Insert[LENGTH]) | |
| up(Insert[OD]/2) | |
| yrot(90) | |
| Socket(); | |
| cuboid(2*(Insert[OD] + 2*WallThick),anchor=RIGHT); | |
| } | |
| if (Layout == "Show") { | |
| Adapter(); | |
| if (BaseAngle) | |
| back(sign(BaseAngle)*(RetainerSize[LENGTH] + Slot.y/2 + Gap)) | |
| up(Slot.y/2) | |
| left(MountOffset) | |
| xrot(BaseAngle) | |
| Retainer(); | |
| else | |
| left(MountOffset) down(RetainerSize[LENGTH] + Gap) | |
| Retainer(); | |
| } | |
| if (Layout == "Build") { | |
| up(Insert[ID]/2 + WallThick + Insert[OD]/2) | |
| yrot(90-MountAngle) | |
| Adapter(); | |
| right(Insert[LENGTH] + Insert[OD]) | |
| Retainer(); | |
| if (false) | |
| right(Insert[LENGTH] + 5.0) // fit test piece | |
| difference() { | |
| left(0.75*Insert[LENGTH]) | |
| up(Insert[OD]/2) | |
| yrot(90) | |
| Socket(); | |
| cuboid(2*(Insert[OD] + 2*WallThick),anchor=RIGHT); | |
| } | |
| } |
| // Delta shower head holder extension | |
| // Ed Nisley – KE4ZNU | |
| // 2025-05-02 | |
| include <BOSL2/std.scad> | |
| Layout = "Show"; // [Show,Build,Plug,Socket,Connector] | |
| MountAngle = 30; // between OEM and new holder | |
| MountOffset = 20.0; | |
| /* [Hidden] */ | |
| HoleWindage = 0.2; | |
| Protrusion = 0.1; | |
| NumSides = 3*3*4; | |
| ID = 0; | |
| OD = 1; | |
| LENGTH = 2; | |
| Insert = [21.0,24.5,35.0]; // hose connector taper | |
| PlugSize = Insert + [-0.3,-0.3,0]; // … for better fit in OEM holder | |
| Slot = [Insert[OD],14.6,3*Insert[LENGTH]]; // slot on OEM holder, arbitrary length | |
| WallThick = 8.0; // holder wall thickness | |
| Radius = 3.0; // shapely rounding | |
| $fn=NumSides; | |
| //———- | |
| // Define Shapes | |
| module Plug() { | |
| cyl(l=Insert[LENGTH],d1=PlugSize[ID],d2=PlugSize[OD],anchor=BOTTOM); | |
| } | |
| module Socket() { | |
| difference() { | |
| tube(l=Insert[LENGTH],id1=Insert[ID],id2=Insert[OD],wall=WallThick,anchor=BOTTOM); | |
| cuboid(Slot + [0,1.0,0],anchor=LEFT+CENTER); | |
| right(Insert[OD]/2) | |
| cube([Insert[OD],Insert[OD] + 2*WallThick,3*Insert[LENGTH]],anchor=LEFT+CENTER); | |
| } | |
| } | |
| module Connector() { | |
| difference() { | |
| left(MountOffset) | |
| cuboid([MountOffset + Insert[LENGTH]*sin(MountAngle),Slot.y,Insert[LENGTH]*cos(MountAngle)], | |
| anchor=LEFT+BOTTOM); | |
| yrot(MountAngle) right(Insert[ID]/2 + WallThick) | |
| cyl(l=Insert[LENGTH],d1=Insert[ID] + 2*WallThick,d2=Insert[OD] + 2*WallThick,anchor=BOTTOM); | |
| } | |
| } | |
| module Adapter() { | |
| union() { | |
| left(MountOffset) | |
| Plug(); | |
| yrot(MountAngle) right((Insert[ID] + 2*WallThick)/2) | |
| Socket(); | |
| Connector(); | |
| } | |
| } | |
| //———- | |
| // Build things | |
| if (Layout == "Plug") | |
| Plug(); | |
| if (Layout == "Socket") | |
| Socket(); | |
| if (Layout == "Connector") | |
| Connector(); | |
| if (Layout == "Show") | |
| Adapter(); | |
| if (Layout == "Build") | |
| up(Insert[ID]/2 + 1*WallThick + Insert[OD]/2) | |
| yrot(90-MountAngle) | |
| Adapter(); |
Spam comments get trashed, so don’t bother; you’re not as clever as you think. Comment moderation may cause a delay.