Exit Full View
Up

/Games/PuzzleBox.foocad

PuzzleBox
FooCAD Source Code
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*

class PuzzleBox : Model {
    
    @Custom
    var width = 70.0 // Internal size

    @Custom
    var depth =  60.0 // Internal size

    @Custom
    var middleHeight = 22.0 // Internal size

    @Custom
    var topHeight = 10.0 // Internal size

    @Custom
    var bottomHeight = 30.0 // Internal size

    @Custom
    var radius = 6.0 // Radius of the INSIDE. Other radii are calculated.

    @Custom
    var radiusSides = 4.0

    @Custom
    var thickness = 4.0 // Thickness of the top (sides and top/bottom)

    @Custom
    var patternThickness = 0.6 // he depth of the pattern cut into the top/bottom

    @Custom
    var lipThickness = 2.0 // Should probably be half of thickness (or close to it)

    @Custom
    var lipHeight = 6.0 // The height of the lip on the bottom piece.

    // I created small prototypes to find the best value for this.
    // It is the additional space in the top part for the bottom's lip
    // to fit into. I wanted an interferance fit.
    @Custom
    var lipSlack = 0.3 

    @Custom
    var chamfer = 2.0 // Chamfer of the bottom and top edges.

    /**
        For large boxes, it is important to add ears to the corners, so
        that the box doesnt lift off of the print bed.
        For very large boxes, maybe ears along the edges are also
        needed (especially when using ABS), but this model doesn't
        support ears along the edges. So either improves the model,
        or add extra ears anually in your slicer.
        NOTE, I also tape the ears to the bed with masking tape after they
        have been printed (i.e. after the first layer is complete)
    */
    @Custom
    var cornerEars = 0.0

    fun createProfile( offset : double ) =
        Square( width + offset*2, depth + offset*2 )
            .center()
            .roundAllCorners( radius + offset, radiusSides )
    

    fun createContent( height : double ) =
        Square( width -1, depth -1 ).roundAllCorners( radius - 0.5 )
            .extrude( height )

    @Piece
    fun top() = buildPart( "top" )

    @Piece
    fun middle() = buildPart( "middle" )

    @Piece
    fun bottom() = buildPart( "bottom" )

    @Piece
    fun view() = buildPart("view")

    override fun build() = buildPart("exploded")

    fun buildPart( part : String) : Shape3d {

        val contentCubes =
            createContent( bottomHeight-1).translateZ(1) +
            createContent( middleHeight).translateZ( bottomHeight + 1) +
            createContent( topHeight-1).translateZ( bottomHeight + middleHeight + 2)
        val contents = contentCubes
            .centerXY()
            .translateZ(thickness)
            .color("Orange")

        val inside = createProfile( 0 )
        val outside = createProfile( thickness )
        val outsideChamfer = createProfile( thickness - chamfer)
        val outsideLip = createProfile( lipThickness )
        val slackLip = createProfile(lipThickness - lipSlack )

        val top : Shape3d = ExtrusionBuilder().apply {
            crossSection( outsideChamfer )
            forward( chamfer )
            crossSection( outside )
            forward( topHeight + thickness - chamfer )
            crossSection( outside )
            crossSection( outsideLip )
            forward( -lipHeight )
            crossSection( outsideLip )
            crossSection( inside )
            forward( -topHeight + lipHeight)
            crossSection( inside )
        }.build().color( "Blue" )

        val bottom : Shape3d = ExtrusionBuilder().apply {
            crossSection( outsideChamfer )
            forward( chamfer )
            crossSection( outside )
            forward( bottomHeight + thickness - chamfer )
            crossSection( outside )
            crossSection( slackLip )
            forward( lipHeight - 1 )
            crossSection( slackLip )
            crossSection( inside )
            forward( -bottomHeight - lipHeight + 1 )
            crossSection( inside )
        }.build().color( "Blue" )


     
        val middleSolid = ExtrusionBuilder().apply {
            crossSection( outside )
            forward( middleHeight )
            crossSection( outside )
            crossSection( slackLip )
            forward( lipHeight )
            crossSection( slackLip )
        
        }.build()
            .color( "Blue" ).brighter()

        val middleHole = ExtrusionBuilder().apply {
            forward(-0.1)
            crossSection( outsideLip )
            forward(0.1 + lipHeight + 1 )
            crossSection( outsideLip )
            forward( thickness ) // Chamfer so that we don't need support material
            crossSection( inside )
            forward( middleHeight - thickness -1 + 0.1)
            crossSection( inside )
            
        }.build()

        val middle = middleSolid - middleHole

        val view = contents +
            bottom +
            middle.translateZ( bottomHeight + thickness + 1 ).color("Yellow") +
            top
                .rotateX(180)
                .toOriginZ()
                .translateZ( bottomHeight + middleHeight + thickness + 2 )
            

        val pattern : Shape3d = pattern()
            .extrude( patternThickness )
            .translateZ( -0.01)
            .color( "WhiteSmoke" )
                
        val inspection =  Cube( 600, 5, 600 ).center() +
            Cube( 5, 400, 400 ).center() +
            Cube(400, 1, 400).centerY().rotateZ(45)

        val ears = Circle( cornerEars )
                .translate(
                    width/2 + cornerEars*0.5 - radius/2,
                    depth/2 + cornerEars*0.5 - radius/2
                )
                .mirrorX().also()
                .mirrorY().also()
                .extrude( 0.2 )
        

        return if (part == "top" ) {
            top - pattern + ears
        } else if (part == "middle" ) {
            middle + ears
        } else if (part == "bottom" ) {
            bottom - pattern + ears
        } else if (part == "view" ) {
            view
        } else if (part == "exploded" ) {
            bottom.translateX( width +10).centerXY() +
            //contents +
            middle.translateZ(bottomHeight + 20).centerXY() +
            top.rotateX(180).translateZ( bottomHeight + middleHeight + topHeight + 50).centerXY()
        } else {
            view / inspection
        }

    }


