Exit Full View
ZipUp

/Garden/SmallBirdTable.foocad

SmallBirdTable

I want a separate bird table just for small birds : tits, robins, sparrows etc. My current table is dominated by starlings, and the smaller birds are pushed away!

The columns (which are close together to exclude large birds and squirrels) are ice lolly sticks. The columns are glued permanently into the two yellow rings.

The three pieces are held together using more dowels. Therefore cleaning is easy - just remove the dowels.

Pieces required : 2x innerRing 1x base 1x lid 3x eye (to hang it up) lots of columns (wooden dowels - ice lolly sticks) lots of pin (wooden dowels - ice lolly sticks)

If you do NOT want to hang it, then replace lid with another base and don't print the eyes.

FooCAD Source Code
import uk.co.nickthecoder.foocad.cup.v1.*
import static uk.co.nickthecoder.foocad.cup.v1.Cup.*
import uk.co.nickthecoder.foocad.threaded.v2.*
import uk.co.nickthecoder.foocad.extras.v1.*
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*
import uk.co.nickthecoder.foocad.smartextrusion.v1.*
import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*

class SmallBirdTable : Model {

    @Custom( about="Upcycled dowels from ice lollies. Form the columns to keep large birds/squirrels out" )
    var dowelSize = Vector2( 100, 6 )

    @Custom( about="Distance between the columns" )
    var columnSpacing = 40

    @Custom
    var roughDiameter = 200

    @Custom( about="Width and height of the ring with the columns (yellow)" )
    var innerRingSize = Vector2( 10, 8 )

    @Custom( about="Width and height of the outside ring (green)")
    var outerRingSize = Vector2( 8, 14 )

    @Custom
    var baseThickness = 2.0

    @Custom
    var clearance = 0.1

    @Custom
    var pinLength = 160 - dowelSize.x


    @Custom( about="Radius, depth and thickness of the pots" )
    var potSize = Vector3( 60, 30, 1.8 )

    @Custom( about="How many pots make up a complete circle" )
    var potCount = 3



    meth dowelCount() = roughDiameter*Math.PI ~/ columnSpacing
    /**
     * The radius of the mid point of the innerRings.
     */
    meth radius() = dowelCount() * columnSpacing / Math.PI / 2


    @Piece
    meth testFit() : Shape3d {
        val solid = Square( innerRingSize.x ).center().roundAllCorners(2).extrude(innerRingSize.y)
        val hole = Circle( dowelSize.y / 2 + clearance ).hole()
            .smartExtrude( innerRingSize.y - 1 )
            .top( Chamfer(0.5).reverse() )
            .bottomTo( 1.01 )
        return solid - hole
    }

    @Piece( about="Use wooden dowels instead???" )
    meth column() = dowel( dowelSize.x )

    @Piece( about="Use wooden dowels instead???" )
    meth pin() = dowel( pinLength )

    meth dowel( length : double ) : Shape3d {
        val shape = Circle( dowelSize.y / 2 )
        val full = shape.extrude( length ).rotateX(-90).centerXY()

        return full.bottomTo( -dowelSize.y / 10 )
            .remove(Cube( length + 2 ).centerXY().topTo(0))
    }

    @Piece( printable=false )
    meth assembly() : Shape3d {

        val dowelCount = dowelCount()
        val radius = radius()

        val ringShape = (Circle( radius + innerRingSize.x/2 ) - Circle( radius - innerRingSize.x/2 ))

        val columns = Circle( dowelSize.y / 2 ).hole()
            .extrude( dowelSize.x )
            .translateX( radius )
            .bottomTo(1)
            .repeatAroundZ( dowelCount )

        val pinHole = Circle( dowelSize.y/2 + clearance ).hole()
            .extrude( pinLength )
            .rotateY(90)
            .leftTo( radius - innerRingSize.x / 2 + 1 )
            .centerZTo( innerRingSize.y / 2 )
        val pinHoles = pinHole.repeatAroundZ( 6 ).rotateZ(4)


        val bottomRing = (
            ringShape
                .smartExtrude( innerRingSize.y )
                .top( Chamfer(1) ) -
            columns - pinHoles
            ).label( "innerRing")

        val topRing = bottomRing.mirrorZ()
            .topTo( columns.top + 2 )

        val baseRadius = radius + innerRingSize.x / 2 + clearance*2
        val baseRing = (Circle( baseRadius + outerRingSize.x ) - Circle( baseRadius ) )
            .smartExtrude( outerRingSize.y  )
            .top( Fillet(outerRingSize.x/2) )
            .bottom( Chamfer(1.5) )
            .bottomTo( - baseThickness ) - pinHoles

        val baseFlat = Circle( radius + 10 )
            .extrude( baseThickness )
            .topTo( 0.01 )

        val base = (baseFlat + baseRing - pinHoles)
            .color("Green")
            .label( "base" )
            .translateZ( -0.1 )

        val plainLid = base.mirrorZ().topTo( topRing.top + baseThickness )

        val thread = Thread(10)
        val threadedHole = thread.threadedHole( 7 )

        val reinforceRadius = radius - innerRingSize.x/2 - clearance*2
        val connection = Circle( 12 )
            .smartExtrude(threadedHole.size.z)
                .bottom(Chamfer(3) )
            .remove( threadedHole )
            .topTo( plainLid.top )
            .rightTo( reinforceRadius )
        val connections = connection
            .repeatAroundZ(3).withCavities()
        val reinforcements = (
                Ring( reinforceRadius, reinforceRadius - innerRingSize.x ) +
                Square( radius* 2 - innerRingSize.x - 2, innerRingSize.x )
                    .center()
                    .roundAllCorners(2)
                    .repeatAround(3)
            )
            .smartExtrude( 4 )
            .bottom( Chamfer(1) )

            .topTo( plainLid.top - baseThickness ) 

        val lid = (plainLid and reinforcements and connections)
            .color("Green")
            .label("lid")

        val eyeThread = thread.threadedRod( threadedHole.size.z + 1 )
            .rotateY(90).bottomTo( -thread.diameter/5 )
        val eyeHole = Ring( 6, 2 )
            .smartExtrude(eyeThread.top + eyeThread.bottom)
            .edges( Chamfer(0.5) )
            .rightTo(1)

        val eye = (eyeThread.intersection( Cube( 50 ).centerXY() ) + eyeHole)
            .color("Orange")
            .label("eye")
            
        val eyes = eye.rotateY(90)
            .translateX( connections.right - connection.size.x/2 -2 )
            .bottomTo( lid.top )
            .repeatAroundZ(3)

        //return lid + topRing
        return base + bottomRing + topRing + columns.previewOnly() + lid + pinHoles.previewOnly() + eyes
    }

