Exit Full View
Up

/Garden/BirdTable2.foocad

BirdTable2
FooCAD Source Code
import uk.co.nickthecoder.foocad.layers.v1.Layer
import uk.co.nickthecoder.foocad.layers.v1.Layers
import static uk.co.nickthecoder.foocad.layers.v1.Layers.*
import static uk.co.nickthecoder.foocad.changefilament.v1.ChangeFilament.*
import static uk.co.nickthecoder.foocad.size.v1.Size.*
import uk.co.nickthecoder.foocad.size.v1.Size.*
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*
import static uk.co.nickthecoder.foocad.screws.v1.Screws.*
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
import static uk.co.nickthecoder.foocad.extras.v1.v1.Extras.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
import static uk.co.nickthecoder.foocad.along.v2.Along.*

/*
    A plan for a wooden bird table.

    The "floor" section is not attached to the middle section, so the whole top
    can be lifted up.
    A plastic, easy clean sheet can sit on top of the plywood floor for easy cleaning.

    This is the 2nd iteration. The first was overly "fussy", and isn't as easy to clean
    as I anticipated (the lip means that food scraps are tricky to remove).

*/
class BirdTable2 : Model {
    

    var mainSize = Vector3(300, 200, 30)

    var columnHeight = 150
    
    var columnCount = Vector2( 4, 3 )

    val batten = Lumber( "batten", 20, 10 ).stockLength(2400)

    val square = Lumber( "square", 20, 20 ).stockLength(2400)

    /**
        I cut curves for an old-fashioned look
    */
    val skirt = Lumber( "skirt", 40, 10 ).stockLength(2400).color("Indigo")

    val dowel = Lumber( "dowel", Circle(12/2) ).stockLength(2400).color("Ivory")


    @Custom( about="Move the pieces away from each other for an exploded view" )
    var explode = 0

    fun floor() : Shape3d {
        val thickness = 12
        var cube = Cube( mainSize ).centerXY().previewOnly()

        val floor = Cube(mainSize.x, mainSize.y, thickness).center()
            .topTo(0).color("Blue")

        return floor // + battensX + battensY 
    }


    fun middle() : Shape3d {
        val inside = Cube(
            mainSize.x,
            mainSize.y,
            columnHeight
        ).centerXY()
            .previewOnly()

        val battenX = batten.cut( inside.size.x - square.width()* 2).label("columnsX")
            .label( "columns" )
            .alongX2().bottomTo(0).centerX()
            .frontTo(inside.front)
            .mirrorY().also()

        val battenY = batten.cut( inside.size.y - square.width()* 2).label("columnsY")
            .label( "columns" )
            .alongY().bottomTo(0).centerY()
            .leftTo(inside.left)
            .mirrorX().also()
            .brighter()
        val battens = battenX + battenY

        val skirtX = skirt.cut( mainSize.x )
            .label("skirtX")
            .alongX2().topTo(battenX.top).centerX()
            .frontTo(battenX.back)
            .mirrorY().also()
            

        val skirtY = skirt.cut( skirtX.size.y )
            .label("skirtX")
            .alongY().topTo(battenY.top).centerY()
            .leftTo(battenY.right)
            .mirrorX().also()
            

        val corner = square.cut( inside.size.z )
            .label("corner")
            .leftTo( inside.left )
            .frontTo( inside.front )

        val corners = corner.mirrorX().also().mirrorY().also()


        // Stops greedy pidgeons getting in!
        val perchX = dowel.cut( corners.size.x )
            .alongX().centerX()
            .centerYTo( corners.front )
            .centerZTo( 80 )
            .mirrorY().also()
        val perchY = dowel.cut( corners.size.y )
            .alongY().centerY()
            .centerXTo( corners.left )
            .centerZTo( 80 )
            .mirrorX().also()

        val sheet = Cube( inside.size.x, inside.size.y, 1 ).color("Green")
            .centerXY().bottomTo(inside.bottom)

        return (battens).color("Ivory") + sheet + corners + skirtX + skirtY + perchX + perchY
    }

