// =============================================================================
// SaltyBot — Prototype Base Plate  (Rev C — compact, stem-mount)
// Agent: sl-mechanical  |  2026-02-28
//
// Laser-cut or CNC-routed flat plate (6 mm Al / 8 mm acrylic).
// Uses CALIPER-VERIFIED hub motor axle measurements (see PR #7 / #11).
//
// ARCHITECTURE CHANGE (Rev C):
//   Batteries are NO LONGER on the base plate.
//   They stand vertically on a central stem via stem_battery_clamp.scad.
//   The base plate is now compact — only axle dropouts + stem socket + FC mount.
//
// ── AXLE PROFILE (stepped D-cut, caliper-verified) ───────────────────────────
//  Zone   │ Feature          │ Ø / Width          │ Length
//  ───────┼──────────────────┼────────────────────┼──────────
//  Base   │ Round (near hub) │ Ø 16.11 mm         │ 15.00 mm
//  D-cut  │ Round OD         │ Ø 15.95 mm         │ 43.35 mm
//         │ Flat chord       │   13.00 mm         │
//  Tip    │ Shoulder/end     │ —                  │  3.00 mm
//  Total  │ Hub face → tip   │ —                  │ 65.50 mm
//  Bearing seat collar:  Ø 37.8 mm
//  Tire:  10 × 2.125" pneumatic (Ø 254 mm, 35 PSI)
// =============================================================================

$fn = 128;

// =============================================================================
// AXLE PARAMETERS — caliper-verified
// =============================================================================

AXLE_BASE_DIA   = 16.11;
AXLE_BASE_LEN   = 15.00;
AXLE_DCUT_DIA   = 15.95;
AXLE_DCUT_FLAT  = 13.00;
AXLE_DCUT_LEN   = 43.35;
AXLE_TIP_LEN    =  3.00;
AXLE_TOTAL      = 65.50;
BEARING_SEAT_OD = 37.80;
TIRE_OD         = 254.0;
AXLE_CL_HEIGHT  = TIRE_OD / 2;   // 127 mm above ground

// =============================================================================
// PLATE PARAMETERS
// =============================================================================

WHEELBASE       = 600.0;  // mm  axle C/L to axle C/L
// Plate depth now driven only by structural + FC needs (no battery footprint).
PLATE_DEPTH     = 130.0;  // mm  front-to-rear
PLATE_OVERHANG  =  40.0;  // mm  plate past axle C/L each side
PLATE_THICK     =   6.0;  // mm

// Fork slot
FORK_W          = AXLE_BASE_DIA + 0.4;   // 16.51 mm
FORK_DEPTH      =  50.0;

// Dropout clamp (two-piece sandwich)
CLAMP_L         =  80.0;
CLAMP_H         =  60.0;
CLAMP_THICK     =   8.0;
CLAMP_BOLT_D    =   5.3;   // M5
CLAMP_BOLT_DX   =  22.0;
CLAMP_BOLT_DY   =  22.0;
CLAMP_ALIGN_D   =   4.1;   // Ø4 pin

// D-cut bore clearance
DCUT_CL         =  0.3;

// FC mount — ESP32-S3 BALANCE 30.5 × 30.5 mm M3
FC_PITCH        = 30.5;
FC_HOLE_D       =  3.2;
// FC is offset toward front of plate (away from stem)
FC_X_OFFSET     = -40.0;  // mm from plate centre (negative = front/motor side)

// =============================================================================
// STEM SOCKET PARAMETERS
// =============================================================================

STEM_OD         = 38.1;   // mm  1.5" EMT conduit OD
STEM_BORE       = STEM_OD + 0.5;   // 38.6 mm with clearance
// Flange ring (laser-cut, bolts above + below plate to grip tube):
STEM_FLANGE_OD  = 82.0;   // mm  flange outer diameter
STEM_FLANGE_BC  = 66.0;   // mm  bolt circle diameter (4× M5 at 90°)
STEM_FLANGE_T   =  6.0;   // mm  = PLATE_THICK (flush-mount)
// Stem position: at plate centre (X=0, Y=0)

// =============================================================================
// UTILITIES
// =============================================================================

M3 = 3.2; M4 = 4.3; M5 = 5.3;
PLATE_X_HALF = WHEELBASE/2 + PLATE_OVERHANG;   // ± 340 mm
DCUT_R  = (AXLE_DCUT_DIA + 2*DCUT_CL) / 2;
DCUT_FC = AXLE_DCUT_FLAT + 2*DCUT_CL;

// =============================================================================
// RENDER CONTROL
// =============================================================================
//
//  "assembly"        full 3-D preview
//  "plate_2d"        DXF — base plate
//  "clamp_lower_2d"  DXF — lower dropout clamp  (× 2)
//  "clamp_upper_2d"  DXF — upper dropout clamp  (× 2)
//  "stem_flange_2d"  DXF — stem flange ring      (× 2, one above + one below plate)