    @Piece
    meth innerRing() = assembly().find("innerRing")

    @Piece
    meth eye() = assembly().find("eye")

    @Piece
    meth base() = assembly().find("base")

    @Piece
    meth lid() = assembly().find("lid").rotateX(180).bottomTo(0)

    @Piece( about="Optional - Omit a column and replace it with a door" )
    meth door() : Shape3d {
        val doorThickness = 2.2
        val radius = radius()
        val insideRadius = radius + dowelSize.y/2 + 1
        val dowelCount = dowelCount()

        val arc = CircularArc( insideRadius + doorThickness, insideRadius, 0, 360 / dowelCount * 2 + 2 )

        val door = arc.extrude( dowelSize.x - (outerRingSize.y-baseThickness) * 2 )
        val hingeR =  dowelSize.y / 2 + clearance*2
        val hingeShape = Circle( hingeR + doorThickness + 1 ) - Circle( hingeR )
        val hinge = hingeShape.translateX( radius )
            .extrude( door.size.z )

        val clip = CircularArc( hingeR + 2, hingeR+0.5, -5, 140 )
            .extrude( door.size.z /3 )
            .translateX(radius)
            .rotateZ( 360/dowelCount*2 )
        
        val handle = Trapezium( 20, 10 ).deltaX(10)
            .smartExtrude(3).edges(Chamfer(1))
            .rotateY(90).rotateZ(90).centerY().bottomTo(3).leftTo( insideRadius )
                .rotateZ( 360/dowelCount*2 )

        val spacer = (Circle( hingeR + 2 ) - Circle( hingeR ))
            .extrude(outerRingSize.y - innerRingSize.y)
            .rightTo( hinge.left - 1 )

        return door + hinge + clip + spacer + handle
    }


    @Piece( print="printPerchJoint", about="Add a horizontal perch between two columns" )
    meth perchJoint() : Shape3d {
        val hingeR =  dowelSize.y / 2 + clearance*2

        val ringShape = Circle( hingeR + 2 ) remove Circle( hingeR )
        val spacer = ringShape
            .smartExtrude( 30 )

        val acrossShape = Square( hingeR*2 + 4, hingeR*2 + 6 ).center()
                .roundCorner(3, 2, 1)
                .roundCorner(2, 2, 1)
                .roundCorner(1, hingeR + 1)
                .roundCorner(0, hingeR + 1) remove
            Circle( hingeR - clearance ).safeOverhang().mirrorY()

        val forPerch = acrossShape
            .smartExtrude(ringShape.size.x)
                .edges( Chamfer(0.5) )
            .rotateX(90).centerY()
            .topTo( spacer.top )
            .leftTo( hingeR -1.5 )

        return spacer and forPerch
    }
    @Piece
    meth printPerchJoint() = perchJoint().mirrorZ().bottomTo(0)

    @Piece
    meth pot() : Shape3d {
        val shape = Sector( potSize.x, 0, 360/potCount )
            .roundCorner(-1,5)
            .roundCorner(1,5)
            .roundCorner(0,5)

        val top = ProfileEdge.chamfer(2).reverse() and ProfileEdge.flat(2) and ProfileEdge.chamfer(1)

        return shape.cup( potSize.y, potSize.z )
            .outsideBottom( ProfileEdge.roundedChamfer(3) )
            .insideBottom( Fillet(3) )
            .insideTop( top )
            .outsideTop( Chamfer(0.5) )
    }

    @Piece( printable=false )
    override meth build() : Shape3d {
        val pot = pot()
        val door = door().translateZ(outerRingSize.y).previewOnly()
        return assembly() + door + pot
    }

}