import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.* import uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.* import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.* class PannierAdaptor : Model { @Custom var thickness = 24 @Custom( about="The box has a pair of ribs at the top. The gap between them." ) var ribGap = 16 @Custom( about="The internal depth of the channel made by these ribs." ) var ribDepth = 12 @Custom( about="The height and thickness. used by both pieces" ) var locatorSize = Vector2(12,7) @Custom var insertDiameter = 7.8 /** * This piece attaches to the pannier bag, instead of the stock clip */ @Piece fun bagClip() : Shape3d { // Use the svg diagram used by [PaccnierClip.foocad] val doc = SVGDocument("pannierClip.svg" ) val scale = 50.0 / doc["fivecm"].size.x val angle = 13.5 val curvy = doc["main"].scale(scale).rotate(angle) // Now let's chop off part of it, and move it to the origin val movedCurvy = curvy.center().translateX(17) val clipped = movedCurvy intersection Square( 43, 12 ).toOrigin() // Take the tab from the svg document, and transform it the as `curvy`: var tab2d : Shape2d= doc["rect"].scale(scale).rotate(angle).toPolygon() tab2d = tab2d.translate( -curvy.corner + movedCurvy.corner ) val tab = tab2d.roundAllCorners(1).chamferedExtrude( thickness, 1.5 ) // Add a block that we can add a screw threw to. val block = Square( 17, 11.35 ).roundAllCorners(2).rightTo(15).frontTo(0.45) val locator = Square( locatorSize + Vector2(0,3) ).roundAllCorners(2).centerXTo( 7 ).backTo(3) val profile = clipped.toOrigin() + block + locator val solid = profile.chamferedExtrude( thickness, 1 ) // Holes to attach it to the pannier bag. val hole = Cylinder.hole( 100, 2.5, 8 ) .rotateX(90) .centerXTo( tab2d.middle.x ) .frontTo( tab2d.front ) .centerZTo( thickness/2 ) val hole2 = Cylinder.hole( 10, 5, 8 ) .rotateX(90) .centerXTo( tab2d.middle.x ) .backTo( tab2d.front-1.2 +3.5 ) .centerZTo( thickness/2 ) // Hole for a threaded insert val hole3 = Cylinder( 100, insertDiameter/2 ).center() .rotateX(90) .centerZTo( thickness/2 ) .leftTo( 3 ) return solid + tab.color( "Green" ) - hole - hole2 - hole3 } /** * This piece is glued to the box between the pair of ribs around the top. */ @Piece fun boxBlock() : Shape3d { val block = Square( thickness + 16, ribGap ) .roundAllCorners(1) .center() .chamferedExtrude( ribDepth, 1 ) val slot = Square( thickness + 1, locatorSize.x + 1 ) .center() .chamferedExtrude( locatorSize.y, 0, -1 ) .centerYTo(block.middle.y) .topTo( block.top+0.1 ) // Hole for an M6 bolt (with 1mm slack) val hole = Cylinder( 100, 3.5 ).centerZ() return block - slot - hole } /** * See BagClip.foocad. * This prevents the clip from moving. The screw thread still takes no load though. */ @Piece fun blockForBagClip() : Shape3d { val block = Square( thickness, ribGap ) .roundAllCorners(1) .center() .chamferedExtrude( ribDepth, 1 ) // Hole for the screw in bolt in BagClip.foocad val hole = ( Circle( 8/2 + 0.5 ).backTo( block.back - 1.5 ) hull Circle( 8/2 + 0.5 ).frontTo( block.front + 1.5 ) ).extrude( block.size.z ).bottomTo(1) return block - hole } // Copy pasted from FlatBedAttachments @Piece fun boltKnob() : Shape3d { val size = Vector2(10, 10) val knobSpacer = 2 val headD = 12 val holeD = 6.6 val knobDiameter = 25 val recessDepth = 8 val height = knobSpacer + recessDepth val hex = Circle( knobDiameter/2 ).sides(6).roundAllCorners( 3 ) val knob = hex.smartExtrude( height ) .filletTop( 2 ) .chamferBottom( 1 ) val throughHole = Cylinder( 100, holeD / 2 ).center() val recess = Circle( headD/2 ).sides( 6 ).extrude( recessDepth + 0.1 ) .topTo( knob.top + 0.1 ) return knob - recess - throughHole } // Copy pasted from FlatBedAttachments @Piece fun cap() : Shape3d { val headD = 12 val capHeight = 4 return Circle( headD / 2 - 0.3 ).sides(6).roundAllCorners(1, 1).extrude( capHeight ) } override fun build() : Shape3d { return arrangeX(3, bagClip(), boxBlock(), boltKnob(), cap(), blockForBagClip() ).centerX() } }