FooCAD Source Codeimport static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
/**
A torch using "bog standard" 1W LEDs, an 18650 cell,
a battery charger module, and a boost module.
I chose to boost the voltage to 18V and use 6 LEDs in series.
*/
class Torch18650 : Model {
var chamfer = 1.0
var width = 35
var height = 24
var thickness = 1.8
var frontLength = 12
var middleLength = 60
var backLength = 40
var slack = 0.2
var lip = 5
var lipT = 2
var buttonD = 12.8
fun profile() = Square(
width + thickness*2,
height + thickness *2
).center().roundAllCorners( chamfer )
fun outerLipProfile() = Square(
width - slack,
height -slack
).center().roundAllCorners( chamfer )
@Piece
fun front() : Shape3d {
val internalChamfer = chamfer / Math.sqrt(2)
val profile = profile()
val solid = ExtrusionBuilder().apply {
crossSection( profile.offset( -chamfer ) )
forward( chamfer )
crossSection( profile )
forward( frontLength - chamfer )
crossSection()
crossSection( -thickness )
forward( -frontLength + thickness + internalChamfer )
crossSection()
forward( -internalChamfer )
crossSection( - internalChamfer )
}.build()
val holes = Cylinder( thickness + 0.02, 4, 3 )
.repeatX( 3, 10 )
.repeatY( 2, 10 )
.centerXY()
.translateZ(-0.01)
.color("Red")
return solid - holes
}
@Piece
fun middle() : Shape3d {
val profile = profile()
val solid = ExtrusionBuilder().apply {
crossSection( profile )
forward( middleLength )
crossSection()
crossSection( outerLipProfile() )
// Lip
forward( lip )
crossSection()
crossSection( -lipT )
forward( -lip-lipT )
crossSection()
forward( -lipT )
crossSection( profile.offset(-thickness) )
forward( -middleLength+lipT*2 )
crossSection()
}.buildClosed()
val buttonHole = Cylinder( height, buttonD/2 ).centerX()
.rotateY(-90).translateZ( middleLength - buttonD/2 - lipT*2 - 6)
return solid - buttonHole
}
@Piece
fun middleWithBrim() : Shape3d {
val p = profile()
val brim = (p.offset( 10 ) - p ).extrude( 0.2 )
return middle() + brim
}
@Piece
fun back() : Shape3d {
val internalChamfer = chamfer / Math.sqrt(2)
val profile = profile()
val solid = ExtrusionBuilder().apply {
crossSection( profile.offset( -chamfer ) )
forward( chamfer )
crossSection( profile )
forward( backLength - chamfer )
crossSection()
crossSection( -thickness )
// Lip
crossSection( outerLipProfile() )
forward( lip )
crossSection()
crossSection( -lipT )
forward( -lip-lipT )
crossSection()
forward( -lipT )
crossSection( profile.offset(-thickness) )
forward( -backLength +lipT*2 + thickness + internalChamfer )
crossSection()
forward( -internalChamfer )
crossSection( - internalChamfer )
}.build()
val chargerHole =
Cube( 3.5, 8.5, thickness*3 ).center() + // main hole
Cube( 5, 11, 0.5 ).centerXY() + // For the "lips" of the socket
Cube( 6, 18, thickness ).centerXY().translateZ(0.9) // Circuit board
return solid - chargerHole.translateX(9).color("Red")
}
/**
The charger module is glued to one side, and the battery stuck with
double sided tape on the other.
*/
@Piece
fun chargeAndBatterySled() : Shape3d {
val d = 18+2
val w = height-lipT*2-slack*2-1
val h = 30
val shaped = Cube( d/2+1, w, h ).centerY().centerZ() - Cylinder( h+1, d/2 ).center()
val floor = Cube( d, w, thickness ).centerY().translateX(-d/2)
return shaped.toOriginZ() + floor
}
@Piece
fun batteryCap() : Shape3d {
return Cube( 23, 23, 12 ).centerXY() - Cylinder( 12, 18.5/2 ).bottomTo(1)
}
fun battery18650() = Cylinder( 63, 18/2 ) + Cylinder( 65, 6 )
// Used for debugging only. Shows the size of the boost module, as well
// as the mounting holes.
fun buckModule() : Shape3d {
val board = Cube( 43.5, 21.5, 1.5 ).color("Green")
val components = Cube(43.5, 21.5, 10).translateZ(1.5).color("WhiteSmoke")
val holes = Cylinder.hole( 20, 1.5 ).translate(15,-7.5,-1)
.translate(-30,15,-1).also()
return (board + components).centerXY() - holes
}
@Piece
fun all() = layoutY( 2,
layoutX( 2, front(), back() ),
layoutX( 2, chargeAndBatterySled(), middleWithBrim())
)
override fun build() : Shape3d {
val all = back() +
middle().color("Green").bottomTo( backLength ) +
front().rotateX(180).bottomTo( backLength + middleLength ) +
chargeAndBatterySled().color("Blue")
.translateX(-5)
.bottomTo( thickness ) +
battery18650().translateX((height-width)/2).previewOnly()
.translateZ( thickness*2 ) +
buckModule().previewOnly().rotateY(90)
.bottomTo( 30 )
.rightTo( width /2 - lipT ) +
batteryCap().color("Orange")
.rotateX(180)
.bottomTo( 60 )
.leftTo( -width/2 )
return all
}
}