FooCAD Source Codeimport uk.co.nickthecoder.foocad.threaded.v2.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
import uk.co.nickthecoder.foocad.smartextrusion.v1.*
import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.*
class Bug : Model {
var bump = 0.9
var clearance = 0.3
var scale = 2.0
var threadSize = 10
meth thread() = Thread( threadSize )
@Piece
meth foot() : Shape3d {
val bigToe = Sphere(4).translate(8,2.0,0)
val littleToe = Sphere(3.8).translate(6.9,-0.8,0)
val heal = Sphere(7)
val hull = bigToe.hull(littleToe).hull(heal)
val onlyTop = hull.boundingCube().bottomTo(0)
val solid = hull.translateZ(1).intersection(onlyTop)
.scale( scale )
val hole = lockingHole( solid.top - 1 )
.rotateX(90).rotateZ(-90)
.centerZ()
.topTo( solid.top + 1 )
return solid - hole
}
@Piece
meth footLeft() = foot().mirrorY()
meth zOffset() = 7
meth segmentShape() = Circle(10).translateX(zOffset()).intersection( Square( 30 ).centerY() )
meth centerHeight() = zOffset() * scale
@Piece
meth segment1() : Shape3d {
val shape = segmentShape()
val unscaledSolid = shape.smartExtrude(10)
.edges( Fillet(2) )
.rotateY(-90)
.centerX()
val solid = unscaledSolid
.scale( scale )
val indentHalf = Sphere(9)
.leftTo(unscaledSolid.right-2)
val indent = indentHalf.hull( indentHalf.translateZ(-10) )
.scale( scale )
.translateZ(centerHeight()).mirrorX().also()
val legHolePath = PolygonBuilder().apply {
moveTo( 0, solid.front - 2 )
lineBy( 0.75, 2 )
bezierBy(
Vector2( 5, solid.size.y/4 ),
Vector2( -5, solid.size.y/4 ),
Vector2( 0, solid.size.y )
)
lineBy( -0.75, 2 )
}.buildPath()
val legHoles = holeShape().extrude( legHolePath )
.rotateY(-90)
.bottomTo( centerHeight() - 1 )
val spineHole = holeShape().smartExtrude( solid.size.x )
.rotateY(90)
.centerX()
.translateZ( centerHeight() )
return solid - indent - legHoles - spineHole
}
meth lump() : Shape3d {
val a = Sphere(9-clearance)
.scale( scale )
.translateZ(centerHeight())
.intersection( Cube( 30*scale ).centerXY() )
val b = Circle( a.size.x / 2 )
.smartExtrude(centerHeight())
.bottom( roundedChamfer(3.5*scale) )
return a + b
}
@Piece
meth segment2() : Shape3d {
val shape = segmentShape()
val solid = shape.smartExtrude(10)
.edges( Fillet(2) )
.rotateY(-90)
.centerX()
.scale( scale )
val lump = lump()
val openingSize = 4 * scale
val spineOpening = holeShape().smartExtrude( openingSize + 0.1 )
.top( Chamfer(-openingSize, openingSize) )
.rotateY(90)
.translateZ( centerHeight() )
.rightTo( lump.right )
val spineOpenings = spineOpening.mirrorX().also()
val spineHole = holeShape().extrude( lump.size.x )
.rotateY(90).centerX()
.translateZ( centerHeight() )
val lockHole = thread().threadedHole( centerHeight() + spineHole.size.z/2 + 0.1 )
.topTo( spineHole.top )
return solid + lump - spineOpenings - spineHole - lockHole
}
@Piece
meth head() : Shape3d {
val neck = Sphere(9-clearance)
.scale( scale )
.translateZ(centerHeight())
.intersection( Cube( 30*scale ).centerXY() )
val head = Sphere( 11 )
.translateZ( 3 )
val eye = Sphere( 3 ) - Sphere( 1.4 ).translate( 2.1, 0.5, 1 )
val eyes = eye.translate(7.5,5,7).mirrorY().also()
val spineHole = holeShape().extrude( neck.size.x )
.rotateY(90).centerX()
.translateZ( centerHeight() )
val openingSize = 4 * scale
val spineOpening = holeShape().smartExtrude( openingSize + 0.1 )
.top( Chamfer(-openingSize, openingSize) )
.rotateY(-90)
.translateZ( centerHeight() )
.leftTo( neck.left )
val slice = Cube( 200 ).centerXY()
val addition = (head + eyes).translateX(4)
.scale( scale )
.translateZ( centerHeight() )
.intersection(slice)
val lockHole = thread().threadedHole( centerHeight() + spineHole.size.z/2 + 0.1 )
.topTo( spineHole.top )
val antenaHoles = lockingHole(addition.top - centerHeight() + 1).rotateX(90)
.bottomTo(centerHeight())
.rotateX(20)
.rotateY(10)
.translate( 3 * scale, 3*scale, 0 )
.mirrorY().also()
return neck + addition - spineOpening - lockHole - spineHole - antenaHoles
}
@Piece
meth antena() : Shape3d {
val ball = Sphere(4)
.translateZ(3)
.scale(scale)
val slice = Cube( 100 ).centerXY()
val hole = lockingHole( ball.top - 2 )
.rotateX(90)
.bottomTo(-0.1)
return ball.intersection(slice) - hole - hole
}
@Piece
meth tail() : Shape3d {
val segment = segment2()
val tailA = Circle( segment.size.y * 0.75 / 2 )
.extrude( 1 )
.rotateY(90)
.translateX( -segment.size.x * 0.2 )
val tailB = Sphere( segment.size.y * 0.2 )
.translate( -segment.size.y * 0.7, 0, -2 )
val tail = tailA.hull(tailB)
.centerZTo( centerHeight() )
.intersection( Cube( 200 ).centerXY() )
return segment + tail
}
@Piece
meth lock() : Shape3d {
val rod = thread().threadedRod( centerHeight() - 1 )
.chamferBottom(false)
val slot = Cube(20, 1, 1.5).centerXY()
return (rod - slot).rotateY(90).bottomTo(-threadSize*0.2).intersection(Cube(40).centerXY())
}
meth lockingHole( length : double ) =
holeShape()
.extrude( wigglePath( length, bump ) )
.color("Red")
meth holeShape() = Circle((1.75 + 0.7)/2).hole()
// The path a hole makes. This is the "clever" part of the system which
// ensures that the filament is held firmly in the holes despite the fact that
// the diameter of the holes is LARGER than the filament.
// The holes wiggle, which forces the filament to wiggle, and therefore
// it presses outwards (due to the springy nature of the filament).
meth wigglePath( length : double, bump : double ) : Path2d {
val pre = 1
val halfLength = length/2 - pre
return PolygonBuilder().apply {
moveTo( 0, -length/2 )
lineBy( 0, pre )
bezierBy(
Vector2( 0, halfLength/2 ),
Vector2( 0, halfLength/2 ),
Vector2( bump, halfLength )
)
bezierBy(
Vector2( 0, halfLength/2 ),
Vector2( 0, halfLength/2 ),
Vector2( -bump, halfLength )
)
lineBy( 0, pre )
}.buildPath()
}
@Piece( printable=false )
override fun build() : Shape3d {
val feet = foot().translate(0,-17*scale,0).mirrorY().also()
val segment1 = segment1().translateZ(10*scale)
val segment2 = segment2().translate(12*scale, 0, 10*scale).color("Orange")
val part = feet + segment1 + segment2
val tail = tail().translate(-12*scale, 0, 10*scale).color("Orange" )
val extraLegs = (feet + segment1).translateX(12*scale*8)
val head = head().translate(12*scale*9, 0, 10*scale).color("Orange")
val antena = antena()
.leftTo( head.right )
.bottomTo( head.top + 20 )
.frontTo( -20 )
.mirrorY().also()
return part.repeatX(4, 24*scale) + tail + extraLegs + head + antena
}
}