RENDER = "assembly";

if (RENDER == "assembly") {
    assembly();
} else if (RENDER == "plate_2d") {
    projection(cut=true) translate([0,0,-PLATE_THICK/2]) base_plate();
} else if (RENDER == "clamp_lower_2d") {
    projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_lower();
} else if (RENDER == "clamp_upper_2d") {
    projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_upper();
} else if (RENDER == "stem_flange_2d") {
    projection(cut=true) translate([0,0,-STEM_FLANGE_T/2]) stem_flange();
}

// =============================================================================
// ASSEMBLY
// =============================================================================

module assembly() {
    color("Silver",       0.85) base_plate();

    for (side = [-1, 1]) {
        color("SteelBlue",    0.80)
            translate([side * WHEELBASE/2, 0, PLATE_THICK])
                clamp_lower();
        color("CornflowerBlue", 0.80)
            translate([side * WHEELBASE/2, 0, PLATE_THICK + CLAMP_THICK])
                clamp_upper();
    }

    // Stem flange rings (above and below plate)
    color("DimGray", 0.70)
        translate([0, 0, -STEM_FLANGE_T])
            stem_flange();
    color("DimGray", 0.70)
        translate([0, 0, PLATE_THICK])
            stem_flange();

    // Reference ghosts
    %color("Orange", 0.25)
        translate([0, 0, PLATE_THICK + STEM_FLANGE_T])
            cylinder(d=STEM_OD, h=800);   // vertical stem
    %for (side = [-1, 1])
        color("Tomato", 0.2)
            translate([side * WHEELBASE/2, 0, 0])
                rotate([0, side*90, 0])
                    axle_ghost();
}

// =============================================================================
// BASE PLATE  (Part A — compact)
// =============================================================================

