Exit Full View
Up

/Garden/FruitShaper.foocad

FruitShaper

Shapes fruit, such as cucumbers, courgettes, while growing. Place the fruit inside the tube when it is small enough to fit in easily. Add the ribs, to hold the two halves closed. Wait.

If the ribs slip too easily along the tube, add paper to wedge them in place.

Print Notes

Use a generous rim for the tube, as it is tall, with very little bed contact.

Print as many ribs are you see fit. Fruit can exert quite a lot of pressure while growing. They can easily bend a thin plastic tube!

FooCAD Source Code
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*

class FruitShaper : Model {
    
    @Custom( about = "Diameter and length of the tube" )
    var size = Vector2( 30, 110 )

    @Custom( about="Thickness of the tube" )
    var thickness = 3

    @Custom( about="Width and thickness of the ribs" )
    var ribSize = Vector2( 5, 2.0 )

    @Custom( about = "How much bigger are the ribs compared to the tube" )
    var slack = 0.4

    /**
        Override this method for other shapes. See HeartFruitShaper.foocad
    */
    fun plainProfile() : Shape2d =
        Square( 100 ).roundAllCorners(10).center()
        

    fun scaledProfile() : Shape2d {
        val plain = plainProfile()
        val scaled = plain.scale( size.x / plain.size.x )

        return scaled
    }

    fun tabbedProfile() : Shape2d {
        val scaled = scaledProfile().offset( thickness )
        return scaled + Square( 7, scaled.size.y + 8 ).center()
    }

    @Piece
    fun bothHalves() = tube( true ).translateX(1) + tube( false )
    @Piece
    fun halfA() = tube( true )

    @Piece
    fun halfB() = tube( false )

    fun tube( rightPart : bool ) : Shape3d {
        val inside = scaledProfile()
        val outside = tabbedProfile()
        val ring = outside - inside
        val dy = ring.size.y / 20

        val cut = PolygonBuilder().apply {
            moveTo(0,0)
            for ( i in 0..10) {
                lineBy( dy, dy )
                lineBy( -dy, dy )
            }
            lineBy( ring.size.x, 0 )
            lineTo( ring.size.x, 0 )
        }.build()
            .roundAllCorners( 0.5 )
            .frontTo( ring.front -dy).leftTo(-dy/2)

        return if ( rightPart ) {
            (ring / cut.offset(-slack/2)).extrude( size.y ).color("Green")
        } else {
            (ring - cut.offset(slack/2)).extrude( size.y ).color("Blue")
        }
    }

    @Piece
    fun rib() : Shape3d {
        val profile = tabbedProfile()
        val ring = profile.offset( ribSize.x + slack ) -
            profile.offset( slack )

        return ring.extrude( ribSize.y ).color("Orange" )
    }

    override fun build() : Shape3d {
        val rib : Shape3d = rib()
        return tube(true) + tube(false) + rib.repeatZ( 5, size.y / 4 )
    }
}