/Bike/BagClip.foocad
A hook for the box on the back of my bike, for hanging bags on the sides/back if I need extra space for shopping.
The box has ribs at the top, so the hook has to clear those ribs.
There's an option for a bolt, which ensures the bag cannot jump out of the hook, and the hook cannot jump off the box The end of the bolt sits between the ribs at the top of the box.
Design Notes
I initially left the end of the bolt unthreaded, but changed my mind. The threads make the bolt stronger, and having threads along the whole length allows the bolt to stay in place while placing the bag on the clip.
Strength
My test print (with sparse infill) is much stronger than necessary. The bag handles (or the box) would break before the clip.
Print Notes
The clip lifted off the bed on my test print, so I resorted to adding a brim :-( Even a small lift can deform the threaded holes, and ruin the print.
My test prints used a 0.3 layer height with a 0.4 nozzle. This was slightly too agressive for the overhangs in the threaded holes, but the thread still worked, but was stiff until screwed and unscrewed several times.
bolt
Print with a brim, as it is tall and thin.
import static uk.co.nickthecoder.foocad.debug.v1.Debug.* import uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.* import uk.co.nickthecoder.foocad.threaded.v2.* import static uk.co.nickthecoder.foocad.threaded.v2.Thread.* class BagClip : Model { //@Custom( about="Length and Height" ) //var size = Vector2( 10, 60 ) @Custom var width = 12 @Custom var thickness = 8 @Custom var boxThickness = 2 @Custom( about="X=Width of the box's lip. Y=Space to get the clip off" ) var boxLip = Vector2(16,40) @Custom( about="Size of the gap for the bag handles" ) var hookSize = Vector2(16,36) @Custom var overhang = 17 @Custom var radius1 = 3 @Custom var radius2 = 9 @Custom var chamfer = 1.2 val thread = Thread(8).pitch(10).threadSize(1.5, 3).threadCount(3).headSize( Vector2( 20, 5 ) ) @Piece @Slice( brimWidth=10 ) fun bolt() = thread.bolt( thickness*2 + hookSize.x + boxLip.x - 2 ) @Slice( brimWidth=10 ) override fun build() : Shape3d { var profile = PolygonBuilder().apply { moveTo( 0, 0 ) // Top left lineBy( thickness * 2 + boxLip.x + boxThickness, 0 ) lineBy( 0, -thickness - boxLip.y ) // Bottom of the hook lineBy( hookSize.x, 0 ) lineBy( 0, hookSize.y ) lineBy( thickness, 0 ) // Now at the top right lineBy( 0, - hookSize.y - thickness ) // Now at the bottom right lineBy( -thickness - hookSize.x - thickness - boxLip.x, 0 ) // Now touching the box lineBy( 0, thickness ) lineBy( boxLip.x, 0 ) lineBy( 0, boxLip.y ) lineBy( - boxLip.x - boxThickness, 0 ) // Now at the top left of the box rim. lineBy( 0, -overhang ) lineBy( -thickness, 0 ) lineTo(0,0) // Top left }.build().frontTo(0) val solid = profile .roundCorner( 13, radius1 ) // Top left .roundCorner( 12, radius2 ) // Top middle .roundCorner( 11, radius1 ) .roundCorner( 10, radius1 ) .roundCorner( 9, radius1 ) .roundCorner( 8, radius1 ) // Top right .roundCorner( 7, radius2 ) // Bottom right .roundCorner( 5, radius1 ) // .roundCorner( 4, radius1 ) // .roundCorner( 3, radius1 ) .roundCorner( 1, radius1 ) // Overhang .roundCorner( 0, radius1 ) // Overhang .smartExtrude( width ) .chamfer( chamfer ) val bolt = bolt() .rotateY(-90) .rightTo( solid.right + thread.headSize.y ) .centerYTo( thickness + hookSize.y - 7 ) .centerZTo( solid.size.z/2 ) .previewOnly() val forBolt = thread.threadedHole( thickness*2 + hookSize.x ) .chamferStart( false ) .rotateY(90) .rightTo( solid.right ) .centerYTo( thickness + hookSize.y - 7 ) .centerZTo( solid.size.z/2 ) return solid - forBolt + bolt } }