Exit Full View
Up

/GardenFurniture/ColdFrame.foocad

ColdFrame
FooCAD Source Code
import static uk.co.nickthecoder.foocad.along.v2.Along.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*

/*
A Cold Frame / Seating / Table / Storage

Designed with double seed trays in mind, which measure 52x42 cm
https://www.wilko.com/en-uk/wilko-black-premium-gravel-tray-52cm/p/0099388

Modular. Each item is constructed using layers; one layer just sits
on top of a previous layer.

Not shown on the model are either :
Posts in the corners keeping things aligned

There are two kinds of lid.
a) A transparent one, when being used as a cold frame.
   This may use plastic, or glass.
b) Seat sections. Each item has 2 seat sections which fit over the
   transparent lid. Each section is bigger than the lid.
   Rotate the sections by 90 degrees, and then then fit inside.
   (or keep them elsewhere then using as a cold frame).

If we make the lid with GLASS, then cut it slightly smaller, so that
plastic angled sections can be addded around the edges to hold the glass
in place, and protect from the glass's edges.
uPVC or anodised aluminium :
https://www.wickes.co.uk/Wickes-Angle---Anodised-Aluminium-15-x-15mm-x-2m/p/188259

Alternate Lid
=============
Instead of the glass being topmost, have the glass sit on a shelf inside the
lid. OR the shelf is a piece of uPVC/Al "U" section trim which the glass fits
into (the trim screws directly to the lid.(maybe with packing).

*/
class ColdFrame : Model {

    // A double seed tray is 42x56, and I want 2 per cold frame.
    // for 50mm extra width and front-to-back...
    @Custom
    var internal = Vector2( 890, 580 )

    @Custom( about="Extra space for the seat to sit on top of the lid. Remember hinges!" )
    var seatExtra = 30

    // https://www.wickes.co.uk/Wickes-Deck-Board---25mm-x-120mm-x-2-4m/p/114551
    @Custom    
    var decking = Lumber( "Decking", 120, 25, "Green" ).stockLength( 2400 )
    
    // decking cut width-ways in 4. Used for the posts, and the clear lid.
    // Note the thickness must be the same as decking's thickness.
    @Custom    
    val quarter = Lumber( "Quarter", 25, decking.thickness, "Orange" ).stockLength( 2400 )

    @Custom
    val third = Lumber( "Third", 38, decking.thickness, "Yellow" ).stockLength( 2400 )

    @Piece
    fun seedTray() = ExtrusionBuilder().apply {
        crossSection( Square( 420, 520 ).offset(-25).center().roundAllCorners( 30 ) )
        forward( 80 )
        crossSection( 20 )
        crossSection( 5 )
        forward(10)
        crossSection()
        crossSection( -15 )
        forward( -83 )
        crossSection( -13 )
    }.build().bottomTo(0)

    /*
        A placeholder for a tubuar heater
        https://www.ebay.co.uk/itm/133174729094?_trkparms=amclksrc%3DITM%26aid%3D1110006%26algo%3DHOMESPLICE.SIM%26ao%3D1%26asc%3D20201210111314%26meid%3D0c3b6b0306d6474f857d3d9aaed62d4e%26pid%3D101195%26rk%3D2%26rkt%3D12%26sd%3D303989286097%26itm%3D133174729094%26pmt%3D1%26noa%3D0%26pg%3D2047675%26algv%3DSimplAMLv9PairwiseWithPLXWeb%26brand%3DSUNHOUSE&_trksid=p2047675.c101195.m1851&amdata=cksum%3A1331747290940c3b6b0306d6474f857d3d9aaed62d4e%7Cenc%3AAQAGAAACELO5sAcQBCho2VkUOn3NsyVL1xh8j%252BO6V%252BZ1KuDlFGJOzkYTe%252B1%252Br9%252FdPR79pl4NIQzG6zZCGa8UixbkT2spMhRK%252BCaX3D2URKHFg8tOKeuUT31jFebjvofcwlJzJGrIbeaCnUGH%252FmQrScLj8uAYCdGymM%252FP0Oo1qRm5HOeA5VHhlEizjyMvmNNJaiQuaMwQ1LSEkt73WNlA1sKm917G1RWwQnHiQ7HjinNIo%252B5oxSuMq7McEPIjgIvN6SeKC6O%252FeMDXQTg%252BTsLjEs5BlWsfxBhI8uQ5rGNYO2uzRPnPawqQyN%252Bydc4eqQ%252FSiPlSwLHxSQEUb%252BmptvYu6u5clNnegnbED4Lkw7DXwAP6%252FOc8NWp2e6W9bRLp4%252BfcCgnsaJuB04jF%252FO34X7RtlMT1QCI61UsBd29bvuvH6MfuGJYz07CcVaOi4PuhjmuYwAAQz%252B5oRF1mwJ3Coz%252BRSE3b5tuMFd%252FuWmHT%252BO6iQ4F3dTFxFIkaizODjCRyhRL%252BY6fHIKc9%252FiZoy8TuW5ZhUHKchnU5YpP7uvnetTVH77fjJerT3H4mZ2AuN7UlYsPD8kNUt6fCOi8f8MAxthuaE%252FSThG3bCKPOO%252Bmi9CCDBWKW47K%252B%252BNiGDWdJ3xE4jb5OjhTU%252FhZahOll76woQwsH2YAnZ9v9nyjB%252Boh%252BKkADITEmedioQSFEMs3W7azcmb5qwtmFUy3%252Bsg%253D%253D%7Campid%3APL_CLK%7Cclp%3A2047675
    */
    fun heater() : Shape3d {    
        return Cylinder( 713, 57/2 ).center().rotateY(90) +
            Cube( 20, 20, 81 ).tileX(2, 300).centerX().centerZ().backTo(50)
    }

