From 98e2abd0e068cede2dbca4d1b8608f0e075cbbaf Mon Sep 17 00:00:00 2001 From: Cory Locklear Date: Thu, 11 Jun 2026 21:03:37 -0400 Subject: [PATCH] Add parametric Ring solar siding adapter model. Track OpenSCAD sources and project notes; ignore generated STL exports. --- .gitignore | 1 + AGENTS.md | 3 + ring-solar-siding-adapter.backup.scad | 136 +++++++++++++++++++ ring-solar-siding-adapter.scad | 185 ++++++++++++++++++++++++++ 4 files changed, 325 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 ring-solar-siding-adapter.backup.scad create mode 100644 ring-solar-siding-adapter.scad diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1567411 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.stl diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..683d920 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +## Printer + +**Bambu Lab P2S** — see `.cursor/skills/openscad-bosl2/references/bambu-p2s.md` for specs (general printer reference: `.cursor/skills/openscad-bosl2/references/printers.md`). diff --git a/ring-solar-siding-adapter.backup.scad b/ring-solar-siding-adapter.backup.scad new file mode 100644 index 0000000..6fc71d4 --- /dev/null +++ b/ring-solar-siding-adapter.backup.scad @@ -0,0 +1,136 @@ +// Ring Solar Charger — Vinyl Siding Adapter Plate +// BACKUP: flat upper rear + slow lower taper, no bead coping groove yet. +// Units: millimeters. Vanilla OpenSCAD only. + +/* [Plate] */ +plate_width = 93.1; +plate_height = 171.45; +min_wall_thickness = 3.0; +edge_round = 0; + +/* [Siding Profile] */ +siding_projection = 27.0; +face_inset_depth = 8.0; +flat_upper_ratio = 0.55; +course_pitch = 165.1; +profile_segments = 8; +profile_mode = "curved"; +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"; +profile_strip_width = 20; + +/* [Hidden] */ +$fn = 64; +epsilon = 0.01; + +function projection_z() = siding_projection - rear_clearance; + +function face_inset_z() = projection_z() - face_inset_depth; + +function face_y_flat_end() = plate_height * (1 - flat_upper_ratio); + +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 = projection_z(), + z_top = face_inset_z() + ) + z_top + (z_bottom - z_top) * (1 - ease); + +function stepped_rear_z(y) = + y >= face_y_flat_end() ? face_inset_z() : projection_z(); + +function rear_z_at(y) = + y >= face_y_flat_end() ? face_inset_z() + : profile_mode == "stepped" ? stepped_rear_z(y) + : lower_curve_z(y); + +function rear_profile_points() = + let(step = plate_height / max(profile_segments * 12, 24)) + [for (y = [0 : step : plate_height]) + [y, rear_z_at(y)]]; + +function max_rear_z() = + let(pts = rear_profile_points()) + max([for (p = pts) p[1]]); + +function front_z() = max_rear_z() + 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] + ]; + +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); + } +} + +module adapter_plate() { + width = part_mode == "profile_strip" ? profile_strip_width : plate_width; + if (part_mode == "full") { + difference() { + rounded_body(width); + mount_holes(); + } + } else { + rounded_body(width); + } +} + +adapter_plate(); diff --git a/ring-solar-siding-adapter.scad b/ring-solar-siding-adapter.scad new file mode 100644 index 0000000..1f81f21 --- /dev/null +++ b/ring-solar-siding-adapter.scad @@ -0,0 +1,185 @@ +// 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) +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] +profile_strip_width = 20; + +/* [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; + +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() = + face_inset_z() + min_wall_thickness * bottom_taper_depth_ratio; + +// Slow taper from bottom_rear_z at the plate bottom up to the flat 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 = face_inset_z() + ) + z_top + (z_bottom - z_top) * (1 - ease); + +function stepped_rear_z(y) = + y >= face_y_flat_end() ? face_inset_z() : bottom_rear_z(); + +// Concave bead coping on the rear face near the plate top. +// Flat chord at face_inset_z; arc scoops into the plate (toward the front). +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) : shelf; + +// 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]]); + +function front_z() = max_rear_z() + 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); + } +} + +module adapter_plate() { + width = part_mode == "profile_strip" ? profile_strip_width : plate_width; + if (part_mode == "full") { + difference() { + rounded_body(width); + mount_holes(); + } + } else { + rounded_body(width); + } +} + +adapter_plate();