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 } }