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