import uk.co.nickthecoder.foocad.extras.v1.* import static uk.co.nickthecoder.foocad.extras.v1.Extras.* import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.* class ComponentOrganiser : AbstractModel() { @Custom( about="The gap between the inserts" ) var insertGap = 0.5 // The size of the inserts. Note "insertGap" is subtracted from the // x and y sizes of the actual inserts. // Note Raaco states that the smallest A insert is 39x55x47 // Swap width and depth for inserts oriented the other way. @Custom var unitWidth = 55 @Custom var unitDepth = 40 // NOTE this isn't 39! @Custom var unitHeight = 46.5 // Does NOT include the feet. @Custom var shoeHeight = 1.8 @Custom( about="Use by the `sixth`" ) var tabHeight = 6.0 var insertThickness = 1.0 var insertTaper = 1.0 var insertRadius = 2.0 // The radius of the circles in the drawers that the feet fit into. var shoeRadius = 9 meth floorShape( sizeX : int, sizeY : int ) : Shape2d { val freeWidth = unitWidth * sizeX val freeDepth = unitDepth * sizeY val thickness = 1.8 val circles = Circle( shoeRadius ).ring( thickness ) .repeatX( sizeX+1, unitWidth ) .repeatY( sizeY+1, unitDepth ) .center() val clippedCircles = circles / Square( freeWidth, freeDepth ).center() // Lines connecting the shoes. // These aren't *needed*, but they do add extra strength to the floor, // for little extra time and plastic. They look nice too ;-) val verticals = Square( thickness, unitDepth - shoeRadius*2 ) .repeatY( sizeY, unitDepth ) .repeatX( sizeX+1, unitWidth ) .center() val horizontals = Square( unitWidth - shoeRadius*2, thickness ) .repeatX( sizeX, unitWidth ) .repeatY( sizeY+1, unitDepth ) .center() return clippedCircles + verticals + horizontals } meth floorPattern( sizeX : int, sizeY : int ) = floorShape( sizeX, sizeY ).extrude( shoeHeight ) meth lidShape( sizeX : int, sizeY : int ) : Shape2d { val outside = insertShape(1,1).offset( insertTaper - 1 ) val inside = outside.offset(-1.2) val one = (outside - inside) return one .repeatY( sizeY, unitDepth ) .repeatX( sizeX, unitWidth ) .center() } meth lidPattern( sizeX : int, sizeY : int ) : Shape3d { return lidShape( sizeX, sizeY ).extrude(2) } meth footShape() : Shape2d { val width = unitWidth - insertGap var depth = unitDepth - insertGap val profile = Square( width - insertTaper*2 - insertThickness * 2, depth - insertTaper*2 - insertThickness * 2 ).center().roundAllCorners(insertRadius,6) return profile.offset( insertThickness ) / Circle( shoeRadius-1 ).translate( 1/2*unitWidth, 1/2*unitDepth ) } meth insertShape( x : int, y : int ) : Shape2d { val width = unitWidth * x - insertGap var depth = unitDepth * y - insertGap return Square( width - insertTaper*2 - insertThickness * 2, depth - insertTaper*2 - insertThickness * 2 ).center().roundAllCorners(insertRadius,6) } meth insert( x : int, y : int ) : Shape3d { val height = unitHeight - insertGap val profile = insertShape( x, y ) // We build them upside down, starting at the inside of the base. val main = ExtrusionBuilder().apply { forward( height - insertThickness ) crossSection( profile ) forward( -height + insertThickness ) crossSection( insertTaper ) crossSection( insertThickness ) forward( height ) crossSection( -insertTaper ) }.build().mirrorZ().toOriginZ() return main } @Piece meth foot() = footShape().extrude(shoeHeight-0.2) @Piece meth insert1x1() = insert( 1, 1 ) @Piece meth insert1x2() = insert( 1, 2 ) @Piece meth insert1x3() = insert( 1, 3 ) @Piece meth insert1x4() = insert( 1, 4 ) @Piece meth insert2x1() = insert( 2, 1 ) @Piece meth insert2x2() = insert( 2, 2 ) @Piece meth insert2x3() = insert( 2, 3 ) @Piece meth insert2x4() = insert( 2, 4 ) @Piece meth insert3x1() = insert( 3, 1 ) @Piece meth insert3x2() = insert( 3, 2 ) @Piece meth insert3x3() = insert( 3, 3 ) @Piece meth insert3x4() = insert( 3, 4 ) @Piece meth insert4x1() = insert( 4, 1 ) @Piece meth insert4x2() = insert( 4, 2 ) @Piece meth insert4x3() = insert( 4, 3 ) @Piece meth insert4x4() = insert( 4, 4 ) /** Designed to fit 5 smaller containers inside a 1x2 insert, specifically for through-hole resistors. */ @Piece meth tabbedFifths() = arrangeY( 0.7, tabbedFifth( 0 ), tabbedFifth( 1 ), tabbedFifth( 2 ), tabbedFifth( 3 ), tabbedFifth( 4 ) ).centerY() + insert1x2().rotateZ(90) .centerY() .bottomTo( -insertThickness ) .previewOnly() @Piece( about="Mini tubs, arranged as a 1x5 inside `insert2x2`" ) fun tabbedFifth( tabPosition : int ) : Shape3d { val width = unitDepth * 2 - insertThickness*2 - insertTaper * 2 - 1 val depth = (unitWidth - insertThickness*2 - insertTaper * 2) / 5 - 0.5 val height = unitHeight - insertGap - insertThickness - tabHeight - 1 val thickness = insertThickness val profile = Square( width, depth ).center().roundAllCorners(2,4) val main = ExtrusionBuilder().apply { crossSection( profile ) forward( height ) crossSection() crossSection( - thickness ) forward( -height + thickness ) crossSection() }.build() val tabWidth = (main.size.x - 4) / 5 val tab = Square( tabWidth, tabHeight ) .roundCorner(3,3) .roundCorner(2,3) .extrude( thickness ).rotateX(90) .bottomTo( main.top ) .backTo( main.back ) .leftTo(main.left + 2 + tabPosition * tabWidth ) return main + tab } @Piece( about="Mini tubs, arranged as a 3x2 inside `insert2x2`" ) fun tabbedSixth() : Shape3d { val width = (unitDepth * 2 - insertThickness*2 - insertTaper * 2) / 3 - 1.0 val depth = (unitWidth - insertThickness*2 - insertTaper * 2) / 2 - 1.0 val height = unitHeight - insertGap - insertThickness - tabHeight - 1 val thickness = insertThickness val profile = Square( width, depth ).center().roundAllCorners(2,4) val main = ExtrusionBuilder().apply { crossSection( profile ) forward( height ) crossSection() crossSection( - thickness ) forward( -height + thickness ) crossSection() }.build() val tabWidth = (main.size.x - 4) val tab = Square( tabWidth, tabHeight ) .roundCorner(3,3) .roundCorner(2,3) .extrude( thickness ).rotateX(90) .bottomTo( main.top ) .backTo( main.back ) .leftTo(main.left + 2 ) val one = main + tab val set = one.tileX(3, 1).tileY(2, 1).centerXY() return set + insert1x2().rotateZ(90) .centerY() .bottomTo( -insertThickness ) .previewOnly() } @Piece( printable=false ) override fun build() : Shape3d { val i1x1 = insert1x1().color("Yellow") val i1x2 = insert1x2().color("Yellow").darker() val i1x3 = insert1x3().color("Yellow").darker().darker() val i2x1 = insert2x1().color("Orange") val i2x2 = insert2x2().color("Orange").darker() val i2x3 = insert2x3().color("Orange").darker().darker() val i3x1 = insert3x1().color("Red") val i3x2 = insert3x2().color("Red").darker() val i3x3 = insert3x3().color("Red").darker().darker() // NOTE, the feet are only included on one of the inserts. val feet = foot().mirrorX().also().mirrorY().also() .centerXTo( i1x1.middle.x ).centerYTo( i1x1.middle.y ) .topTo( i1x1.bottom ) .color("Blue") val floorPattern = floorPattern( 6, 6 ) .centerXY() .topTo( 0 ) .color("Purple") val inserts = arrangeX( arrangeY(insertGap, i1x1 + feet, i1x2, i1x3), arrangeY(insertGap, i2x1, i2x2, i2x3), arrangeY(insertGap, i3x1, i3x2, i3x3) ).centerXY() val lidPattern = lidPattern( 6, 6 ) .centerXY() .topTo( inserts.top ) .previewOnly() return inserts + floorPattern + lidPattern } }