    @Custom
    var pattern = "circles2"
    
    @Custom
    var patternSize = 7.0
    
    fun pattern() : Shape2d {

        return if (pattern == "striped") {
            Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center().translateY(patternSize)
        } else if (pattern == "striped2" ) {
            Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center()
        } else if (pattern == "diagonal" ) {
            Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center().translateY(patternSize).rotate(45)
        } else if (pattern == "diagonal2" ) {
            Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center().rotate(45)
        } else if (pattern == "spots" ) {
            val scale = 0.5
            Circle( patternSize )
                .tileX( Math.floor(0.25 * width / patternSize), patternSize*2 )
                .tileY( Math.floor(0.25 * depth / patternSize), patternSize*2 )
                .translate( patternSize * 2, patternSize * 2 ).also()
                .center()
        } else if ( pattern == "diamond" ) {
            val count = width ~/ patternSize
            val result = listOf<Shape2d>()
            for ( i in 0 until count ) {
                result.add(
                    Square( (i*2+1) * patternSize ).center().rotate(45) -
                    Square( (i*2) * patternSize ).center().rotate(45)
                )
            }
            Union2d( result )

        } else if ( pattern == "diamond2" ) {
            val count = width ~/ patternSize
            val result = listOf<Shape2d>()
            for ( i in 1 until count ) {
                result.add(
                    Square( (i*2) * patternSize ).center().rotate(45) -
                    Square( (i*2-1) * patternSize ).center().rotate(45)
                )
            }
            Union2d( result )

        } else if (pattern == "circles" ) {
            val count = 0.4 * Math.sqrt( width * width + depth * depth ) ~/ patternSize
            val result = listOf<Shape2d>()
            for ( i in 0 until count ) {
                result.add(
                    Circle( (i*2+1) * patternSize ) - Circle( (i*2) * patternSize )
                )
            }
            Union2d( result )
        } else if (pattern == "circles2" ) {
            val count = 0.4 * Math.sqrt( width * width + depth * depth ) ~/ patternSize
            val result = listOf<Shape2d>()
            for ( i in 1 until count ) {
                result.add(
                    Circle( i*2 * patternSize ) - Circle( (i*2-1) * patternSize )
                )
            }
            Union2d( result )
        } else {
            Square( 1 ).translateX( 1000 )
        }
    }

}