/Garden/SquirrelFeeder.foocad

A variation of BirdFeeder2. Attached to vertical wall/post instead of hanging it.
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
import uk.co.nickthecoder.foocad.smartextrusion.v1.*
import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.*
import uk.co.nickthecoder.foocad.screws.v3.*
import uk.co.nickthecoder.foocad.compound.v1.*
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*
import uk.co.nickthecoder.foocad.cup.v1.*
import static uk.co.nickthecoder.foocad.cup.v1.Cup.*
class SquirrelFeeder : Model {
@Custom( about="Width of the tube" )
var diameter = 60
@Custom( about="The height of the tube." )
var height = 120
@Custom( about="The radius of the tube's corners" )
var radius = 10
@Custom( about="Size of the feeding hole" )
var holeSize = Vector2(15,15)
@Custom( about="Size (y & z) of the base (Used by piece `base`, not `perchBase`)" )
var baseSize = Vector3(66, 140, 8)
@Custom( about="Size (y & z) of tray for piece `perchBase` (not piece `base`)" )
var traySize = Vector3(28, 20, 10)
@Custom( about="Additional height for piece `perchBase` (not used by `base`)" )
var perchBaseHeight = 4
@Custom( about="Makes holeDiamter smaller" )
var holeCoverWidth = 5
@Custom
val pocketHanger = PocketHanger(22,3,30).wallThickness(3).standOff(2)
// Other parameters, not exposed as @Custom
var thickness = 1.0
var lipSize = Vector2( 1.5, 5 )
var clearance = 0.5 // Distance for pieces to easily slot together
var interferanceFit = 0.2 // Distance for pieces to only just fit together.
var holeHeight = 2
var attachHeight = 8.5 // Beware! Too small, and it will affect the tube's chamfer.
var perchDiameter = 4.95
var perchHeight = 10
var hangOffset = 20 // Distance from top to the holes for the wire hanger.
var lidSize = Vector2( 8, 2) // Height and thickness of the lid
val attachmentSize = Vector3( 4, 20, 30 )
meth profile() = Square( diameter ).center()
.roundAllCorners( radius )
meth hole() = Circle( holeSize.x/2 ).scaleY(holeSize.y/holeSize.x) -
Square( holeSize.x ).center().backTo( -holeSize.y * 0.4 )
meth lip() = fatLip( lipSize.y, lipSize.x )
meth perchShape() = Circle( perchDiameter / 2 ).sides(32) +
Square( perchDiameter, perchDiameter / 2 ).centerX().backTo(0)
meth holeAndReinforcement( shape : Shape2d, y : double, outside : bool ) : Shape3d {
val hole = shape
.extrude( y * 2 + 10 )
.rotateX(90)
.centerY()
val reinforcement = shape.offset( 4 )
.smartExtrude( lipSize.x )
.outsideTop( Chamfer( lipSize.x ) )
.rotateX(90)
.backTo( if (outside) {-y } else y )
return reinforcement.mirrorY().also().remove( hole )
}
@Piece
meth connector() = perchShape()
.offset(-interferanceFit)
.extrude( diameter + 4 )
.rotateX(90)
.centerXY()
.bottomTo(0)
.color("darkGreen")
@Piece
meth tube() = tubeUpright().mirrorZ().rotateZ(90).bottomTo(0)
@Piece( print="tube" )
meth tubeUpright() : Shape3d {
val tube = profile().outline( 0, thickness)
.smartExtrude( height )
.insideTop( lip() )
.outsideBottom( lip() )
val feedingHole = holeAndReinforcement( hole(), diameter/2, true )
.translateZ( holeHeight + holeSize.y/2 )
// Use offset to ensure a tighter fit.
val baseAttachment = holeAndReinforcement( perchShape().offset(-clearance*0.25), diameter/2, true )
.centerZTo( attachHeight )
.rotateZ(90)
val attachment = pocketHanger
.rotateZ(90)
.topTo( height -10 )
.rightTo( -diameter/2 )
return Compound(true).apply {
+ tube
+ baseAttachment
+ attachment
+ feedingHole
}.build().color("WhiteSmoke")
}
@Piece
meth base() : Shape3d {
val sunken = 0 // Lifts the insert up/down.
val profile = profile().offset(-thickness-clearance)
val baseProfile = Square( baseSize.x, baseSize.y )
.center().roundAllCorners(radius)
val base = baseProfile.smartExtrude( baseSize.z )
.top( Fillet( 2 ) )
.bottom( Chamfer( 2 ) )
val top = profile.extrude(1)
.bottomTo( base.top )
val attachment = holeAndReinforcement( perchShape(), base.right, false )
.translateZ( base.top + attachHeight )
.rotateZ(90)
val hollow = Square( baseProfile.size.x, baseProfile.back - profile.back )
.center()
.offset( -6 )
.roundAllCorners( radius - 6 )
.frontTo( baseProfile.front + 6 )
.smartExtrude( base.size.z - 2 )
.top( Fillet( -2, 2 ) )
.bottom( Fillet( 4 ) )
.topTo( base.top + 0.1 )
.mirrorY().also()
val insert = insert()
.bottomTo( base.top )
return Compound(true).apply {
+ base
+ top
+ insert
- hollow
}.build().color("darkGreen")
}
@Piece( about="An alternate base instead of piece `base`")
meth perchBase() : Shape3d {
val sunken = 0 // Lifts the insert up/down.
val profile = profile().offset(-thickness-clearance)
val insert = insert().bottomTo(perchBaseHeight)
val underInsert = profile().offset( lipSize.x )
.extrude( perchBaseHeight )
val attachment = holeAndReinforcement( perchShape(), insert.right, false )
.translateZ( underInsert.top + attachHeight )
.rotateZ(90)
val hollowProfile1 = Square( traySize.x, traySize.y )
.center()
.backTo( insert.front-2 )
.roundAllCorners( 4 )
val hollowProfile = hollowProfile1 hull hollowProfile1.mirrorY()
val tubeExclusion = Cube( diameter + (lipSize.x + clearance)*2 )
.centerXY().bottomTo( perchBaseHeight )
val raised = hollowProfile.offset(6)
.smartExtrude( traySize.z + perchBaseHeight )
.top( Fillet( 3 ) )
val hollow = hollowProfile1
.smartExtrude( traySize.z + 0.01 )
.top( Fillet( -2, 2 ) )
.bottom( Fillet( 4 ) )
.bottomTo( perchBaseHeight )
.mirrorY().also()
val drainHoles = Cylinder( 10, 1.5 )
.bottomTo(-1)
.centerYTo( underInsert.front - traySize.y/2 )
.mirrorY().also()
val perches = Square( traySize.x + 30, 6 )
.centerY()
.roundAllCorners(1)
.smartExtrude( 6 )
.top( Fillet( 3 ) )
.bottom( Chamfer(1) )
.repeatAroundZ( 2, -90 )
.translateY( hollow.back - 100 )
.mirrorY().also()
return Compound().apply {
+ perches
+ underInsert
+ raised
- tubeExclusion
+ insert
- hollow
- drainHoles
}.build().color("darkGreen")
}
meth insert() : Shape3d {
val profile = profile().offset( -thickness -clearance )
val base = profile.extrude(holeHeight + 2)
val hump = profile
.smartExtrude( 14 )
.scaleTop( Vector2(1, 0.05) )
.bottomTo( base.top )
val angle = profile.extrude(20) -
Cube( profile.size.x * 2 )
.centerXY()
.rotateY(25)
val angles = angle.mirrorX().also()
val attachmentHole = perchShape().offset(clearance)
.extrude( profile.size.x + 10 )
.center()
.rotateZ(90)
.rotateY(90)
.translateZ( attachHeight )
return base + hump + angles - attachmentHole
}
@Piece
meth lid() = lidUpright().mirrorZ().bottomTo(0)
@Piece( print="lid" )
meth lidUpright() : Shape3d {
val profile = profile().offset( lidSize.y + clearance )
val lid = profile.cup( lidSize.x, lidSize.y )
.outsideBottom( Chamfer( lidSize.y - 1 ) )
.outsideTop( Chamfer( 0.6 ) )
return lid.color( "darkGreen" )
}
@Piece
meth attachment() = pocketHanger.attachment()
meth attachmentProfile() = Square( attachmentSize.x, attachmentSize.y*2 )
.center()
.roundCorner( 3, attachmentSize.x, 1 )
.roundCorner( 0, attachmentSize.x, 1 )
.scaleY(0.5)
@Piece
fun holeCover() : Shape3d {
val profile = hole().offset(2)
val outside = ExtrusionBuilder().apply {
crossSection( profile )
forward(1)
crossSection()
crossSection( -1 )
forward( 3 )
crossSection()
}.build()
val inside = profile.offset( -holeCoverWidth )
.smartExtrude( outside.size.z + 0.02 )
.chamfer(-1,1)
.translateZ(-0.01)
return outside - inside
}
// My original print's hole size was too big, and too much food spilled out.
// Instead of reprinting the tube, I made this smaller part to restrict the flow.
@Piece( about="Restricts the size of the opening to adjusts the flow of food." )
meth shutter() : Shape3d {
val insideWidth = diameter - thickness * 2 - clearance*2
val base = Cube( holeSize.x+4, insideWidth, 15 )
.centerXY()
val base2 = Cube( insideWidth, 15, 2 )
.centerXY()
val post = (Cylinder(6, 4) remove (Cylinder(30,1.5).center()))
.leftTo( base.right )
val foo = Cube( base.size.x+2, base.size.y-4, base.size.z )
.centerXY().bottomTo(2)
return Compound(true).apply {
+ (base + base2 - foo)
+ post
+ post.mirrorX()
}.build()
}
@Piece( printable = false )
override meth build() = Compound().apply {
val base = base()
val perchBase = perchBase()
.translateZ( baseSize.z - perchBaseHeight )
val tube = tubeUpright().bottomTo( baseSize.z ).previewOnly()
val connector = connector().rotateZ(90).centerZTo( tube.bottom + attachHeight + clearance )
val lid = lidUpright().mirrorZ().topTo( tube.top + lidSize.y + clearance )
val attachment = attachment()
.rotateZ(90).rotateY(-90)
.rightTo( -diameter/2)
.centerZTo( tube.middle.z + 20 )
val shutter = shutter()
.bottomTo(29)
+ tube
//+ base
+ perchBase
//+ lid
+ connector
+ attachment
//+ shutter
}.build()
}