// ============================================================
// saltytank_skid_plate.scad — SaltyTank Underside Skid Plate
// Issue: #121  Agent: sl-mechanical  Date: 2026-03-01
// ============================================================
//
// Bolt-on underside protection panel for rough terrain.
// Mounts to the underside of the deck plate, covering the hull
// floor between the two track frames.
//
// Purpose:
//   • Protects electronics bay wiring + deck underside from rock strikes
//   • Sacrificial layer — replace without reworking deck plate
//   • Provides a smooth low-drag hull bottom for terrain sliding
//   • Four drain / inspection slots (water / mud egress)
//
// Material options:
//   Primary    : 4 mm HDPE (high-density polyethylene)
//                — lightweight, impact resistant, low friction surface
//   Alternative: 2 mm 304 stainless steel
//                — heavier (+0.5 kg), extremely wear resistant
//   Prototype  : 6 mm plywood (quick, cheap; not weatherproof)
//
// Dimensions: BODY_W × BODY_L × SKID_T
//   = 360 × 500 × 4 mm  (fits between track inner faces)
//
// Mounting:
//   8× M4 SHCS from below; nuts captured in deck plate M4 rivet-nuts
//   (see saltytank_chassis.scad skid plate attachment holes)
//   Countersunk (FHCS) variant available — set COUNTERSINK = true
//
// Coordinate: Z=0 at skid top face (= deck plate underside)
//   Ground contact surface at Z = -(FRAME_H) = -90 mm (in deck coords)
//   Skid sits immediately below deck: deck_bottom → skid_top
//
// RENDER options:
//   "skid_stl"    STL for 3D printing (4 mm PETG, prototype)
//   "skid_2d"     DXF for waterjet / CNC (HDPE or steel plate)
//   "assembly"    preview with deck ghost
//
// Export commands:
//   DXF (HDPE / steel — recommended):
//     openscad saltytank_skid_plate.scad -D 'RENDER="skid_2d"' -o saltytank_skid.dxf
//   STL (3D print prototype):
//     openscad saltytank_skid_plate.scad -D 'RENDER="skid_stl"' -o saltytank_skid.stl
// ============================================================

$fn  = 64;
e    = 0.01;

// ── Skid plate dimensions ────────────────────────────────────────────────────
// Must match saltytank_chassis.scad BODY_W and BODY_L exactly.
BODY_L    = 500.0;   // fore-aft — copy from saltytank_chassis.scad
BODY_W    = 360.0;   // left-right
SKID_T    =   4.0;   // skid plate thickness (4 mm HDPE)
SKID_R    =  12.0;   // corner radius (matches deck corners, ease of cleaning)

FRAME_H   =  90.0;   // side frame height (from saltytank_chassis.scad)

// ── Skid reinforcement ribs (under-surface, printed variant only) ─────────────
// Ribs add stiffness to the printed skid without adding flat-surface area.
// Ribs are omitted on the CNC/laser version (material stiffness is sufficient).
RIB_H      =  8.0;    // rib height below skid top face
RIB_T      =  3.0;    // rib wall thickness
RIB_PRINT  = false;   // set true to add ribs (print only; skip for DXF)

// ── Attachment bolt holes (M4) ───────────────────────────────────────────────
// Pattern must match saltytank_chassis.scad skid plate holes exactly.
// 2 rows × 3 columns = 6 holes + 2 corners = 8 total
BOLT_D     =  4.3;    // M4 clearance
BOLT_INSET_X =  20.0; // CL inset from left/right edge
BOLT_INSET_Y =  25.0; // CL inset from front/rear edge

// Countersinking (flat-head bolts flush with skid surface — preferred for terrain)
COUNTERSINK = true;
CSINK_ANGLE = 82;     // M4 FHCS countersink angle (82° for DIN 7991)
CSINK_OD    =  8.0;   // M4 FHCS head diameter

// ── Drain / inspection slots ─────────────────────────────────────────────────
DRAIN_W    =  20.0;   // slot width
DRAIN_L    =  60.0;   // slot length
DRAIN_R    =  10.0;   // slot corner radius

// ── Road-wheel axle pass-through slots ───────────────────────────────────────
// The road wheel axles sit OUTSIDE the skid plate (they're in the side frames),
// so no axle holes are needed in the skid plate.
// The skid plate is a solid panel between the track frames.

// ── Fasteners ─────────────────────────────────────────────────────────────────
M4_D = 4.3;

// ============================================================
// RENDER DISPATCH
// ============================================================
RENDER = "assembly";

