// ============================================================
// gopro_mount.scad — GoPro 3-Prong T-Slot Sensor Rail Mount
// Issue: #195  Agent: sl-mechanical  Date: 2026-03-02
// ============================================================
//
// Universal GoPro camera mount for sensor_rail.scad (T-slot 2020).
//
// Top interface:   Standard GoPro 3-prong (HERO 5+)
//                  Prongs slide into standard GoPro accessories.
//                  No additional clips or fasteners needed.
//
// Bottom clamp:    T-slot 20×20 mm rail system (OpenBuilds compatible).
//                  M3 thumbscrew retention + M5 index holes for 15° detents.
//
// Tilt mechanism:  Rotating hinge bracket allows camera tilt 0°–90° from
//                  horizontal, locked at 15° increments via index holes.
//                  Rotation axis is perpendicular to rail (about Y-axis).
//
// Assembly:
//   1. Print gopro_mount_base (single part, flat-side-down, no supports)
//   2. Slide built-in T-nut into rail T-groove
//   3. Clamp with M3 thumbscrew from outside rail face
//   4. Rotate camera to desired tilt angle (0°, 15°, 30°, ... 90°)
//   5. Insert M5 alignment pin to lock tilt
//
// RENDER options:
//   "mount"        assembled mount view (default)
//   "base_stl"     base clamp for slicing (print flat)
//   "camera_bracket" rotatable camera bracket for slicing
// ============================================================

RENDER = "mount";

// ── GoPro 3-prong interface (top) ──────────────────────────
// Standard GoPro HERO 5+ 3-prong attachment point.
// Overall footprint ~43×25 mm, 3 prongs slide into camera mounting slot.

GOPRO_WIDTH     =  43.0;       // front-to-back along camera face
GOPRO_DEPTH     =  25.0;       // left-to-right width
GOPRO_PRONG_H   =   5.0;       // height of prongs above base
GOPRO_PRONG_D   =   4.0;       // diameter/thickness of each prong
GOPRO_PRONG_SP  =  14.0;       // spacing between outer prongs (centre-to-centre)

// Centre prong is at Y=0; outer prongs at Y±GOPRO_PRONG_SP/2
// Prongs extend upward (+Z direction)

// ── T-slot clamp (bottom) ──────────────────────────────────
// Matches sensor_rail.scad T-slot 2020 dimensions.
RAIL_W          =  20.0;       // rail outer width/height
SLOT_INNER_W    =  10.2;       // T-groove inner width
SLOT_INNER_H    =   5.8;       // T-groove inner height (depth)
SLOT_NECK_H     =   3.2;       // distance from outer face to T-groove

TNUT_W          =   9.8;       // printable T-nut width (SLOT_INNER_W - 0.4)
TNUT_H          =   5.5;       // printable T-nut height (SLOT_INNER_H - 0.3)
TNUT_L          =  12.0;       // T-nut body length
TNUT_BOLT_D     =   3.3;       // M3 clearance bore through T-nut

THUMB_D         =  16.0;       // thumbwheel OD (visual only)
THUMB_H         =   8.0;       // thumbwheel height

// Index hole pitch for tilt detents
INDEX_PITCH     =  25.0;       // 25 mm spacing on rail
TILT_ANGLE_STEP =  15.0;       // 15° tilt detents (0°, 15°, 30°, ... 90°)
M5_INDEX_D      =   5.3;       // M5 clearance hole diameter

// ── Rotation/tilt mechanism ───────────────────────────────
// Camera bracket rotates about Y-axis relative to base.
// Rotation axis is at the rail clamp interface (Z=0, Y=0).
// Tilt range: 0° to 90°.

MAX_TILT_DEG    =  90.0;       // maximum tilt angle

// Hinge pin: M5 bolt passes through base and camera bracket
HINGE_PIN_D     =   5.3;       // M5 bolt clearance
HINGE_PIN_L     =  25.0;       // pin length (accommodates rail width + brackets)

