/Garden/PlantLabel.foocad

I'm reinventing plant labels. Most of my seedlings are the same year after year. So why do I clean off the lables, and write the names again each year? Because finding the correctl label is a PITA.
But it could be easy if I keep them in alphabetical order, and have different sets (such as a flower set, a salad/herb set a vegetable set etc).
Print Notes
My first batch used PLA, but this tends to warp over time (when in the sun). I may try again with PETG.
Use different colours for each "key ring".
- Red : Fruit and Veg (excluding leafy veg)
- Yellow : Flowers
- Green : Leafy Veg + Herbs
I've used the same colours for my seed boxes ;-)
Colours
- Leaf Veg and Herbs : 3DQF British Racing Green?
- Other Veg : 3DQF Slignal Red
- FLowers : Filamentive Yellow
- Cukes : Eryone Galaxy Red
- Stakes and slots : 3DQF 2 of : Jet Black? Pearl Black? Regal Black?
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
import static uk.co.nickthecoder.foocad.changefilament.v1.ChangeFilament.*
class PlantLabel : AbstractModel(), PostProcessor {
//@Custom(about="Length, width and thickness os the main part (does not include the text)")
var size = Vector3( 80, 12.6, 1.2 )
//@Custom( about="1 or 2 layers is normal" )
var textThickness = 0.6
//@Custom(about="Should be about doub-le the wire's diameter")
var holeD = 5.0
//@Custom(about="Length and width of the slot. Y should be just less than the wire's diameter")
var slotSize = Vector2( 10, 2 )
//@Custom
var style = TextStyle("Liberation Sans:style=Bold", 8)
//@Custom(about="Should we add automatic printer pauses to change filament?")
var changeColor = true
@Custom( about="Text to appear on the labels (separated by new lines)", lines=10 )
var names = """Lettuce
Mustard
Rocket
Spinach
Swiss Chard
Basil
Chamomile
Coriander
Fennel
Parsley
Thyme"""
@Custom
var columns = 2
@Custom( about="Add extra to labels to help bed adhesion" )
var ears = false
//@Custom
var slotMargin = 2.0
//@Custom(about="The slack. The Y value is slack in the z direction!")
var slack = Vector2( 1.0, 0.5 )
var stakeExtra = 40
var tStakeExtra = 40
@Piece
fun label() = label("")
@Piece
fun labels() : Shape3d {
var y = 0
var x = 0
var column = 1
var union : Shape3d = Union3d()
for ( name in names.split("\n") ) {
val label : Shape3d = label( name ).frontTo( y ).leftTo(x)
if (column < columns) {
x = label.right + 3
column ++
} else {
x = 0
column = 1
y = label.back + 3
}
union = union + label
}
return union
}
fun labels( gcode: GCode ) {
if ( changeColor && names != "" ) {
pauseAtHeight( gcode, size.z, "Change Filament" )
}
}
fun hole() : Shape2d {
return (Circle( holeD/2 ).sides(4).translateX(slotSize.x/2) hull Square(slotSize.x/2, slotSize.y).centerY())
.mirrorX().also().rightTo(size.x+holeD/2)
}
fun label( name : String ) : Shape3d {
val outline = labelOutline()
val hole : Shape2d = hole()
val main = (outline-hole).extrude(size.z)
val result = if (name == "" ) {
main
} else {
val text = Text( name, style )
.centerXTo(main.middle.x)
.centerY()
.extrude( size.z + textThickness )
.color( "Black" )
if (text.right > hole.left) {
main + text.rightTo( hole.left )
} else {
main + text
}
}
return if (ears) {
val ears = Circle( size.y*0.8 ).roundAllCorners(3).extrude( 0.3 )
.rightTo(3).centerYTo(result.middle.y)
.mirrorX().leftTo(result.right-3).also(2)
.margin( -size.y*0.8-1.5, -size.y, 0 )
.color("Green")
result + ears
} else {
result
}
}
fun labelOutline() = Square( size.x, size.y ).roundAllCorners(size.y/3).centerY()
@Piece
fun slotSide() : Shape3d {
val outline = labelOutline().offset(slack.x)
val window = outline.offset(-slotMargin/2)
val outside = outline.offset(slotMargin)
val inside = outline.extrude( size.z + textThickness + slack.y + 1).bottomTo( size.z )
return (outside-window).extrude(size.z * 2 + textThickness + slack.y ) -
(inside.translateX(-10).also())
}
@Piece
fun slotTop() : Shape3d {
val outline = labelOutline().offset(slack.x)
val window = outline.offset(-slotMargin/2)
val outside = outline.offset(slotMargin)
val inside = outline.extrude( size.z + textThickness + slack.y + 1).bottomTo( size.z )
.translate(-10, -6, 0).also()
return (outside-window).extrude(size.z * 2 + textThickness + slack.y ) - inside
}
@Piece
fun stake() : Shape3d {
val slot = slotSide()
val width = slot.size.y
val length = slot.size.x + stakeExtra
val outline = PolygonBuilder().apply {
radius(width/3)
moveTo(0,0)
lineTo(0,width)
lineTo(length,width)
radius(1)
lineTo(length + width*1.5, width/2)
radius(5)
lineTo(length,0)
}.build().centerY()
return outline.extrude(size.z)
}
@Piece
fun tStake() : Shape3d {
val slot = slotSide()
val width = slot.size.y
val length = tStakeExtra
val vertical = PolygonBuilder().apply {
radius(width/3)
moveTo(0,0)
lineTo(0,width)
lineTo(length,width)
radius(1)
lineTo(length + width*1.5, width/2)
radius(5)
lineTo(length,0)
}.build().rotate(-90).centerX()
val outline = labelOutline().offset(slack.x + slotMargin).centerX() + vertical
return outline.extrude(size.z)
}
@Piece
fun nStake() : Shape3d {
val slot = slotSide()
val width = slot.size.y * 2/3
val length = tStakeExtra
val vertical = PolygonBuilder().apply {
radius(width/3)
moveTo(0,0)
lineTo(0,width)
lineTo(length,width)
radius(1)
lineTo(length + width*1.5, width/2)
radius(5)
lineTo(length,0)
}.build().rotate(-90).translateX(size.x * 0.6).also().centerX()
val outline = labelOutline().offset(slack.x + slotMargin).centerX() + vertical
return outline.extrude(size.z)
}
/*
To cover up the rough end of a piece of wire that the labels are threaded onto.
*/
@Piece
fun endStop() : Shape3d {
val solid = Square( 10, 5 ).roundCorner(2,1).roundCorner(1,1)
.chamferedExtrude( 5, 1 )
.center()
val hole = Cylinder.hole(10,2.4/2).rotateY(90).center()
.rightTo(0)
return (solid - hole).bottomTo(0)
}
@Piece
fun tStakePair() = slotSide().centerX().frontTo(1) + tStake().backTo(-1)
@Piece
fun stakePair() = slotSide().leftTo(1) + stake().rightTo(-1)
override fun postProcess( pieceName : String, gcode : GCode ) {
if (pieceName == "labels") {
pauseAtHeight( gcode, size.z, "Change Filament" )
}
}
override fun postProcess( gcode : GCode ) {
}
@Piece( printable = false )
override fun build() : Shape3d {
return label("Example").centerX() +
slotSide().color("Orange").mirrorZ().leftTo(0).bottomTo(0) +
stake().topTo(0).color("Blue") +
slotSide().color("LightGreen").mirrorZ().bottomTo(0).centerX().backTo(-20) +
tStake().topTo(0).color("Green").backTo(-20)
}
}

