Exit Full View
Up

/House/Floodlight.foocad

Floodlight
FooCAD Source Code
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*

class Floodlight : Model {

    val size = Vector3(120,80,16)
    val thickness = 2
    val margin = 5

    val slack = 0.3
    val recessT = 5
    var radius = 6
    var postChamfer = 1
    var frontChamfer = 3

    val postDist = 92
    var postT = 5  
    var postHeight = 30  

    var heatSize = Vector3( 80.5, 61.5, 39 )
    // I'm using old heat sinks from CPUs, which have a lip on one edge.
    var heatLip = 1.1

    @Custom
    var apeture = Vector2(55, 45)

    @Custom
    var led = Vector2( 27, 27 )

    @Custom
    var board = Vector2(79, 45 ) 

    var caseScrews = Vector2( 104, 64 )

    var cablePos = 44


    fun heatSink() : Shape3d {
        return Cube(heatSize).centerXY().previewOnly()
    }

    @Piece
    fun back() : Shape3d {
        val backP = (
            Square( size.x, size.y ).center().roundAllCorners(radius) -
            Square( board ).center()
        )
        val backP2 = (
            Square( size.x, size.y ).center().roundAllCorners(radius)
        )

        val back = backP.extrude( thickness ) +
            (backP2 - backP2.offset( -thickness )).extrude(thickness*3)

        val recess = (
            Square( heatSize.x + thickness *2, heatSize.y + thickness* 2 ).center() -
            Square( heatSize.x + slack*2, heatSize.y + slack*2 ).center()
        ).extrude( recessT ).translateZ(thickness)
        
        val lip = Cube( heatSize.x, 5, heatLip ).centerX().bottomTo( thickness ).frontTo(recess.front).color("Red")

        val postP = Circle( 7 ).translateY(postHeight).hull(
            Square( 30, 0.1 ).centerX()
        )
        val postP2 = postP - Circle( 10 ).translateY(postHeight)

        val post = postP
            .chamferedExtrude( postT, postChamfer ) -
            Cylinder( postT*3, 2).translateY(postHeight).centerZ() +
            postP2.chamferedExtrude( postT*2, postChamfer )

        val posts = post
            .rotateX(90).rotateZ(90)
            .translateX( postDist/2 )
            .mirrorX().also().withCavities()

        val screwPost = Cylinder( 6, 5 ).remove(Cylinder( 12, 2 ).center()).remove(Cylinder( 4, 0, 4 ).topTo(6.01))
        val screwPosts = screwPost.translate( caseScrews.x/2, caseScrews.y/2, 0 )
               .mirrorX().also().withCavities().mirrorY().also().withCavities()             

        return (back + recess + lip + posts).and( screwPosts )
    }

    @Piece
    fun bracket() : Shape3d {
        val backP = (
                Square( size.x, 50 ).center()- 
                Square( 40, 30 ).center()
            )
            .translateY(6)
            .roundAllCorners(radius)

        val back = backP.extrude(thickness) +
            (backP - backP.offset( -thickness )).extrude(thickness*3)

        val postP = Circle( 7 ).translateY(postHeight).hull(
            Square( 30, 0.1 ).centerX()
        )
        val postP2 = postP - Circle( 10 ).translateY(postHeight)

        val gap = 0.5 // Space for a locking washer (has teeth which aids friction).
        val post = postP.chamferedExtrude(postT, postChamfer).bottomTo( postT ) +
            postP2.chamferedExtrude( postT*2, postChamfer ) -
            Cylinder( 10, 2 ).translateY( postHeight )

        val posts = post
            .rotateX(90).rotateZ(90)
            .translateX( postDist/2)
            .mirrorX().also().withCavities()

        val screwPost = Cylinder( 6, 5 ).remove(Cylinder( 12, 2 ).center()).remove(Cylinder( 4, 0, 4 ).topTo(6.01))
        val screwPosts = screwPost.translateX(  postDist/2 ).mirrorX().also().withCavities().translateY(22)
        return (back + posts) .and ( screwPosts )
            
    }
 
    var cableD = 8
    var grometT = 2

    @Piece
    fun front() : Shape3d {
        val profile = Square( size.x, size.y )
            .roundAllCorners( radius )
            .offset( thickness + slack )
            .center()

        val overlap = 5
        val front = ExtrusionBuilder().apply {
            crossSection( profile.offset(-frontChamfer) )
            forward( frontChamfer )
            crossSection( profile )
            forward( size.z + thickness * 3 - frontChamfer )
            crossSection()
            crossSection( -thickness )
            forward(-thickness*3)
            crossSection()
            crossSection( -overlap )
            forward( -overlap )
            crossSection( overlap )

            forward( -size.z + thickness + overlap +frontChamfer )
            crossSection()
            forward( -frontChamfer )
            crossSection( -frontChamfer )

        }.build()

        val holeP = Square( apeture ).roundAllCorners( 1, 10 )
        val hole = holeP.extrude( thickness*3 ).center()

    
        val screwPost = Cylinder( size.z, 5 ) - Cylinder( 12, 2 ).translateZ(size.z-10)
        val screwPosts = screwPost.translate( caseScrews.x/2, caseScrews.y/2, 0 )
               .mirrorX().also().mirrorY().also()             
        
        val gripPostDist = 13
        val gripD = 8
        val gripHoleD = 1.5
        val gripPost = ( Cylinder( 6, gripD/2 ) - Cylinder( 12, gripHoleD ) ).translateX(-gripPostDist/2)
        val gripPosts = (
                gripPost.mirrorX().also().translateZ(4) +
                gripPost.hull( gripPost.mirrorX() )
        ).translate(cablePos,16,0)

        val gripBar = (
            Circle( gripD/2 ).hull( Circle( gripD/2 ).translateX( -gripPostDist ) ) -
            Circle( gripHoleD+0.5 ) - Circle( gripHoleD+0.5 ).translateX(-gripPostDist)
        ).extrude( 2 ) +
            // A little nub, that can grip the cable, if the cable is thin.
            // Flip over the out of the way if not needed.
            Square( gripPostDist - gripD -0.5, 2).center().translateX(-gripPostDist/2)
                .chamferedExtrude( 4, 0.6 )
    
        val cableHole = Cylinder( 8, cableD/2 + grometT/2 )
            .center()
            .rotateX(90)
            .translate( cablePos, size.y/2,  cableD/2+6)

        return front + screwPosts + gripPosts - hole - cableHole + gripBar
    }

    @Piece
    fun gromet() : Shape3d {
        val h = 8
        return (
            Cylinder( h, cableD/2 + grometT ) +
            Cylinder( grometT*2, 10 ) -
            Cylinder( h, cableD/2+1, cableD/2 )
        )
    }

    override fun build() : Shape3d {
        
        return back().color("Orange") +
            front().translateZ(-50)+
            bracket().mirrorZ().translateZ(postHeight*2).color("Yellow") +
            heatSink().bottomTo(thickness) +
            gromet().translateX(-80)
    }
}