Exit Full View
Up

/Hardware/LatchHandle.foocad

LatchHandle
FooCAD Source Code
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.*
import uk.co.nickthecoder.foocad.threaded.v2.*
import static uk.co.nickthecoder.foocad.threaded.v2.Thread.*
import static uk.co.nickthecoder.foocad.screws.v3.Screws.*
class LatchHandle : Model {

    @Custom
    var size = Vector2( 70, 40 )

    @Custom
    var width = 10

    @Custom
    var thickness = 5

    @Custom
    var gap = 2

    @Custom
    var rounding = 3
    
    @Custom
    var hubOuterDiameter = 25

    @Custom
    var hubInnerDiameter = 13

    @Custom
    var knobSize = Vector2( 30, 20 )

    @Custom
    var fancyKnob = true

    @Custom( about="0: Woodscrew, 1: Plastic, 2: Machine Screw (uses screwDiameter)" )
    var attachmentType = 2

    @Custom( about="Only used when attachmentType == 1" )
    var boltLength = 30
    
    @Custom( about="Only used when attachmentType == 2" )
    var boltDiameter = 4.0


    fun capThread() = Thread( hubInnerDiameter-1 )
        .rodChamfer(0.5)

    fun fixtureThread() = Thread( 7 )

    @Piece
    fun latch() : Shape3d {
        
        val path : Path2d = if (size.y > width) {
            PolygonBuilder().apply {
                moveTo( 0, 0 )
                lineBy( 0, width )
                bezierBy( Vector2(0, size.y/2), Vector2(size.x/2,0), Vector2(size.x-width, size.y-width) )
                lineBy( width, 0 )
            }.buildPath()
        } else {
            PolygonBuilder().apply {
                moveTo(0,0)
                lineTo( size.x, 0 )
            }.buildPath()
        }

        val curve = path.thickness(width) + Circle( width/2 ).translate(size.x, size.y)

        val rib = curve.offset( - width/2 + 1 )
            .intersection(
                Square( size.x*2, size.y*2 )
                    .frontTo( hubOuterDiameter/2 )
                    .rightTo( curve.right - width*1.2 )
                )
            .chamferedExtrude( thickness/2, 0, 0.5 )
            .bottomTo( thickness )
            .darker()
        
        val foo = path.thickness(1).intersection( Circle( hubOuterDiameter/2 + width ) ).toPolygon()
        val wider = Circle( hubOuterDiameter/2 ).hull(
            Circle(width/2).translate( foo.right, foo.back )
        )

        val withHub = curve + wider - Circle( hubInnerDiameter/2 )
        val main = withHub.chamferedExtrude( thickness, 0, 1 )

        val knob : Shape3d = if (fancyKnob) {
            fancyKnob()
        } else {
            plainKnob()
        }.centerYTo( curve.back - width/2 )
            .centerXTo( curve.right - width*2 )

        return main + rib and knob
    }

    fun plainKnob() : Shape3d {
        val firstChamfer = knobSize.x/2 - width*0.5

        return ExtrusionBuilder().apply {
            crossSection( Circle( width * 0.5 ) )
            forward( thickness+1 )
            crossSection()
            forward( firstChamfer )
            crossSection( firstChamfer )
            forward( 4 )
            crossSection()
            forward( 2 )
            crossSection(-2)
        }.build()
    }

    @Piece
    fun fancyKnob() : Shape3d {

        val slack = 0.4
        val outer = Circle( width )
            .chamferedExtrude( thickness, 0, 1 )

        val toRemove = ExtrusionBuilder().apply {
            crossSection( Circle( width/2 + slack ) )
            forward( thickness /2 )
            crossSection( thickness/2 )
            forward( thickness/2 )
            crossSection( - thickness/2 )
            forward( knobSize.y )
            crossSection( knobSize.y )
        }.build()

        val firstChamfer = knobSize.x/2 - width/2

        val knob = ExtrusionBuilder().apply {
            crossSection( Circle( width/2 ) )
            forward( thickness /2 )
            crossSection( thickness/2 )
            forward( thickness/2 )
            crossSection( - thickness/2 )
            forward( firstChamfer )
            crossSection( firstChamfer )
            forward( knobSize.y - firstChamfer )
            crossSection()
            forward( 2 )
            crossSection(-2)
        }.build()

        
        return outer.remove( toRemove ).insert( knob )
    }

    @Piece
    fun fixture() : Shape3d {
        val back = Circle( hubOuterDiameter/2 ).extrude(1)

        val shaft = Circle( hubInnerDiameter/2 - 0.3 )
            .extrude( thickness + 1 )
            .bottomTo( back.top )

        val thread = capThread().threadedRod( 6 )
            .bottomTo(shaft.top - 2)

        val hole : Shape3d = if ( attachmentType == 0 ) {
            Countersink().topTo(thread.top)
        } else if ( attachmentType == 1 ) {
            fixtureThread().threadedHole( thread.top - 1).clipped(true).build()
        } else {
            Cylinder( thread.top - 1, boltDiameter/2 )
        }

        return back + shaft + thread - hole
    }


    @Piece
    fun cap() : Shape3d {
        val cap = Circle( hubOuterDiameter/2 )
            .chamferedExtrude( 6, 0, 2 )
        val hole = fixtureThread().threadedHole( 5 ).clipped(true)

        return cap - hole
    }

    @Piece
    fun catcher() : Shape3d {
        val big = Square( width, width * 2 ).centerX()
            .frontTo( - width/2 )
            .roundAllCorners( rounding )
            .extrude( thickness )

        val small = Square( width ).center()
            .roundAllCorners( rounding )
            .extrude( thickness + gap )
            .bottomTo( big.top )

        val hole : Shape3d = if ( attachmentType == 0 ) {
            Countersink().mirrorZ()
        } else if ( attachmentType == 1 ) {
            fixtureThread().threadedHole( small.top-1 ).clipped(true).build()
                .bottomTo(1)
        } else {
            Cylinder( small.top - 1, boltDiameter/2 ).topTo(small.top + 0.1 )
        }

        return big + small - hole
    }

    @Piece
    fun bolt() = fixtureThread().bolt( boltLength )
    
    override fun build() : Shape3d {
        val bolts : Shape3d = if (attachmentType == 1) {
            bolt().leftTo(1).rightTo(-1).also()
        } else {
            Cube(0)
        }
        return arrangeX( 1,
            arrangeY( 1, cap(), fixture(), catcher(), bolts ),
            latch()
        )
    }
    
    @Piece
    override fun preview() : Shape3d {
        val latch = latch().color("Green")
        val catcher = catcher().mirrorZ()
            .color( "Orange" )
            .rightTo( latch.right )
            .bottomTo(0)
            .backTo( size.y + width/2 - 1)

        val fixture = fixture()
            .bottomTo(-1.5)

        val cap = cap()
            .bottomTo( thickness )

        return fixture + latch + cap + catcher
    }

}