Exit Full View
ZipUp

/Garden/BirdBathMoat.foocad

BirdBathMoat

A bird bath with an extra container at the center where I can drop slugs/snails etc. for food. Heopfully the water acts as a moat to prevent the the pests escaping.

Print with PETG? In my experience PLA never holds water.

FooCAD Source Code
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
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 uk.co.nickthecoder.foocad.cup.v1.*
import static uk.co.nickthecoder.foocad.cup.v1.Cup.*

class BirdBathMoat : Model {

    var size = Vector2( 235, 40 )

    var insideSize = Vector2( 70, 30 )

    var thickness = 2.0
    
    var baseThickness = 1.2

    var round = 20

    var measureDiameter = 217

    @Piece( printable=false, about="Work in progress" )
    meth shade() : Shape3d {
        
        val height = 100
        val radius = 100

        val path = PolygonBuilder().apply {
            moveTo( 0, height )
            bezierTo( Vector2( radius/2, height/2 ), Vector2(radius/10, height/2), Vector2( radius, 0 ) )
            lineBy(8,0)
            bezierBy( Vector2( 4, 4 ), Vector2( -13, 8 ), Vector2( 10, 18 ) )
        }.buildPath()
        val shape = path.thickness( thickness )
            .leftTo(0.1)

        val end = Circle( thickness ).translate( path[-1] )

        val main = (shape + end)
            .revolve()

        val ball = Sphere(6).bottomTo( main.top - thickness*1.5 )

        return main + ball
        /*
        val r = 100
        val ball = Sphere( r ) - Sphere( r - thickness )

        val dome = ball.remove( Cube( r*3 ).center().topTo( r*0.5 ) )
        
        return dome.rotateY(30)
        */
    }

    @Piece( about="Stick 3 to the base, to locate the bath above model 'SmallBirdTable'" )
    meth locatingFoot() : Shape3d {
        val d = 9
        val post = Circle( d/2 ).smartExtrude( 10 ).top( Fillet(d/3) )
        val base = Circle(20).extrude(1.0)
        return base + post
    }

    @Piece( about="For a free-standing bath. Slugs/snails may hide underneither ;-))" )
    meth foot() : Shape3d {
        val radius = 14
        val tipOffset = Vector2( 4, 30 ) // X offfset and height of the prong

        val base = Circle( radius ).rightTo(0)
            .smartExtrude(4 + 1)
            .scaleBottom( Vector2(1.2, 1.5) )

        val mid = Circle( radius )
            .rightTo(0)
            .extrude(1).translateZ(base.top-1)

        val tip = Sphere( 4 )
            .translate( tipOffset.x, 0, tipOffset.y )

        val all = base + (tip hull mid)

        return all
    }
    @Piece
    meth footRing() : Shape3d {
        val foot = foot()
        val width = 34
        val radius = size.x/2-round/2-1
        val ringShape = Ring( radius, radius - width*0.6)
        val ring = ringShape.extrude(1)
        val feet = foot.leftTo(ring.right - width).repeatAroundZ(5)

        return (ring + feet).color("Green")
    }

    @Piece
    @Slice( perimeters=6 )
    meth main() : Shape3d {

        val lipPath = PolygonBuilder().apply {
            val r = 4
            moveTo( 0,0 )
            bezierBy( Vector2(-r/2,r/2), Vector2(0,r/2), Vector2(-r, r*1.1) )
            circularArcBy( r, r, r, false, false )
    
        }.buildPath()

        val main =  Circle( size.x/2 ).cup( size.y, thickness )
            .baseThickness( baseThickness )
            .insideBottom( Fillet( round ) )
            .outsideBottom( ProfileEdge.roundedChamfer( round ) )
            .outsideTop( ProfileEdge( lipPath ) )
            .insideTop( Fillet(thickness-0.1) )

        val inside = Circle( insideSize.x/2 ).cup( insideSize.y, thickness )
            .baseThickness( baseThickness )
            .insideBottom( Fillet( round ) )
            .outsideBottom( Fillet( round ).reverse() )
            .insideTop( ProfileEdge( lipPath ) )
            .outsideTop( Fillet(thickness-0.1) )

        return main + inside
    }

    @Piece( print="main" )
    override fun build() : Shape3d {
    
        // I wanted the very bottom to be the same diameter as my SmallBirdTable's top.
        val measure = Cube( measureDiameter, 1, 1 ).center().topTo(0).previewOnly()

        //val feet = foot().rightTo( main.right-6 ).mirrorZ().topTo(0).repeatAroundZ(5)
        val feet = footRing().mirrorZ()

        return main() + measure + feet
    }

}