$fn=64; // --- Parameters --- jar_diameter = 55; // Inner diameter of the treat jar cup jar_depth = 40; // Inner depth of the cup wall_thickness = 3; // Thickness of the walls rail_thickness = 6; // Thickness of the rail to clip over hook_drop = 25; // Downward length of the attachment hook clip_width = 30; // Width of the hook retaining_lip = 1.5; // Size of the snap-fit lip on the hook // --- Calculated Variables --- cup_outer_d = jar_diameter + wall_thickness * 2; cup_outer_h = jar_depth + wall_thickness; mount_y = cup_outer_d / 2 - 10; hook_outer_y = cup_outer_d / 2 + rail_thickness + wall_thickness; hook_length = hook_outer_y - mount_y; // --- Module --- module treat_jar_holder() { difference() { union() { // Main Cup Outer Shell cylinder(d=cup_outer_d, h=cup_outer_h); // Rail Attachment translate([-clip_width/2, 0, 0]) { // Top overhang connecting cup to hook translate([0, mount_y, cup_outer_h - wall_thickness]) cube([clip_width, hook_length, wall_thickness]); // Solid mount block merging into the cup cylinder translate([0, mount_y, cup_outer_h - hook_drop]) cube([clip_width, 10, hook_drop]); // Downward hook translate([0, cup_outer_d/2 + rail_thickness, cup_outer_h - hook_drop]) cube([clip_width, wall_thickness, hook_drop]); // Retaining lip for secure rail attachment translate([0, cup_outer_d/2 + rail_thickness - retaining_lip, cup_outer_h - hook_drop]) cube([clip_width, retaining_lip, wall_thickness]); } } // Hollow out the cup (also clears out overlapping attachment geometry) translate([0, 0, wall_thickness]) cylinder(d=jar_diameter, h=jar_depth + 1); } } // --- Render --- treat_jar_holder();