// ── General ────────────────────────────────────────────────
$fn = 64;
e   = 0.01;

// ─────────────────────────────────────────────────────────────
// gopro_mount_base()
//   T-slot clamp bracket with integrated T-nut.
//   Mounted at bottom, rail clamp at Z=0 face.
//   Hinge pin axis at Y=0, rotates camera bracket ±X.
//
// Print: flat-side-down (rail face down), no supports needed.
// ─────────────────────────────────────────────────────────────
module gopro_mount_base() {
    clamp_h = RAIL_W + 8.0;    // total clamp height
    clamp_w = TNUT_L + 8.0;    // clamp width (along rail)
    clamp_d = 18.0;             // clamp depth (front-to-back)

    difference() {
        union() {
            // Main clamp body
            translate([-clamp_w/2, -clamp_d/2, 0])
                cube([clamp_w, clamp_d, clamp_h]);

            // T-nut integration: slot to hold T-nut at rail interface
            // T-nut sits in T-groove when rail is inserted from above
            translate([-TNUT_L/2, -TNUT_W/2, SLOT_NECK_H])
                cube([TNUT_L, TNUT_W, TNUT_H]);

            // Hinge boss (supports rotation axis pin)
            translate([-HINGE_PIN_L/2 - 2, -8, clamp_h - 4])
                cube([HINGE_PIN_L + 4, 4, 4]);
        }

        // Rail bore (25.4 mm) — matches stem adapter pattern
        // When rail is vertical, this bore slides onto rail extrusion
        translate([0, 0, -e])
            cylinder(d=RAIL_W + 0.4, h=clamp_h + 2*e);

        // T-nut through-bolt (M3 thumbscrew hole)
        // Bolt comes from outside rail face, through T-nut, clamping it
        translate([0, 0, SLOT_NECK_H + TNUT_H/2])
            rotate([90, 0, 0])
                cylinder(d=TNUT_BOLT_D, h=clamp_d + 2*e);

        // Hinge pin (M5) passes through base to camera bracket
        translate([-HINGE_PIN_L/2, 0, clamp_h - 2])
            rotate([0, 90, 0])
                cylinder(d=HINGE_PIN_D, h=HINGE_PIN_L + 2*e);

        // Index hole pockets for tilt angle lock (M5)
        // One index hole on each side to lock camera tilt
        for (angle = [0 : TILT_ANGLE_STEP : MAX_TILT_DEG]) {
            // Holes are radially spaced around the hinge axis
            y_offset = (HINGE_PIN_L/2 - 2) * sin(angle);
            z_offset = clamp_h - 2 - (HINGE_PIN_L/2 - 2) * (1 - cos(angle));
            translate([0, y_offset, z_offset])
                cylinder(d=M5_INDEX_D, h=4, center=false);
        }

        // Vent slots (optional, aesthetic + weight reduction)
        for (dz = [4, 8, 12])
            translate([-clamp_w/2 + 2, -clamp_d/2 - e, dz])
                cube([clamp_w - 4, 2, 1.5]);
    }
}

// ─────────────────────────────────────────────────────────────
// gopro_camera_bracket()
//   Rotatable bracket that holds GoPro camera via 3-prong mount.
//   Rotates about Y-axis (hinge pin), tilt 0°–90°.
//
// Hinge pin (M5 bolt) enters from base and locks bracket rotation.
// Index hole on bracket aligns with base index holes at each 15° step.
//
// Print: flat-side-down (prong face down), no supports.
// ─────────────────────────────────────────────────────────────
module gopro_camera_bracket() {
    bracket_h = GOPRO_WIDTH + 8.0;   // height when vertical
    bracket_w = GOPRO_DEPTH + 6.0;   // width (left-right)
    bracket_t = 4.0;                  // bracket thickness

