import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* class PuzzleBox : Model { @Custom var width = 70.0 // Internal size @Custom var depth = 60.0 // Internal size @Custom var middleHeight = 22.0 // Internal size @Custom var topHeight = 10.0 // Internal size @Custom var bottomHeight = 30.0 // Internal size @Custom var radius = 6.0 // Radius of the INSIDE. Other radii are calculated. @Custom var radiusSides = 4.0 @Custom var thickness = 4.0 // Thickness of the top (sides and top/bottom) @Custom var patternThickness = 0.6 // he depth of the pattern cut into the top/bottom @Custom var lipThickness = 2.0 // Should probably be half of thickness (or close to it) @Custom var lipHeight = 6.0 // The height of the lip on the bottom piece. // I created small prototypes to find the best value for this. // It is the additional space in the top part for the bottom's lip // to fit into. I wanted an interferance fit. @Custom var lipSlack = 0.3 @Custom var chamfer = 2.0 // Chamfer of the bottom and top edges. /** For large boxes, it is important to add ears to the corners, so that the box doesnt lift off of the print bed. For very large boxes, maybe ears along the edges are also needed (especially when using ABS), but this model doesn't support ears along the edges. So either improves the model, or add extra ears anually in your slicer. NOTE, I also tape the ears to the bed with masking tape after they have been printed (i.e. after the first layer is complete) */ @Custom var cornerEars = 0.0 fun createProfile( offset : double ) = Square( width + offset*2, depth + offset*2 ) .center() .roundAllCorners( radius + offset, radiusSides ) fun createContent( height : double ) = Square( width -1, depth -1 ).roundAllCorners( radius - 0.5 ) .extrude( height ) @Piece fun top() = buildPart( "top" ) @Piece fun middle() = buildPart( "middle" ) @Piece fun bottom() = buildPart( "bottom" ) @Piece fun view() = buildPart("view") override fun build() = buildPart("exploded") fun buildPart( part : String) : Shape3d { val contentCubes = createContent( bottomHeight-1).translateZ(1) + createContent( middleHeight).translateZ( bottomHeight + 1) + createContent( topHeight-1).translateZ( bottomHeight + middleHeight + 2) val contents = contentCubes .centerXY() .translateZ(thickness) .color("Orange") val inside = createProfile( 0 ) val outside = createProfile( thickness ) val outsideChamfer = createProfile( thickness - chamfer) val outsideLip = createProfile( lipThickness ) val slackLip = createProfile(lipThickness - lipSlack ) val top : Shape3d = ExtrusionBuilder().apply { crossSection( outsideChamfer ) forward( chamfer ) crossSection( outside ) forward( topHeight + thickness - chamfer ) crossSection( outside ) crossSection( outsideLip ) forward( -lipHeight ) crossSection( outsideLip ) crossSection( inside ) forward( -topHeight + lipHeight) crossSection( inside ) }.build().color( "Blue" ) val bottom : Shape3d = ExtrusionBuilder().apply { crossSection( outsideChamfer ) forward( chamfer ) crossSection( outside ) forward( bottomHeight + thickness - chamfer ) crossSection( outside ) crossSection( slackLip ) forward( lipHeight - 1 ) crossSection( slackLip ) crossSection( inside ) forward( -bottomHeight - lipHeight + 1 ) crossSection( inside ) }.build().color( "Blue" ) val middleSolid = ExtrusionBuilder().apply { crossSection( outside ) forward( middleHeight ) crossSection( outside ) crossSection( slackLip ) forward( lipHeight ) crossSection( slackLip ) }.build() .color( "Blue" ).brighter() val middleHole = ExtrusionBuilder().apply { forward(-0.1) crossSection( outsideLip ) forward(0.1 + lipHeight + 1 ) crossSection( outsideLip ) forward( thickness ) // Chamfer so that we don't need support material crossSection( inside ) forward( middleHeight - thickness -1 + 0.1) crossSection( inside ) }.build() val middle = middleSolid - middleHole val view = contents + bottom + middle.translateZ( bottomHeight + thickness + 1 ).color("Yellow") + top .rotateX(180) .toOriginZ() .translateZ( bottomHeight + middleHeight + thickness + 2 ) val pattern : Shape3d = pattern() .extrude( patternThickness ) .translateZ( -0.01) .color( "WhiteSmoke" ) val inspection = Cube( 600, 5, 600 ).center() + Cube( 5, 400, 400 ).center() + Cube(400, 1, 400).centerY().rotateZ(45) val ears = Circle( cornerEars ) .translate( width/2 + cornerEars*0.5 - radius/2, depth/2 + cornerEars*0.5 - radius/2 ) .mirrorX().also() .mirrorY().also() .extrude( 0.2 ) return if (part == "top" ) { top - pattern + ears } else if (part == "middle" ) { middle + ears } else if (part == "bottom" ) { bottom - pattern + ears } else if (part == "view" ) { view } else if (part == "exploded" ) { bottom.translateX( width +10).centerXY() + //contents + middle.translateZ(bottomHeight + 20).centerXY() + top.rotateX(180).translateZ( bottomHeight + middleHeight + topHeight + 50).centerXY() } else { view / inspection } } @Custom var pattern = "circles2" @Custom var patternSize = 7.0 fun pattern() : Shape2d { return if (pattern == "striped") { Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center().translateY(patternSize) } else if (pattern == "striped2" ) { Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center() } else if (pattern == "diagonal" ) { Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center().translateY(patternSize).rotate(45) } else if (pattern == "diagonal2" ) { Square( width *3, patternSize ).tileY( width / patternSize, patternSize ).center().rotate(45) } else if (pattern == "spots" ) { val scale = 0.5 Circle( patternSize ) .tileX( Math.floor(0.25 * width / patternSize), patternSize*2 ) .tileY( Math.floor(0.25 * depth / patternSize), patternSize*2 ) .translate( patternSize * 2, patternSize * 2 ).also() .center() } else if ( pattern == "diamond" ) { val count = width ~/ patternSize val result = listOf() for ( i in 0 until count ) { result.add( Square( (i*2+1) * patternSize ).center().rotate(45) - Square( (i*2) * patternSize ).center().rotate(45) ) } Union2d( result ) } else if ( pattern == "diamond2" ) { val count = width ~/ patternSize val result = listOf() for ( i in 1 until count ) { result.add( Square( (i*2) * patternSize ).center().rotate(45) - Square( (i*2-1) * patternSize ).center().rotate(45) ) } Union2d( result ) } else if (pattern == "circles" ) { val count = 0.4 * Math.sqrt( width * width + depth * depth ) ~/ patternSize val result = listOf() for ( i in 0 until count ) { result.add( Circle( (i*2+1) * patternSize ) - Circle( (i*2) * patternSize ) ) } Union2d( result ) } else if (pattern == "circles2" ) { val count = 0.4 * Math.sqrt( width * width + depth * depth ) ~/ patternSize val result = listOf() for ( i in 1 until count ) { result.add( Circle( i*2 * patternSize ) - Circle( (i*2-1) * patternSize ) ) } Union2d( result ) } else { Square( 1 ).translateX( 1000 ) } } }