Exit Full View
Up

/Vases/LatticePot.foocad

LatticePot
FooCAD Source Code
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()
    }
}