Exit Full View
ZipUp

/Garden/Bug.foocad

Bug
FooCAD Source Code
import 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
    }

}