if (RENDER == "skid_stl") {
    skid_plate();
} else if (RENDER == "skid_2d") {
    projection(cut = true)
        translate([0, 0, -SKID_T / 2])
            skid_plate_flat_only();
} else if (RENDER == "assembly") {
    assembly();
}

// ============================================================
// ASSEMBLY PREVIEW
// ============================================================
module assembly() {
    color("Sienna",  0.85) skid_plate();

    // Ghost deck plate above
    %color("Silver", 0.25)
        translate([0, 0, SKID_T])
            linear_extrude(8)
                minkowski() {
                    square([BODY_L - 24, BODY_W - 24], center = true);
                    circle(r = 12);
                }

    // Ghost side frames (simplified)
    for (sx = [-1, 1])
        %color("SteelBlue", 0.20)
            translate([sx * (BODY_W/2 + 3), 0, SKID_T])
                cube([6, BODY_L, FRAME_H + 8], center = true);
}

// ============================================================
// SKID PLATE (3D print — includes optional ribs)
// ============================================================
module skid_plate() {
    difference() {
        union() {
            // ── Base plate ─────────────────────────────────────────────
            linear_extrude(SKID_T)
                skid_profile_2d();

            // ── Reinforcement ribs (printed version only) ──────────────
            if (RIB_PRINT) {
                // Longitudinal centre rib
                translate([0, 0, SKID_T])
                    cube([RIB_T, BODY_L - 40, RIB_H], center = true);
                // Lateral ribs (3× fore-aft)
                for (ry = [-BODY_L/4, 0, BODY_L/4])
                    translate([0, ry, SKID_T])
                        cube([BODY_W - 40, RIB_T, RIB_H], center = true);
            }
        }

        // ── Attachment bolt holes ──────────────────────────────────────
        for (bpos = bolt_positions())
            translate([bpos[0], bpos[1], -e]) {
                cylinder(d = BOLT_D, h = SKID_T + 2*e);
                if (COUNTERSINK)
                    // Countersink cone from top face
                    translate([0, 0, SKID_T + e])
                        cylinder(d1 = CSINK_OD, d2 = BOLT_D,
                                 h = (CSINK_OD - BOLT_D) / (2 * tan(CSINK_ANGLE/2)));
            }

        // ── Drain / inspection slots (4×, one per quadrant) ───────────
        for (sx = [-1, 1])
        for (sy = [-1, 1]) {
            dx = sx * (BODY_W/4 + 15);
            dy = sy * (BODY_L/4 + 20);
            translate([dx - DRAIN_W/2 + DRAIN_R, dy - DRAIN_L/2 + DRAIN_R, -e])
                linear_extrude(SKID_T + 2*e)
                    minkowski() {
                        square([DRAIN_W - 2*DRAIN_R, DRAIN_L - 2*DRAIN_R]);
                        circle(r = DRAIN_R);
                    }
        }
    }
}

// 2D profile of skid plate (shared by skid_plate and DXF export)
module skid_profile_2d() {
    minkowski() {
        square([BODY_L - 2*SKID_R, BODY_W - 2*SKID_R], center = true);
        circle(r = SKID_R);
    }
}

// Flat-only version for DXF projection (no ribs)
module skid_plate_flat_only() {
    difference() {
        linear_extrude(SKID_T) skid_profile_2d();
        for (bpos = bolt_positions())
            translate([bpos[0], bpos[1], -e])
                cylinder(d = BOLT_D, h = SKID_T + 2*e);
        for (sx = [-1, 1])
        for (sy = [-1, 1]) {
            dx = sx * (BODY_W/4 + 15);
            dy = sy * (BODY_L/4 + 20);
            translate([dx - DRAIN_W/2 + DRAIN_R, dy - DRAIN_L/2 + DRAIN_R, -e])
                linear_extrude(SKID_T + 2*e)
                    minkowski() {
                        square([DRAIN_W - 2*DRAIN_R, DRAIN_L - 2*DRAIN_R]);
                        circle(r = DRAIN_R);
                    }
        }
    }
}

// ── Bolt position list (8 positions matching saltytank_chassis.scad) ──────────
function bolt_positions() = [
    // From saltytank_chassis.scad skid plate holes (sx × (BODY_W/2 - 20), sy × BODY_L/3)
    [-BODY_W/2 + BOLT_INSET_X,  -BODY_L/3],
    [-BODY_W/2 + BOLT_INSET_X,   0],
    [-BODY_W/2 + BOLT_INSET_X,  +BODY_L/3],
    [+BODY_W/2 - BOLT_INSET_X,  -BODY_L/3],
    [+BODY_W/2 - BOLT_INSET_X,   0],
    [+BODY_W/2 - BOLT_INSET_X,  +BODY_L/3]
];
