import uk.co.nickthecoder.foocad.cup.v1.* import static uk.co.nickthecoder.foocad.cup.v1.Cup.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* import static uk.co.nickthecoder.foocad.circular.v1.Circular.* import uk.co.nickthecoder.foocad.cup.v1.* import static uk.co.nickthecoder.foocad.cup.v1.Cup.* import uk.co.nickthecoder.foocad.smartextrusion.v1.* import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.* import uk.co.nickthecoder.foocad.threaded.v2.* class Bottle : Model { @Custom var size = Vector3( 80, 80, 150 ) @Custom var throatDiameter = 40 @Custom var wallThickness = 1.8 @Custom var baseThickness = 2.0 @Custom var capThickness = 2.0 @Custom var round = 20 @Custom var bottomFillet = 10 @Custom var neckHeight = 50 @Custom val capHeight = 14 @Custom val funnelSize = Vector3( 120, 160, 100 )//Vector3( size.x, size.y, Math.max(size.x, size.y) ) meth capThread() = Thread( throatDiameter + 8, 3 ) .rodChamfer(1) @Piece @Slice( topSolidLayers=10, bottomSolidLayers=10, perimeters=4 ) // Solid! meth cap() : Shape3d { val thread = capThread() val solid = Circle( throatDiameter/2 + 6 ) .cup( capHeight, 6 ).baseThickness(capThickness) .outsideBottom( ProfileEdge.roundedChamfer(3) ) .insideBottom( Chamfer(capThickness) ) .outsideTop( Chamfer(1) ) val hole = thread.threadedHole( solid.top - capThickness*1.5 ) .chamferStart(false) .chamferEnd(true) .bottomTo( capThickness*1.5 ) return solid - hole } @Piece @Slice( brimWidth=5, brimWidthInterior=5, seamPosition="random" ) meth body() : Shape3d { val bodyHeight = size.z - capHeight - neckHeight val thread = capThread() val diameter = Math.min( size.x, size.y ) val size2d = Vector2( size.x, size.y ) val squarish = Square( size.x, size.y ).center().roundAllCorners(round, 20) val main = squarish .cup( bodyHeight, wallThickness ).baseThickness( baseThickness ) .bottom( roundedChamfer(bottomFillet) ) val steps = 10 val transD = thread.coreRadius() *2 - 1.5 val neck = extrudeRoundedSquareToCircle( neckHeight, size2d, round, transD ) .bottomTo( main.top ) val insideNeck = extrudeRoundedSquareToCircle( neckHeight, size2d, round, transD, -wallThickness ) .bottomTo( main.top ) val threaded = ( thread.threadedRod( capHeight - wallThickness ) - Circle( thread.coreRadius() - 4 ) .smartExtrude( capHeight + 0.02 ) .bottom( Chamfer( 2 ).reverse() ) .bottomTo(-0.01) ).bottomTo( neck.top - 1 ) return main + neck + threaded - insideNeck } @Piece @Slice( brimWidth=5, brimWidthInterior=5, seamPosition="random" ) meth funnel() : Shape3d { val neckDiameter = capThread().coreRadius() * 2 - wallThickness val size2d = Vector2( funnelSize.x, funnelSize.y ) val cap = cap().bottomTo( funnelSize.z - capThickness*1.5 ) val capAngle = Circle( cap.size.x/2 - 0.55 ) .smartExtrude( 10 ) .offsetBottom( -10 ) .topTo( cap.bottom +1 ) val lip = (Circle( throatDiameter/2 - 1 ) - Circle( throatDiameter/2 - 3 )) .smartExtrude( cap.size.z - 2 ) .outsideBottom( Chamfer(5).reverse() ) .insideBottom( Chamfer(4) ) .outsideTop( Chamfer(1) ) .bottomTo( cap.bottom ) val main = extrudeRoundedSquareToCircle( funnelSize.z, size2d, round, neckDiameter ) val insideMain = extrudeRoundedSquareToCircle( funnelSize.z, size2d, round, neckDiameter, -wallThickness ) return main + cap + capAngle- insideMain + lip } func extrudeRoundedSquareToCircle( height : double, startSize : Vector2, startRound : double, endDiameter : double, offset : double ) : Shape3d { val steps = 20 return ExtrusionBuilder().apply { for ( step in 0 .. steps ) { val t = step/steps crossSection( roundedSquareToCircle( startSize, startRound, endDiameter, t).offset(offset) ) forward( height/steps ) } }.build() } func extrudeRoundedSquareToCircle( height : double, startSize : Vector2, startRound : double, endDiameter : double ) : Shape3d { return extrudeRoundedSquareToCircle( height, startSize, startRound, endDiameter, 0 ) } func roundedSquareToCircle( startSize : Vector2, startRound : double, endDiameter : double, t : double ) : Shape2d { val s = Eases.EASE_IN_OUT.ease(t) val x = startSize.x*(1-s) + endDiameter*s val y = startSize.y*(1-s) + endDiameter*s val endRound = endDiameter/2 - 0.1 val round = startRound *(1-s) + endRound*s return Square( x, y ).center().roundAllCorners(round, 20) } @Piece( printable=false ) override fun build() : Shape3d { val body = body().color("Green") val cap = cap().rotateX(180).topTo( body.top + baseThickness*1.5 ) return body + cap } }