Exit Full View
Up

/Garden/SeedlingSupport.foocad

SeedlingSupport

Various TPU parts to place on small bamboo stakes (sold as BBQ skewers) to support seedlings.

Medium size "split" bamboo canes (sold for gardening, often dyed green) can also be used. Change the @Custom sizes appropriately.

NOTE. These pieces are NOT suitable for "full-size" (unsplit) bamboo canes, which vary in thickness along their length.

FooCAD Source Code
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
import uk.co.nickthecoder.foocad.smartextrusion.v1.*
import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.*
import uk.co.nickthecoder.foocad.extras.v1.*
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*


enum class ConnectionType {
    TRIANGLE, // Must be slid onto the cane from the top
    SPLIT, // Like TRIANGLE, but with a slit, so that it can be "snapped" on anywhere. Too loose.
    GRIP, // Can be snapped on anywhere, with better grip than "SPLIT"
    GRIP2 // An improved version of "GRIP", which is less likely to fall off when pushed up/down.
}

class SeedlingSupport : Model {

    @Custom( about="Diameter of the hole for the cane" )
    var caneDiameter = 2.9

    @Custom( about="Width and thickness" )
    var connectionSize = Vector2(2, 3)

    @Custom( about="Diameter, width, thickness" )
    var clawSize = Vector3(16, 2, 3)

    @Custom( about="How open/closed the claw piece is. " )
    var clawAngle = 325
    
    @Custom( about="Adds a slit to the triangular part. Looser, so adjust `diameter` too." )
    var connectionType = ConnectionType.GRIP2//TRIANGLE

    meth connectionShape() : Shape2d {
        if ( connectionType == ConnectionType.GRIP ) {
            // GRIP
            val length = caneDiameter + connectionSize.x
            val deltaY = length * 0.2
            return PolygonBuilder().apply {
                moveTo(-length, length/2-deltaY)
                lineBy( length, deltaY )
                lineBy( 0, -length )
                lineBy(-length, deltaY )
                lineTo(-length, 0)
            }.buildPath()
                .thickness( connectionSize.x )
                .translateX( caneDiameter/2 + connectionSize.x/2 )
        } else if ( connectionType == ConnectionType.GRIP2 ) {
            // GRIP2
            val length = caneDiameter + connectionSize.x
            val deltaY = length * 0.2
            return PolygonBuilder().apply {
                moveTo(-length+connectionSize.x/2, length/2-deltaY)
                lineBy( length-connectionSize.x/2, deltaY )
                lineBy( 0, -length )
                lineBy(-length-0.5, deltaY )
                lineBy(0, caneDiameter+1)
            }.buildPath()
                .thickness( connectionSize.x )
                .translateX( caneDiameter/2 + connectionSize.x/2 )
        } else {
            // TRIANGLE / SPLIT
            val inside = Circle( caneDiameter ).sides(3)
            val outside = inside.offset(connectionSize.x)
                .roundAllCorners(1,1)
            if (connectionType == ConnectionType.SPLIT) {
                val notch = Square( 10, 0.5 ).centerY().rightTo(0)
                    .rotate(60)
                return outside - inside - notch
            } else {
                return outside - inside
            }
        }
    }
    
    meth connection() = connection( connectionSize.y )

    meth connection( height : double ) : Shape3d {
        return connectionShape()
            .smartExtrude(height)
                .edges( Chamfer(connectionSize.y/8) )
    }

    @Piece( about="One arm to hole the plant" )
    meth claw() : Shape3d {
        val connection = connection()
        val claw = RoundedCircularArc( clawSize.x/2+clawSize.y, clawSize.x/2, -90, -90 + clawAngle )
            .translateY( clawSize.x/2 + clawSize.y/2 )
            .rotate(-30)
            .smartExtrude(clawSize.z)
                .edges( Chamfer(clawSize.y/8) )
            .translate( connection.right -1, 0.3, 0 )

        return connection + claw

    }

    @Piece( about="Two arms to hug the plant" )
    meth pincer() : Shape3d {
        val connection = connection()

        val shape = RoundedCircularArc( clawSize.x/2 + clawSize.y, clawSize.x/2, -20, 180 ) +
            RoundedCircularArc( clawSize.x/2 + clawSize.y*1.5 + 0.5, clawSize.x/2 + clawSize.y/2 + 0.5, 180, 380 )
                .translateX( clawSize.y/2 + 0.5 )

        val pincer = shape.leftTo( connection.right -1 )
            .smartExtrude(clawSize.z)
                .edges( Chamfer(clawSize.y/8) )

        return connection + pincer
    }

    @Piece( about="Use shop-bought plastic coated metal hoops to hold the plant, or off-cuts of garden wire" )
    meth forMetalHoop() : Shape3d {
        val connection = connection()
        val diameter = 4
        val thickness = 1.3

        val shape = Square(diameter + thickness*2).center()
                .roundCorner(3,diameter/2,1)
                .roundCorner(2,diameter/2,1)
                .roundCorner(1,diameter/2,1) -
            Circle(diameter/2).sides(4).rotate(0).center()
        val block = shape.extrude(4)
            .centerZ()
            .rotateX(90)
            .bottomTo(0)
            .leftTo( connection.right - thickness )

        return connection + block
    }

    @Piece( about="Ends of stakes can be an eye hazzard. Makes them less pointy!" )
    meth tip() : Shape3d {
        val connection = connection( connectionSize.y * 2 )
        val base = Hull2d( connectionShape() )
            .smartExtrude( connectionSize.y / 2 )
                .bottom( Chamfer(clawSize.y/8) )

        return connection + base
    }

    @Piece( about="Holds 6 (or 3) stakes togther at the top of the teepee shape" )
    meth teepeeTop() : Shape3d {

        val ringShape = Ring( caneDiameter + connectionSize.x, caneDiameter )
        val connection = connection().rightTo(-caneDiameter - connectionSize.x/2)
        val connections = connection.repeatAroundZ(6)
        val ring = ringShape
            .smartExtrude( connectionSize.y )
            .edges( Chamfer(clawSize.y/8) )

        return connections + ring
    }

    @Piece( printable=false )
    override meth build() : Shape3d {
        val claws = claw()
            .translateZ(10).rotateZ(-40)
                .mirrorY().translateZ(6).also(2)

        val pincer = pincer().translateZ(40)

        val stake = Cylinder( 90, caneDiameter/2 )
            .previewOnly()

        val teepeeTop = teepeeTop()
            .translateX( - caneDiameter*3 )
            .topTo( stake.top - 20 )

        val tip = tip()
            .mirrorZ()
            .topTo( stake.top + connectionSize.y )

        return stake + claws + pincer + teepeeTop + tip
    }

}