$fn = 64; // --- Parametric Dimensions --- rack_width = 180; // Total width of the rack rack_depth = 75; // How far the shelf extends from the wall thickness = 5; // General material thickness backplate_height = 45; // Height of the wall-mounting plate num_slots = 6; // Number of brush slots slot_length = 55; // Depth of the slot slot_front_width = 14; // Width at the opening (fits thicker handles) slot_back_width = 6; // Width at the back (wedges thinner handles) // Wall rail mount (ArtArsenal / French Cleat style) cleat_depth = 6; // Depth of the rail hook cleat_height = 15; // Drop height of the hook cleat_thickness = 4; // Thickness of the hook material module brush_drying_rack() { union() { // 1. Backplate cube([rack_width, thickness, backplate_height]); // 2. Shelf with slots difference() { // Solid shelf (overlaps backplate slightly for manifold geometry) translate([0, thickness - 0.1, backplate_height - thickness]) cube([rack_width, rack_depth + 0.1, thickness]); // Cut tapered slots for(i = [0 : num_slots - 1]) { spacing = rack_width / num_slots; x_pos = (spacing / 2) + i * spacing; translate([x_pos, thickness + rack_depth, backplate_height - thickness]) v_slot(); } } // 3. Gussets (Under-shelf supports) translate([0, thickness, 0]) gusset(); translate([rack_width / 2 - thickness / 2, thickness, 0]) gusset(); translate([rack_width - thickness, thickness, 0]) gusset(); // 4. Wall Rail Mount (Top Cleat Hook) translate([0, 0, 0]) rotate([90, 0, 90]) linear_extrude(rack_width) polygon([ [0.1, backplate_height], // Overlap into backplate [-cleat_depth - cleat_thickness, backplate_height], [-cleat_depth - cleat_thickness, backplate_height - cleat_height], [-cleat_depth, backplate_height - cleat_height], [0.1, backplate_height - cleat_height + cleat_depth] // 45-degree engagement ]); // 5. Bottom Standoff (Keeps rack plumb against the wall) translate([0, -cleat_depth - cleat_thickness, 0]) cube([rack_width, cleat_depth + cleat_thickness + 0.1, thickness]); } } module v_slot() { // Hull creates the tapered V-shape for wedging brush handles hull() { translate([0, 0, -1]) cylinder(d=slot_front_width, h=thickness + 2); translate([0, -slot_length, -1]) cylinder(d=slot_back_width, h=thickness + 2); } // Ensure the front opening is completely clear translate([-slot_front_width / 2, 0, -1]) cube([slot_front_width, 10, thickness + 2]); } module gusset() { // Triangular support profile extruded and rotated into place rotate([90, 0, 90]) linear_extrude(thickness) polygon([ [-0.1, -0.1], // Bottom corner (overlaps backplate) [rack_depth - 10, backplate_height - thickness + 0.1], // Front top corner [-0.1, backplate_height - thickness + 0.1] // Back top corner ]); } // Generate the final model // Note: For optimal support-free 3D printing, rotate the model 90 degrees // on the X or Y axis in your slicer so it prints on its side. brush_drying_rack();