    difference() {
        union() {
            // Main bracket body
            translate([-bracket_w/2, -bracket_t/2, 0])
                cube([bracket_w, bracket_t, bracket_h]);

            // Hinge pivot boss
            translate([-HINGE_PIN_L/2 - 2, 0, bracket_h - 4])
                cube([HINGE_PIN_L + 4, bracket_t, 4]);

            // GoPro prong mounting boss
            translate([-GOPRO_DEPTH/2, 0, bracket_h - GOPRO_WIDTH/2])
                cube([GOPRO_DEPTH, bracket_t + 2, GOPRO_WIDTH]);
        }

        // Hinge pin bore (M5)
        translate([-HINGE_PIN_L/2, 0, bracket_h - 2])
            rotate([0, 90, 0])
                cylinder(d=HINGE_PIN_D + 0.2, h=HINGE_PIN_L + 2*e);

        // Index hole (M5) for angle lock
        // Bracket has one index hole that aligns with base holes at tilt angles
        translate([GOPRO_DEPTH/2 + 1, 0, bracket_h - 4])
            cylinder(d=M5_INDEX_D, h=bracket_t + 2*e);

        // GoPro prong socket (3 mounting prongs)
        // Centre prong at Y=0, outer prongs at Y±GOPRO_PRONG_SP/2
        for (dy = [0, GOPRO_PRONG_SP/2, -GOPRO_PRONG_SP/2]) {
            translate([0, bracket_t + e, bracket_h - GOPRO_WIDTH/2 + 5 - dy])
                rotate([90, 0, 0])
                    cylinder(d=GOPRO_PRONG_D + 0.4, h=4);
        }
    }
}

// ─────────────────────────────────────────────────────────────
// gopro_prong_interface()
//   Visual representation of the 3 prongs that camera mounts to.
//   Not printed; shown during assembly view.
// ─────────────────────────────────────────────────────────────
module gopro_prong_interface() {
    bracket_h = GOPRO_WIDTH + 8.0;
    bracket_w = GOPRO_DEPTH + 6.0;

    // 3 cylindrical prongs
    for (dy = [0, GOPRO_PRONG_SP/2, -GOPRO_PRONG_SP/2]) {
        translate([0, bracket_w/2 + 0.5, bracket_h - GOPRO_WIDTH/2 + 5 - dy])
            rotate([90, 0, 0])
                cylinder(d=GOPRO_PRONG_D, h=GOPRO_PRONG_H, $fn=32);
    }
}

// ─────────────────────────────────────────────────────────────
// Assembly view: base + rotatable camera bracket
// ─────────────────────────────────────────────────────────────
module gopro_mount_assembly() {
    // Base clamp (fixed)
    color("SteelBlue", 0.9)
        gopro_mount_base();

    // Camera bracket (rotatable, at 45° tilt for visualization)
    color("CornflowerBlue", 0.85)
        rotate([0, 45, 0])
            translate([0, 0, RAIL_W + 8])
                gopro_camera_bracket();

    // Visual GoPro prongs
    color("LightSteelBlue", 0.7)
        rotate([0, 45, 0])
            translate([0, 0, RAIL_W + 8])
                gopro_prong_interface();

    // Phantom M5 hinge pin (visual reference)
    color("Silver", 0.5)
        translate([-HINGE_PIN_L/2, 0, RAIL_W + 8 - 2])
            rotate([0, 90, 0])
                cylinder(d=5, h=HINGE_PIN_L);
}

// ─────────────────────────────────────────────────────────────
// Render selector
// ─────────────────────────────────────────────────────────────
if (RENDER == "mount") {
    gopro_mount_assembly();

} else if (RENDER == "base_stl") {
    // Flat print orientation: rail-facing side down
    rotate([180, 0, 0])
        translate([0, 0, -RAIL_W - 8])
            gopro_mount_base();

} else if (RENDER == "camera_bracket") {
    // Flat print orientation: prong-facing side down
    rotate([0, 0, 0])
        translate([0, 0, GOPRO_WIDTH + 14])
            rotate([180, 0, 0])
                gopro_camera_bracket();
}
