FooCAD Source Codeimport static uk.co.nickthecoder.foocad.insertgcode.v1.InsertGCode.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
class LatticePot : Model, PostProcessor {
@Custom
var size = Vector2( 200, 150 )
@Custom( about="How much smaller is the base compared to the top" )
var baseRatio = 0.7
@Custom( about="A bezier control point as a proportion of `size`" )
var control1 = Vector2( 0.6, 0.1 )
@Custom( about="A bezier control point as a proportion of `size`" )
var control2 = Vector2( 1.3, 0.65 )
@Custom( about= "Thickness of the walls, and base" )
var thickness = 1.7
@Custom
var lipDiameter = 10
@Custom
var baseDiameter = 10
@Custom
var decorationCount = 6
@Custom
var decorationSweep = 90
@Custom
var decorationOffset = 0
@Custom
var decorationSize = Vector2(4,8)
@Custom
var decorationMirror = true
@Custom
var pause = true
@Custom
var quality = 3
fun semiCircleX( r : double ) : Shape2d = Circle(r) intersection Square(r*2).centerY()
fun semiCircleY( r : double ) : Shape2d = Circle(r) intersection Square(r*2).centerX()
fun profile() : Shape2d {
val bottomCorner = Vector2(size.x / 2 * baseRatio, 0)
val topCorner = Vector2( size.x / 2, size.y )
Quality.increase(quality)
return PolygonBuilder().apply {
moveTo( 0,0 )
lineTo( bottomCorner )
bezierTo( topCorner * control1, topCorner * control2, topCorner )
lineTo( 0, topCorner.y )
Quality.decrease(quality)
}.build()
}
fun lipProfile() : Shape2d {
return Circle( lipDiameter/2 ).translateY(lipDiameter/2) hull Circle(thickness)
}
fun baseProfile() = semiCircleY( baseDiameter/2 )
fun decoration() : Shape3d {
val shape = semiCircleX( decorationSize.x/2 ).scaleY( decorationSize.y / decorationSize.x )
val profile = profile()
val path = profile.paths[0]
return ExtrusionBuilder().apply {
//moveTo( path[1].to3d() )
//crossSection( shape )
for ( i in 1 until path.points.size()-1 ) {
val x = path[i].x
val z = path[i].y
val angle = decorationOffset + decorationSweep * z / profile.size.y
moveTo( Vector3( 0,0,z ) )
crossSection( shape.translateX( x ).rotate(angle) )
}
}.build()
}
fun drainage() = Circle( 10 )
@Piece
fun base() : Shape3d {
val profile = profile()
val path = profile.paths[0]
val bottomCorner = path[1]
val ring = baseProfile().translate( bottomCorner ).revolve()
val struts = Cube( bottomCorner.x, 3, ring.size.z ).centerY()
.repeatAroundZ(6)
val drainage = drainage().extrude( ring.size.z * 3 ).center()
val drainageRing = drainage().offset(thickness).extrude( ring.size.z-1 )
return ring + drainageRing + struts - drainage
}
@Piece
@Slice( fillDensity=0 )
fun pot() : Shape3d {
val profile = profile()
val path = profile.paths[0]
val bottomCorner = path[1]
val topCorner = path[-2]
Quality.increase(quality)
val base = baseProfile().translate( bottomCorner )
.revolve()
.color("Orange")
val insideProfile = profile.offset(-thickness) +
Square( thickness, profile.size.y - thickness )
.translateY( thickness )
val inside = insideProfile.revolve()
val solid = profile.revolve()
Quality.decrease(quality)
val opening = Cylinder( thickness *3, topCorner.x - thickness/2 )
.centerZTo( solid.top )
val lip = lipProfile().translate( topCorner )
.revolve()
.color("Orange")
var decoration : Shape3d = decoration()
.repeatAroundZ(decorationCount)
if ( decorationMirror ) {
decoration = decoration.mirrorX().also()
}
val drainage = Cylinder( thickness*3, 10 ).center()
return base + solid - inside - opening - drainage + lip + decoration
}
override fun postProcess( piece : String, gcode : GCode ) {
if (piece == "pot" && pause) {
val profile = profile()
val baseProfile = baseProfile()
val change1 = baseProfile().back - thickness/2
val change2 = profile().paths[0][-2].y - thickness/2
gcode.pauseAtHeight( change1, change2 )
}
}
override fun postProcess( gcode : GCode ) {
}
override fun build() : Shape3d {
val pot = pot()
val base = base().mirrorZ().color("Orange")
.topTo(pot.bottom)
val change1 = baseProfile().back - thickness/2
val change2 = profile().paths[0][-2].y - thickness/2
val midSection = Cube( pot.right, 10, change2 - change1 ).translate( 0, 0, change1 )
return pot + base //+ midSection.previewOnly()
}
}