$fn = 64; // --- PARAMETERS --- roll_width = 210; // Width of the paper roll (210mm = standard A4 width, adjust as needed) roll_max_diameter = 120; // Maximum diameter of the full paper roll core_diameter = 20; // Inner diameter of the cardboard core wall_thickness = 5; // Structural wall thickness // --- DERIVED DIMENSIONS --- base_w = roll_width + wall_thickness * 2; base_h = roll_max_diameter / 2 + 40; arm_ext = roll_max_diameter / 2 + 20; spindle_r = core_diameter / 2 - 1; // --- MAIN DISPENSER BODY --- union() { // 1. Backplate cube([base_w, wall_thickness, base_h]); // 2. ArtArsenal / French Cleat Wall Mount (Back) // Top Cleat Spacer translate([0, -10, base_h - 20]) cube([base_w, 10, 20]); // Top Cleat Wedge (downward angle for rail locking) hull() { translate([0, -10, base_h - 20 - 0.1]) cube([base_w, 10, 0.1]); translate([0, -0.1, base_h - 35]) cube([base_w, 0.1, 0.1]); } // Bottom Standoff / Plumb Bumper translate([0, -10, 0]) cube([base_w, 10, 10]); // 3. Left Arm translate([0, wall_thickness, 0]) difference() { hull() { cube([wall_thickness, 0.1, base_h]); translate([0, arm_ext, 0]) cube([wall_thickness, 0.1, 15]); translate([0, arm_ext - 15, base_h - 15]) rotate([0, 90, 0]) cylinder(h=wall_thickness, r=15); } // Spindle drop-in slot (leaves 2mm outer wall) translate([2, arm_ext - 15, base_h - 15]) { rotate([0, 90, 0]) cylinder(h=wall_thickness, r=spindle_r+1); translate([0, -(spindle_r+1), 0]) cube([wall_thickness, (spindle_r+1)*2, 20]); } } // 4. Right Arm translate([base_w - wall_thickness, wall_thickness, 0]) difference() { hull() { cube([wall_thickness, 0.1, base_h]); translate([0, arm_ext, 0]) cube([wall_thickness, 0.1, 15]); translate([0, arm_ext - 15, base_h - 15]) rotate([0, 90, 0]) cylinder(h=wall_thickness, r=15); } // Spindle drop-in slot (leaves 2mm outer wall) translate([-1, arm_ext - 15, base_h - 15]) { rotate([0, 90, 0]) cylinder(h=wall_thickness - 1, r=spindle_r+1); translate([0, -(spindle_r+1), 0]) cube([wall_thickness - 1, (spindle_r+1)*2, 20]); } } // 5. Integrated Cutter Bar (Bottom Front) // Overlaps slightly with arms to ensure manifold geometry translate([wall_thickness - 0.1, arm_ext + wall_thickness - 10, 0]) hull() { cube([roll_width + 0.2, 10, wall_thickness]); // Angled tear edge pointing up and forward translate([0, 9.5, wall_thickness]) cube([roll_width + 0.2, 0.5, 4]); } } // --- SPINDLE --- // Printed vertically next to the main body for optimal strength translate([base_w + 20, 20, 0]) union() { // Main shaft (sized to fit between the 2mm outer slot walls with play) cylinder(h=roll_width + 2, r=spindle_r); // Slight chamfer at the bottom for easier printing cylinder(h=1, r1=spindle_r - 1, r2=spindle_r); }