// Shower Phone Holder // Hangs from an aluminum shower rail via two hooks at the top, // with a pocket/tray at the bottom to hold a phone. // // Print suggestions: // Print orientation: flat on one side. // Hook will need support on the side that is not flat on the bed. // Depending on the back plate design you may need supports as well. // // Coordinate system: // Origin (0,0,0) = bottom-left corner of back plate, rear face. // X = left to right (width) // Y = bottom to top (height) // Z = back to front — hooks go -Z, pocket goes +Z /* [Phone] */ phone_width = 165; // mm (landscape) phone_height = 80; // mm (landscape) phone_thickness = 12; // mm (with case) /* [Rail] */ rail_width = 25; // mm (measure your rail!) rail_thickness = 10; // mm (depth of the rail) /* [Tolerances] */ rail_tolerance = 1.0; // mm clearance around rail phone_tolerance = 2.0; // mm clearance around phone /* [Structure] */ wall = 3; // mm general wall thickness back_thickness = 3; // mm back plate thickness /* [Hook] */ hook_width = 25; // mm width of each hook hook_drop = 15; // mm how far back leg drops behind rail /* [Pocket] */ pocket_depth = 25; // mm how far pocket extends forward pocket_lip = 15; // mm front wall height pocket_floor = 3; // mm floor thickness /* [Drain Holes] */ drain_radius_top = 3; // mm radius at pocket interior (wide end) drain_radius_bottom = 1.5; // mm radius at pocket exterior (narrow end) drain_count = 5; // number of drain holes across pocket width /* [Corners] */ corner_radius = 5; // mm radius for rounded corners corner_fn = 40; // segments per rounded arc /* [Back Plate Style] */ back_style = "open"; // "solid", "hex", or "open" (open = single hole with diagonal brace) hex_radius = 5; // mm circumradius of each hexagon (hex style only) hex_wall = 2; // mm wall thickness between adjacent hexagons (hex style only) hex_margin = 5; // mm inset from plate edges / features /* [Open Style] */ open_border_left = 10; // mm left border width (open style only) open_border_right = 10; // mm right border width (open style only) open_diagonal_width = 10; // mm width of diagonal brace (open style only) // --- Derived dimensions --- plate_width = phone_width + phone_tolerance + 2 * wall; hook_rise = rail_thickness + rail_tolerance + wall; // height hooks need above phone area plate_height = pocket_lip + phone_height + hook_rise; module round_convex(r) { offset(r = r, $fn = corner_fn) offset(delta = -r) children(); } module center_opening(w, h, brace_w) { diag_len = sqrt(w * w + h * h); diag_angle = atan2(h, w); difference() { square([w, h]); // Diagonal brace from bottom-left to top-right (solid) translate([w / 2, h / 2]) rotate([0, 0, diag_angle]) translate([-diag_len / 2, -brace_w / 2]) square([diag_len, brace_w]); } } module hex_grid(width, height) { pitch_x = hex_radius * sqrt(3) + hex_wall; pitch_y = pitch_x * sqrt(3) / 2; cols = floor(width / pitch_x); rows = floor(height / pitch_y); for (row = [0 : rows]) { x_offset = (row % 2 == 0) ? 0 : pitch_x / 2; for (col = [0 : cols]) { x = col * pitch_x + x_offset; y = row * pitch_y; if (x >= 0 && x <= width && y >= 0 && y <= height) translate([x, y, -1]) linear_extrude(back_thickness + 2) rotate([0, 0, 30]) circle(r = hex_radius, $fn = 6); } } } module back_plate() { difference() { linear_extrude(back_thickness) intersection() { square([plate_width, plate_height]); round_convex(corner_radius) square([plate_width, plate_height + corner_radius]); } if (back_style == "hex") { safe_x = hex_margin; safe_y = pocket_lip + hex_margin; safe_w = plate_width - 2 * hex_margin; hex_pitch_y = (hex_radius * sqrt(3) + hex_wall) * sqrt(3) / 2; safe_h = plate_height - pocket_lip - 2 * hex_margin - hex_pitch_y; translate([safe_x, safe_y, 0]) hex_grid(safe_w, safe_h); } if (back_style == "open") { safe_x = open_border_left; safe_y = pocket_lip + hex_margin; safe_w = plate_width - open_border_left - open_border_right; hex_pitch_y = (hex_radius * sqrt(3) + hex_wall) * sqrt(3) / 2; safe_h = plate_height - pocket_lip - 2 * hex_margin - hex_pitch_y; translate([safe_x, safe_y, -1]) linear_extrude(back_thickness + 2) center_opening(safe_w, safe_h, open_diagonal_width); } } } // Single hook — L-shaped profile extruded along hook width. // Outer convex corners are rounded; the inner elbow stays sharp (concave). module hook() { rail_slot_width = rail_width + rail_tolerance; bridge_depth = rail_slot_width + wall; // Clamp radius so it fits within the wall thickness hook_r = min(corner_radius, wall / 2 - 0.1); // L-profile in a depth (X) / height (Y) coordinate frame. // Negative X = toward plate; positive X = away from plate. // The profile extends past the plate face by hook_r so the // junction is seamless (extra material hides inside the plate union). profile = [ [-hook_r, 0], // plate junction (extended) [-hook_r, wall], // plate junction top [bridge_depth, wall], // outer top of bridge [bridge_depth, -hook_drop], // outer bottom of leg [bridge_depth - wall, -hook_drop], // inner bottom of leg [bridge_depth - wall, 0] // inner elbow (concave, stays sharp) ]; // Extrude along X (hook_width), then rotate so: // polygon X (depth) → -Z, polygon Y (height) → Y, extrude Z → X rotate([0, 90, 0]) linear_extrude(hook_width) round_convex(hook_r) polygon(profile); } module drain_holes() { inner_width = plate_width - 2 * wall; spacing = inner_width / (drain_count + 1); inner_depth = pocket_depth - wall; z_row1 = inner_depth / 3; z_row2 = inner_depth * 2 / 3; // Front row for (i = [1 : drain_count]) { translate([wall + i * spacing, pocket_floor + 1, z_row1]) rotate([90, 0, 0]) cylinder(h = pocket_floor + 2, r1 = drain_radius_top, r2 = drain_radius_bottom, $fn = 24); } // Back row — offset by half spacing for staggered coverage for (i = [0 : drain_count]) { x = wall + i * spacing + spacing / 2; if (x > wall + drain_radius_top && x < plate_width - wall - drain_radius_top) translate([x, pocket_floor + 1, z_row2]) rotate([90, 0, 0]) cylinder(h = pocket_floor + 2, r1 = drain_radius_top, r2 = drain_radius_bottom, $fn = 24); } } module pocket() { difference() { // Outer solid — bottom corners rounded, top edge straight linear_extrude(pocket_depth) intersection() { square([plate_width, pocket_lip]); round_convex(corner_radius) square([plate_width, pocket_lip + corner_radius]); } // Inner cutout — bottom corners rounded, open top and back (back plate provides the back wall) translate([wall, pocket_floor, -1]) linear_extrude(pocket_depth - wall + 1) intersection() { square([plate_width - 2 * wall, pocket_lip - pocket_floor + 1]); round_convex(corner_radius) square([plate_width - 2 * wall, pocket_lip - pocket_floor + 1 + corner_radius]); } // Conical drainage holes through pocket floor drain_holes(); } } module holder() { // Back plate at origin back_plate(); // Pocket on the front face of the plate, at the bottom translate([0, 0, back_thickness]) pocket(); // Two hooks at the top of the plate, flush with sides hook_y = plate_height - wall; // align hook bridge with plate top translate([0, hook_y, 0]) hook(); translate([plate_width - hook_width, hook_y, 0]) hook(); } holder();