    /*
        Note, the front and end rafters should be cut to look nice,
        and the remainer cut to slot ontop of the columns
    */
    fun roof() : Shape3d {

        val cube = Cube( mainSize.x, mainSize.y + 40, 10 )
            .centerXY().bottomTo( columnHeight - 10 )
            .previewOnly()

        val triangle = Circle( cube.size.y/2 ).sides(4) / Square( cube.size.x*3 ).centerX()
        val rafter = triangle.extrude( cube.size.z ).alongX()
            .bottomTo(cube.bottom)


        val rafters = rafter.spreadX( 2, cube.size.x + rafter.size.x*2 ).centerX()
            .labelSize( false, false, true )

        println( "Rafters : $rafters" )
        //for (c in (rafters as Union3d).dependencies3d) {
        //    println( "Child : $c ${c.isPreviewOnly()}" )
        //}
        println( "Size = ${rafters.size}" )
        println( "Was = ${rafter.spreadX( 2, cube.size.x + rafter.size.x*2 ).centerX().size}" )

        val overhang = 5

        val hyp = Math.sqrt(2) * rafters.size.z + 14 + overhang
        val board = Cube( rafters.size.x, hyp, 10 )
            .rotateX(180+45)
            .centerX()
            .translateZ( rafters.top + 14)
            .color("DarkSeaGreen")

        val trimMitre = MitreJoint( batten, 3 )
        val trim = batten.cut( hyp + 10 ).label( "roof trim" )
            .cutZRatio(trimMitre,0)
            .alongY()
            .rotateX(180+45)
            .topTo( board.top + 8)
            .rightTo( rafters.left )
            .color("LightGreen")
            .mirrorY().also()
            .mirrorX().also()

        return rafters.color("Ivory") + board + trim //+ cube
    }


    // I don't want pidgeons standing on the roof, shitting on my lovely bird table!
    // The pigeons can stand on this pointy boy :-( Fingers crossed they will find a
    // more comfy perch elsewhere!
    // I drilled holes in the ends, and nailed it to the bird table.
    @Piece
    fun roofRidge() : Shape3d {
        val angle = 90
        val length = 490/3 // Table is 490 long, but I will print it in 3 sections

        val pattern1 = Circle(17).sides(4).centerYTo(-3).scaleY(1.5) / Square( 22 ).centerX()
        val pattern2 = pattern1.scale(0.4,0.4)

        val support = Square(20,2).centerY().rotate(-angle/2).mirrorX().also().extrude(length)
            .rotateX(90).bottomTo(-2).centerY() / Cube(length+2).centerXY()

        val repeat = length ~/ pattern1.size.x

        val hole = Circle( 3 ).translateY(7)
        val pattern = (pattern1 - hole).tileX(repeat).centerX() +
            pattern2.repeatX(repeat-1, pattern1.size.x).centerX()

        val ridge = pattern.scaleX( length / pattern.size.x )
            .extrude(2).centerZ()
            .rotateX(90).rotateZ(90).centerY().bottomTo( support.size.z -2 )

        return support + ridge
    }


    /**
        Drill holes though the skirt and into floor. Insert these pins to stop the
        top coming off in the wind etc.
    */
    @Piece
    fun pin() : Shape3d {
    
        val pinL = 20
        val pinD = 5.5
        val cyl = Circle( pinD/2 ).chamferedExtrude( pinL, 0, 1 )
        val flattened = cyl.backTo(pinD*0.75) / Cube( pinD, pinD*0.75, pinL + 2 ).centerX()
        val end = Square( pinD*2, pinD ).roundCorner(3,2).roundCorner(2,2)
            .chamferedExtrude(2,1,0).rotateX(90).centerX().frontTo(0)
       
        return flattened.rotateX(90) + end
        
    }

    val backgroundLayer = Layer( "background", 1.2, "White" )
    val pictureLayer = Layer( "picture", 1.8, "Black" )
    val circleLayer = Layer( "circle", 2.4, "Red" )


    @Piece
    fun noPigeon() : Shape3d {
        val height = 44
        val profile = SVGParser().parseFile( "pigeon.svg" ).shapes["pigeon"].mirrorX()
        val bird = profile.center().scale(height/profile.size.y)
            .translate(-8,-2)
            .chamferedExtrude( pictureLayer, 0, 0.4 )

        val noEntryP = Circle( 43 ).sides(100) - Circle( 35 ).sides(100) +
            Square( 80, 9 ).center().rotate(-45)
        val noEntry = noEntryP
            .chamferedExtrude(circleLayer, 0, 0.4) +
            Circle(43).sides(100).extrude( backgroundLayer )

        val textP = Text("No Pigeons", BOLD)
            .centerX().backTo(noEntry.front-10)
        val text = textP
            .extrude( pictureLayer )
 
        val barP = textP.boundingSquare().offset(5).roundAllCorners(2)
            .centerX().centerYTo(text.middle.y)
        val bar = barP
            .extrude( backgroundLayer ) +
            (barP - barP.offset(-2)).extrude( pictureLayer )

        return bird + noEntry + text + bar
    }
    fun noPigeon( gcode : GCode ) {
        postProcess( gcode, backgroundLayer, pictureLayer, circleLayer )
    }

    override fun build() : Shape3d {
        
        val floor = floor()
        val middle = middle().translateZ(explode*2)
        val roof = roof().translateZ(explode*3)


        val pins = pin().frontTo( middle.back + 10 )
            .repeatX(2, floor.size.x * 0.5).centerX()
            .mirrorY().also()

        val roofRidge = roofRidge().rotateZ(90)
            .rightTo( roof.right )
            .bottomTo( roof.top - 10 )
            .color("Ivory")


        return floor + middle + roof + roofRidge + pins
    }
}