$fn = 64; // --- Parameters --- roll_diameter = 36; roll_length = 65; wall_thickness = 2.5; slot_width = 12; slot_length = 40; cap_overlap = 12; tolerance = 0.2; // --- Calculated --- outer_diameter = roll_diameter + 2 * wall_thickness; body_height = roll_length + wall_thickness; // --- Layout --- translate([-outer_diameter/2 - 5, 0, 0]) dispenser_body(); translate([outer_diameter/2 + 5, 0, 0]) dispenser_cap(); // --- Modules --- module dispenser_body() { difference() { // Main body cylinder(d=outer_diameter, h=body_height); // Inner cavity translate([0, 0, wall_thickness]) cylinder(d=roll_diameter, h=roll_length + 1); // Chamfer inner top edge for easier cap insertion translate([0, 0, body_height - 1]) cylinder(d1=roll_diameter, d2=roll_diameter + 2, h=1.1); // Snap fit groove translate([0, 0, body_height - 4]) rotate_extrude() translate([roll_diameter/2, 0, 0]) circle(d=1.2); // Dispensing slot (front side only) translate([0, 0, wall_thickness + roll_length/2]) hull() { translate([0, outer_diameter/2, slot_length/2 - slot_width/2]) rotate([90, 0, 0]) cylinder(d=slot_width, h=outer_diameter/2 + 1); translate([0, outer_diameter/2, -(slot_length/2 - slot_width/2)]) rotate([90, 0, 0]) cylinder(d=slot_width, h=outer_diameter/2 + 1); } // Finger hole at the bottom to push roll up translate([0, 0, -1]) cylinder(d=15, h=wall_thickness + 2); } } module dispenser_cap() { difference() { union() { // Cap flange and carabiner loop difference() { union() { // Flange cylinder(d=outer_diameter, h=wall_thickness); // Carabiner loop extension translate([0, outer_diameter/2 + 4, 0]) cylinder(d=16, h=wall_thickness); translate([-8, 0, 0]) cube([16, outer_diameter/2 + 4, wall_thickness]); } // Carabiner hole translate([0, outer_diameter/2 + 4, -1]) cylinder(d=8, h=wall_thickness + 2); } // Plug with snap fit translate([0, 0, wall_thickness]) difference() { union() { // Main plug body cylinder(d=roll_diameter - 2*tolerance, h=cap_overlap - 1); // Chamfered tip translate([0, 0, cap_overlap - 1]) cylinder(d1=roll_diameter - 2*tolerance, d2=roll_diameter - 2*tolerance - 2, h=1); } // Hollow interior of plug translate([0, 0, -1]) cylinder(d=roll_diameter - 2*wall_thickness, h=cap_overlap + 2); } // Snap fit ridge translate([0, 0, wall_thickness + 4]) rotate_extrude() translate([(roll_diameter - 2*tolerance)/2, 0, 0]) circle(d=1.2); } // Flex slits to allow the plug to compress during insertion translate([0, 0, wall_thickness + 1 + cap_overlap/2]) { cube([roll_diameter + 2, 1.5, cap_overlap], center=true); cube([1.5, roll_diameter + 2, cap_overlap], center=true); } } }