Exit Full View
Up

/Garden/BirdFeeder.foocad

BirdFeeder

A bird feeder for birds that sit upright on a perch while feeding on seeds, such as sparrows.

Notes

I chose to use a rounded square, instead of a circle, so that the "front" pieces would be flat, and therefore easy to place on the build plate.

The perch is octagonal, so that it (and the holes) have shallow overhangs.

You can drill small holes in the "front" pieces, and screw then together. Or just glue them to the tube.

The "gromet" spreads the load over a greater area (stronger). It also looks nice ;-)

Use thick galvanised wire (garden wire) to form a hanger.

I have made only some of the variables @Custom, as the others are not worth tinkering with IMHO.

Print Notes

tube

Use clear filament. Clear PLA is fine, it is easy to see the seeds. All other pieces can use an opaque filament (green!) Needs a brim.

perch

Needs a brim or ears to prevent it from lifting. 10+ perimeters to make it solid.

lid

Use a low infill. 5% ???

History

My first print used the "wrong" usage of holeD. It should be the diameter of the feeding hole on the "front" piece. (and the hole in the tube should be larger by thickness + slack).

The first print, the groove in the lid was bigger by "slack" for a tight fit. The second I made it larger, for a loose fit, but also made it deeper. Hmm.

FooCAD Source Code
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.*

class BirdFeeder : Model{
    
    @Custom( about="The internal width of the tube" )
    var diameter = 50

    @Custom( about="The height of the tube" )
    var height = 120

    @Custom( about="The radius of the tube's corners" )
    var radius = 10

    @Custom( about="Which way do the hanging holes face?")
    var sideways = false

    var thickness = 0.8
    var rim = 2
    var rimE = 2

    var holeD = 20
    var holeHeight = 7 + 37 // 37 + pegZ

    @Custom( about="The amount the perch sticks out" )
    var perchOut = 30

    var perchD = 6
    var perchZ = 7

    var innerT = 1.4


    @Custom( about="How easily the parts fit together" )
    var slack = 0.3


    fun profile() = Square( diameter ).center().roundAllCorners(radius)


    fun plainTube() : Shape3d {
        val outside = profile().offset(thickness)

        return ExtrusionBuilder().apply {
                crossSection( outside.offset(rim) )
                forward(rimE)
                crossSection()
                forward(rim*2)
                crossSection( outside )
                forward( height - rim*4 -rimE*2 )
                crossSection()
                forward(rim*2)
                crossSection( outside.offset(rim) )
                forward(rimE)
                crossSection()
                crossSection( outside.offset(-thickness) )
                forward( -height ) 
                crossSection()
            }.buildClosed()
            .bottomTo( thickness )
    }

    fun hangingHole() : Shape3d {
        return Square( 10, 10 ).roundAllCorners(1,3).extrude(100)
            .center()
            .rotateY(90)
            .rotateZ( if (sideways) 90 else 0 )
    }

    /**
        A (transpent) tube where the seeds are stored.
        There is a rim at both ends, to add extra strength, without having to make
        the tube thick in the middle.
    */
    @Piece
    fun tube() : Shape3d {

        val tube = plainTube()
        val hangingHole : Shape3d = hangingHole().topTo(tube.top-10)

        return (tube - hangingHole - feedingHole() - perchHole() )
            .color("White").bottomTo(0)
    }

    @Piece
    fun gromet() : Shape3d {
        val inside = Square( 10 - slack*2 ).center()
        val main = (inside.roundAllCorners(2) - Circle(2)).extrude(5)
        val flat = (inside.offset(2).roundAllCorners(4) - Circle(2)).extrude(0.8)

        return (flat + main).color("Green")
    }

    @Piece
    fun base() : Shape3d {
        val profile : Shape2d= profile()
        val pegHole = perchHole()
        val height = holeHeight - holeD/2+3
        val mainHeight = perchZ+perchD

        val mid = Square( diameter-slack*2 - radius*2, diameter-slack*2 ).center()
        val top = Square( 5, diameter-slack*2 ).center()
        val base = ExtrusionBuilder().apply {
            crossSection( profile.offset(rim+thickness) )
            forward(thickness)
            crossSection()
            crossSection( profile.offset(-slack) )
            forward( mainHeight-thickness )
            crossSection()
        }.build() -
            mid.offset(-innerT).extrude( height ).bottomTo(-1)

        val wedge = ExtrusionBuilder().apply {
            forward( height - innerT )
            crossSection( top.offset(-innerT) )
            forward( -height + mainHeight )
            crossSection( mid.offset(-innerT) )
            crossSection( mid )
            forward( height - mainHeight +innerT)
            crossSection( top )
        }.build()

        return (base + wedge- pegHole).color("Green")
    }

