Exit Full View
Up

/Boxes/ComponentOrganiserCase.foocad

ComponentOrganiserCase
FooCAD Source Code
import static uk.co.nickthecoder.foocad.extend.v1.Extend.*
import uk.co.nickthecoder.foocad.hinge.v1.*
import uk.co.nickthecoder.foocad.extras.v1.*
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*
import uk.co.nickthecoder.foocad.smartextrusion.v1.*
import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.*
import uk.co.nickthecoder.foocad.cup.v1.*
import static uk.co.nickthecoder.foocad.cup.v1.Cup.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*

include ComponentOrganiser.foocad

class ComponentOrganiserCase: Model {

    @Custom
    var size = Vector2( 4, 4 )

    @Custom
    var baseThickness = 1.2

    @Custom
    var thickness = 1.2

    @Custom
    var lipSize = Vector2(1.2, 6)
    
    @Custom
    var hinge = Hinge( 40 ).chamfer(0.5)

    @Custom
    var lidThickness = 1.2

    @Custom
    var lidClearance = 0.2

    // The same diameter bolt is also used for the handle (but the length is different).
    @Custom( about="Defines the bolt length & diameter and the distance of the latch" )
    var hingedLatch = HingedLatch()
        .boltSize( Vector2(20, 3.9) )
        .distance( 24 ) // Original was 25.
        .ribSize( Vector3(3, 6, 35) )
        .chamfer(0.5)
        //.realBolt(true) // Uncomment to test the alignment of the holes.

    @Custom
    var handleSize = Vector3( 100, 33, 8 )


    meth handleHinge() = Hinge( handleSize.z * 2 )
        .innerDiameter( hingedLatch.boltSize.y )
        .outerDiameter( handleSize.z + 2 )
        .chamfer(0.5)

    meth componentOrganiser() = ComponentOrganiser().apply {
        drawerX = size.x
        drawerY = size.y
    }

    meth internalSize() : Vector2 {
        val co =  componentOrganiser()
        return Vector2(
            size.x * co.unitWidth + (size.x-1) * co.insertGap,
            size.y * co.unitDepth + (size.y-1) * co.insertGap
        )
    }

    meth insideShape() : Shape2d {
        return Square( internalSize() )
            .roundAllCorners(4)
            .center()
    }

    meth hingePositionFromBoxTop() = -1
    
    @Piece
    meth box() : Shape3d {
        val co = componentOrganiser()
        val bottomPattern = co.drawerBottom().centerXY().bottomTo( baseThickness ).color("Green")

        val inserts = co.insert(1,1)
            .tileX( size.x, co.insertGap )
            .tileY( size.y, co.insertGap )
            .centerXY()
            .previewOnly()
            .bottomTo( bottomPattern.top )

        val inside = insideShape()
        val outside = inside.offset( thickness )

        val lip = ProfileEdge.chamferedStep( lipSize.x, lipSize.y ).reverse() and
            ProfileEdge.chamfer(0.5)

        val height = baseThickness + bottomPattern.size.z + co.unitHeight

        val box = outside
            .cup( height, thickness )
                .baseThickness( baseThickness )
                .bottom( Chamfer( 1 ) )
                .outsideTop( lip )
            .label("plainBox")
        println( "Plain case = $box" )

        val hinges = hinge.forBox( outside.back, height + hingePositionFromBoxTop() )
            .centerXTo( inside.left + hinge.length*0.8 )
            .mirrorX().also().withCavities()

        val latchRibs = hingedLatch.ribs()
            .centerXTo( inside.left + 30 )
            .translateZ( height - hingedLatch.distance -2.5) // Positioned by eye.
            .backTo( outside.front + hingedLatch.chamfer )
            .label( "latchRibs" )
            .mirrorX().also()
        //println( "Handle bolt length = ${latchRibs.find("latchRibs").size.x}" )

        val handleHinge = handleHinge().forBox( 0, box.top/2 + 6 )
            .mirrorY()
        val handleMount = Square( handleHinge.size.x + 4, height-2 ).centerX()
            .smartExtrude( lipSize.x )
                .top( Chamfer(lipSize.x) )
            .rotateX(90)
            .backTo( outside.front )
            .bottomTo( 1 )
            .translateX( handleSize.x / 2 - handleSize.z/2 )
        val forHandle = (
                handleMount +
                handleHinge
                    .centerXTo( handleMount.middle.x )
                    .backTo( handleMount.front + handleHinge().chamfer )
            ).label("forHandle").mirrorX().also()
        
        println( "box and hinges.find = ${(box and hinges).find("plainBox")}" )

        return (box and hinges) + latchRibs + bottomPattern + forHandle // + inserts 
    }

