Files
3d-models/kallax-organization/quarter-drawers-pla-construction.scad
T
2025-12-26 13:24:46 -06:00

526 lines
16 KiB
OpenSCAD

// 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();