/Bike/PannierRackMount.foocad

I want to attach a box, OR a flat piece of plywood to my pannier, and swap them easily. When buying groceries, I use the box, but bulky items will use the "flat bed" plywood.
I never printed this, as I opted for a different solution.
Print Notes
I suggest adding "ears" to prevent warping.
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
class PannierRackMount : AbstractModel() {
@Custom( about="Size of the bars" )
var size = Vector3( 200, 14, 8 )
@Custom( about="Diameter of the rack's tubes (with extra for clearance)" )
var diameter = 11
@Custom( about="The distance of the two sides of the rack" )
var distance = 150
@Custom( about="The distance of the extra holes" )
var extraHoleDistance = 100
@Custom( about="The length of the slot, to allow for variation in the rack's dimensions")
var play = 3
@Custom
var clipThickness = 2
@Custom
var clipOffset = 14
@Custom( about="Diameter for the main bolts" )
var mainHoleDiameter = 6.5
@Custom( about="Diameter for the bolts holding the clip" )
var clipHoleDiameter = 4
@Custom( about="Diameter of the clips' slots" )
var clipSlotDiameter = 4
fun mainHolesLayout( shape : Shape2d ) : Shape2d {
return shape
.repeatX( 3, distance/2 ).centerX() +
shape.translateX( extraHoleDistance/2 ).mirrorX().also()
}
fun clipHolesLayout( shape : Shape2d ) : Shape2d {
return shape
.translateX(clipOffset)
.mirrorX().also()
.translateX( distance/2 )
.mirrorX().also()
}
@Piece
fun bar() : Shape3d {
val bar = Square( size.x, size.y )
.roundAllCorners(size.y/2)
.center()
// The bumps ensure that there is equal "meat" along the length.
// For strength, wherever there is a hole, we add a bump.
val mainHole = Circle( mainHoleDiameter/2 ).center()
val mainHoles = mainHolesLayout( mainHole )
val mainBump = Circle( (size.y + mainHoleDiameter)/2 )
val mainBumps = mainHolesLayout( mainBump )
val clipBump = Circle( (size.y + clipHoleDiameter)/2 )
val clipBumps = clipHolesLayout( clipBump )
val clipHoles = clipHolesLayout( Circle( clipHoleDiameter/2 ).center() )
// Force extra perimeters near the holes.
val cross = Square( size.y * 3, 0.2 ).center()
.rotate(45)
.mirrorX().also()
val forceMorePlastic = ( mainHolesLayout( cross ) + clipHolesLayout( cross ) )
.extrude(0.2)
.center()
.spreadZ( 3, size.z, 0.5 )
val holes = mainHoles + clipHoles
val flat = bar + mainBumps + clipBumps
return flat.extrude( size.z ) - forceMorePlastic - holes.chamferedExtrude( size.z, -1, -1 )
}
/**
This isn't load bearing, it only keeps the bars in place.
So it can be thin.
*/
@Piece
fun clip() : Shape3d {
val extra = 4 + play
val arcDiameter = diameter + clipThickness
val profile = PolygonBuilder().apply {
moveTo(0,0)
lineBy( clipOffset - arcDiameter/2 + extra, 0 )
lineBy( 0, diameter / 2 - clipThickness/2 )
arcBy( Vector2(arcDiameter,0), Vector2(arcDiameter/2,arcDiameter/2), 180, true, false )
lineBy( 0, -diameter / 2 + clipThickness/2 )
lineBy( clipOffset - arcDiameter/2 + extra, 0 )
}.buildPath().thickness( clipThickness )
.centerX()
val hole = ( Circle(clipSlotDiameter/2).sides(6).translateX(-play/2) hull Circle(2).sides(6).translateX(play/2))
.extrude( 100 )
.center()
.rotateX(90)
val holes = hole
.translate( clipOffset, 0, size.y / 2 )
.mirrorX().also()
val tube = Cylinder( 60, diameter/2 )
.center()
.translate( 0, diameter/2 - clipThickness/2, size.y/2 )
.previewOnly()
return profile.extrude( size.y ) - holes + tube
}
@Custom
var spacerSize = Vector3( 30, 30, 16 )
/**
The base of reinfored plastic boxes have fins.
Glue these spacers to the bottom of the box, so that it rests firmly on the rack mount.
*/
@Piece
fun spacer() : Shape3d {
val shape = Square( spacerSize.x, spacerSize.y ).center()
.roundAllCorners ( 3 )
val hole = Circle( mainHoleDiameter / 2 + 2 )
return shape.extrude( spacerSize.z ) - hole.chamferedExtrude( spacerSize.z, -1 )
}
override fun build() : Shape3d {
val clips = clip()
.translateX( distance/2 )
.rotateX(-90)
.centerY()
.topTo(-0.1)
.mirrorX().also()
return bar().color("Green") + clips
}
}

