// System to divide a Kallax half-shelf into four equal drawers // All units measured in millimeters include <../lib/mortice-and-tenon.scad> include <./constants.scad> $fn = 32; module sampleBox(dims, wallThickness) { // base cube([dims[0], dims[1], wallThickness]); // top translate([0, 0, dims[2] - wallThickness]) cube([dims[0], dims[1], wallThickness]); // middle shelf translate([0, 0, (dims[2] - wallThickness) / 2]) cube([dims[0], dims[1], wallThickness]); // left wall cube([wallThickness, dims[1], dims[2]]); // middle divider translate([(dims[0] - wallThickness) / 2, 0, 0]) cube([wallThickness, dims[1], dims[2]]); // right wall translate([dims[0] - wallThickness, 0, 0]) cube([wallThickness, dims[1], dims[2]]); } module tally(count, rightAlign = false) { groups = floor(count / 5); rem = count % 5; xAlign = rightAlign ? -(6 * groups) - (rem * 1.2) : 0; translate([xAlign, 0, 0]) union() { if (count > 4) { for (i = [0: max(0, groups - 1)]) { translate([6*i, 0, 0]) for (j = [0:3]) translate([j*1.2, 0, 0]) cube([0.6, 4, 0.41]); translate([6*i - 0.8, 0.6, 0]) rotate([0, 0, -60]) cube([0.6, 6.2, 0.41]); } } if (rem != 0) { translate([6*groups, 0, 0]) for (j = [0: rem - 1]) translate([j*1.2, 0, 0]) cube([0.6, 4, 0.41]); } } } wallThickness = 4; // Thickness of walls feltClearance = 2.5; // Clearance for felt pads // Dimensions for the box that fits inside a Kallax half-shelf boxWidth = kallaxSide - 2 * feltClearance; boxDepth = kallaxDepth; boxHeight = shelfHeight - 2 * feltClearance; drawerWidth = (boxWidth - 3 * wallThickness) / 2; drawerHeight = (boxHeight - 3 * wallThickness) / 2; tenonThickness = wallThickness * 0.6; //tenon([6, 8, 2.6]); //morticeBlank([6, 8, 2.6], 0.1); // Frame for the box will be made in sections to fit on the print bed // There are three identical horizontal layers (base, middle shelf, top) that // will each be made of four pieces. Looking down overhead from the Z axis they // are: // // x1 x2 // ├──┴──┼─┴─┤ // ┌─────┬───┐ ┬ // │ C │ D │ │ // ├─────┼───┤ ├y // │ A │ B │ │ // └─────┴───┘ ┴ // // There are three identical drawer sides for the lower drawers that are split // into two pieces. Looking from the side, along the X axis they are: // // ├───y───┤ // ┌───┬───┐ ┬ // │ F │ E │ ├drawerHeight // └───┴───┘ ┴ // // There are three identical drawer sides for the upper drawers that are split // into two pieces. Looking from the side, along the X axis they are: // // ├───y───┤ // ┌───┬───┐ ┬ // │ H │ G │ ├drawerHeight // └───┴───┘ ┴ x1 = boxWidth * 0.6; x2 = boxWidth - x1; y = boxDepth / 2; tenonDims = [6, 8, tenonThickness]; tenonZOffset = (wallThickness - tenonThickness) / 2; verticalTenonDims = [6, wallThickness, tenonThickness]; throughTenonDims = [6, 8 + wallThickness, tenonThickness]; morticeClearance = 0.1; module leftSideHorizontalPiece(cutWeight = true) { difference() { cube([x1, y, wallThickness]); if (cutWeight) { union() { // cutouts to save materials translate([2*wallThickness, 2*wallThickness, -0.1]) cube([drawerWidth - 2*wallThickness, y/2 - 3*wallThickness, wallThickness + 0.2]); translate([2*wallThickness, y/2 + wallThickness, -0.1]) cube([drawerWidth - 2*wallThickness, y/2 - 3*wallThickness, wallThickness + 0.2]); translate([drawerWidth + 3*wallThickness, 2*wallThickness, -0.1]) cube([x1 - drawerWidth - 5*wallThickness, y/2 - 3*wallThickness, wallThickness + 0.2]); translate([drawerWidth + 3*wallThickness, y/2 + 2*wallThickness, -0.1]) cube([x1 - drawerWidth - 5*wallThickness, y/2 - 3*wallThickness, wallThickness + 0.2]); } } } } module rightSideHorizontalPiece(cutWeight = true) { difference() { cube([x2, y, wallThickness]); if (cutWeight) { union() { // cutouts to save materials translate([2*wallThickness, 2*wallThickness, -0.1]) cube([x2 - 4*wallThickness, y/2 - 3*wallThickness, wallThickness + 0.2]); translate([2*wallThickness, y/2 + wallThickness, -0.1]) cube([x2 - 4*wallThickness, y/2 - 3*wallThickness, wallThickness + 0.2]); } } } } module sidePiece(cutWeight = true) { union() { difference() { cube([wallThickness, y, drawerHeight]); if (cutWeight) { // cutouts to save materials translate([-0.1, 2*wallThickness, 2*wallThickness]) cube([wallThickness + 0.2, y - 4*wallThickness, drawerHeight - 3*wallThickness]); } } if (cutWeight) { translate([0, wallThickness, 1.414*wallThickness]) rotate([-41, 0, 0]) cube([wallThickness, wallThickness, drawerHeight * 1.19]); translate([0, y - 2*wallThickness, 1.414*wallThickness]) rotate([65, 0, 0]) cube([wallThickness, wallThickness, drawerHeight * 1.97]); } } } module pieceA(cutWeight = true) { difference() { union() { leftSideHorizontalPiece(cutWeight); // tenons for joining to piece C translate([x1*0.2, y, tenonZOffset]) tenon(tenonDims); translate([x1*0.7, y, tenonZOffset]) tenon(tenonDims); // tenons for joining to piece B translate([x1, y*0.2, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) tenon(tenonDims); translate([x1, y*0.7, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) tenon(tenonDims); } union() { // mortices for joining to piece vertical pieces translate([tenonZOffset, y* 0.3, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([tenonZOffset, y* 0.8, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([drawerWidth + wallThickness + tenonZOffset, y* 0.3, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([drawerWidth + wallThickness + tenonZOffset, y* 0.8, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); // piece label translate([2, 2, wallThickness - 0.4]) tally(1); } } } module pieceB(cutWeight = true) { difference() { union() { rightSideHorizontalPiece(cutWeight); // tenons for joining to piece D translate([x2*0.2, y, tenonZOffset]) tenon(tenonDims); translate([x2*0.7, y, tenonZOffset]) tenon(tenonDims); } union() { // mortices for joining to piece A translate([0, y*0.2, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) morticeBlank(tenonDims, morticeClearance); translate([0, y*0.7, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) morticeBlank(tenonDims, morticeClearance); // mortices for joining to piece vertical pieces translate([x2 - wallThickness + tenonZOffset, y* 0.3, 0]) rotate([90, 0, 90]) morticeBlank(tenonDims, morticeClearance); translate([x2 - wallThickness + tenonZOffset, y* 0.8, 0]) rotate([90, 0, 90]) morticeBlank(tenonDims, morticeClearance); // piece label translate([2, 2, wallThickness - 0.4]) tally(2); } } } module pieceC(cutWeight = true) { difference() { union() { leftSideHorizontalPiece(cutWeight); // tenons for joining to piece D translate([x1, y*0.2, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) tenon(tenonDims); translate([x1, y*0.7, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) tenon(tenonDims); } union() { // mortices for joining to piece A translate([x1*0.2, 0, tenonZOffset]) morticeBlank(tenonDims, morticeClearance); translate([x1*0.7, 0, tenonZOffset]) morticeBlank(tenonDims, morticeClearance); // mortices for joining to piece F/H translate([tenonZOffset, y* 0.3, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([tenonZOffset, y* 0.8, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([drawerWidth + wallThickness + tenonZOffset, y* 0.3, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([drawerWidth + wallThickness + tenonZOffset, y* 0.8, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); // piece label translate([2, 2, wallThickness - 0.4]) tally(3); } } } module pieceD(cutWeight = true) { difference() { rightSideHorizontalPiece(cutWeight); union() { // mortices for joining to piece C translate([0, y*0.2, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) morticeBlank(tenonDims, morticeClearance); translate([0, y*0.7, tenonZOffset]) mirror([0, 1, 0]) rotate([0, 0, -90]) morticeBlank(tenonDims, morticeClearance); // mortices for joining to piece B translate([x2*0.2, 0, tenonZOffset]) morticeBlank(tenonDims, morticeClearance); translate([x2*0.7, 0, tenonZOffset]) morticeBlank(tenonDims, morticeClearance); // mortices for joining to piece F/H translate([x2 - wallThickness + tenonZOffset, y* 0.3, 0]) rotate([90, 0, 90]) morticeBlank(tenonDims, morticeClearance); translate([x2 - wallThickness + tenonZOffset, y* 0.8, 0]) rotate([90, 0, 90]) morticeBlank(tenonDims, morticeClearance); // piece label translate([2, 2, wallThickness - 0.4]) tally(4); } } } module pieceE(cutWeight = true) { difference() { union() { sidePiece(cutWeight); // tenons for joining to horizontal pieces translate([tenonZOffset, y*0.3, 0]) mirror([1, 0, 0]) rotate([-90, 0, 90]) tenon(verticalTenonDims); translate([tenonZOffset, y*0.8, 0]) mirror([1, 0, 0]) rotate([-90, 0, 90]) tenon(verticalTenonDims); // tenon for joining to piece F translate([tenonZOffset, y, drawerHeight/2]) mirror([0, 0, 1]) rotate([0, 90, 00]) tenon(tenonDims); // tenons for joining to piece G translate([tenonZOffset, y*0.3, drawerHeight]) rotate([90, 0, 90]) tenon(throughTenonDims); translate([tenonZOffset, y*0.8, drawerHeight]) rotate([90, 0, 90]) tenon(throughTenonDims); //translate([tenonZOffset, y*0.8, 0]) mirror([1, 0, 0]) // rotate([-90, 0, 90]) tenon(verticalTenonDims); } // piece label translate([0.4, 2, 2]) rotate([90, 0, -90]) tally(5, true); } } module pieceF(cutWeight = true) { difference() { union() { sidePiece(cutWeight); // tenons for joining to horizontal pieces translate([tenonZOffset, y*0.3, 0]) mirror([1, 0, 0]) rotate([-90, 0, 90]) tenon(verticalTenonDims); translate([tenonZOffset, y*0.8, 0]) mirror([1, 0, 0]) rotate([-90, 0, 90]) tenon(verticalTenonDims); // tenons for joining to piece H translate([tenonZOffset, y*0.3, drawerHeight]) rotate([90, 0, 90]) tenon(throughTenonDims); translate([tenonZOffset, y*0.8, drawerHeight]) rotate([90, 0, 90]) tenon(throughTenonDims); } union() { // mortice for joining to piece E translate([tenonZOffset, 0, drawerHeight/2]) mirror([0, 0, 1]) rotate([0, 90, 00]) tenon(tenonDims); // piece label translate([0.4, 2, 2]) rotate([90, 0, -90]) tally(6, true); } } } module pieceG(cutWeight = true) { difference() { union() { sidePiece(cutWeight); // tenons for joining to vertical pieces above translate([tenonZOffset, y*0.3, drawerHeight]) rotate([90, 0, 90]) tenon(verticalTenonDims); translate([tenonZOffset, y*0.8, drawerHeight]) rotate([90, 0, 90]) tenon(verticalTenonDims); // tenon for joining to piece H translate([tenonZOffset, y, drawerHeight/2]) mirror([0, 0, 1]) rotate([0, 90, 00]) tenon(tenonDims); } union() { // mortices for joining to piece E translate([tenonZOffset, y*0.3, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([tenonZOffset, y*0.8, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); // piece label translate([0.4, 2, 2]) rotate([90, 0, -90]) tally(7, true); } } } module pieceH(cutWeight = true) { difference() { union() { sidePiece(cutWeight); // tenons for joining to vertical pieces above translate([tenonZOffset, y*0.3, drawerHeight]) rotate([90, 0, 90]) tenon(verticalTenonDims); translate([tenonZOffset, y*0.8, drawerHeight]) rotate([90, 0, 90]) tenon(verticalTenonDims); } union() { // mortices for joining to piece F translate([tenonZOffset, y*0.3, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); translate([tenonZOffset, y*0.8, 0]) rotate([90, 0, 90]) morticeBlank(verticalTenonDims, morticeClearance); // mortice for joining to piece G translate([tenonZOffset, 0, drawerHeight/2]) mirror([0, 0, 1]) rotate([0, 90, 00]) tenon(tenonDims); // piece label translate([0.4, 2, 2]) rotate([90, 0, -90]) tally(8, true); } } } // Mortice and tenon test pieces // rotate([0, 90, 0]) union() { // cube([10, 16, wallThickness]); // translate([2, 16, tenonZOffset]) tenon(tenonDims); // } // // translate([10, 0, 0]) rotate([0, 90, 0]) difference() { // cube([10, 16, wallThickness]); // translate([2 - 0.1, 16 - 0.1, tenonZOffset - 0.1]) // mirror([0, 1, 0]) morticeBlank(tenonDims, morticeClearance); // } module completeBox(gap = 10, cutWeight = true) { // vertical layers for (i = [0:2]) { z = i * (wallThickness + 1.5*gap + drawerHeight); translate([0, 0, z]) pieceA(cutWeight); translate([x1 + gap, 0, z]) pieceB(cutWeight); translate([0, y + gap, z]) pieceC(cutWeight); translate([x1 + gap, y + gap, z]) pieceD(cutWeight); } // lower box walls for (i = [0:2]) { x = i * (wallThickness + drawerWidth) + (i == 2 ? gap : 0); translate([x, 0, wallThickness + gap]) pieceE(cutWeight); translate([x, y + gap, wallThickness + gap]) pieceF(cutWeight); } // upper box walls for (i = [0:2]) { x = i * (wallThickness + drawerWidth) + (i == 2 ? gap : 0); translate([x, 0, drawerHeight + 2*wallThickness + 2*gap]) pieceG(cutWeight); translate([x, y + gap, drawerHeight + 2*wallThickness + 2*gap]) pieceH(cutWeight); } } module printStack() { for (i = [0:2]) { translate([0, 0, i * (wallThickness + 0.4)]) pieceA(); translate([0, 0, (i+3) * (wallThickness + 0.4)]) pieceC(); translate([0, 0, (i+6) * (wallThickness + 0.4)]) pieceB(); translate([0, 0, (i+9) * (wallThickness + 0.4)]) pieceD(); translate([ 0, y + drawerHeight +throughTenonDims[1] + 4, i * (wallThickness + 0.4) + wallThickness]) rotate([90, 90, 0]) pieceE(); translate([0, y + drawerHeight +throughTenonDims[1] + 4, (i+3) * (wallThickness + 0.4) + wallThickness]) rotate([90, 90, 0]) pieceF(); translate([ 0, y + drawerHeight +throughTenonDims[1] + 4, (i+6) * (wallThickness + 0.4) + wallThickness]) rotate([90, 90, 0]) pieceG(); translate([ 0, y + drawerHeight +throughTenonDims[1] + 4, (i+9) * (wallThickness + 0.4) + wallThickness]) rotate([90, 90, 0]) pieceH(); } } module testStack() { for (i = [0:2]) { translate([0, 0, i * (wallThickness + 0.4)]) pieceA(); translate([0, 0, (i+3) * (wallThickness + 0.4)]) pieceB(); translate([ 0, y + drawerHeight +throughTenonDims[1] + 4, i * (wallThickness + 0.4) + wallThickness]) rotate([90, 90, 0]) pieceE(); translate([0, y + drawerHeight +throughTenonDims[1] + 4, (i+3) * (wallThickness + 0.4) + wallThickness]) rotate([90, 90, 0]) pieceF(); } } completeBox(gap = 10, cutWeight = false); // testStack();