import 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() } }