A soaker hose leaped under a descending garden fork and accumulated a nasty gash:

Mary deployed a spare and continued the mission, while I pondered how to fix such an odd shape.
For lack of anything smarter, I decided to put a form-fitting clamp around the hose, with silicone caulk buttered around the gash to (ideally) slow down any leakage:

As usual, some doodling got the solid model started:

A hose formed from chopped rubber doesn’t really have consistent dimensions, so I set up the model to spit out small test pieces:

Lots and lots of test pieces:

Each iteration produced a better fit, although the dimensions never really converged:

The overall model looks about like you’d expect:

The clamp must hold its shape around a hose carrying 100 psi (for real!) water, so I put 100 mil aluminum backing plates on either side. Were you doing this for real, you’d shape the plates with a CNC mill, but I just bandsawed them to about the right size and transfer-punched the hole positions:

Some drill press action with a slightly oversize drill compensated for any misalignment and Mr Disk Sander rounded the corners to match the plastic block:

A handful of stainless steel 8-32 screws holds the whole mess together:

These hoses spend their lives at rest under a layer of mulch, so I’m ignoring the entire problem of stress relief at those sharp block edges. We’ll see how this plays out in real life, probably next year.
I haven’t tested it under pressure, but it sure looks capable!
The OpenSCAD source code as a GitHub Gist:
// Rubber Soaker Hose Splice | |
// Ed Nisley KE4ZNU July 2018 | |
Layout = "Build"; // Hose Block Show Build | |
TestFit = false; // true to build test fit slice from center | |
//- Extrusion parameters must match reality! | |
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); | |
//---------- | |
// Dimensions | |
// Hose lies along X axis | |
Hose = [200,27.0,12.0]; // X = longer than anything else | |
Block = [80.0,50.0,4.0 + Hose.z]; // overall splice block size | |
echo(str("Block: ",Block)); | |
Kerf = 0.1; // cut through middle to apply compression | |
ID = 0; | |
OD = 1; | |
LENGTH = 2; | |
// 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]; | |
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() { | |
RimThick = 10.0; // outer sections | |
RimOD = RimThick; | |
RimFlatRecess = -0.7; // recess to front flat surface | |
OuterOC = Hose.y - RimOD; // outer tube centers | |
RecessM = 1.5; // back recess chord | |
RecessC = OuterOC; | |
RecessR = (pow(RecessM,2) + pow(RecessC,2)/4) / (2*RecessM); | |
RidgeM = 1.0; // front ridge chord | |
RidgeC = 8.0; | |
RidgeR = (pow(RidgeM,2) + pow(RidgeC,2)/4) / (2*RidgeM); | |
NumSides = 12*4; | |
rotate([0,-90,0]) | |
translate([0,0,-Hose.x/2]) | |
linear_extrude(height=Hose.x,convexity=4) | |
difference() { | |
union() { | |
for (j=[-1,1]) // outer channels | |
translate([0,j*OuterOC/2]) | |
circle(d=RimOD,$fn=NumSides); | |
translate([-RimOD/4,0]) // rear flat fill | |
square([RimOD/2,OuterOC],center=true); | |
translate([(RimOD/4 + RimFlatRecess),0]) // front flat fill | |
square([RimOD/2,OuterOC],center=true); | |
intersection() { | |
translate([Hose.z/2,0]) | |
square([Hose.z,OuterOC],center=true); | |
translate([-RidgeR + RimOD/2 + RimFlatRecess + RidgeM,0]) | |
circle(r=RidgeR,$fn=NumSides); | |
} | |
} | |
translate([-(RecessR + RimOD/2 - RecessM),0]) | |
circle(r=RecessR,$fn=2*NumSides); | |
} | |
} | |
// Outside shape of splice Block | |
// Z centered on hose rim circles, not overall thickness through center ridge | |
module SpliceBlock() { | |
difference() { | |
hull() | |
for (i=[-1,1], j=[-1,1]) // rounded block | |
translate([i*(Block.x/2 - CornerRadius),j*(Block.y/2 - CornerRadius),-Block.z/2]) | |
cylinder(r=CornerRadius,h=Block.z,$fn=4*8); | |
for (i = [0:NumScrews - 1], j=[-1,1]) // screw holes | |
translate([-(Block.x/2 - CornerRadius) + i*ScrewOC.x, | |
j*ScrewOC.y/2, | |
-(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() { | |
SpliceBlock(); | |
HoseProfile(); | |
} | |
} | |
//---------- | |
// Build them | |
if (Layout == "Hose") | |
HoseProfile(); | |
if (Layout == "Block") | |
SpliceBlock(); | |
if (Layout == "Bottom") | |
BottomPlate(); | |
if (Layout == "Top") | |
TopPlate(); | |
if (Layout == "Show") { | |
difference() { | |
SpliceBlock(); | |
HoseProfile(); | |
} | |
color("Green",0.25) | |
HoseProfile(); | |
} | |
if (Layout == "Build") { | |
SliceOffset = TestFit && !NumScrews%2 ? ScrewOC.x/2 : 0; | |
intersection() { | |
translate([SliceOffset,0,Block.z/4]) | |
if (TestFit) | |
cube([ScrewOC.x/2,4*Block.y,Block.z/2],center=true); | |
else | |
cube([4*Block.x,4*Block.y,Block.z/2],center=true); | |
union() { | |
translate([0,0.6*Block.y,Block.z/2]) | |
ShapedBlock(); | |
translate([0,-0.6*Block.y,Block.z/2]) | |
rotate([0,180,0]) | |
ShapedBlock(); | |
} | |
} | |
} |