import static uk.co.nickthecoder.foocad.screws.v2.Screws.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* 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.* /** A small feeder for squirrels, to screw onto my fence. There are holes in the `main` part, to make it easy to screw to the fence. These holes can be covered up, with the optional `label` piece. POST PROCESSING Drill out the drainage holes with a 2mm drill bit. Fix the `main` part to the fence with screws. Drop in the tray, and fill with peanuts. */ class SquirrelFeeder : Model { @Custom var insideSize = Vector3( 30, 45, 13 ) @Custom var trayThickness = 2.0 // For a flush bottom : insideSize.z + thickness // An alternative : insideSize.z @Custom var mainHeight = insideSize.z + trayThickness @Custom var radius = 4 @Custom var slack = 0.6 @Custom var lip = 6 @Custom var labelText = "NUTS" fun insideProfile() : Shape2d = Square( insideSize.x, insideSize.y ) .roundAllCorners(radius).center() fun mainProfile() : Shape2d { val r2 = radius + lip return Square( insideSize.x + trayThickness*2 + lip*2, insideSize.y + trayThickness*2 + lip*2 ) .roundCorner( 3, radius ) .roundCorner( 2, r2 ) .roundCorner( 1, r2 ) .roundCorner( 0, radius ) .center() } fun screwHoleDistance() = insideSize.y - radius * 2 - 10 fun mainUpright() : Shape3d { val insideProfile : Shape2d = insideProfile().offset(trayThickness + slack) val mainProfile : Shape2d = mainProfile() val main = ExtrusionBuilder().apply { crossSection( mainProfile.offset( -trayThickness ) ) forward( trayThickness ) crossSection( trayThickness ) forward( mainHeight - trayThickness ) crossSection() }.build() val inside = insideProfile.extrude( mainHeight + 2 ).bottomTo(-1) val screwHoles = counterSink().rotateY(90) .leftTo(main.left) .translateZ( mainHeight/2 + trayThickness/2 ) .repeatY( 2, screwHoleDistance() ) .centerY() return main - inside - screwHoles } fun counterSink() = Countersink() .holeD(3).headD(6).recess(100).depth(100+lip*0.5) fun trayUpright() : Shape3d { val profile = insideProfile() val mainProfile : Shape2d = mainProfile() val tray = ExtrusionBuilder().apply { crossSection( profile ) forward( trayThickness ) crossSection( trayThickness ) forward( insideSize.z ) crossSection() crossSection( mainProfile ) forward(trayThickness) crossSection() crossSection( profile ) forward( -insideSize.z ) crossSection() forward( -trayThickness ) crossSection( - trayThickness ) }.build().color("Green") // NOTE the holes deliberately don't go all the way through // so that the "base" is printed as a "bridge". val drainageHoles = Cylinder( trayThickness, 1 ) .spreadX( 3, insideSize.x - trayThickness *2 ) .spreadY( 3, insideSize.y - trayThickness *2 ) .centerXY().topTo(trayThickness-0.6) .color("Red") return tray - drainageHoles } @Piece fun main() = mainUpright().mirrorZ().bottomTo(0) @Piece fun tray() = trayUpright().mirrorZ().bottomTo(0) @Piece fun label() : Shape3d { val pegR = counterSink().headD/2 - 0.3 val pegs = Cylinder( lip, pegR ).repeatY( 2, screwHoleDistance() ).centerXY() val flat = Square( 13, pegs.size.y + 8 ) .roundAllCorners( 2 ) .center() .extrude(2) val text = Text( labelText ).mirrorX().extrude(1) .centerTo( flat.middle ) .rotateZ(-90) .bottomTo(-0.1 ) return (pegs + flat - text).color("white") } override fun build() : Shape3d { val main = mainUpright() val tray = trayUpright() val label : Shape3d = label().rotateY(-90) .rightTo( main.right + 2 ) .centerZTo( main.middle.z + trayThickness/2 ) return main + label + tray.topTo( main.top + trayThickness ) //.translateZ(30) } }