/Garden/SmallBirdTable.foocad

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.
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
}
}