    @Piece
    meth lid() : Shape3d {
        val co = componentOrganiser()
        val inside = insideShape()
        val outside = inside.offset( thickness )
        val boxShape = outside.offset( lipSize.x )
        val chamfer = 1

        val lip = ProfileEdge.chamferedStep( lipSize.x, lipSize.y ).reverse() and
            ProfileEdge.chamfer(0.5)

        val height = lidThickness + lipSize.y

        val cup = boxShape.offset( lidThickness + 1 )
            .cup( height, 1 )    
                .baseThickness( lidThickness )
                .bottom( Chamfer( chamfer ) )
        val lid = cup -
            Cube( cup.size.x, cup.size.y, cup.size.y )
                .centerX()
                .rotateX(45)
                .translateY( cup.back )
                

        val hinges = hinge.forLid( outside.back, lidThickness + lidClearance - hingePositionFromBoxTop() )
            .centerXTo( inside.left + hinge.length*0.8 )
            .mirrorX().also().withCavities()

        val pattern  = co.lidPattern()
            .extrude(2) // Original was 3.
            .bottomTo( lidThickness )

        val latchMount = Square( hingedLatch.hinge().length + 6, 2 + chamfer)
            .roundCorner( 1, chamfer,1 )
            .roundCorner( 0, chamfer,1 )
            .centerX()
            .smartExtrude( height )
                //.edges( Chamfer( chamfer/2 ) )
            .backTo( lid.front + chamfer )

        val latchHinges = (
                hingedLatch.forLid( latchMount.front+chamfer, height-2 ) and
                latchMount
            )
            .centerXTo( inside.left + 30 )
            .mirrorX().also()

        return ((lid and hinges and latchHinges ) + pattern)
    }


    @Piece
    meth handle() : Shape3d {
        val handleHinge = handleHinge()

        val shape = PolygonBuilder().apply {
            moveTo( 0, handleSize.z/2 )
            radius(10)
            lineBy( 0, -handleSize.y + handleSize.z/2 )
            lineBy( handleSize.x - handleSize.z, 0 )
            radius(0)
            lineBy( 0, handleSize.y - handleSize.z/2 )

        }.buildPath().thickness( handleSize.z ).centerX()

        val main = shape.smartExtrude( handleSize.z )
            .bottom( ProfileEdge.roundedChamfer( handleSize.z/2 ) )
            .top( Fillet( handleSize.z/2-1) )
        val holes = Circle( handleHinge.innerDiameter/2 + 0.2 ).safeOverhang().rotate(90)
            .extrude( handleSize.x + 2 )
            .rotateY(90)
            .centerX()
            .centerZTo( handleSize.z / 2 )

        return main - holes
    }

    @Piece
    @Slice( seamPosition="rear" )
    meth latch() = hingedLatch.latch()

    @Piece( printable=false )
    override meth build() : Shape3d {

        val box = box()
        val plainBox = box.find("plainBox")

        val axis = Vector3( 0, box.back - hinge.outerDiameter/2, box.size.z - hinge.outerDiameter/2)

        val lid = lid().mirrorZ()
            .topTo( plainBox.top + lidThickness + lidClearance )
            .color("Green")

        val latches = latch()
            .rotateZ(4 - 0) // 4° is the closed position.
            .rotateX(90).rotateZ(90)
            .translateY( box.front+4 )
            .centerXTo( box.left + 32.5 )
            .translateZ( lid.top - 4 )
            .mirrorX().also()
            .color("Violet")

        val handle = handle().rotateX(90)
            .backTo( plainBox.front - 1 )
            .bottomTo(2)
            .color("lightBlue")

        val rotatedLid = (lid + latches).rotateAxisAbout( 60, Vector3( 1,0,0 ), axis )

        return box + rotatedLid + handle
    }

}