    fun around( name : String, lumber : Lumber ) : Shape3d {
        val frontBack = lumber.cut( internal.x + decking.thickness() * 2 )
            .label( "${name}FrontBack" )
            .alongX()
            .centerX()
            .translateY( internal.y / 2 )
            .mirrorY().also()
        val sides = lumber.cut( internal.y )
            .darker()
            .label( "${name}Side" )
            .alongY().translateY( lumber.thickness() )
            .leftTo(frontBack.left)
            .centerY()
            .mirrorX().also()

        return frontBack + sides
    }

    /*
        A single ring of wood making up the sides of the cold frame.
        Stories can be stacked ontop of each other to make any height you like.
        Each storey has a post in each corner which aligns the story above it
        (or the lid).
    */
    fun storey( withPosts : bool ) : Shape3d {
        val around = around("storey", decking)

        val posts = quarter.cut( decking.width() * 0.9 )
            .color( "Blue" )
            .label( "post" )
            .translateX( -internal.x /2 )
            .translateY( -internal.y /2 )
            .topTo( around.top + quarter.thickness )
            .mirrorX().also()
            .mirrorY().also()

        return if (withPosts) {
            (around + posts)
        } else {
            around
        }
    }

    @Piece
    fun lid() : Shape3d {

        val middle = third.cut( internal.y )
            .label( "lidMiddle" )
            .alongY()
            .centerXY()
        val wood = around( "lid", third ) + middle
        return wood

    }


    /*
    I've removed the trim, because without it, the seat can be placed half way,
    not only at ONE end.
    Also, without the trim, two seat sections can be stacked nicely.
    */
    @Piece
    fun seat( pieces : int ) : Shape3d {
        val lengths = decking.cut( internal.y + decking.thickness() * 4 + seatExtra )
            .color("LightGreen" )
            .label( "seatLength" )
            .alongY2()
            .tileX( pieces, 5 )
            .centerXY()
        val edges = quarter.cut( lengths.size.x )
            .color("LightGreen" ).darker()
            .label( "seatEdge" )
            .alongX()
            .centerX()
            .frontTo( lengths.front )
            .topTo(0)
            .mirrorY().also()
        val trim = quarter.cut( internal.y + decking.thickness() * 2 )
            .color( "LightGreen" ).darker()
            .label( "seatTrim" )
            .alongY()
            .rightTo( lengths.right ).centerY()
            .topTo(0)

       

        return lengths + edges // + trim
    }

    @Piece
    fun coldFrame(stories : int, withSeat : bool, withSupports : bool ) : Shape3d {

        val base = storey(true).tileZ(stories, 5 - quarter.thickness)
        val lid = lid().bottomTo( base.top + 5 - quarter.thickness )
        val seat = seat( 4 ).bottomTo(lid.top)
        val seats = seat.leftTo(2).mirrorX().also()

        // Show a cylider heater in the bottom.
        // The seed trays are lifter up off the ground, and above the tubular heater.
        // The spare seating sections does fit below the supports.
        val heater = heater().previewOnly().backTo( base.back - 20 ).bottomTo(0)
        val supports = third.cut( base.size.y )
            .cutZRatio( LapJoint( third, 1, decking.thickness ), 0 )
            .cutZRatio( LapJoint( third, 1, decking.thickness ), 1 )
            .label("support")
            .alongY()
            .tileX( 4, 200 )
            .centerX()
            .frontTo(base.front).topTo(decking.width)

        var result : Shape3d = base + lid
        if (withSeat) result += seats
        if (withSupports) result += heater + supports

        return result
    }

    override fun build() : Shape3d {

        val a = coldFrame( 5, true, false )
        val b = coldFrame( 2, false, false ).leftTo( a.right + 10 )
        val c = coldFrame( 3, false, true ).backTo( a.front + 10 )
        val coldFrames = a + b + c

        // Show seed trays in one of the cold frames
        val seedTrays = seedTray().previewOnly().leftTo(b.left + 40).tileX(2, 10)

        // Show the 2 piece seat sections stacked on eachother at the bottom of the cold frame.
        val extraSeat = seat(4).bottomTo(0)
            .rotateZ(90).frontTo( c.front + 30 )
        val extraSeats = (extraSeat + extraSeat.mirrorZ().translate( 40, 0, quarter.thickness ))
            .bottomTo(0)

        return coldFrames + seedTrays + extraSeats
    }

}