Exit Full View
Up

/Garden/LayeringPod.foocad

LayeringPod

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.

FooCAD Source Code
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*

class LayeringPod : Model {

    @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 : boolean) = 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 in = grometSize.x + 1
        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()

        return box.rotateX(90).backTo(thickness) intersection Cube( 1000 ).centerXY()
    }

    val dove = 4
    val margin = dove + thickness * 2

    fun dovetailProfile( round : boolean ) : 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 case = shape(false).buildPath().thickness(thickness).revolve().sides(6)
            .rotateX(-90).translateY(thickness/2) / Cube( 1000 ).centerXY()

        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 in = dovetailProfile( false ).offset(slack).mirrorY().also().rotate(90)
        val out = Square( in.size.x + thickness*4, in.size.y + thickness*2 ).center()
        val slot = Square( thickness * 2 + slack*2, 10 ).centerX()
        val tube = (out - in - 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 )
    }

    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
    }
}