// Ring Solar Charger — Vinyl Siding Adapter Plate // Mounts the integrated Ring Solar Charger on horizontal beaded vinyl lap siding. // Units: millimeters. Vanilla OpenSCAD only. // // Rear profile (y=0 bottom, y=plate_height top): // TOP — bead coping cavity scooped into rear face (concave semicircle) // MID — flat rear shelf continues downward // BOTTOM — slow taper outward toward siding projection // // Backup (flat + taper only): ring-solar-siding-adapter.backup.scad /* [Plate] */ plate_width = 93.1; plate_height = 171.45; min_wall_thickness = 5.0; edge_round = 0; /* [Siding Profile] */ siding_projection = 27.0; face_inset_depth = 8.0; bead_coping_radius = 10.00; // Semicircle radius for bead groove (measured on siding) bead_coping_offset_top = 11.00; // Distance from plate top to groove center (tune on wall) flat_upper_ratio = 0.55; // Fraction of plate height that stays flat at the top bottom_taper_depth_ratio = 1.6; // Rear depth at plate bottom = min_wall_thickness * this (outward from flat shelf) rear_extension = 7.5; // Extra depth toward wall; front face + bead groove stay fixed vs front/top course_pitch = 165.1; profile_segments = 8; profile_mode = "curved"; // [curved, stepped] rear_clearance = 0.2; /* [Mounting Holes] */ mount_hole_offset_top = 50.0; mount_hole_offset_bottom = 44.7; mount_hole_dia = 4.5; mount_hole_x = 0; /* [Export] */ part_mode = "full"; // [full, profile_strip, front_slice] profile_strip_width = 20; front_slice_depth = 3.5; // [2:0.5:8] /* [Hidden] */ $fn = 64; epsilon = 0.01; // --------------------------------------------------------------------------- // Siding rear-profile geometry // OpenSCAD 2D polygon uses [plate_Y, plate_Z]; linear_extrude() goes along +Z (width). // OpenSCAD 3D: X = plate height (y), Y = depth (z), Z = plate width. // Lower depth Y = toward wall (back). Higher depth Y = toward Ring (front). // --------------------------------------------------------------------------- function projection_z() = siding_projection - rear_clearance; function face_inset_z() = projection_z() - face_inset_depth; // Flat rear shelf shifted toward the wall; bead groove keeps absolute depth at face_inset_z. function flat_rear_z() = face_inset_z() - rear_extension; function bead_coping_y_center() = plate_height - bead_coping_offset_top; // Bottom of flat rear shelf (flat_upper_ratio measured from plate top downward). function face_y_flat_end() = plate_height * (1 - flat_upper_ratio); // Rear depth where the bottom taper ends (outward from flat shelf, in wall-thickness units). function bottom_rear_z() = flat_rear_z() + min_wall_thickness * bottom_taper_depth_ratio; // Slow taper from bottom_rear_z at the plate bottom up to the flat rear shelf. function lower_curve_z(y) = let( y1 = face_y_flat_end(), t = y / max(y1, epsilon), ease = pow(sin(t * 90), 1.4), z_bottom = bottom_rear_z(), z_top = flat_rear_z() ) z_top + (z_bottom - z_top) * (1 - ease); function stepped_rear_z(y) = y >= face_y_flat_end() ? flat_rear_z() : bottom_rear_z(); // Bead coping: fixed depth vs front/top. Flat chord at face_inset_z; arc scoops into plate. function bead_coping_rear_z(y) = let( r = bead_coping_radius, shelf = face_inset_z(), dy = y - bead_coping_y_center(), disc = r * r - dy * dy ) disc >= 0 ? shelf + sqrt(disc) : flat_rear_z(); // Flat top shelf + bead cavity + lower taper. function rear_z_at(y) = y >= face_y_flat_end() ? bead_coping_rear_z(y) : profile_mode == "stepped" ? stepped_rear_z(y) : lower_curve_z(y); function rear_profile_points() = let( step = plate_height / max(profile_segments * 12, 24), yc = bead_coping_y_center(), r = bead_coping_radius, arc_step = r / 16 ) concat( [for (y = [0 : step : max(yc - r - epsilon, 0)]) [y, rear_z_at(y)]], [for (y = [yc - r : arc_step : yc + r]) [y, rear_z_at(y)]], [for (y = [min(yc + r + step, plate_height) : step : plate_height]) [y, rear_z_at(y)]] ); function max_rear_z() = let(pts = rear_profile_points()) max([for (p = pts) p[1]]); // Front face anchored to bead groove + min wall (independent of rear_extension). function front_z() = face_inset_z() + bead_coping_radius + min_wall_thickness; function rear_y_min() = min([for (p = rear_profile_points()) p[1]]); function minkowski_active() = edge_round > 0 && min_wall_thickness >= 2 * edge_round + 1; function hole_y_min() = rear_y_min() - (minkowski_active() ? edge_round : 0) - epsilon; function hole_y_max() = front_z() + (minkowski_active() ? edge_round : 0) + epsilon; function cross_section_polygon() = let( rear_pts = rear_profile_points(), fz = front_z() ) [ for (p = rear_pts) p, [plate_height, fz], [0, fz] ]; // --------------------------------------------------------------------------- // Modules // --------------------------------------------------------------------------- module cross_section_2d() { polygon(cross_section_polygon()); } module adapter_body(width) { linear_extrude(width, center = true) cross_section_2d(); } module mount_holes() { y_min = hole_y_min(); y_max = hole_y_max(); hole_depth = y_max - y_min; center_y = (y_min + y_max) / 2; for (y_pos = [plate_height - mount_hole_offset_top, mount_hole_offset_bottom]) { translate([y_pos, center_y, mount_hole_x]) rotate([90, 0, 0]) cylinder(h = hole_depth, d = mount_hole_dia, center = true); } } module rounded_body(width) { if (minkowski_active()) { minkowski() { adapter_body(max(width - 2 * edge_round, 1)); sphere(r = edge_round); } } else { adapter_body(width); } } // Keep only the front face cap for fit-checking width, height, and hole placement. module front_slice_mask(width) { fz = front_z(); translate([plate_height / 2, fz - front_slice_depth / 2, 0]) cube( [plate_height + 2 * epsilon, front_slice_depth + 2 * epsilon, width + 2 * epsilon], center = true ); } module adapter_plate() { width = part_mode == "profile_strip" ? profile_strip_width : plate_width; if (part_mode == "full") { difference() { rounded_body(width); mount_holes(); } } else if (part_mode == "front_slice") { intersection() { difference() { rounded_body(width); mount_holes(); } front_slice_mask(width); } } else { rounded_body(width); } } adapter_plate();