    fun feedingHole() : Shape3d {
        val radius = holeD/2 + innerT + slack
        // Make the hole easy to print, by making the top angled, then flat
        // so that the overhang is small, then a bridge.
        val profile = Hull2d( Circle( radius ), Square(radius).centerY().rightTo(0) )

        return profile.extrude( 100 ).centerZ().rotateY(90).centerZTo( holeHeight )
    }

    fun perchProfile() = Square( perchD ).center().roundAllCorners(2,1)



    fun perchHole() : Shape3d {
        return perchProfile().extrude( 100 ).center().rotateY(90).centerZTo(perchZ)
    }

    @Piece
    fun perch() : Shape3d {
        return perchProfile().offset(-slack).chamferedExtrude( diameter + perchOut * 2, 0.6 )
            .rotateY(90).bottomTo(0)
            .color("Green")
    }

    @Piece
    fun front() : Shape3d {
        val outerR = holeD/2 + innerT

        val shape = Circle( outerR + 3 )
            .centerXTo( holeHeight )
        val front = shape.extrude( innerT )

        val a = Cylinder( diameter/2 + thickness + innerT - slack, outerR )
            .centerXTo( holeHeight )
        val b = Cylinder( a.size.z, holeD/2 )
            .centerXTo( holeHeight )
            .topTo( a.top - innerT*2 )

        val cutAway = Cube( holeD, holeD, holeD*2 ).centerXY()
            .bottomTo( innerT ).rightTo( holeHeight - holeD*0.3 )

        val hole = Cylinder( diameter, 1 ).centerXTo( holeHeight )
            .bottomTo( b.top + innerT )

        return (front + a - b - cutAway - hole).color("Green") 
    }

    @Piece
    fun lid() : Shape3d {
        val profile : Shape2d= profile().offset( 12 )
        val lid = ExtrusionBuilder().apply {
            crossSection( profile )
            forward( 3 )
            crossSection()
            for ( i in 0..10) {
                forward( 1 )
                crossSection( profile.offset( -10 + 10 * Degrees.sin( 90 -i * 7)))
            }
            forward( diameter*0.3 )
            crossSection( profile.scale(0.05, 0.35) )
        }.build()

        val gSlack = slack + 1
        val groove = (profile().offset(rim+thickness+gSlack) - profile().offset(-gSlack))
            .extrude( rimE * 2)

        val rodHole = Cylinder( 100, 2 ).center().rightTo( - diameter/2-5 )
            .mirrorX().also()

        return (lid - groove - rodHole).color("Green")
    }

    @Piece
    fun allBarTube() : Shape3d {
        val fronts = front().tileX(2,2).toOrigin()
        val gromets = gromet().tileX(2,2).toOrigin()
        val lid : Shape3d = lid().frontTo(0).toOrigin()
        val base : Shape3d = base().toOrigin()
        val perch : Shape3d = perch()
        val perchWithEars = perch + Cylinder( 0.2, 10 ).centerXTo(perch.left)
            .centerXTo(perch.right).also()
        
        val y1 = layoutY(2, lid, gromets.rightTo(lid.right) )   
        val y2 = layoutY(2, base, fronts )
        val x = layoutX(2, y1, y2).leftTo(0)
        return layoutY(-2, x, perchWithEars.leftTo(0) )
    }

    override fun build() : Shape3d {
        val tube : Shape3d = tube().bottomTo(thickness)//.previewOnly()
        val base : Shape3d = base()
        val perch : Shape3d = perch().centerZTo(perchZ+slack).centerX()
        val front : Shape3d = front().rotateY(-90).rightTo( diameter/2 + thickness + innerT )
            //.mirrorX().also()
        val lid : Shape3d = lid().bottomTo( tube.top + 0 )
        val gromet : Shape3d = gromet().rotateY(-90).topTo( tube.top - 8 ).rightTo(diameter/2+thickness*2)
            //.mirrorX().also()
            
        return tube + front + base  + lid + gromet + perch
    }

}