import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* class TissueBoxCover : Model { var boxWidth = 120 var boxDepth = 120 var boxHeight = 130 var margin = 10 var cornerRadius = 3 var extra = 8 fun profile( amplitude : double ) : Shape2d { val width = boxWidth + extra val depth = boxWidth + extra val profile = PolygonBuilder().apply { val bumps = 4 val dx = (width - margin) / bumps val dy = (depth - margin) / bumps val xRadius = Vector2( dx/4, amplitude ) val yRadius = Vector2( amplitude, dy/4 ) // First side moveTo( margin, 0 ) radius( 0 ) for ( i in 0 until bumps ) { arcTo( Vector2(margin + dx * i, 0), xRadius, 0, true, true ) arcTo( Vector2(margin + dx * i + dx/2, 0), xRadius, 0, true, false ) } radius( cornerRadius ) lineTo( width, 0 ) radius( 0 ) // Second side lineTo( width, margin ) for ( i in 0 until bumps ) { arcTo( Vector2( width, margin + dy * i), yRadius, 0, true, true ) arcTo( Vector2( width, margin + dy * i + dy/2), yRadius, 0, true, false ) } radius( cornerRadius ) lineTo( width, depth ) radius( 0 ) // Third side lineTo( width-margin, depth ) for ( i in 0 until bumps ) { arcTo( Vector2( width - (margin + dx * i), depth), xRadius, 0, true, true ) arcTo( Vector2( width - (margin + dx * i + dx/2), depth), xRadius, 0, true, false ) } radius( cornerRadius ) lineTo( 0, depth ) radius( 0 ) // Forth side lineTo( 0, depth - margin ) for ( i in 0 until bumps ) { arcTo( Vector2( 0, depth - (margin + dy * i) ), yRadius, 0, true, true ) arcTo( Vector2( 0, depth - (margin + dy * i + dy/2) ), yRadius, 0, true, false ) } radius( cornerRadius ) lineTo( 0,0 ) }.build() return profile } @Piece fun body() : Shape3d { val middle = ExtrusionBuilder().apply { crossSection( profile( 1 ) ) forward( 2 ) crossSection( profile( 3 ) ) forward( boxHeight - margin * 2 - 4 ) crossSection() forward( 2 ) crossSection( profile( 1 ) ) }.build() val ends = baseProfile(0).extrude( margin ) return ends + middle.centerXY().translateZ(margin) + ends.translateZ( boxHeight - margin ) } fun baseProfile( offset : double ) : Shape2d { val profile = Square( boxWidth + extra, boxDepth + extra ).center() .roundAllCorners( cornerRadius ) return if ( offset == 0 ) { profile } else { val scale = (profile.size - Vector2( offset, offset )) / profile.size profile.scale( scale ) } } @Piece fun lid() : Shape3d { val thickness = 1.2 val chamfer = 1 val lipOffset = 2 val lipInset = 2 val lipThickness = 2 val lipHeight = 5 val lid = ExtrusionBuilder().apply { crossSection( baseProfile(chamfer) ) forward( chamfer ) crossSection( baseProfile(0) ) forward( thickness ) crossSection() crossSection( baseProfile( lipInset ) ) forward( lipHeight ) crossSection() crossSection( baseProfile( lipInset + lipThickness ) ) forward( - lipHeight ) crossSection() }.build() val a = Circle( 12 ).translateX(14) val holeP = a.hull( a.mirrorX() ) val hole = holeP.internalChamferedExtrude( 4, 1, 1 ) val thicker = (holeP.offset( 2 ) - holeP).extrude( 4 ) return lid + thicker - hole } override fun build() : Shape3d { val box = Cube( boxWidth, boxDepth, boxHeight ).previewOnly().centerXY() val main = body() val lid : Shape3d = lid().mirrorZ().bottomTo(main.top) return box.translateZ(10) + main + lid } }