/Bike/PannierBoxFixture.foocad

Helps attach a "heavy duty" plastic box to my bike's pannier. If the box is hit (or the bike falls over), it is likely that the clips will break. I prefer the clips break than the box. This has happened more than once, when my bike fell over. I keep spares on me!
The bottom of the box is not flat, and instead has reenforcing fins.
So for these fixtures to touch the bottom of the box, there is a block built into the model.
The shape of the clip is similar to the [PannierClip], but in this case, I've created it progamatically, rather than using an SVG diagram.
I'm using M5 brass inserts, which are melted into the plastic with a soldering iron.
Print Notes
PETG is preferable, as it has a bit of spring to it.
PLA is more rigid, and therefore breaks more readily.
Version
V1 I made the "block" larger than needed.
import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.*
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
class PannierBoxFixture : AbstractModel() {
val toHole = 16 // 27
var pannierWidth = 166
var clipDiameter = 12
var thickness = 3
var clipDepth = 12
//var tabDepth = 18
var holeDiameter = 6.5
// The size while it is printed, i.e, Z is the "thickness".
// The original use Y=20mm
var blockSize = Vector3( 20, 15, 18 )
var spacerThickness = 3.0
var washerSize = Vector2( 16, 6 )
@Custom
var boltHeadSize = Vector2(14, 4)
@Piece
@Slice( brimWidth=4 )
fun mirror() : Shape3d = one().mirrorX()
@Piece
@Slice( brimWidth=4 )
fun one() : Shape3d {
val profile = clip(clipDiameter-thickness/2, toHole)
val wedge = Cube( 100 ).centerY().rotateX(-45-90).translateZ(clipDepth)
val hole = Cylinder( 100, holeDiameter/2 )
.rotateX(-90)
.translate(toHole, 0, blockSize.z/2 )
val block = Cube( blockSize.x, blockSize.y, blockSize.z ).centerXTo(hole.middle.x)frontTo( profile.back - thickness )
val insert =
(
Circle( blockSize.z/2 ).sides(6).roundAllCorners(2) +
Square( 10, blockSize.z ).center() -
Circle( holeDiameter ).sides(6)
).extrude( holeDiameter )
.rotateX(-90)
.translate(toHole, 0, blockSize.z/2).backTo(profile.back - thickness )
return (
profile.extrude(clipDepth) +
(profile.extrude(blockSize.z) intersection wedge)
) + block - hole // + wedge.previewOnly()
}
// Print using TPU, and glue to the block, to give a rubbery feel.
// This make help protect the fixture and the box on rough terrain.
// Use the Tile extension to print 4 at once.
@Piece
fun spacer() : Shape3d {
return Cube( blockSize.x, blockSize.z, spacerThickness ).centerXY() -
Cylinder( 100, holeDiameter / 2 ).center()
}
// Use the Tile extension to print 4 at once.
@Piece
@Slice( brimWidth=4, perimeters=6 )
fun washer() : Shape3d {
return (Circle( washerSize.x/2 ) - Circle( holeDiameter/2 )).extrude( boltHeadSize.y + 1 ) -
Cylinder( 100, boltHeadSize.x / 2 ).translateZ( 1 )
}
// I printed this using TPU. Use the Tile extension to print 4 at once.
@Piece
fun washerCap() : Shape3d {
val t = 2
val innerD = washerSize.x + 0.6
val outerD = innerD + t
return Circle( outerD /2 ).chamferedExtrude( washerSize.y + t, 0.8, 0 ) -
Cylinder( 100, innerD / 2 ).translateZ( t )
}
@Slice( brimWidth=4, perimeters=6 )
override fun build() : Shape3d = arrange( 3, one(), mirror() )
fun clip(left : double, right : double) : Shape2d {
val scale = clipDiameter + thickness
val a = Vector2( -0.5, 0.5 ) * scale
val b = Vector2( -0.38, 0.36 ) * scale
val c = Vector2( -0.5, 0 ) * scale
val delta1 = Vector2( 0.1, 0 ) * scale
val delta2 = Vector2( 0, -0.09 ) * scale
val delta3 = Vector2( 0, 0.25 ) * scale
val path = PolygonBuilder().apply {
moveTo( -left, a.y )
lineTo( a )
//lineTo( b )
bezierByTo( delta1, delta2, b )
//lineTo( c )
bezierByTo( delta2, -delta3, c )
circularArcTo( c.mirrorX(), 0.5*scale, true, true )
//lineTo( b.mirrorX() )
bezierByTo( delta3, -delta2, b.mirrorX() )
//lineTo( a.mirrorX() )
bezierByTo( -delta2, delta1, a.mirrorX() )
lineBy( right, 0 )
}.buildPath()
val circles = Circle( thickness/2 ).centerTo(path.points[0]) +
Circle( thickness/2 ).centerTo(path.points[path.points.size()-1])
return path.thickness( thickness ) + circles
}
}

