/Garden/LayeringPod.foocad

A tool for air-layering plants.
Cut part of the bark from a stem, and cover with this pod. Fill with moss/coir/compost. Keep it moist, but not waterlogged. Fingers crossed, after several weeks, roots will have formed, and can be cut to make a new plant.
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*
class LayeringPod : AbstractModel() {
@Custom( about="Width x Height of the pod" )
var size = Vector2( 50, 60 )
@Custom
var thickness = 2.0
@Custom( about="Diameter of the hexagonal gromets" )
var grometSize = Vector2( 30, 4 )
@Custom( about="Branch diameter for piece 'gromet'" )
var branchD = 10
@Custom( about="Size of 'clip' is bigger than the dovetails")
var slack = 0.5
fun shape(closed : bool) = PolygonBuilder().apply {
val neck = 5
moveTo(0,0)
lineBy( size.x / 2, 0 )
lineBy( 0, size.y )
lineBy( -size.x / 4, 0 )
lineBy( 0, neck )
lineBy( size.x / 4, size.x / 4 )
if( closed ) {
lineTo( 0, size.y + size.x / 4 + neck )
}
}
fun washerSlot() : Shape3d {
val box = Circle( grometSize.x/2 + thickness + 1 )
.sides(6)
.extrude( grometSize.y + 1 + thickness * 2 )
.center() -
Circle( grometSize.x/2 + 1 )
.sides(6)
.extrude( grometSize.y + 1 )
.center()
val unclipped = box.rotateX(90).backTo(thickness)
return unclipped intersection unclipped.boundingCube().bottomTo(0)
}
val dove = 4
val margin = dove + thickness * 2
fun dovetailProfile( round : bool ) : Shape2d {
val radius = if (round) 1 else 0
return Square( dove/2 ,dove )
.roundCorner(3,radius)
.rightTo(0) +
Triangle( dove/2, dove )
.roundCorner(1, radius)
.mirrorY()
.frontTo(0)
}
@Piece
fun half() : Shape3d {
val shape : Shape2d = shape(true).build().mirrorX().also()
val base = (Square(shape.size.x + margin * 2, shape.size.y ).centerX() - shape)
.extrude(thickness)
val wholeCase = shape(false).buildPath().thickness(thickness).revolve().sides(6)
.rotateX(-90).translateY(thickness/2)
val case = wholeCase intersection wholeCase.boundingCube().bottomTo(0)
val hole = Circle( grometSize.x * 0.4 )
.sides(6)
.extrude(30)
.center()
.rotateX(90)
val dovetail = dovetailProfile( true ).extrude( base.size.y )
.rotateX(90)
.frontTo( 0 )
.leftTo( -base.right )
return washerSlot() + base + case - hole + dovetail.mirrorX().also()
}
// Two halves joined by a thin (bendable?) hinge.
@Piece
fun pair() : Shape3d {
val one = half().leftTo(slack).mirrorX().also()
return one.mirrorX() +
Cube( slack *3, one.back, 0.2 ).centerX().color("Green")
}
@Piece
fun clip() : Shape3d {
val inn = dovetailProfile( false ).offset(slack).mirrorY().also().rotate(90)
val out = Square( inn.size.x + thickness*4, inn.size.y + thickness*2 ).center()
val slot = Square( thickness * 2 + slack*2, 10 ).centerX()
val tube = (out - inn - slot).extrude( thickness + size.y + size.x / 4 + 5 )
val end = out.extrude(thickness)
return ( end + tube.bottomTo(thickness))
.rotateX(-90)
.bottomTo(0)
.color( "lightBlue" )
}
fun gromet( branchD : double ) : Shape3d {
val outside = Circle( grometSize.x/2 ).sides(6)
.roundAllCorners(4)
val torus = (outside - Circle( branchD/2 ))
.extrude( grometSize.y )
val slot = Cube( grometSize.x*2, 1, 100).center().leftTo( 4-grometSize.x / 2 )
val flap = outside.extrude(0.4) - slot.rotateZ(90)
return torus + flap - slot
}
@Piece
fun gromet() : Shape3d {
return gromet( branchD )
}
@Piece
fun grometSelection() : Shape3d {
val mid = gromet( 12 )
val small = gromet( 8 ).rightTo( mid.left - 1 )
val large = gromet( 16 ).leftTo( mid.right + 1 )
return small + mid + large
}
@Piece
fun all() : Shape3d {
val main = pair()
val clips = clip().leftTo(1).mirrorX().also()
return main + clips.leftTo(main.right + 1 )
}
@Piece( printable = false )
override fun build() : Shape3d {
val half : Shape3d = half()
val grom = gromet()
.color("white")
.rotateX(90).frontTo(half.front + thickness + 0.5 )
val clip : Shape3d = clip()
.rotateY(90)
.mirrorY()
.rightTo( half.right + thickness + slack)
.frontTo(0)
return half +
//(half.rotateY(180).translateZ(-0.1).color("green")) +
grom +
clip
}
}

