/Boxes/SmoothInsideBox.foocad
A box, with thin walls, but thicker there the lip of the lid meets the main body.
The box can be built in 2, 3 or more parts. You will always need 1 "top" and 1 "bottom", and you can add 0 or more "middle" pieces.
"slack" is quite and important @Custom parameter, and will need fine tuning for your partiuclar printer. Too small, and the lid will jam on too tightly. Too large, and the lid will be horribly loose. Only the "top" piece uses "slack", so if you need to adjust it, only the "top" piece needs to be re-printed.
The shape of the box is arbitrary (override profile() method).
To get all the other profile sizes (for the lip, inside, chamfer etc), I currently use "scale". This ensures it works with any profile you want, but has the disadvantage that it doesn't produce perfectly consistant width lips.
The "better" solution would be to use Shape2d.offset(n), but this can produce non 1-to-1 correspondance between the points that make up sucessive profiles, and ExtrusionBuilder does a very bad job sometimes! A future version of FooCAD may fix this, and then Shape2d.offset will be better than Shape2d.scale.
import static uk.co.nickthecoder.foocad.chamferedExtrude.ChamferedExtrude.* import static uk.co.nickthecoder.foocad.layout.Layout2d.* import static uk.co.nickthecoder.foocad.layout.Layout3d.* class SmoothInsideBox : Model { @Custom var height = 40 // Make the top higher than the "lipHeight" + "topChamfer" @Custom var extraTopHeight = 0 @Custom var middleHeight = 30 @Custom var topChamfer = 2.0 @Custom var bottomChamfer = 2.0 @Custom var lipHeight = 8.0 @Custom var lipThickness = 2.0 // Make this bigger than wallThickness, so that the rim of the bottom and middle // pieces are stronger. The "rims" are parts that are on the inside pointing upwards, // and the "lips" are outside pointing downwards. @Custom var rimThickness = 1.5 @Custom var wallThickness = 1.0 @Custom var baseThickness = 1.0 @Custom var slack = 0.3 fun profile() : Shape2d = Square( 70 ).center() fun inset( amount : double ) : Shape2d { val profile : Shape2d = profile() return profile.scale( (profile.size.x - amount * 2) / profile.size.x, (profile.size.y - amount * 2) / profile.size.y ) } @Piece fun top() : Shape3d { val out = slack + lipThickness + rimThickness - wallThickness val topHeight = extraTopHeight + topChamfer + baseThickness val outside = inset( - out ) val chamfered = inset( topChamfer - out ) val lipA = inset( -slack - rimThickness + wallThickness ) val inside = inset( wallThickness ) val insideChamfered = inset( wallThickness + topChamfer ) return ExtrusionBuilder().apply { joinStrategy = OneToOneJoinStrategy.instance crossSection( chamfered ) forward( topChamfer ) crossSection( outside ) forward( topHeight + lipHeight - topChamfer ) crossSection() crossSection( lipA ) forward( -lipHeight ) crossSection() crossSection( inside ) forward( -topHeight + topChamfer + baseThickness ) crossSection() forward( -topChamfer ) crossSection( insideChamfered ) }.build().mirrorX() } @Piece fun middle() : Shape3d { if ( middleHeight <= 0 ) return Cube(0) val out = slack + lipThickness + rimThickness - wallThickness val diff = rimThickness - wallThickness val profile : Shape2d = profile() val inside = inset( wallThickness ) val outside = inset( -out ) val lipA = inset( - diff - slack ) val rim = inset( -rimThickness + wallThickness ) return ExtrusionBuilder().apply { joinStrategy = OneToOneJoinStrategy.instance crossSection( rim ) forward( lipHeight - diff ) if ( diff != 0 ) { crossSection() forward( diff ) } crossSection( profile ) forward( middleHeight - lipHeight - out ) crossSection() forward( out ) crossSection( outside ) forward( lipHeight - lipThickness ) crossSection() forward( lipThickness ) crossSection( lipA ) forward( -lipHeight ) crossSection() crossSection( inside ) forward( -middleHeight ) crossSection() }.build().mirrorX() - Cube ( profile.size.x * 3, profile.size.y * 3, 2 ) .centerXY().translateZ(-2) } @Piece fun bottom() : Shape3d { val diff = rimThickness - wallThickness val profile : Shape2d = profile() val chamfered = inset( bottomChamfer ) val inside = inset( wallThickness ) val insideChamfer = inset( wallThickness + bottomChamfer ) val rim = inset( -diff ) return ExtrusionBuilder().apply { joinStrategy = OneToOneJoinStrategy.instance crossSection( chamfered ) forward( bottomChamfer ) crossSection( profile ) forward( height - bottomChamfer - lipHeight - diff ) crossSection() forward( diff ) crossSection( rim ) forward( lipHeight ) crossSection() crossSection( inside ) forward( -height + bottomChamfer + baseThickness ) crossSection() forward( -bottomChamfer ) crossSection( insideChamfer ) }.build() } @Piece override fun build() : Shape3d { var gap = 10 return bottom() .color("Blue") + middle() .mirrorZ() .translateZ(height + middleHeight + slack + gap) .color("Silver") + top() .mirrorZ() .translateZ(height + middleHeight + extraTopHeight + topChamfer + baseThickness + slack*2 + gap*2 ) .color("Green") } }