/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() }