FooCAD Source Codeimport 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
}
}