$fn = 64; // --- Parameters --- // Brush Organization num_slots = 12; // Number of brush slots hole_dia = 12; // Diameter of the brush holes slot_opening = 9; // Width of the front slot opening (smaller than hole_dia for a snap-fit) hole_spacing = 16; // Distance between hole centers margin = 16; // Space on the left and right edges // Main Body Dimensions body_depth = 35; // Depth of the holder body_height = 20; // Height/thickness of the holder body_width = (num_slots - 1) * hole_spacing + 2 * margin; // Dovetail Mount (ArtArsenal / standard wall rail) dovetail_depth = 5; // How far the dovetail sticks out the back dovetail_width_top = 18; // Widest part of the dovetail (furthest from body) dovetail_width_base = 12; // Narrowest part of the dovetail (at the body) // --- Calculated Variables --- d_center = body_height / 2; z_top = d_center + dovetail_width_base / 2; z_bot = d_center - dovetail_width_base / 2; tip_top = d_center + dovetail_width_top / 2; tip_bot = d_center - dovetail_width_top / 2; // --- Main Geometry --- difference() { // Main Body with Dovetail Profile // Using multmatrix to map 2D [Depth, Height] extrusion to 3D [Width, Depth, Height] multmatrix([ [0, 0, 1, 0], // Map Extrusion Z to Final X (Width) [1, 0, 0, 0], // Map Polygon X to Final Y (Depth) [0, 1, 0, 0], // Map Polygon Y to Final Z (Height) [0, 0, 0, 1] ]) linear_extrude(height = body_width) polygon([ [0, 0], // Bottom-back of body [body_depth, 0], // Bottom-front of body [body_depth, body_height], // Top-front of body [0, body_height], // Top-back of body [0, z_top], // Dovetail start top [-dovetail_depth, tip_top], // Dovetail outer top [-dovetail_depth, tip_bot], // Dovetail outer bottom [0, z_bot] // Dovetail start bottom ]); // Brush Slots (Holes + Front Openings) for (i = [0 : num_slots - 1]) { x_pos = margin + i * hole_spacing; y_pos = body_depth * 0.55; // Positioned slightly forward of center // Cylindrical Hole translate([x_pos, y_pos, -1]) cylinder(h = body_height + 2, d = hole_dia); // Front Slot Cutout translate([x_pos - slot_opening / 2, y_pos, -1]) cube([slot_opening, body_depth, body_height + 2]); } }