FooCAD Source Codeimport 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)
}
}