FooCAD Source Codeimport 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
}
}