/Garden/FruitShaper.foocad
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!
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 ) } }