Exit Full View
ZipUp

/Games/Wooden Block Puzzle/Box3.foocad

Box3

A three part box. There are two "lids", and a tube joining them.

FYI, I have created another version of this in Boxes/PuzzleBox.foocad where the middle section is optional, or you can add many middle sections.

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

include WoodenBlock.foocad

class Box3 : AbstractModel() {
    
    @Custom
    var part = "view" // top, middle, bottom, or debug or view or exploded

    @Custom
    var width = 63 // Internal size

    @Custom
    var depth =  63 // Internal size

    @Custom
    var height = 155 // Internal size

    @Custom
    var topHeight = 28 // Internal size

    @Custom
    var bottomHeight = 18 // Internal size

    @Custom
    var radius = 6 // Radius of the outside. Other radii are calculated.

    @Custom
    var radiusSides = 4

    @Custom
    var thickness = 4 // Thickness of the top (the bottom will be thicker)

    @Custom
    var patternThickness = 0.6

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

    @Custom
    var lipHeight = 6 // 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 // Chamfer of the bottom and top edges.

    override fun build() : Shape3d {

        val contents = Cube( 60, 60, 60 )
            .centerXY()
            .translateZ(thickness + 1)
            .color("Orange")


        val inside = Square( width, depth ).center().roundAllCorners(radius- thickness, radiusSides)

        val outside = Square(
            width + thickness * 2,
            depth + thickness * 2
        ).center().roundAllCorners(radius, radiusSides)

        val outsideChamfer = Square(
            width + thickness * 2 - chamfer * 2,
            depth + thickness * 2 - chamfer * 2
        ).center().roundAllCorners(radius - chamfer, radiusSides)

        val outsideLip = Square(
            width + lipThickness*2,
            depth + lipThickness*2
        ).center().roundAllCorners(radius - thickness + lipThickness, radiusSides)

        val slackLip = Square(
            width + lipThickness*2 - lipSlack*2,
            depth + lipThickness*2 - lipSlack*2
        ).center().roundAllCorners(radius - thickness + lipThickness + lipSlack, radiusSides)


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

        val bottom = 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 middleHeight = height - topHeight * 2

        val middleSolid = outside.extrude( middleHeight + lipHeight *2 )
            .color( "Blue" ).brighter()
        val middleHole = ExtrusionBuilder().apply {
            forward(-1)
            crossSection( outsideLip )
            forward(1 + lipHeight + 1 )
            crossSection( outsideLip )
            forward( thickness ) // Chamfer so that we don't need support material
            crossSection( inside )
            forward( middleHeight - 2 - thickness )
            crossSection( inside )
            crossSection( outsideLip )
            forward(1 + lipHeight + 1 )
            crossSection( outsideLip )
            
        }.build()

        val middle = middleSolid - middleHole

        val view = contents +
            bottom +
            middle.translateZ( bottomHeight + thickness + 1 ) +
            top.rotateX(180).toOriginZ().translateZ( bottomHeight + middleHeight + thickness + lipHeight + 3 )

        val pattern = topPattern()
            .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)

        return if (part == "top" ) {
            top - pattern
        } else if (part == "middle" ) {
            middle
        } else if (part == "bottom" ) {
            bottom - pattern
        } else if (part == "view" ) {
            view
        } else if (part == "exploded" ) {
            contents + middle.translateZ(topHeight) + top.rotateX(180).translate( width + 10, 0, height + topHeight + 50)
        } else {
            view / inspection
        }

    }

    fun topPattern() : Shape2d{

        return Square( width *3, 5 ).tileY( 6, 5 ).center()//.rotate(45)
    }

}