Exit Full View
Up

/Garden/SquirrelFeeder.foocad

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