module base_plate() {
    R = 12;   // corner radius
    difference() {
        // ── Outer profile ──
        linear_extrude(PLATE_THICK)
            minkowski() {
                square([2*(PLATE_X_HALF - R), PLATE_DEPTH - 2*R], center=true);
                circle(r=R);
            }

        // ── Fork slots (open at ±X edges, semicircular tip) ──────────────
        for (side = [-1, 1]) {
            translate([side*(PLATE_X_HALF - FORK_DEPTH), -FORK_W/2, -1])
                cube([FORK_DEPTH + 1, FORK_W, PLATE_THICK + 2]);
            translate([side*(PLATE_X_HALF - FORK_DEPTH), 0, -1])
                cylinder(d=FORK_W, h=PLATE_THICK + 2);
        }

        // ── Bearing seat relief (prevents Ø37.8 mm collar binding on edge) ─
        for (side = [-1, 1])
            translate([side*PLATE_X_HALF - side*(BEARING_SEAT_OD/2 + 1),
                       -BEARING_SEAT_OD/2, -1])
                cube([BEARING_SEAT_OD/2 + 2, BEARING_SEAT_OD, PLATE_THICK + 2]);

        // ── Dropout clamp bolt through-holes ─────────────────────────────
        for (side = [-1, 1])
        for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
        for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
            translate([side*WHEELBASE/2 + dx, dy, -1])
                cylinder(d=CLAMP_BOLT_D, h=PLATE_THICK + 2);

        // ── Alignment pin holes (Ø4) ──────────────────────────────────────
        for (side = [-1, 1])
        for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8])
            translate([side*WHEELBASE/2, dy, -1])
                cylinder(d=CLAMP_ALIGN_D, h=PLATE_THICK + 2);

        // ── Stem socket bore ──────────────────────────────────────────────
        translate([0, 0, -1])
            cylinder(d=STEM_BORE, h=PLATE_THICK + 2);

        // ── Stem flange bolt holes (4× M5, 90° pattern on STEM_FLANGE_BC) ─
        for (a = [0, 90, 180, 270])
            rotate([0, 0, a])
                translate([STEM_FLANGE_BC/2, 0, -1])
                    cylinder(d=M5, h=PLATE_THICK + 2);

        // ── FC mount (ESP32-S3 BALANCE 30.5 × 30.5 M3) ────────────────────────
        for (x = [FC_X_OFFSET - FC_PITCH/2, FC_X_OFFSET + FC_PITCH/2])
        for (y = [-FC_PITCH/2, FC_PITCH/2])
            translate([x, y, -1])
                cylinder(d=FC_HOLE_D, h=PLATE_THICK + 2);

        // ── Wiring / cable pass-through slots (2×, flanking stem) ─────────
        for (dy = [-30, 30])
            hull() {
                translate([15, dy, -1])  cylinder(d=14, h=PLATE_THICK + 2);
                translate([-15, dy, -1]) cylinder(d=14, h=PLATE_THICK + 2);
            }

        // ── Lightening slots (between FC zone and dropout zones) ──────────
        for (sx = [-1, 1]) {
            // One slot each side of stem, in the structural corridor
            lx = sx * (WHEELBASE/4);
            hull() {
                translate([lx, -(PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2);
                translate([lx,  (PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2);
            }
        }
    }
}

// =============================================================================
// STEM FLANGE RING  (laser-cut, qty 2 — one above, one below plate)
// =============================================================================

module stem_flange() {
    difference() {
        cylinder(d=STEM_FLANGE_OD, h=STEM_FLANGE_T);
        // Stem bore (tight fit — tube presses into flange)
        translate([0, 0, -1])
            cylinder(d=STEM_BORE, h=STEM_FLANGE_T + 2);
        // 4× M5 flange bolts
        for (a = [0, 90, 180, 270])
            rotate([0, 0, a])
                translate([STEM_FLANGE_BC/2, 0, -1])
                    cylinder(d=M5, h=STEM_FLANGE_T + 2);
    }
}

// =============================================================================
// DROPOUT CLAMP — LOWER (round bore, base-section diameter)
// =============================================================================

module clamp_lower() {
    difference() {
        hull() {
            translate([-CLAMP_L/2 + CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
            translate([ CLAMP_L/2 - CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
        }
        // Round bore (base zone)
        translate([0, 0, -1]) cylinder(d=AXLE_BASE_DIA + 0.4, h=CLAMP_THICK + 2);
        // Slide-in slot (open toward wheel side)
        translate([-CLAMP_L/2 - 1, -FORK_W/2, -1])
            cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]);
        // Clamp bolts
        for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
        for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
            translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2);
        // Alignment pins
        for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8])
            translate([0, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2);
    }
}

// =============================================================================
// DROPOUT CLAMP — UPPER (D-cut bore, anti-rotation)
// =============================================================================

module clamp_upper() {
    dcut_r = DCUT_R;
    dcut_d = sqrt(pow(dcut_r, 2) - pow(DCUT_FC/2, 2));

    difference() {
        hull() {
            translate([-CLAMP_L/2 + CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
            translate([ CLAMP_L/2 - CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
        }
        // D-cut bore
        translate([0, 0, -1])
            linear_extrude(CLAMP_THICK + 2)
                dcut_profile_2d(dcut_r, dcut_d);
        // Slide-in slot
        translate([-CLAMP_L/2 - 1, -FORK_W/2, -1])
            cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]);
        // Clamp bolts
        for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
        for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
            translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2);
        // Alignment pins
        for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8])
            translate([0, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2);
        // Orientation emboss
        translate([0, dcut_d + 1.5, CLAMP_THICK - 0.8])
            linear_extrude(1)
                polygon([[0,0],[-3,-5],[3,-5]]);
    }
}

// ── D-cut 2D profile helper ───────────────────────────────────────────────────
module dcut_profile_2d(r, flat_d) {
    intersection() {
        circle(r=r);
        translate([-r - 1, -r - 1])
            square([2*(r+1), r + 1 + flat_d]);
    }
}

// =============================================================================
// AXLE GHOST (visualisation only)
// =============================================================================

module axle_ghost() {
    cylinder(d=BEARING_SEAT_OD, h=12);
    translate([0,0,12]) cylinder(d=AXLE_BASE_DIA, h=AXLE_BASE_LEN);
    translate([0,0,12+AXLE_BASE_LEN]) cylinder(d=AXLE_DCUT_DIA, h=AXLE_DCUT_LEN);
}

// =============================================================================
// DXF EXPORT
// =============================================================================
//
//  Part 1 — Base plate:
//    openscad prototype_baseplate.scad -D 'RENDER="plate_2d"' -o baseplate.dxf
//
//  Part 2 — Dropout clamp, lower (× 2):
//    openscad prototype_baseplate.scad -D 'RENDER="clamp_lower_2d"' -o clamp_lower.dxf
//
//  Part 3 — Dropout clamp, upper (× 2):
//    openscad prototype_baseplate.scad -D 'RENDER="clamp_upper_2d"' -o clamp_upper.dxf
//
//  Part 4 — Stem flange ring (× 2, one each side of plate):
//    openscad prototype_baseplate.scad -D 'RENDER="stem_flange_2d"' -o stem_flange.dxf
//
//  Materials:
//    Plate + flanges : 6 mm 5052-H32 aluminium  (preferred)
//                      8 mm clear acrylic         (quick proto)
//    Dropout clamps  : 8 mm 6061-T6 aluminium
//  Stem tube         : 38.1 mm OD × 1.5 mm wall 6061-T6  (or 1.5" EMT)
//    Cut stem to ~1050 mm — allows batteries from ~100 mm to ~950 mm height.
// =============================================================================
