diff --git a/.github/workflows/generate-cut-list-pdf.yml b/.github/workflows/generate-cut-list-pdf.yml new file mode 100644 index 0000000..d2baedd --- /dev/null +++ b/.github/workflows/generate-cut-list-pdf.yml @@ -0,0 +1,157 @@ +name: Generate Structural Steel Cut List PDF + +on: + push: + branches: [ main, master ] + paths: + - 'LifeTrac-v25/mechanical_design/openscad/**/*.scad' + - 'LifeTrac-v25/mechanical_design/generate_cut_list.py' + pull_request: + branches: [ main, master ] + paths: + - 'LifeTrac-v25/mechanical_design/openscad/**/*.scad' + - 'LifeTrac-v25/mechanical_design/generate_cut_list.py' + workflow_dispatch: + +jobs: + generate-cut-list: + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.head_ref || github.ref_name }} + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install OpenSCAD for 3D renders + run: | + sudo apt-get update + sudo apt-get install -y openscad xvfb + openscad --version + + - name: Install Python dependencies + run: | + pip install -r LifeTrac-v25/mechanical_design/requirements.txt + + - name: Generate Structural Steel Cut List PDF + run: | + cd LifeTrac-v25/mechanical_design + + echo "=========================================" + echo "Generating Structural Steel Cut List PDF" + echo "=========================================" + + # Start Xvfb for OpenSCAD rendering + Xvfb :99 -screen 0 1024x768x16 & + export DISPLAY=:99 + sleep 2 + + python3 generate_cut_list.py + + # Verify the PDF was created + if [ -f structural_steel_cut_list.pdf ]; then + echo "✅ Cut list PDF generated successfully" + ls -lh structural_steel_cut_list.pdf + du -h structural_steel_cut_list.pdf + + # Get page count + echo "Checking PDF content..." + file structural_steel_cut_list.pdf + else + echo "❌ Failed to generate cut list PDF" + exit 1 + fi + + - name: Update README with cut list reference + run: | + cd LifeTrac-v25/mechanical_design + + # Check if README already has cut list section + if ! grep -q "## Structural Steel Cut List" README.md; then + echo "Adding cut list section to README..." + + { + echo "" + echo "## Structural Steel Cut List" + echo "" + echo "**[Download Cut List PDF](structural_steel_cut_list.pdf)**" + echo "" + echo "Printable cut list for all angle iron and square tubing parts. Each unique part has its own page with:" + echo "- Engineering drawing showing total length and hole positions" + echo "- Dimensions in both inches and millimeters" + echo "- Complete list of cutting and drilling operations with checkboxes" + echo "- Part code (A1, A2, etc.) for use in assembly" + echo "- Quantity needed for complete machine" + echo "" + echo "### Parts Summary" + echo "" + echo "| Part Code | Name | Material | Quantity |" + echo "|-----------|------|----------|----------|" + echo '| A1 | Platform Angle Arm | 2" × 2" × 1/4" Angle Iron | 2 |' + echo '| A2 | Frame Tube Mounting Angle | 2" × 2" × 1/4" Angle Iron | 16 |' + echo '| A3 | Loader Arm Mounting Angle | 2" × 2" × 1/4" Angle Iron | 4 |' + echo '| A4 | Side Panel Vertical Angle | 2" × 2" × 1/4" Angle Iron | 8 |' + echo '| A5 | Bottom Plate Horizontal Angle | 2" × 2" × 1/4" Angle Iron | 8 |' + echo "" + } >> README.md + echo "✓ README updated with cut list section" + else + echo "ℹ️ README already has cut list section" + fi + + - name: Commit and push if changed + run: | + cd LifeTrac-v25/mechanical_design + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git add structural_steel_cut_list.pdf README.md || true + + if git diff --staged --quiet; then + echo "ℹ️ No changes to commit" + else + echo "📝 Committing changes..." + # Note: [skip ci] prevents infinite loops from the workflow re-triggering itself + # The paths filter also helps, but [skip ci] is an extra safety measure + git commit -m "Update structural steel cut list PDF [skip ci]" + + # Fetch and rebase to incorporate any remote changes + BRANCH_NAME="${{ github.head_ref || github.ref_name }}" + echo "🔄 Fetching latest changes from branch: ${BRANCH_NAME}..." + if git fetch origin "${BRANCH_NAME}"; then + echo "🔄 Rebasing on latest changes..." + if ! git rebase "origin/${BRANCH_NAME}"; then + echo "❌ Failed to rebase. There may be conflicts." + echo "Aborting rebase and exiting..." + git rebase --abort + exit 1 + fi + + echo "📤 Pushing changes with --force-with-lease..." + if git push --force-with-lease; then + echo "✅ Changes pushed successfully" + else + echo "⚠️ Push failed. This is expected for PRs from forks or if you do not have write permissions." + echo "The files are available as workflow artifacts." + fi + else + echo "⚠️ Could not fetch branch ${BRANCH_NAME} from origin. This may be a PR from a fork." + echo "⚠️ Skipping push to avoid pushing potentially stale commits." + echo "📦 The files are available as workflow artifacts." + fi + fi + + - name: Upload PDF as artifact + uses: actions/upload-artifact@v4 + with: + name: structural-steel-cut-list + path: LifeTrac-v25/mechanical_design/structural_steel_cut_list.pdf + retention-days: 90 diff --git a/AI_PROMPT_FOR_STRUCTURAL_PARTS.md b/AI_PROMPT_FOR_STRUCTURAL_PARTS.md new file mode 100644 index 0000000..3a6d48f --- /dev/null +++ b/AI_PROMPT_FOR_STRUCTURAL_PARTS.md @@ -0,0 +1,304 @@ +# AI Prompt for Creating LifeTrac v25 Structural Steel Part Files + +## Context + +You are working on the LifeTrac v25 open-source construction machine project. The goal is to create individual OpenSCAD (.scad) files for each unique structural steel part (angle iron and square/rectangular tubing) used in the assembly, and then integrate these parts into the main assembly file. + +## Current State + +**Repository Location:** `OpenSourceEcology/LifeTrac` +**Working Directory:** `LifeTrac-v25/mechanical_design/openscad/` + +**Existing Structural Part Files:** +Currently, only 7 unique parts with 40 total pieces are documented: +- `parts/structural/angle_iron_a2_frame_mount.scad` (Part A2 - 16 pieces) +- `parts/structural/angle_iron_a3_loader_mount.scad` (Part A3 - 4 pieces) +- `parts/structural/angle_iron_a4_side_panel_vertical.scad` (Part A4 - 8 pieces) +- `parts/structural/angle_iron_a5_bottom_plate_horizontal.scad` (Part A5 - 8 pieces) +- `parts/structural/frame_tube_t1_front.scad` (Part T1 - 1 piece) +- `parts/structural/frame_tube_t2_rear.scad` (Part T2 - 1 piece) +- `parts/platform_angle_arm.scad` (Part A1 - 2 pieces) + +**Target:** Identify and create individual SCAD files for ALL 89 total structural steel pieces. + +## Expected Part Counts (from user requirements) + +### Angle Iron Parts (78 total): +1. **Standing platform:** 4 pieces +2. **Back stiffener plate:** 6 pieces +3. **Arm crossbeam mounts:** 4 pieces +4. **Bottom stiffener plate:** 30 pieces +5. **Frame tubing mounts:** 24 pieces +6. **Front stiffener plate:** 10 pieces + +### Tubing Parts (11 total): +1. **Crossbeam:** 1 piece (2"×2" square tubing) +2. **Arms:** 2 pieces (2"×6" rectangular tubing sandwiched between CNC-cut arm plates) +3. **Frame tubes:** 2 pieces (already created as T1/T2) +4. **Bucket pivot mounts:** 2 pieces +5. **Bucket hydraulics mounts:** 2 pieces +6. **Crossbeam hydraulic mounts:** 2 pieces + +## Your Task + +### Phase 1: Exploration and Cataloging + +1. **Analyze the main assembly file** (`lifetrac_v25.scad`) to identify ALL structural steel usage: + - Search for all instances of angle iron placement functions (e.g., `vertical_angle_iron_smart()`, `horizontal_angle_iron_smart()`, `split_horizontal_angle_iron()`) + - Search for all tube/rectangular tubing placements + - Document the location, dimensions, and hole patterns for each unique part + +2. **Catalog each unique part** with the following information: + - **Part Code:** Assign a logical code (A6, A7, A8... for angle iron, T3, T4, T5... for tubing) + - **Descriptive Name:** Based on function/location (e.g., "Standing Platform Vertical Support") + - **Material Specification:** Size and wall thickness (e.g., "2\"×2\"×1/4\" Angle Iron") + - **Length/Height:** Total dimension + - **Hole Specifications:** + - Number of holes + - Diameter + - Positions from one end + - Which leg of angle iron (vertical/horizontal) or which face of tubing + - Purpose/description of each hole + - **Quantity Needed:** How many of this exact part are required + - **Location/Usage:** Where in the assembly this part is used + +3. **Group parts logically:** + - Standing platform angle irons + - Front stiffener plate angle irons + - Back stiffener plate angle irons + - Bottom stiffener plate angle irons (likely split into multiple segments) + - Frame tubing mount angle irons + - Arm crossbeam mount angle irons + - Various tubing parts (arms, crossbeam, hydraulic mounts, pivot mounts) + +### Phase 2: Create Individual SCAD Files + +For each unique part identified in Phase 1: + +1. **Create a new .scad file** in `parts/structural/` with a descriptive filename following this pattern: + - Angle iron: `angle_iron_a[N]_[description].scad` (e.g., `angle_iron_a6_standing_platform_vertical.scad`) + - Tubing: `tube_t[N]_[description].scad` (e.g., `tube_t3_loader_arm_left.scad`) + +2. **File Structure Template:** + +```openscad +// Part [CODE]: [Descriptive Name] +// Material: [Size] [Type] +// Quantity: [N] pieces +// Location: [Where used in assembly] +// +// This file defines a single structural steel part used in the LifeTrac v25 assembly. + +use <../../lifetrac_v25_params.scad> +use <../../modules/structural_steel.scad> + +// Part dimensions +PART_[NAME]_LENGTH = [value]; // mm - or HEIGHT for vertical parts +PART_[NAME]_HOLES_LEG_A = [ // Holes in vertical/first leg + [position, diameter, "description"], + [position, diameter, "description"], + // ... more holes +]; +PART_[NAME]_HOLES_LEG_B = [ // Holes in horizontal/second leg (if applicable) + [position, diameter, "description"], + [position, diameter, "description"], + // ... more holes +]; + +// Main module +module part_[code]_[short_name](show_holes=true) { + // For angle iron: + difference() { + // Base angle iron shape + [call appropriate module from structural_steel.scad or create custom geometry] + + if (show_holes) { + // Drill holes in leg A + for (hole = PART_[NAME]_HOLES_LEG_A) { + translate([...]) + rotate([...]) + cylinder(d=hole[1], h=[depth], center=true, $fn=32); + } + + // Drill holes in leg B (if applicable) + for (hole = PART_[NAME]_HOLES_LEG_B) { + translate([...]) + rotate([...]) + cylinder(d=hole[1], h=[depth], center=true, $fn=32); + } + } + } +} + +// Render the part when this file is opened directly +part_[code]_[short_name](show_holes=true); +``` + +3. **Key Requirements for Each File:** + - Must be self-contained (can be rendered independently) + - Must have a `show_holes` parameter to toggle hole visibility + - Holes must be positioned on the correct leg/face + - Must use parameters from `lifetrac_v25_params.scad` where applicable + - Must include clear comments documenting the part purpose and specifications + +### Phase 3: Integration with Main Assembly + +After creating all individual part files, update `lifetrac_v25.scad`: + +1. **Add use statements** at the top for all new structural part files: +```openscad +use +use +// ... etc +``` + +2. **Replace inline structural steel with module calls:** + - Find each location where structural steel is currently placed + - Replace with calls to the appropriate part module from the dedicated files + - Example: + ```openscad + // OLD: + vertical_angle_iron_smart(height=550, size=[50.8, 6.35]); + + // NEW: + part_a6_standing_platform_vertical(show_holes=true); + ``` + +3. **Ensure correct positioning:** Make sure the replacement maintains the same position and orientation as the original + +## Important Guidelines + +1. **Unique Parts Rule:** If two parts have different dimensions OR different hole patterns, they are unique parts and need separate files, even if they serve similar functions. + +2. **Naming Convention:** + - Use descriptive names that indicate function/location + - Keep filenames lowercase with underscores + - Module names should match: `part_[code]_[short_description]` + +3. **Hole Positioning:** + - Be precise about which leg/face holes are drilled through + - Measure hole positions from a consistent reference point (usually one end) + - Include clearance for bolt heads/nuts if specified in the assembly + +4. **Material Specifications:** + - Standard angle iron: 2"×2"×1/4" (50.8mm × 6.35mm wall) + - Frame tubes: 2"×6"×1/4" rectangular + - Crossbeam: 2"×2"×1/4" square + - Verify actual sizes from `lifetrac_v25_params.scad` + +5. **Documentation:** + - Each file should have a header comment block + - Each hole should have a description comment + - Keep code readable and well-organized + +## Specific Areas to Investigate + +### In lifetrac_v25.scad, look for: + +1. **Standing Platform Section:** + - Search for: "standing", "platform", "deck" + - Look for vertical angle irons supporting the standing platform + - Expected: 4 pieces + +2. **Front/Back/Bottom Stiffener Plates:** + - Search for: "stiffener", "split_horizontal_angle_iron", "ANGLE_SEGMENT" + - The bottom plate likely uses split angle iron (3 segments with gaps for wheels) + - Front/back plates use vertical and horizontal angle irons + - Expected: Front=10, Back=6, Bottom=30 pieces + +3. **Frame Tubing Mounts:** + - Search for: "frame_tube", "FRAME_TUBE" + - Angle irons that attach the frame tubes to other components + - Expected: 24 pieces + +4. **Arm Crossbeam Mounts:** + - Search for: "crossbeam", "CROSS_BEAM", "arm" + - Mounts connecting crossbeam to the loader arms + - Expected: 4 pieces + +5. **Tubing Parts:** + - Loader arms: Search for "ARM_TUBE", "loader_arm" + - Crossbeam: Search for "CROSS_BEAM" + - Hydraulic mounts: Search for "hydraulic", "cylinder" + - Bucket pivot: Search for "bucket", "pivot" + +## Example Analysis Format + +For each part you identify, document like this: + +``` +Part: A6 +Name: Standing Platform Front Vertical Support +Material: 2"×2"×1/4" Angle Iron +Length: 550mm +Quantity: 2 +Location: Front corners of standing platform, welded to frame + +Holes - Vertical Leg: + - 50mm from bottom, 9.5mm dia, for platform deck attachment + - 150mm from bottom, 9.5mm dia, for platform deck attachment + - 300mm from bottom, 9.5mm dia, for railing attachment + +Holes - Horizontal Leg: + - 75mm from bottom, 9.5mm dia, for frame tube attachment + - 200mm from bottom, 9.5mm dia, for side panel attachment + +Code location in lifetrac_v25.scad: Line 2330-2335 +Current code: vertical_angle_iron_smart(550, [50.8, 6.35]) +``` + +## Deliverables + +1. **Comprehensive catalog document** listing all 89 parts with their specifications +2. **All individual .scad files** created in `parts/structural/` +3. **Updated lifetrac_v25.scad** with: + - All `use` statements for new part files + - All inline structural steel replaced with module calls from part files +4. **Summary document** explaining: + - How parts are organized + - Any naming conventions used + - Which parts are used where in the assembly + - Total material requirements (length of each material type needed) + +## Files to Examine + +**Primary files:** +- `lifetrac_v25.scad` - Main assembly (search this for all structural steel placements) +- `lifetrac_v25_params.scad` - Parameter definitions +- `modules/structural_steel.scad` - Helper modules for structural steel (if exists) +- `parts/side_panel.scad` - May contain stiffener information +- `parts/platform_deck.scad` - May contain platform angle iron info +- `parts/standing_deck.scad` - Standing platform information +- `parts/loader_arm_v2.scad` - Loader arm specifications + +**Existing examples to reference:** +- `parts/structural/angle_iron_a2_frame_mount.scad` +- `parts/structural/angle_iron_a3_loader_mount.scad` +- `parts/structural/angle_iron_a4_side_panel_vertical.scad` +- `parts/structural/angle_iron_a5_bottom_plate_horizontal.scad` +- `parts/structural/frame_tube_t1_front.scad` +- `parts/structural/frame_tube_t2_rear.scad` + +## Success Criteria + +- [ ] All 89 structural steel pieces are accounted for +- [ ] Each unique part has its own .scad file +- [ ] Each file can be rendered independently +- [ ] All parts are properly integrated into the main assembly +- [ ] The main assembly renders correctly with all new part modules +- [ ] Part codes are assigned logically (A1-A[n] for angle iron, T1-T[n] for tubing) +- [ ] All files follow the established naming and coding conventions +- [ ] Hole positions are accurate and on the correct legs/faces +- [ ] Documentation is complete and clear + +## Notes + +- This is a substantial task requiring careful analysis +- Work systematically through each section of the assembly +- Double-check part counts against the expected totals +- Ask for clarification if you find ambiguities in the assembly +- The goal is accuracy over speed - take time to get it right +- Parts with similar dimensions but different hole patterns are different parts +- Maintain consistency with the existing 6 structural part files that have already been created + +Good luck! This work is essential for creating accurate fabrication documentation for the LifeTrac v25 construction machine. diff --git a/LifeTrac-v25/AI NOTES/2026-02-03_Frame_Tubes_Stiffener_Updates.md b/LifeTrac-v25/AI NOTES/2026-02-03_Frame_Tubes_Stiffener_Updates.md new file mode 100644 index 0000000..508d629 --- /dev/null +++ b/LifeTrac-v25/AI NOTES/2026-02-03_Frame_Tubes_Stiffener_Updates.md @@ -0,0 +1,307 @@ +# LifeTrac v25 Frame Tubes and Stiffener Plate Updates +**Date:** February 3, 2026 + +## Summary +This session focused on fixing frame tube placement, adding bolt holes for angle iron connections, widening the front stiffener plate, and reshaping the inner wall plates. + +--- + +## Changes Made + +### 1. Frame Tube Rotation Fix +**Files Modified:** `lifetrac_v25.scad` + +**Problem:** Frame tubes (T1 and T2) were misoriented - the 6" and 2" dimensions were swapped. + +**Solution:** Added `rotate([90, 0, 0])` to both frame tube placements in `cross_frame_tubes()` module. Also adjusted Y translation to compensate for the rotation shifting the tube position. + +```scad +// Before: +translate([0, FRONT_FRAME_TUBE_Y, FRAME_TUBE_Z]) +part_t1_front_frame_tube(); + +// After: +translate([0, FRONT_FRAME_TUBE_Y + FRAME_TUBE_WIDTH, FRAME_TUBE_Z]) +rotate([90, 0, 0]) +part_t1_front_frame_tube(); +``` + +--- + +### 2. Arm Crossbeam (T3) Length Adjustment +**Files Modified:** +- `tube_t3_arm_crossbeam.scad` +- `lifetrac_v25.scad` + +**Initial Change:** Shortened crossbeam to be flush with outer faces of inner arm CNC plates: +```scad +PART_T3_LENGTH = ARM_SPACING - TUBE_2X6_1_4[0] - 2*ARM_PLATE_THICKNESS; // ~837mm +``` + +**Final Change:** Extended crossbeam through the arm CNC plates (user requested it reach through): +```scad +PART_T3_LENGTH = ARM_SPACING - TUBE_2X6_1_4[0]; // ~849mm +``` + +Also updated `CROSS_BEAM_SPAN` constant in main assembly to match. + +--- + +### 3. Frame Tube Bolt Holes (T1 and T2) +**Files Modified:** +- `tube_t1_front_frame.scad` +- `tube_t2_rear_frame.scad` +- `structural_parts.scad` + +**Added bolt holes for angle iron mounts at 6 X positions:** +1. Left outer panel +2. Left inner panel +3. Left motor plate +4. Right motor plate +5. Right inner panel +6. Right outer panel + +**Hole Configuration:** +- 24 holes per tube (6 positions × 2 faces × 2 holes per face) +- 1/2" diameter bolts +- Holes drilled through top and bottom Z faces (which become front/rear Y faces after rotation) +- Y positions at ~50.8mm and ~101.6mm (matching angle iron hole spacing) + +**Key Fix:** Initial holes were too close to tube edges. Corrected by: +- Drilling through Z faces instead of Y faces +- Positioning holes along the 6" Y dimension (not the 2" Z dimension) + +**Motor Plate Alignment Fix:** Added `plate_thickness/2` offset to motor plate hole positions to match actual angle iron placement: +```scad +_left_motor_x = -(_track_width/2 - _sandwich_spacing/2) + _motor_plate_inboard + _motor_plate_thick/2; +_right_motor_x = (_track_width/2 - _sandwich_spacing/2) - _motor_plate_inboard - _motor_plate_thick/2; +``` + +--- + +### 4. Front Stiffener Plate Widening +**Files Modified:** `lifetrac_v25.scad` + +**Change:** Extended the center (10") section from outer edge of left motor plate to outer edge of right motor plate. + +```scad +// Before: +translate([-MOTOR_PLATE_X, y_pos, z_start]) +cube([2*MOTOR_PLATE_X, plate_thickness, center_height]); + +// After: +center_half_width = MOTOR_PLATE_X + MOTOR_PLATE_THICKNESS/2; +translate([-center_half_width, y_pos, z_start]) +cube([2*center_half_width, plate_thickness, center_height]); +``` + +The outer 5" sections were also adjusted to start at the new center section edges. + +--- + +### 5. Inner Wall Plate Reshaping +**Files Modified:** `lifetrac_v25.scad` + +**Changes to `side_panel_left_inner()` and `side_panel_right_inner()`:** + +1. **Trimmed front section** beyond front stiffener plate position (`BOTTOM_PLATE_Y_END`) + +2. **Created flat 5" vertical section** at front, flush with front stiffener plate: + - Starts at `BOTTOM_PLATE_INNER_TRIM` (31.75mm) + - Height: 127mm (5") + - Matches outer section height of front stiffener plate + +3. **Angled continuation** from top of 5" section to existing slope line: + - Removes triangular section between flat top and original slope + - Creates smooth transition to upper panel profile + +```scad +// Key parameters: +flat_section_height = 127.0; // 5" flat section at front +flat_section_top = BOTTOM_PLATE_INNER_TRIM + flat_section_height; +``` + +--- + +## Technical Notes + +### Coordinate System Reminder +For frame tubes after `rotate([90, 0, 0])` in assembly: +- Part Y (0 to 152.4mm) → World Z (height) +- Part Z (0 to 50.8mm) → World -Y (depth) + +### Part Module Pattern +- Individual part files use underscore-prefixed modules: `_part_xxx()` +- `structural_parts.scad` provides wrapper modules: `part_xxx()` +- Wrappers pass through parameters like `show_holes` + +### Hole Count Summary +| Part | Holes | Description | +|------|-------|-------------| +| T1 Front Frame Tube | 24 | 6 positions × 2 faces × 2 holes | +| T2 Rear Frame Tube | 24 | 6 positions × 2 faces × 2 holes | +| T3 Arm Crossbeam | Existing | Via angle iron mounts | + +--- + +## Files Modified +- `lifetrac_v25.scad` - Main assembly +- `parts/structural/tube_t1_front_frame.scad` - Front frame tube part +- `parts/structural/tube_t2_rear_frame.scad` - Rear frame tube part +- `parts/structural/tube_t3_arm_crossbeam.scad` - Arm crossbeam part +- `parts/structural/structural_parts.scad` - Wrapper modules + +--- + +## Part File Details + +### T1: Front Cross Frame Tube +**File:** `parts/structural/tube_t1_front_frame.scad` + +**Material:** 2"×6"×1/4" Rectangular Tubing (50.8mm × 152.4mm × 6.35mm wall) + +**Quantity:** 1 piece + +**Dimensions:** +- Length: `TRACK_WIDTH + SANDWICH_SPACING + 2*PANEL_THICKNESS + 2*12.7mm` (extends 1/2" past outer panels) +- Width: 152.4mm (6") +- Height: 50.8mm (2") + +**Bolt Holes Added:** +- 24 total holes (6 X positions × 2 faces × 2 holes per position) +- 1/2" diameter +- Drilled through top and bottom faces (Z direction in part coords) +- Y positions: ~50.8mm and ~101.6mm from edge + +**X Position Calculations:** +```scad +// Panel positions +_left_outer_x = -(_track_width/2 + _sandwich_spacing/2); +_left_inner_x = -(_track_width/2 - _sandwich_spacing/2) + _panel_thick; +_right_inner_x = (_track_width/2 - _sandwich_spacing/2) - _panel_thick; +_right_outer_x = (_track_width/2 + _sandwich_spacing/2); + +// Motor plate positions (with plate_thickness/2 offset) +_left_motor_x = -(_track_width/2 - _sandwich_spacing/2) + _motor_plate_inboard + _motor_plate_thick/2; +_right_motor_x = (_track_width/2 - _sandwich_spacing/2) - _motor_plate_inboard - _motor_plate_thick/2; + +// Hole X positions (1" inset from face toward center) +PART_T1_HOLE_X = [ + _left_outer_x + _angle_thick + _hole_inset, + _left_inner_x + _hole_inset, + _left_motor_x + _hole_inset, + _right_motor_x - _hole_inset, + _right_inner_x - _hole_inset, + _right_outer_x - _angle_thick - _hole_inset +]; +``` + +**Module Signature:** +```scad +module _part_t1_front_frame_tube(show_cutaway=false, show_holes=true) +``` + +--- + +### T2: Rear Cross Frame Tube +**File:** `parts/structural/tube_t2_rear_frame.scad` + +**Material:** 2"×6"×1/4" Rectangular Tubing (identical to T1) + +**Quantity:** 1 piece + +**Dimensions:** Same as T1 + +**Bolt Holes:** Same pattern as T1 (24 holes) + +**Note:** Identical geometry to T1, positioned at rear wheel location in assembly. + +**Module Signature:** +```scad +module _part_t2_rear_frame_tube(show_cutaway=false, show_holes=true) +``` + +--- + +### T3: Arm Crossbeam Tube +**File:** `parts/structural/tube_t3_arm_crossbeam.scad` + +**Material:** 2"×6"×1/4" Rectangular Tubing + +**Quantity:** 1 piece + +**Length Calculation (Updated):** +```scad +// Crossbeam extends through inner arm CNC plates +_arm_spacing = is_undef(ARM_SPACING) ? 900 : ARM_SPACING; +_tube_w = is_undef(TUBE_2X6_1_4) ? 50.8 : TUBE_2X6_1_4[0]; // Arm tube width (2") + +// Ends flush with inner face of inner arm CNC plates +PART_T3_LENGTH = _arm_spacing - _tube_w; // ~849.2mm +``` + +**Previous Length:** `ARM_SPACING` (900mm) - was too long + +**Intermediate Length:** `ARM_SPACING - TUBE_2X6_1_4[0] - 2*ARM_PLATE_THICKNESS` (~836.5mm) - was too short + +**Final Length:** `ARM_SPACING - TUBE_2X6_1_4[0]` (~849.2mm) - extends through plates + +**Module Signature:** +```scad +module _part_t3_arm_crossbeam(show_holes=true) +``` + +--- + +### Structural Parts Wrapper Updates +**File:** `parts/structural/structural_parts.scad` + +**Updated wrapper modules to pass new parameters:** + +```scad +// T1: Front Cross Frame Tube (1 pc) +use +module part_t1_front_frame_tube(show_cutaway=false, show_holes=true) { + _part_t1_front_frame_tube(show_cutaway, show_holes); +} + +// T2: Rear Cross Frame Tube (1 pc) +use +module part_t2_rear_frame_tube(show_cutaway=false, show_holes=true) { + _part_t2_rear_frame_tube(show_cutaway, show_holes); +} +``` + +--- + +## Assembly Integration + +### cross_frame_tubes() Module +**Location:** `lifetrac_v25.scad`, lines ~3086-3105 + +```scad +module cross_frame_tubes() { + // Front frame tube (T1) + // Part module: length in X, origin at X=0 center, Y=0, Z=0 (bottom) + // Assembly needs: tube at Y=FRONT_FRAME_TUBE_Y, Z=FRAME_TUBE_Z + // Rotate to get 6" width in Y, 2" height in Z + translate([0, FRONT_FRAME_TUBE_Y + FRAME_TUBE_WIDTH, FRAME_TUBE_Z]) + rotate([90, 0, 0]) + part_t1_front_frame_tube(); + + // Rear frame tube (T2) + translate([0, REAR_FRAME_TUBE_Y + FRAME_TUBE_WIDTH, FRAME_TUBE_Z]) + rotate([90, 0, 0]) + part_t2_rear_frame_tube(); + + // Angle iron mounts (16 total: 2 per tube face × 4 wall panels × 2 tubes) + frame_tube_angle_irons(); +} +``` + +### CROSS_BEAM_SPAN Constant Update +```scad +// Cross beam span (extends through inner arm CNC plates) +// Reduced by arm tube width (2") only - ends flush with inner face of plates +CROSS_BEAM_SPAN = ARM_SPACING - TUBE_2X6_1_4[0]; // ~849mm +``` diff --git a/LifeTrac-v25/mechanical_design/CUT_LIST_USAGE_GUIDE.md b/LifeTrac-v25/mechanical_design/CUT_LIST_USAGE_GUIDE.md new file mode 100644 index 0000000..d7607ff --- /dev/null +++ b/LifeTrac-v25/mechanical_design/CUT_LIST_USAGE_GUIDE.md @@ -0,0 +1,124 @@ +# Structural Steel Cut List Usage Guide + +## Overview + +The structural steel cut list provides detailed manufacturing instructions for all angle iron and square tubing parts needed to build the LifeTrac v25 machine. + +## What's Included + +The PDF cut list (`structural_steel_cut_list.pdf`) contains **5 unique parts** requiring **38 total pieces** to fabricate: + +| Part Code | Description | Material | Quantity | +|-----------|-------------|----------|----------| +| A1 | Platform Angle Arm | 2" × 2" × 1/4" Angle Iron | 2 | +| A2 | Frame Tube Mounting Angle | 2" × 2" × 1/4" Angle Iron | 16 | +| A3 | Loader Arm Mounting Angle | 2" × 2" × 1/4" Angle Iron | 4 | +| A4 | Side Panel Vertical Angle | 2" × 2" × 1/4" Angle Iron | 8 | +| A5 | Bottom Plate Horizontal Angle | 2" × 2" × 1/4" Angle Iron | 8 | + +**Total:** 38 pieces + +## How to Use the Cut List + +### Step 1: Print the PDF + +Download and print `structural_steel_cut_list.pdf`. Each page represents one unique part type. + +### Step 2: Purchase Materials + +All parts use standard **2" × 2" × 1/4" angle iron** stock. Calculate total length needed: + +- A1: 2 × 425mm = 850mm (33.5") +- A2: 16 × 146mm = 2,336mm (92") +- A3: 4 × 146mm = 584mm (23") +- A4: 8 × 550mm = 4,400mm (173") +- A5: 8 × 400mm = 3,200mm (126") + +**Total angle iron needed:** ~11.4 meters (37.5 feet) + +💡 **Tip:** Purchase 20-foot lengths (6 meters) - you'll need 2 sticks with some extra for waste. + +### Step 3: Cut to Length + +For each part type: + +1. Locate the page in the PDF (by part code) +2. Note the total length dimension (shown in both inches and millimeters) +3. Cut the specified quantity of pieces to this length +4. Use a metal cutoff saw or angle grinder with cutoff wheel +5. Check off the "Cut" operation on your printed sheet + +### Step 4: Mark Hole Positions + +Each part page includes an engineering drawing showing: +- Total length with dimension arrows +- Hole positions measured from one end +- Hole diameters (typically 1/2" or 3/8") + +Use the dimensions to mark hole centers: +1. Measure from the end as shown on the drawing +2. Use a center punch to mark each hole position +3. Double-check measurements before drilling + +### Step 5: Drill Holes + +For each hole: +1. Reference the operations checklist on the part page +2. Use the specified drill bit size (e.g., "1/2" hole") +3. Drill through the indicated leg of the angle iron +4. Check off each drilling operation as you complete it + +⚠️ **Important Notes:** +- Part A1 (Platform Angle Arm) has holes in BOTH legs + - 2 holes through the vertical leg (pivot end) + - 2 holes through the horizontal leg (deck end) +- All other parts have holes through one leg only +- Use cutting oil and drill slowly to prevent work hardening + +### Step 6: Verify and Label + +After completing each part type: +1. Verify all operations are checked off +2. Count pieces to ensure correct quantity +3. Mark each piece with the part code (use paint marker or label) +4. Store pieces organized by part code + +## Assembly Reference + +When assembling the machine: +- Each part is referenced by its code (A1, A2, etc.) +- The quantity tells you how many of each part should be used +- Part notes provide orientation and installation hints + +## Advanced: Jigs and Templates + +For high precision or multiple machines: + +1. **Drilling jig:** Create a drilling template for each part + - Use a piece of scrap angle iron as a template + - Drill holes in perfect positions + - Clamp template to workpiece for drilling + +2. **Cut stop:** Set up a stop block on your saw for consistent lengths + - Measure once, cut multiple pieces + - Ensures all pieces are exactly the same length + +## Automatic Updates + +This cut list is automatically regenerated when: +- Design files change in the OpenSCAD source +- The GitHub Action workflow runs +- Always use the latest PDF from the repository + +## Questions? + +If you find errors or need clarification: +- Open an issue on the GitHub repository +- Reference the specific part code in your question +- Include photos if asking about fabrication techniques + +--- + +**Part of:** LifeTrac v25 Open Source Utility Loader +**License:** GPL v3 +**Organization:** Open Source Ecology diff --git a/LifeTrac-v25/mechanical_design/README.md b/LifeTrac-v25/mechanical_design/README.md index 772bb09..ff9c2c7 100644 --- a/LifeTrac-v25/mechanical_design/README.md +++ b/LifeTrac-v25/mechanical_design/README.md @@ -323,3 +323,37 @@ The combined CNC layout includes all 23 sheet metal parts with complete manufact - [Bucket Side](output/svg/parts/bucket_side.svg) - 2× needed (mirror for opposite) Total: 23 parts from 8 unique designs + +## Structural Steel Cut List + +**[Download Cut List PDF](structural_steel_cut_list.pdf)** | **[Usage Guide](CUT_LIST_USAGE_GUIDE.md)** + +Printable cut list for all angle iron and square tubing parts. Each unique part has its own page with: +- Engineering drawing showing total length and hole positions +- Dimensions in both inches and millimeters +- Complete list of cutting and drilling operations with checkboxes +- Part code (A1, A2, etc.) for use in assembly +- Quantity needed for complete machine + +### Parts Summary + +| Part Code | Name | Material | Quantity | +|-----------|------|----------|----------| +| A1 | Platform Angle Arm | 2" × 2" × 1/4" Angle Iron | 2 | +| A2 | Frame Tube Mounting Angle | 2" × 2" × 1/4" Angle Iron | 16 | +| A3 | Loader Arm Mounting Angle | 2" × 2" × 1/4" Angle Iron | 4 | +| A4 | Side Panel Vertical Angle | 2" × 2" × 1/4" Angle Iron | 8 | +| A5 | Bottom Plate Horizontal Angle | 2" × 2" × 1/4" Angle Iron | 8 | + +### Using the Cut List + +1. **Download the PDF** - Click the link above to get the latest cut list +2. **Print each page** - One page per unique part type +3. **Purchase materials** - Buy angle iron stock per the specifications +4. **Cut to length** - Follow the dimensions on each page +5. **Drill holes** - Use the engineering drawing to mark and drill holes at specified positions +6. **Check off operations** - Mark each checkbox as you complete cutting and drilling +7. **Track quantity** - Make sure you fabricate the correct number of each part + +The cut list is automatically updated whenever changes are made to the machine's design files. + diff --git a/LifeTrac-v25/mechanical_design/assembly.png b/LifeTrac-v25/mechanical_design/assembly.png index 85e3582..c43f368 100644 Binary files a/LifeTrac-v25/mechanical_design/assembly.png and b/LifeTrac-v25/mechanical_design/assembly.png differ diff --git a/LifeTrac-v25/mechanical_design/generate_cut_list.py b/LifeTrac-v25/mechanical_design/generate_cut_list.py new file mode 100755 index 0000000..620562a --- /dev/null +++ b/LifeTrac-v25/mechanical_design/generate_cut_list.py @@ -0,0 +1,922 @@ +#!/usr/bin/env python3 +""" +Generate PDF cut list for LifeTrac v25 structural steel parts. +Creates one page per unique angle iron and square tubing part with: +- Engineering drawing showing dimensions and hole positions +- List of cutting and drilling operations with checkboxes +- Part code and quantity needed +- OpenSCAD rendered views (top, side, end, isometric) +""" + +import sys +import os +import subprocess +import tempfile +from reportlab.lib.pagesizes import letter +from reportlab.lib.units import inch, mm +from reportlab.pdfgen import canvas +from reportlab.lib import colors +from reportlab.platypus import Table, TableStyle +from reportlab.lib.utils import ImageReader +from PIL import Image + + +class StructuralPart: + """Represents a structural steel part (angle iron or square tubing)""" + + def __init__(self, part_code, name, material, length_mm, quantity, holes=None, notes=""): + self.part_code = part_code + self.name = name + self.material = material + self.length_mm = length_mm + self.length_inches = length_mm / 25.4 + self.quantity = quantity + self.holes = holes or [] + self.notes = notes + + # Categorize holes by leg + self.vertical_leg_holes = [] + self.horizontal_leg_holes = [] + for hole in self.holes: + if 'vertical leg' in hole.get('description', '').lower(): + self.vertical_leg_holes.append(hole) + elif 'horizontal leg' in hole.get('description', '').lower(): + self.horizontal_leg_holes.append(hole) + elif 'other leg' in hole.get('description', '').lower(): + # For parts with holes in both legs but not specifically labeled + self.horizontal_leg_holes.append(hole) + else: + # Default to vertical leg if not specified + self.vertical_leg_holes.append(hole) + + def get_operations(self): + """Generate list of cutting and drilling operations""" + operations = [] + + # Cutting operation + operations.append({ + "description": f"Cut {self.material} to {self.length_inches:.2f}\" ({self.length_mm:.1f} mm)", + "checked": False + }) + + # Drilling operations + for i, hole in enumerate(self.holes, 1): + position_inches = hole["position_mm"] / 25.4 + diameter_inches = hole["diameter_mm"] / 25.4 + # Convert diameter to fraction + frac = self.decimal_to_fraction(diameter_inches) + operations.append({ + "description": f"Drill {frac}\" hole at {position_inches:.2f}\" ({hole['position_mm']:.1f} mm) - {hole['description']}", + "checked": False + }) + + return operations + + @staticmethod + def decimal_to_fraction(decimal): + """Convert decimal inches to common fraction (public utility method)""" + # Common drill bit sizes + fractions = { + 0.125: "1/8", + 0.1875: "3/16", + 0.25: "1/4", + 0.3125: "5/16", + 0.375: "3/8", + 0.4375: "7/16", + 0.5: "1/2", + 0.5625: "9/16", + 0.625: "5/8", + 0.75: "3/4", + 0.875: "7/8", + 1.0: "1" + } + + # Find closest fraction + closest = min(fractions.keys(), key=lambda x: abs(x - decimal)) + if abs(closest - decimal) < 0.01: # Within 0.01" + return fractions[closest] + return f"{decimal:.3f}" + + +def draw_angle_iron_drawing(c, part, x_start, y_start, width, height): + """Draw engineering drawing for angle iron part""" + # Scale to fit drawing area + scale = min(width / (part.length_mm + 100), height / 150) + + # Center the drawing + drawing_width = part.length_mm * scale + x_offset = x_start + (width - drawing_width) / 2 + y_center = y_start + height / 2 + + # Draw angle iron profile (L-shape) - side view + leg_size_mm = 50.8 # 2" angle iron + thick_mm = 6.35 # 1/4" thickness + + # Draw main length line + c.setStrokeColor(colors.black) + c.setLineWidth(2) + c.line(x_offset, y_center, x_offset + drawing_width, y_center) + + # Cross-section profile removed per user request + + # Draw dimension line for total length (blue) + dim_y = y_center - 40 + c.setStrokeColor(colors.blue) + c.setLineWidth(0.5) + # Dimension line + c.line(x_offset, dim_y, x_offset + drawing_width, dim_y) + # End markers + c.line(x_offset, dim_y - 5, x_offset, dim_y + 5) + c.line(x_offset + drawing_width, dim_y - 5, x_offset + drawing_width, dim_y + 5) + # Dimension text + c.setFillColor(colors.blue) + c.setFont("Helvetica", 8) + dim_text = f"{part.length_inches:.2f}\" ({part.length_mm:.1f} mm)" + text_width = c.stringWidth(dim_text, "Helvetica", 8) + c.drawString(x_offset + (drawing_width - text_width) / 2, dim_y - 15, dim_text) + + # Draw holes and dimension lines for A-side (red) + if part.vertical_leg_holes: + dim_y_vertical = y_center + 30 + c.setStrokeColor(colors.red) + c.setLineWidth(0.5) + + for hole in part.vertical_leg_holes: + hole_x = x_offset + (hole["position_mm"] * scale) + hole_radius = (hole["diameter_mm"] * scale) / 2 + + # Draw hole + c.setStrokeColor(colors.red) + c.setFillColor(colors.white) + c.setLineWidth(1.5) + c.circle(hole_x, y_center, hole_radius, fill=1, stroke=1) + + # Draw dimension line from start to hole + c.setStrokeColor(colors.red) + c.setLineWidth(0.5) + c.line(x_offset, dim_y_vertical, hole_x, dim_y_vertical) + c.line(x_offset, dim_y_vertical - 3, x_offset, dim_y_vertical + 3) + c.line(hole_x, dim_y_vertical - 3, hole_x, dim_y_vertical + 3) + + # Dimension text + c.setFillColor(colors.red) + c.setFont("Helvetica", 7) + hole_pos_text = f"{hole['position_mm']:.1f}" + c.drawString(hole_x - 10, dim_y_vertical + 5, hole_pos_text) + + # Add label for A-side + c.setFont("Helvetica-Oblique", 7) + c.drawString(x_offset, dim_y_vertical + 18, "A-Side Holes") + + # Draw holes and dimension lines for B-side (green) + if part.horizontal_leg_holes: + dim_y_horizontal = y_center + 55 + + for hole in part.horizontal_leg_holes: + hole_x = x_offset + (hole["position_mm"] * scale) + hole_radius = (hole["diameter_mm"] * scale) / 2 + + # Draw hole with green circle + c.setStrokeColor(colors.green) + c.setFillColor(colors.white) + c.setLineWidth(1.5) + c.circle(hole_x, y_center, hole_radius, fill=1, stroke=1) + + # Draw dimension line from start to hole + c.setStrokeColor(colors.green) + c.setLineWidth(0.5) + c.line(x_offset, dim_y_horizontal, hole_x, dim_y_horizontal) + c.line(x_offset, dim_y_horizontal - 3, x_offset, dim_y_horizontal + 3) + c.line(hole_x, dim_y_horizontal - 3, hole_x, dim_y_horizontal + 3) + + # Dimension text + c.setFillColor(colors.green) + c.setFont("Helvetica", 7) + hole_pos_text = f"{hole['position_mm']:.1f}" + c.drawString(hole_x - 10, dim_y_horizontal + 5, hole_pos_text) + + # Add label for B-side + c.setFont("Helvetica-Oblique", 7) + c.setFillColor(colors.green) + c.drawString(x_offset, dim_y_horizontal + 18, "B-Side Holes") + + # Draw material callout + c.setFillColor(colors.black) + c.setFont("Helvetica-Bold", 10) + c.drawString(x_start, y_start + height + 10, f"Material: {part.material}") + + +def draw_operations_checklist(c, part, x_start, y_start, width, height): + """Draw operations checklist with checkboxes""" + operations = part.get_operations() + + # Title + c.setFont("Helvetica-Bold", 11) + c.setFillColor(colors.black) + c.drawString(x_start, y_start + height - 20, "Manufacturing Operations:") + + # Draw checkboxes and operations + y = y_start + height - 45 + checkbox_size = 10 + + c.setFont("Helvetica", 9) + for i, op in enumerate(operations, 1): + # Draw checkbox + c.setStrokeColor(colors.black) + c.setLineWidth(1) + c.rect(x_start, y - checkbox_size, checkbox_size, checkbox_size, fill=0, stroke=1) + + # Parse operation description to separate main action from position notes + description = op['description'] + # Check if there's a position note (contains " - " or "at ") + if ' - ' in description: + main_part, detail_part = description.split(' - ', 1) + # Draw main operation + c.setFillColor(colors.black) + c.drawString(x_start + checkbox_size + 5, y - 8, f"{i}. {main_part}") + # Draw detail as bullet point + y -= 15 + c.setFont("Helvetica", 8) + c.setFillColor(colors.darkgray) + c.drawString(x_start + checkbox_size + 15, y - 5, f"• {detail_part}") + c.setFont("Helvetica", 9) + y -= 5 + elif 'at ' in description and 'hole' in description.lower(): + # Split at "at" for hole positions + parts = description.split(' at ', 1) + if len(parts) == 2: + main_part = parts[0] + detail_part = f"at {parts[1]}" + # Draw main operation + c.setFillColor(colors.black) + c.drawString(x_start + checkbox_size + 5, y - 8, f"{i}. {main_part}") + # Draw detail as bullet point + y -= 15 + c.setFont("Helvetica", 8) + c.setFillColor(colors.darkgray) + c.drawString(x_start + checkbox_size + 15, y - 5, f"• {detail_part}") + c.setFont("Helvetica", 9) + y -= 5 + else: + # Draw as single line + c.setFillColor(colors.black) + c.drawString(x_start + checkbox_size + 5, y - 8, f"{i}. {description}") + else: + # Draw as single line + c.setFillColor(colors.black) + c.drawString(x_start + checkbox_size + 5, y - 8, f"{i}. {description}") + + y -= 20 + + if y < y_start + 50: + break + + # Add notes if any + if part.notes and y > y_start + 60: + y -= 15 + c.setFont("Helvetica-Oblique", 8) + c.setFillColor(colors.darkblue) + c.drawString(x_start, y, f"Notes: {part.notes}") + + +def generate_openscad_render(part_code, scad_file, camera_config, output_path, autocenter=True): + """ + Generate PNG render from OpenSCAD file + + Args: + part_code: Part code (A1, A2, etc.) + scad_file: Path to OpenSCAD file + camera_config: Dict with camera, rotation, projection settings + output_path: Where to save the PNG + autocenter: If True, use --autocenter and --viewall for better framing + + Returns: + True if successful, False otherwise + """ + try: + # Check if openscad is available + if subprocess.run(['which', 'openscad'], capture_output=True).returncode != 0: + print(f" ⚠️ OpenSCAD not found, skipping render for {part_code}") + return False + + # Build OpenSCAD command + cmd = [ + 'openscad', + '--render', + '--imgsize=800,600', + '--colorscheme=Tomorrow', + f'--projection={camera_config.get("projection", "ortho")}', + ] + + # Add camera or viewall + if autocenter: + cmd.extend([ + '--autocenter', + '--viewall', + ]) + + # Add camera rotation + if 'camera' in camera_config: + cmd.append(f'--camera={camera_config["camera"]}') + + cmd.extend(['-o', output_path, scad_file]) + + # Run with timeout + result = subprocess.run(cmd, capture_output=True, timeout=30, env={'DISPLAY': ':99'}) + + if result.returncode == 0 and os.path.exists(output_path): + return True + else: + print(f" ⚠️ OpenSCAD render failed for {part_code}: {result.stderr.decode()[:100]}") + return False + + except subprocess.TimeoutExpired: + print(f" ⚠️ OpenSCAD render timeout for {part_code}") + return False + except Exception as e: + print(f" ⚠️ Error rendering {part_code}: {str(e)}") + return False + + +def convert_to_grayscale_with_white_bg(image_path): + """Convert image to grayscale with white background for easier printing""" + try: + img = Image.open(image_path) + + # Convert to RGBA to handle transparency + if img.mode != 'RGBA': + img = img.convert('RGBA') + + # Create white background + white_bg = Image.new('RGBA', img.size, (255, 255, 255, 255)) + + # Composite image onto white background + composite = Image.alpha_composite(white_bg, img) + + # Convert to grayscale + grayscale = composite.convert('L') + + # Save as grayscale + grayscale.save(image_path) + return True + except Exception as e: + print(f" ⚠️ Error converting to grayscale: {str(e)}") + return False + + +def generate_part_renders(part_code, scad_module_name, temp_dir, part=None): + """ + Generate orthogonal and diagonal views of a part + + Args: + part_code: Part code (A1, A2, T1, etc.) + scad_module_name: Name of OpenSCAD module to render + temp_dir: Temporary directory for outputs + part: StructuralPart object with dimensions and hole data + + Returns: + Dict with paths to rendered images, or None if rendering failed + """ + # Map part codes to their dedicated SCAD files + scad_file_map = { + 'A1': '../openscad/parts/structural/angle_iron_a1_back_outer_vertical.scad', + 'A2': '../openscad/parts/structural/angle_iron_a2_back_inner_vertical.scad', + 'A3': '../openscad/parts/structural/angle_iron_a3_front_outer_vertical.scad', + 'A4': '../openscad/parts/structural/angle_iron_a4_frame_tube_mount.scad', + 'A5': '../openscad/parts/structural/angle_iron_a5_arm_crossbeam_mount.scad', + 'A6-1': '../openscad/parts/structural/angle_iron_a6_bottom_horizontal.scad', + 'A6-2': '../openscad/parts/structural/angle_iron_a6_bottom_horizontal.scad', + 'A6-3': '../openscad/parts/structural/angle_iron_a6_bottom_horizontal.scad', + 'A7': '../openscad/parts/structural/angle_iron_a7_platform_side.scad', + 'A8': '../openscad/parts/structural/angle_iron_a8_platform_transverse.scad', + 'A9': '../openscad/parts/structural/angle_iron_a9_front_center.scad', + 'A10': '../openscad/parts/structural/angle_iron_a10_front_outer.scad', + 'T1': '../openscad/parts/structural/tube_t1_front_frame.scad', + 'T2': '../openscad/parts/structural/tube_t2_rear_frame.scad', + 'T3': '../openscad/parts/structural/tube_t3_arm_crossbeam.scad', + 'T4': '../openscad/parts/structural/tube_t4_arm_main.scad', + 'T5': '../openscad/parts/structural/tube_t5_arm_leg_spacer.scad', + } + + # Get the SCAD file path + scad_file_rel = scad_file_map.get(part_code) + if not scad_file_rel: + print(f" ⚠️ No SCAD file defined for part {part_code}") + return None + + # Create absolute path from temp directory + scad_path = os.path.join(temp_dir, f'{part_code}.scad') + + # Get absolute path to structural_parts.scad + script_dir = os.path.dirname(os.path.abspath(__file__)) + structural_parts_path = os.path.join(script_dir, 'openscad/parts/structural/structural_parts.scad') + + # Create a wrapper SCAD file that includes the part file + with open(scad_path, 'w') as f: + # Use the structural_parts.scad master file for consistent module names + if part_code.startswith('A') or part_code.startswith('T'): + # Map part codes to their module names in structural_parts.scad + module_map = { + 'A1': 'part_a1_back_outer_vertical', + 'A2': 'part_a2_back_inner_vertical', + 'A3': 'part_a3_front_outer_vertical', + 'A4': 'part_a4_frame_tube_mount', + 'A5': 'part_a5_arm_crossbeam_mount', + 'A6-1': 'part_a6_bottom_segment_1', + 'A6-2': 'part_a6_bottom_segment_2', + 'A6-3': 'part_a6_bottom_segment_3', + 'A7': 'part_a7_platform_side_angle', + 'A8': 'part_a8_platform_transverse_angle', + 'A9': 'part_a9_front_center_angle', + 'A10': 'part_a10_front_outer_angle', + 'T1': 'part_t1_front_frame_tube', + 'T2': 'part_t2_rear_frame_tube', + 'T3': 'part_t3_arm_crossbeam', + 'T4': 'part_t4_arm_main', + 'T5': 'part_t5_arm_leg_spacer_cut', # Use cut version which accepts show_holes + } + + module_name = module_map.get(part_code) + if module_name: + f.write(f'''use <{structural_parts_path}> + +{module_name}(show_holes=true); +''') + + # Get part dimensions for camera positioning + if part: + if hasattr(part, 'length_mm'): + length = part.length_mm + elif hasattr(part, 'width_mm'): + # For square/rectangular tubing + length = max(part.width_mm, part.height_mm) if hasattr(part, 'height_mm') else part.width_mm + else: + length = 425.0 + + # Determine typical dimensions + if part_code.startswith('T'): + # Tubular parts - use tube dimensions + max_dim = 1200.0 # Approximate length of frame tubes + else: + # Angle iron parts + leg = 50.8 + max_dim = max(length, leg * 2) + else: + length = 425.0 + max_dim = 425.0 + + # Calculate camera distances based on part dimensions + # Using a tighter multiplier for better framing (90% of view area) + cam_dist = max_dim * 0.75 + + # Camera configurations for different views with better positioning + # For angle iron isometric: horizontal leg flat, vertical leg up, view into corner + views = { + 'top': { + 'camera': f'0,0,0,0,0,0,{cam_dist}', + 'projection': 'ortho' + }, + 'side': { + 'camera': f'{cam_dist},0,0,90,0,0,{cam_dist}', + 'projection': 'ortho' + }, + 'end': { + 'camera': f'0,{cam_dist},0,90,0,90,{cam_dist}', + 'projection': 'ortho' + }, + 'diagonal': { + # Isometric view: positioned to see both legs and all holes + # Rotation: 225° (viewing into the inside corner of the L) + # Elevation: 35° (good angle to see both horizontal and vertical surfaces) + 'camera': f'{cam_dist*0.7},{cam_dist*0.7},{cam_dist*0.4},35,0,225,{cam_dist}', + 'projection': 'ortho' + }, + } + + renders = {} + for view_name, camera_config in views.items(): + output_path = os.path.join(temp_dir, f'{part_code}_{view_name}.png') + if generate_openscad_render(part_code, scad_path, camera_config, output_path, autocenter=True): + convert_to_grayscale_with_white_bg(output_path) + renders[view_name] = output_path + else: + # If OpenSCAD fails, we'll skip rendering for this part + return None + + return renders if renders else None + + +def draw_part_renders(c, renders, x_start, y_start, width, height): + """ + Draw the 4 rendered views in a single column (vertical stack) + + Args: + c: Canvas object + renders: Dict with paths to rendered images + x_start, y_start: Position to start drawing + width, height: Available space + """ + if not renders: + return + + # Layout: Single column, 4 images stacked vertically + img_width = width + img_height = height / 4 - 5 # Divide height by 4 images with small gaps + + # Title + c.setFont("Helvetica-Bold", 10) + c.setFillColor(colors.black) + c.drawString(x_start, y_start + height + 5, "3D Views:") + + # Draw images in single column (vertical stack) + view_order = ['top', 'side', 'end', 'diagonal'] + + labels = { + 'top': 'Top View', + 'side': 'Side View', + 'end': 'End View', + 'diagonal': 'Isometric', + } + + for i, view_name in enumerate(view_order): + if view_name in renders: + try: + # Calculate Y position for this image (top to bottom) + y = y_start + height - (i + 1) * (img_height + 5) + + # Draw image + img = ImageReader(renders[view_name]) + c.drawImage(img, x_start, y, width=img_width, height=img_height, + preserveAspectRatio=True, mask='auto') + + # Draw label + c.setFont("Helvetica", 7) + c.setFillColor(colors.black) + c.drawString(x_start + 3, y + img_height - 10, labels[view_name]) + + except Exception as e: + print(f" ⚠️ Error drawing {view_name} render: {str(e)}") + + +def generate_cut_list_page(c, part, temp_dir=None): + """Generate one page for a structural part""" + width, height = letter + margin = 0.75 * inch + + # Header + c.setFont("Helvetica-Bold", 24) + c.setFillColor(colors.black) + c.drawString(margin, height - margin, f"Part {part.part_code}: {part.name}") + + # Part info box + c.setFont("Helvetica", 12) + info_y = height - margin - 40 + c.drawString(margin, info_y, f"Quantity Needed: {part.quantity}") + c.drawString(margin + 200, info_y, f"Material: {part.material}") + + # Draw horizontal line + c.setStrokeColor(colors.black) + c.setLineWidth(1) + c.line(margin, info_y - 10, width - margin, info_y - 10) + + # Engineering drawing section (full width) + drawing_y_start = info_y - 30 + drawing_height = 180 + draw_angle_iron_drawing(c, part, margin, drawing_y_start - drawing_height, + width - 2*margin, drawing_height) + + # Calculate layout for operations and 3D views side by side + content_y_start = drawing_y_start - drawing_height - 30 + content_height = 400 # Height for both operations and 3D views + + # 2/3 width for operations, 1/3 for 3D views + page_width = width - 2*margin + operations_width = page_width * 0.65 # 65% for operations (a bit more than 2/3) + renders_width = page_width * 0.30 # 30% for renders + gap = page_width * 0.05 # 5% gap between sections + + # Operations checklist section (left side, 2/3) + operations_x = margin + draw_operations_checklist(c, part, operations_x, content_y_start - content_height, + operations_width, content_height) + + # 3D Renders section (right side, 1/3) + if temp_dir: + renders_x = margin + operations_width + gap + + print(f" Generating 3D renders for {part.part_code}...") + renders = generate_part_renders(part.part_code, part.name.lower().replace(' ', '_'), temp_dir, part) + + if renders: + draw_part_renders(c, renders, renders_x, content_y_start - content_height, + renders_width, content_height) + else: + # If rendering failed, add a note in the 3D views area + c.setFont("Helvetica-Oblique", 8) + c.setFillColor(colors.grey) + # Wrap the text for narrow column + note = "(3D renders require OpenSCAD)" + c.drawString(renders_x, content_y_start - 10, note) + + # Footer + c.setFont("Helvetica-Oblique", 8) + c.setFillColor(colors.grey) + footer_text = "LifeTrac v25 - Open Source Ecology - GPL v3" + c.drawString(margin, margin - 20, footer_text) + c.drawRightString(width - margin, margin - 20, f"Part Code: {part.part_code}") + + c.showPage() + + +def get_structural_parts(): + """Define all structural steel parts for LifeTrac v25""" + parts = [] + + # A1: Back Stiffener - Outer Wall Vertical + # Height calculated from FRAME_Z_OFFSET + 350 to FRAME_Z_OFFSET + MACHINE_HEIGHT (~600mm) + parts.append(StructuralPart( + part_code="A1", + name="Back Stiffener Outer Vertical", + material='2" × 2" × 1/4" Angle Iron', + length_mm=600.0, + quantity=2, + holes=[ + {"position_mm": 50.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 200.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 350.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 500.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 100.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 250.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 400.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 550.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + ], + notes="Back stiffener plate outer walls. 3/8\" holes at ~150mm spacing on both legs." + )) + + # A2: Back Stiffener - Inner Wall Vertical + parts.append(StructuralPart( + part_code="A2", + name="Back Stiffener Inner Vertical", + material='2" × 2" × 1/4" Angle Iron', + length_mm=600.0, + quantity=4, + holes=[ + {"position_mm": 50.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 200.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 350.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 500.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 100.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 250.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 400.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 550.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + ], + notes="Back stiffener inner walls (2 per panel). Same dimensions as A1." + )) + + # A3: Front Stiffener - Outer Section Vertical (4.75") + parts.append(StructuralPart( + part_code="A3", + name="Front Stiffener Outer Vertical", + material='2" × 2" × 1/4" Angle Iron', + length_mm=120.65, + quantity=6, + holes=[ + {"position_mm": 30.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 90.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 45.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 75.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + ], + notes="Front stiffener 5\" sections. 3/8\" holes, 3\" spacing." + )) + + # A4: Frame Tube Mount Angles + parts.append(StructuralPart( + part_code="A4", + name="Frame Tube Mount Angle", + material='2" × 2" × 1/4" Angle Iron', + length_mm=146.05, + quantity=16, + holes=[ + {"position_mm": 47.625, "diameter_mm": 12.7, "description": "Wall mounting bolt 1 (vertical leg)"}, + {"position_mm": 98.425, "diameter_mm": 12.7, "description": "Wall mounting bolt 2 (vertical leg)"}, + {"position_mm": 47.625, "diameter_mm": 12.7, "description": "Tube mounting bolt 1 (horizontal leg)"}, + {"position_mm": 98.425, "diameter_mm": 12.7, "description": "Tube mounting bolt 2 (horizontal leg)"}, + ], + notes="Mounts 2×6 frame tubes to panels. 1/2\" holes: 4\" spacing on wall leg, 2\" spacing on tube leg." + )) + + # A5: Arm Crossbeam Mount Angles + parts.append(StructuralPart( + part_code="A5", + name="Arm Crossbeam Mount Angle", + material='2" × 2" × 1/4" Angle Iron', + length_mm=146.05, + quantity=4, + holes=[ + {"position_mm": 47.625, "diameter_mm": 12.7, "description": "Plate mounting bolt 1 (vertical leg)"}, + {"position_mm": 98.425, "diameter_mm": 12.7, "description": "Plate mounting bolt 2 (vertical leg)"}, + {"position_mm": 47.625, "diameter_mm": 12.7, "description": "Beam mounting bolt 1 (horizontal leg)"}, + {"position_mm": 98.425, "diameter_mm": 12.7, "description": "Beam mounting bolt 2 (horizontal leg)"}, + ], + notes="Connects loader arm crossbeam to arm plates. 2 per arm × 2 arms = 4 total." + )) + + # A6: Bottom Stiffener - Horizontal (3 segments) + # Segment 1 (rear) + parts.append(StructuralPart( + part_code="A6-1", + name="Bottom Horizontal Segment 1 (Rear)", + material='2" × 2" × 1/4" Angle Iron', + length_mm=200.0, + quantity=8, + holes=[ + {"position_mm": 50.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 150.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 75.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + ], + notes="Bottom plate rear segment. Split pattern with 8\" gaps at wheels." + )) + + # Segment 2 (middle) + parts.append(StructuralPart( + part_code="A6-2", + name="Bottom Horizontal Segment 2 (Middle)", + material='2" × 2" × 1/4" Angle Iron', + length_mm=300.0, + quantity=8, + holes=[ + {"position_mm": 50.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 200.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 75.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + {"position_mm": 225.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + ], + notes="Bottom plate middle segment (between wheels)." + )) + + # Segment 3 (front) + parts.append(StructuralPart( + part_code="A6-3", + name="Bottom Horizontal Segment 3 (Front)", + material='2" × 2" × 1/4" Angle Iron', + length_mm=250.0, + quantity=8, + holes=[ + {"position_mm": 50.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 200.0, "diameter_mm": 9.525, "description": "Plate mounting (horizontal leg)"}, + {"position_mm": 75.0, "diameter_mm": 9.525, "description": "Wall mounting (vertical leg)"}, + ], + notes="Bottom plate front segment." + )) + + # A7: Platform Side Angle Iron + parts.append(StructuralPart( + part_code="A7", + name="Platform Side Angle", + material='2" × 2" × 1/4" Angle Iron', + length_mm=400.0, + quantity=2, + holes=[ + {"position_mm": 60.0, "diameter_mm": 9.525, "description": "Pivot bracket bolt 1 (vertical leg)"}, + {"position_mm": 140.0, "diameter_mm": 9.525, "description": "Pivot bracket bolt 2 (vertical leg)"}, + {"position_mm": 260.0, "diameter_mm": 9.525, "description": "Deck bolt 1 (horizontal leg)"}, + {"position_mm": 340.0, "diameter_mm": 9.525, "description": "Deck bolt 2 (horizontal leg)"}, + ], + notes="Standing platform side arms. Make one left and one right (mirror image)." + )) + + # A8: Platform Transverse Angle Iron + parts.append(StructuralPart( + part_code="A8", + name="Platform Transverse Angle", + material='2" × 2" × 1/4" Angle Iron', + length_mm=300.0, + quantity=2, + holes=[ + {"position_mm": 75.0, "diameter_mm": 9.525, "description": "Deck mounting hole"}, + {"position_mm": 150.0, "diameter_mm": 9.525, "description": "Deck mounting hole"}, + {"position_mm": 225.0, "diameter_mm": 9.525, "description": "Deck mounting hole"}, + ], + notes="Platform transverse bracing (left-right across deck)." + )) + + # A9: Front Stiffener - Center Section (5.75") + parts.append(StructuralPart( + part_code="A9", + name="Front Center Angle", + material='2" × 2" × 1/4" Angle Iron', + length_mm=146.05, + quantity=2, + holes=[ + {"position_mm": 47.625, "diameter_mm": 12.7, "description": "Mounting bolt 1 (vertical leg)"}, + {"position_mm": 98.425, "diameter_mm": 12.7, "description": "Mounting bolt 2 (vertical leg)"}, + {"position_mm": 47.625, "diameter_mm": 12.7, "description": "Mounting bolt 1 (horizontal leg)"}, + {"position_mm": 98.425, "diameter_mm": 12.7, "description": "Mounting bolt 2 (horizontal leg)"}, + ], + notes="Front stiffener motor plate inner faces. Same as A4." + )) + + # A10: Front Stiffener - Outer Section (Motor Plate Sides) + parts.append(StructuralPart( + part_code="A10", + name="Front Outer Angle (Motor Plate)", + material='2" × 2" × 1/4" Angle Iron', + length_mm=120.65, + quantity=8, + holes=[ + {"position_mm": 30.0, "diameter_mm": 9.525, "description": "Mounting hole 1 (vertical leg)"}, + {"position_mm": 90.0, "diameter_mm": 9.525, "description": "Mounting hole 2 (vertical leg)"}, + {"position_mm": 45.0, "diameter_mm": 9.525, "description": "Mounting hole 1 (horizontal leg)"}, + {"position_mm": 75.0, "diameter_mm": 9.525, "description": "Mounting hole 2 (horizontal leg)"}, + ], + notes="Front stiffener outer sections and motor plate sides." + )) + + # T1: Front Cross Frame Tube + parts.append(StructuralPart( + part_code="T1", + name="Front Frame Tube", + material='2" × 6" × 1/4" Rectangular Tubing', + length_mm=1133.0, + quantity=1, + holes=[], + notes="Front cross frame tube. Length = TRACK_WIDTH + SANDWICH_SPACING + 2×PANEL_THICKNESS + extensions." + )) + + # T2: Rear Cross Frame Tube + parts.append(StructuralPart( + part_code="T2", + name="Rear Frame Tube", + material='2" × 6" × 1/4" Rectangular Tubing', + length_mm=1133.0, + quantity=1, + holes=[], + notes="Rear cross frame tube. Same length as T1." + )) + + # T3: Arm Crossbeam Tube + parts.append(StructuralPart( + part_code="T3", + name="Arm Crossbeam", + material='2" × 6" × 1/4" Rectangular Tubing', + length_mm=900.0, + quantity=1, + holes=[], + notes="Loader arm crossbeam connecting left and right arms. Length = ARM_SPACING." + )) + + # T4: Main Arm Tubes + parts.append(StructuralPart( + part_code="T4", + name="Main Arm Tube", + material='2" × 6" × 1/4" Rectangular Tubing', + length_mm=800.0, + quantity=2, + holes=[], + notes="Loader arm main sections (pivot to elbow). One per arm." + )) + + # T5: Arm Leg Spacer Tubes + parts.append(StructuralPart( + part_code="T5", + name="Arm Leg Spacer Tube", + material='2" × 6" × 1/4" Rectangular Tubing', + length_mm=150.0, + quantity=2, + holes=[], + notes="Spacer tubes at loader arm elbows. Plasma cut to tapered profile." + )) + + return parts + + +def main(): + """Generate the cut list PDF""" + output_file = "structural_steel_cut_list.pdf" + + print("Generating LifeTrac v25 Structural Steel Cut List...") + + # Create temporary directory for OpenSCAD renders + with tempfile.TemporaryDirectory() as temp_dir: + # Create PDF + c = canvas.Canvas(output_file, pagesize=letter) + + # Get all parts + parts = get_structural_parts() + + # Generate one page per part + for part in parts: + print(f" Adding page for {part.part_code}: {part.name} (Qty: {part.quantity})") + generate_cut_list_page(c, part, temp_dir) + + # Save PDF + c.save() + + print(f"\n✓ Generated {output_file}") + print(f" Total parts: {len(parts)}") + print(f" Total pages: {len(parts)}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/LifeTrac-v25/mechanical_design/lifetrac_v25_animation.gif b/LifeTrac-v25/mechanical_design/lifetrac_v25_animation.gif index 1721273..c701c86 100644 Binary files a/LifeTrac-v25/mechanical_design/lifetrac_v25_animation.gif and b/LifeTrac-v25/mechanical_design/lifetrac_v25_animation.gif differ diff --git a/LifeTrac-v25/mechanical_design/openscad/lifetrac_v25.scad b/LifeTrac-v25/mechanical_design/openscad/lifetrac_v25.scad index ae4faa6..c2055ed 100644 --- a/LifeTrac-v25/mechanical_design/openscad/lifetrac_v25.scad +++ b/LifeTrac-v25/mechanical_design/openscad/lifetrac_v25.scad @@ -25,6 +25,9 @@ use use use +// Import structural steel parts (individual fabrication-ready parts) +use + // ============================================================================= // GLOBAL PARAMETERS // ============================================================================= @@ -954,8 +957,9 @@ CROSS_TUBE_OUTER = TUBE_2X2_1_4[0]; // 50.8mm outer CROSS_TUBE_WALL = TUBE_2X2_1_4[1]; // 6.35mm wall CROSS_TUBE_INNER = CROSS_TUBE_OUTER - 2 * CROSS_TUBE_WALL; -// Cross beam span (between arm centerlines) -CROSS_BEAM_SPAN = ARM_SPACING; // 900mm +// Cross beam span (extends through inner arm CNC plates) +// Reduced by arm tube width (2") only - ends flush with inner face of plates +CROSS_BEAM_SPAN = ARM_SPACING - TUBE_2X6_1_4[0]; // ~849mm // Moment of inertia for cross beam CROSS_MOMENT_OF_INERTIA = (pow(CROSS_TUBE_OUTER, 4) - pow(CROSS_TUBE_INNER, 4)) / 12; @@ -1562,19 +1566,45 @@ module side_panel_left_outer() { // Left inner panel module side_panel_left_inner() { + // Front stiffener interface parameters + flat_section_height = 127.0; // 5" flat section at front + flat_section_top = BOTTOM_PLATE_INNER_TRIM + flat_section_height; // Top of 5" section + + // Calculate where the slope line intersects the flat section top + // Original slope goes from [WHEEL_BASE, MACHINE_HEIGHT * 0.65] to [200, MACHINE_HEIGHT] + // Slope = (MACHINE_HEIGHT - MACHINE_HEIGHT * 0.65) / (200 - WHEEL_BASE) + slope_rise = MACHINE_HEIGHT - MACHINE_HEIGHT * 0.65; + slope_run = 200 - WHEEL_BASE; + slope = slope_rise / slope_run; + // At Y = flat_section_top, find X on the slope line + // Y - (MACHINE_HEIGHT * 0.65) = slope * (X - WHEEL_BASE) + // X = WHEEL_BASE + (Y - MACHINE_HEIGHT * 0.65) / slope + slope_x_at_flat_top = WHEEL_BASE + (flat_section_top - MACHINE_HEIGHT * 0.65) / slope; + color("DarkSlateGray") translate([-(TRACK_WIDTH/2 - SANDWICH_SPACING/2), 0, FRAME_Z_OFFSET]) rotate([90, 0, 90]) difference() { side_panel(is_inner=true); - // Trim back for stiffener plate (Y < 31.75 in world coords -> X < 31.75 in part coords) - translate([-10, -50, -10]) // Start before 0 to be safe, Z range covers panel thickness - cube([31.75 + 10, MACHINE_HEIGHT + 100, 50]); // Cutout + // Cut off front section, leaving flat 5" section with angled continuation + // Remove everything in front of BOTTOM_PLATE_Y_END below flat_section_top + translate([BOTTOM_PLATE_Y_END, -50, -10]) + cube([WHEEL_BASE, flat_section_top + 50, 50]); + + // Remove the triangular section above the flat section (between flat top and slope) + // This creates the angled continuation from the 5" section to the existing slope + translate([0, 0, -10]) + linear_extrude(height=50) + polygon([ + [BOTTOM_PLATE_Y_END, flat_section_top], + [WHEEL_BASE + 100, flat_section_top], + [WHEEL_BASE + 100, MACHINE_HEIGHT * 0.65], + [WHEEL_BASE, MACHINE_HEIGHT * 0.65] + ]); // Trim entire bottom edge flat at plate height - // In panel coords: Y < BOTTOM_PLATE_INNER_TRIM for full length of panel translate([-10, -50, -10]) - cube([WHEEL_BASE + 100, BOTTOM_PLATE_INNER_TRIM + 50, 50]); // Full bottom cutout + cube([WHEEL_BASE + 100, BOTTOM_PLATE_INNER_TRIM + 50, 50]); // Cross tube cutouts cross_tube_panel_cutout(FRONT_FRAME_TUBE_Y); @@ -1594,19 +1624,33 @@ module side_panel_left_inner() { // Right inner panel module side_panel_right_inner() { + // Front stiffener interface parameters + flat_section_height = 127.0; // 5" flat section at front + flat_section_top = BOTTOM_PLATE_INNER_TRIM + flat_section_height; // Top of 5" section + color("DarkSlateGray") translate([(TRACK_WIDTH/2 - SANDWICH_SPACING/2 - PANEL_THICKNESS), 0, FRAME_Z_OFFSET]) rotate([90, 0, 90]) difference() { side_panel(is_inner=true); - // Trim back for stiffener plate (Y < 31.75 in world coords -> X < 31.75 in part coords) - translate([-10, -50, -10]) // Start before 0 to be safe, Z range covers panel thickness - cube([31.75 + 10, MACHINE_HEIGHT + 100, 50]); // Cutout + // Cut off front section, leaving flat 5" section with angled continuation + // Remove everything in front of BOTTOM_PLATE_Y_END below flat_section_top + translate([BOTTOM_PLATE_Y_END, -50, -10]) + cube([WHEEL_BASE, flat_section_top + 50, 50]); + + // Remove the triangular section above the flat section (between flat top and slope) + translate([0, 0, -10]) + linear_extrude(height=50) + polygon([ + [BOTTOM_PLATE_Y_END, flat_section_top], + [WHEEL_BASE + 100, flat_section_top], + [WHEEL_BASE + 100, MACHINE_HEIGHT * 0.65], + [WHEEL_BASE, MACHINE_HEIGHT * 0.65] + ]); // Trim entire bottom edge flat at plate height - // In panel coords: Y < BOTTOM_PLATE_INNER_TRIM for full length of panel translate([-10, -50, -10]) - cube([WHEEL_BASE + 100, BOTTOM_PLATE_INNER_TRIM + 50, 50]); // Full bottom cutout + cube([WHEEL_BASE + 100, BOTTOM_PLATE_INNER_TRIM + 50, 50]); // Cross tube cutouts cross_tube_panel_cutout(FRONT_FRAME_TUBE_Y); @@ -1936,6 +1980,7 @@ module horizontal_angle_iron_smart(length, size=[50.8, 6.35]) { } // Split horizontal angle iron - creates 3 segments with 8" gaps at wheel axes +// This implements Part A6 geometry from parts/structural/angle_iron_a6_bottom_horizontal.scad // y_offset is the Y position of the angle iron origin relative to bottom plate start module split_horizontal_angle_iron(size=[50.8, 6.35]) { leg = size[0]; @@ -2409,36 +2454,39 @@ module back_stiffener_plate() { } } - // Angles - // Left Outer (Legs +X, +Y) + // Angles - Using structural part modules from parts/structural/ + // Part A1: Back stiffener outer wall vertical angles (2 pcs) + // Part A2: Back stiffener inner wall vertical angles (4 pcs) + + // Left Outer (Legs +X, +Y) - Part A1 translate([-plate_width/2, y_pos + plate_thickness, z_start]) - vertical_angle_iron_smart(plate_height, angle_size); + part_a1_back_outer_vertical(show_holes=true); - // Right Outer (Legs -X, +Y) -> Rot Z 90 + // Right Outer (Legs -X, +Y) -> Rot Z 90 - Part A1 translate([plate_width/2, y_pos + plate_thickness, z_start]) rotate([0, 0, 90]) - vertical_angle_iron_smart(plate_height, angle_size); + part_a1_back_outer_vertical(show_holes=true); - // Inner Angles + // Inner Angles - Part A2 // Left Inner Panel // Left Side (Legs -X, +Y) -> Rot Z 90 translate([-inner_offset, y_pos + plate_thickness, z_start]) rotate([0, 0, 90]) - vertical_angle_iron_smart(plate_height, angle_size); + part_a2_back_inner_vertical(show_holes=true); // Right Side (Legs +X, +Y) translate([-inner_offset + PANEL_THICKNESS, y_pos + plate_thickness, z_start]) - vertical_angle_iron_smart(plate_height, angle_size); + part_a2_back_inner_vertical(show_holes=true); // Right Inner Panel // Left Side (Legs -X, +Y) -> Rot Z 90 translate([inner_offset - PANEL_THICKNESS, y_pos + plate_thickness, z_start]) rotate([0, 0, 90]) - vertical_angle_iron_smart(plate_height, angle_size); + part_a2_back_inner_vertical(show_holes=true); // Right Side (Legs +X, +Y) translate([inner_offset, y_pos + plate_thickness, z_start]) - vertical_angle_iron_smart(plate_height, angle_size); + part_a2_back_inner_vertical(show_holes=true); } // ============================================================================= @@ -2483,15 +2531,18 @@ module front_stiffener_plate() { inner_offset = TRACK_WIDTH/2 - SANDWICH_SPACING/2; - // Center section plate (between motor plates) + // Center section plate (between motor plates - extends to outer edge of motor plates) // Z position for angle iron center (for bolt hole placement) angle_center_z = angle_z_offset + frame_angle_height/2; // For center section outer_angle_center_z = outer_angle_z_offset + outer_angle_height/2; // For outer sections + // Center section width - from outer edge of left motor plate to outer edge of right motor plate + center_half_width = MOTOR_PLATE_X + MOTOR_PLATE_THICKNESS/2; + difference() { color("Silver") - translate([-MOTOR_PLATE_X, y_pos, z_start]) - cube([2*MOTOR_PLATE_X, plate_thickness, center_height]); + translate([-center_half_width, y_pos, z_start]) + cube([2*center_half_width, plate_thickness, center_height]); // Holes for motor plate angles - 2 holes spaced 4" apart (matching frame tube angle pattern) // Left motor plate - right side @@ -2511,11 +2562,11 @@ module front_stiffener_plate() { cylinder(d=frame_bolt_dia, h=20, center=true, $fn=16); } - // Left outer section plate (outer wall to motor plate) + // Left outer section plate (outer wall to outer edge of motor plate) difference() { color("Silver") translate([-plate_width/2, y_pos, z_start]) - cube([plate_width/2 - MOTOR_PLATE_X, plate_thickness, outer_height]); + cube([plate_width/2 - center_half_width, plate_thickness, outer_height]); // Holes for left outer wall angle - 2 holes spaced 3" apart (outer section) translate([-plate_width/2 + hole_offset, y_pos, z_start + outer_angle_center_z - outer_bolt_offset]) @@ -2550,11 +2601,11 @@ module front_stiffener_plate() { cylinder(d=outer_bolt_dia, h=20, center=true, $fn=16); } - // Right outer section plate (motor plate to outer wall) + // Right outer section plate (outer edge of motor plate to outer wall) difference() { color("Silver") - translate([MOTOR_PLATE_X, y_pos, z_start]) - cube([plate_width/2 - MOTOR_PLATE_X, plate_thickness, outer_height]); + translate([center_half_width, y_pos, z_start]) + cube([plate_width/2 - center_half_width, plate_thickness, outer_height]); // Holes for right outer wall angle - 2 holes spaced 3" apart (outer section) translate([plate_width/2 - hole_offset, y_pos, z_start + outer_angle_center_z - outer_bolt_offset]) @@ -2590,62 +2641,61 @@ module front_stiffener_plate() { } // Angle Irons - Outer sections use shorter angles (~4.75"), center uses frame tube angles (5.75") + // NOTE: Using part_a10 for outer sections (4.75") and part_a9/frame_tube_angle_iron for center (5.75") - // === OUTER SECTION ANGLES (shorter, ~4.75" for 5" sections) === + // === OUTER SECTION ANGLES (Part A10 - shorter, ~4.75" for 5" sections) === // Left Outer wall (Legs +X, -Y) -> Rot Z -90 translate([-plate_width/2, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, -90]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); // Right Outer wall (Legs -X, -Y) -> Rot Z 180 translate([plate_width/2, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, 180]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); // Left Inner Panel - left side (Legs -X, -Y) -> Rot Z 180 translate([-inner_offset, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, 180]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); // Left Inner Panel - right side (Legs +X, -Y) -> Rot Z -90 translate([-inner_offset + PANEL_THICKNESS, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, -90]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); // Right Inner Panel - left side (Legs -X, -Y) -> Rot Z 180 translate([inner_offset - PANEL_THICKNESS, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, 180]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); // Right Inner Panel - right side (Legs +X, -Y) -> Rot Z -90 translate([inner_offset, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, -90]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); - // === CENTER SECTION ANGLES (5.75" - exact same as frame tube angles) === - // Motor plate angles in 10" center section - use frame_tube_angle_iron module + // === CENTER SECTION ANGLES (Part A9 - 5.75" same as frame tube angles) === + // Motor plate angles in 10" center section - use part_a4 (same dimensions as A9) // Left motor plate - right side: vertical leg against plate (+X), horizontal leg toward -Y - // Legs +X, -Y -> Rot Z -90 (but origin at corner, so position at plate face) translate([-MOTOR_PLATE_X + MOTOR_PLATE_THICKNESS/2, y_pos, z_start + angle_z_offset]) rotate([0, 0, -90]) - frame_tube_angle_iron(); + part_a4_frame_tube_mount(show_holes=true); // Right motor plate - left side: vertical leg against plate (-X), horizontal leg toward -Y - // Legs -X, -Y -> Rot Z 180 translate([MOTOR_PLATE_X - MOTOR_PLATE_THICKNESS/2, y_pos, z_start + angle_z_offset]) rotate([0, 0, 180]) - frame_tube_angle_iron(); + part_a4_frame_tube_mount(show_holes=true); - // === OUTER SECTION ANGLES on motor plates (shorter, ~4.75" for 5" sections) === + // === OUTER SECTION ANGLES on motor plates (Part A10 - shorter, ~4.75" for 5" sections) === // Left motor plate - left side (Legs -X, -Y) -> Rot Z 180 translate([-MOTOR_PLATE_X - MOTOR_PLATE_THICKNESS/2, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, 180]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); // Right motor plate - right side (Legs +X, -Y) -> Rot Z -90 translate([MOTOR_PLATE_X + MOTOR_PLATE_THICKNESS/2, y_pos, z_start + outer_angle_z_offset]) rotate([0, 0, -90]) - vertical_angle_iron_smart(outer_angle_height, angle_size); + part_a10_front_outer_angle(show_holes=true); } // ============================================================================= @@ -2743,35 +2793,36 @@ module bottom_stiffener_plate() { // Angle Irons - horizontal along Y axis, X-leg flat on plate, Z-leg vertical against wall // Split into 3 segments with 8" gaps centered on wheel axes + // Part A6: Bottom stiffener horizontal angle iron (24 pieces total) - // Left Outer (X-leg extends in +X toward center, Z-leg goes up) + // Left Outer (X-leg extends in +X toward center, Z-leg goes up) - Part A6 translate([-plate_width/2, y_start, z_pos + plate_thickness]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Right Outer (X-leg extends in -X toward center, Z-leg goes up) -> mirror in X translate([plate_width/2, y_start, z_pos + plate_thickness]) mirror([1, 0, 0]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Inner Angles - Left Inner Panel // Left side (OUTSIDE - X-leg extends -X into sandwich gap, Z-leg goes up) -> mirror in X translate([-inner_offset, y_start, z_pos + plate_thickness]) mirror([1, 0, 0]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Right side (INSIDE - X-leg extends +X toward center, Z-leg goes up) -> NO mirror needed translate([-inner_offset + PANEL_THICKNESS, y_start, z_pos + plate_thickness]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Inner Angles - Right Inner Panel // Left side (INSIDE - X-leg extends -X toward center, Z-leg goes up) -> mirror in X translate([inner_offset - PANEL_THICKNESS, y_start, z_pos + plate_thickness]) mirror([1, 0, 0]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Right side (OUTSIDE - X-leg extends +X into sandwich gap, Z-leg goes up) -> NO mirror translate([inner_offset, y_start, z_pos + plate_thickness]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); } // ============================================================================= @@ -2966,27 +3017,28 @@ module motor_mounting_plate() { // ========================================================================== // HORIZONTAL ANGLE IRONS - Bottom edge (connecting to bottom stiffener plate) + // Part A6: Bottom stiffener horizontal angle iron // ========================================================================== // Left motor plate angles (2 angles - one on each side of plate) // Left side of left motor plate (X-leg extends -X, away from center) translate([-MOTOR_PLATE_X - plate_thickness/2, y_start, z_bottom]) mirror([1, 0, 0]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Right side of left motor plate (X-leg extends +X, toward center) translate([-MOTOR_PLATE_X + plate_thickness/2, y_start, z_bottom]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Right motor plate angles (2 angles - one on each side of plate) // Left side of right motor plate (X-leg extends -X, toward center) translate([MOTOR_PLATE_X - plate_thickness/2, y_start, z_bottom]) mirror([1, 0, 0]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // Right side of right motor plate (X-leg extends +X, away from center) translate([MOTOR_PLATE_X + plate_thickness/2, y_start, z_bottom]) - split_horizontal_angle_iron(angle_size); + part_a6_split_horizontal_angle_iron(show_holes=true); // ========================================================================== // VERTICAL ANGLE IRONS - At 2x6 frame tube locations (same as side wall plates) @@ -3048,7 +3100,8 @@ module cross_tube_cutout_profile() { square([FRAME_TUBE_WIDTH, FRAME_TUBE_HEIGHT]); } -// Single 2x6 square tube +// Single 2x6 square tube (legacy module - kept for compatibility) +// New code should use part_t1_front_frame_tube() or part_t2_rear_frame_tube() module cross_tube(y_pos) { color("SteelBlue") translate([-FRAME_TUBE_LENGTH/2, y_pos, FRAME_TUBE_Z]) @@ -3073,56 +3126,31 @@ module cross_tube(y_pos) { } // Both cross tubes +// Uses new individual part modules for fabrication-ready parts module cross_frame_tubes() { - cross_tube(FRONT_FRAME_TUBE_Y); - cross_tube(REAR_FRAME_TUBE_Y); - - // Angle iron mounts (8 total: 2 per tube × 2 sides × 2 tubes) + // Front frame tube (T1) + // Part module: length in X, origin at X=0 center, Y=0, Z=0 (bottom) + // Assembly needs: tube at Y=FRONT_FRAME_TUBE_Y, Z=FRAME_TUBE_Z + // Rotate to get 6" width in Y, 2" height in Z + translate([0, FRONT_FRAME_TUBE_Y + FRAME_TUBE_WIDTH, FRAME_TUBE_Z]) + rotate([90, 0, 0]) + part_t1_front_frame_tube(); + + // Rear frame tube (T2) + translate([0, REAR_FRAME_TUBE_Y + FRAME_TUBE_WIDTH, FRAME_TUBE_Z]) + rotate([90, 0, 0]) + part_t2_rear_frame_tube(); + + // Angle iron mounts (16 total: 2 per tube face × 4 wall panels × 2 tubes) frame_tube_angle_irons(); } // Angle iron for mounting frame tubes to side wall plates -// Same hole pattern as arm crossbeam angle irons -// Oriented with length running vertically (Z direction) -// Vertical leg against wall panel, horizontal leg extends toward tube +// NOTE: This is a wrapper around the new part_a4_frame_tube_mount() module +// Kept for backward compatibility with existing placement code module frame_tube_angle_iron() { - angle_trim = 3.175; // 1/8" trimmed from each end - len = FRAME_TUBE_HEIGHT - 2*angle_trim; // 6" minus 1/4" total = 5.75" (146.05mm) - leg = 50.8; // 2" leg - thick = 6.35; // 1/4" thick - bolt_dia = BOLT_DIA_1_2; // 1/2" bolts - - // Bolt spacing - closer together on tube side, further apart on wall side - plate_bolt_offset = 50.8; // 2.0 inches from center (4 inch spacing) for plate/wall - tube_bolt_offset = 25.4; // 1.0 inch from center (2 inch spacing) for tube - hole_inset = 25.4; // 1 inch from corner for hole placement - - color("DarkGray") - difference() { - union() { - // Vertical leg (against wall panel) - runs full height in Z, thin in X - cube([thick, leg, len]); - // Horizontal leg (toward tube) - extends in +X direction, full height - cube([leg, thick, len]); - } - // Holes in vertical leg (bolts through to wall plate in X direction) - // 2 holes spaced 5" apart (2.5" from center), 1" up from bottom of leg - translate([-1, leg/2, len/2 - plate_bolt_offset]) - rotate([0, 90, 0]) - cylinder(d=bolt_dia, h=thick+2, $fn=32); - translate([-1, leg/2, len/2 + plate_bolt_offset]) - rotate([0, 90, 0]) - cylinder(d=bolt_dia, h=thick+2, $fn=32); - - // Holes in horizontal leg (bolts through in Y direction into tube side) - // 2 holes spaced 2" apart (1" from center), 1" from corner - translate([hole_inset, -1, len/2 - tube_bolt_offset]) - rotate([-90, 0, 0]) - cylinder(d=bolt_dia, h=thick+2, $fn=32); - translate([hole_inset, -1, len/2 + tube_bolt_offset]) - rotate([-90, 0, 0]) - cylinder(d=bolt_dia, h=thick+2, $fn=32); - } + // Use the new standardized part module + part_a4_frame_tube_mount(show_holes=true); } // Place all 16 angle irons for frame tube mounting (2 per wall plate × 4 wall plates × 2 tubes) @@ -3803,43 +3831,12 @@ module loader_arms() { translate([0, -TUBE_2X6_1_4[0]/2, -TUBE_2X6_1_4[1]/2]) loader_arm_v2(angle=0, side="right"); - // Cross Beam + // Cross Beam - Part T3 // Connects the elbow assemblies at the calculated mounting position // CROSS_BEAM_1_POS is calculated in params to align cylinder geometry translate([0, CROSS_BEAM_1_POS, 0]) { - difference() { - rotate([0, 90, 0]) - // Rounded 2x6 tube - // Oriented flat: 6" wide (Y), 2" tall (Z) - linear_extrude(height=ARM_SPACING, center=true) - hull() { - w = 50.8; // 2 inches (Vertical Z) - h = 152.4; // 6 inches (Along arm Y) - r = 12.7; // 1/2 inch radius - translate([-w/2 + r, -h/2 + r]) circle(r=r); - translate([w/2 - r, -h/2 + r]) circle(r=r); - translate([w/2 - r, h/2 - r]) circle(r=r); - translate([-w/2 + r, h/2 - r]) circle(r=r); - } - - // Mounting Holes for Angle Irons - // Located at ends of beam, top and bottom - // X position: ARM_SPACING/2 - (Tube/2 + Plate + AngleHoleOffset) - // AngleHoleOffset = 25.4 (1 inch) - // Tube/2 = 25.4 - // Plate = 6.35 - // X = +/- (ARM_SPACING/2 - 57.15) - - hole_x_offset = ARM_SPACING/2 - (25.4 + ARM_PLATE_THICKNESS + 25.4); - hole_y_spacing = 25.4; // Offset from center of beam (2 inch spacing total) - - for (x_pos = [-hole_x_offset, hole_x_offset]) { - for (y_pos = [-hole_y_spacing, hole_y_spacing]) { - translate([x_pos, y_pos, 0]) - cylinder(d=BOLT_DIA_1_2, h=100, center=true, $fn=32); - } - } - } + // Use Part T3 arm crossbeam tube + part_t3_arm_crossbeam(show_holes=true); // Cylinder Mounts on Cross Beam for (side = [-1, 1]) { @@ -4188,6 +4185,8 @@ module bucket_cylinders() { // Folds from stowed (vertical against rear crossmember) to deployed (horizontal) // Uses 1" pivot pin and 3/8" cotter pin for locking in both positions +// Transverse angle iron for platform deck - Part A8 geometry +// This module is kept for the complex hole placement logic module platform_transverse_angle(length) { angle_bolt_hole_dia = PLATFORM_BOLT_DIA + PLATFORM_BOLT_CLEARANCE; leg = PLATFORM_ANGLE_LEG; @@ -4219,6 +4218,8 @@ module platform_transverse_angle(length) { } } +// Side angle iron for platform arms - Part A7 geometry +// This module is kept for the complex hole placement logic module platform_angle_iron(length=0, is_side=false) { angle_bolt_hole_dia = PLATFORM_BOLT_DIA + PLATFORM_BOLT_CLEARANCE; // Use provided length or default diff --git a/LifeTrac-v25/mechanical_design/openscad/modules/loader_arm_v2.scad b/LifeTrac-v25/mechanical_design/openscad/modules/loader_arm_v2.scad index c90a692..372e740 100644 --- a/LifeTrac-v25/mechanical_design/openscad/modules/loader_arm_v2.scad +++ b/LifeTrac-v25/mechanical_design/openscad/modules/loader_arm_v2.scad @@ -1,5 +1,6 @@ include <../lifetrac_v25_params.scad> use <../parts/arm_plate.scad> +use <../parts/structural/structural_parts.scad> module loader_arm_v2(angle=0, side="left") { main_tube_len = ARM_MAIN_LEN; @@ -29,29 +30,12 @@ module loader_arm_v2(angle=0, side="left") { // Determine which plate is the "Inner" plate (facing machine center) is_left = (side == "left"); + // Arm crossbeam mount angle iron - uses Part A5 + // This is a wrapper that orients part_a5 for the arm assembly module angle_iron_mount() { - angle_trim = 3.175; // 1/8" trimmed from each end - len = 152.4 - 2*angle_trim; // 6" minus 1/4" total = 5.75" (146.05mm) - leg = 50.8; - thick = 6.35; - - // Bolt spacing from center - plate_bolt_offset = 50.8; // 2.0 inches from center (4 inch spacing) - Wider - beam_bolt_offset = 25.4; // 1.0 inch from center (2 inch spacing) - Narrower - - difference() { - union() { - cube([len, thick, leg]); // Vertical leg (against plate) - cube([len, leg, thick]); // Horizontal leg (against beam) - } - // Holes - // Vertical leg (Plate bolts) - translate([len/2 - plate_bolt_offset, thick + 1, 25.4]) rotate([90,0,0]) cylinder(d=BOLT_DIA_1_2, h=thick+2, $fn=32); - translate([len/2 + plate_bolt_offset, thick + 1, 25.4]) rotate([90,0,0]) cylinder(d=BOLT_DIA_1_2, h=thick+2, $fn=32); - // Horizontal leg (Beam bolts) - translate([len/2 - beam_bolt_offset, 25.4, -1]) cylinder(d=BOLT_DIA_1_2, h=thick+2, $fn=32); - translate([len/2 + beam_bolt_offset, 25.4, -1]) cylinder(d=BOLT_DIA_1_2, h=thick+2, $fn=32); - } + // Part A5 dimensions (for reference): + // len = 146.05mm (5.75"), leg = 50.8mm (2"), thick = 6.35mm (1/4") + part_a5_arm_crossbeam_mount(show_holes=true); } rotate([0, -angle, 0]) { diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/STRUCTURAL_PARTS_CATALOG.md b/LifeTrac-v25/mechanical_design/openscad/parts/structural/STRUCTURAL_PARTS_CATALOG.md new file mode 100644 index 0000000..111c2b3 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/STRUCTURAL_PARTS_CATALOG.md @@ -0,0 +1,216 @@ +# LifeTrac v25 Structural Steel Parts Catalog + +This document catalogs all structural steel parts (angle iron and tubing) used in the LifeTrac v25 assembly. + +## Summary + +| Category | Part IDs | Total Pieces | Material | +|----------|----------|--------------|----------| +| Back Stiffener Plate Angles | A1, A2 | 6 | 2"×2"×1/4" Angle Iron | +| Front Stiffener Plate Angles | A3, A9, A10 | 16 | 2"×2"×1/4" Angle Iron | +| Frame Tube Mount Angles | A4 | 16 | 2"×2"×1/4" Angle Iron | +| Arm Crossbeam Mount Angles | A5 | 4 | 2"×2"×1/4" Angle Iron | +| Bottom Stiffener Plate Angles | A6 | 24 | 2"×2"×1/4" Angle Iron | +| Standing Platform Angles | A7, A8 | 4 | 2"×2"×1/4" Angle Iron | +| Cross Frame Tubes | T1, T2 | 2 | 2"×6"×1/4" Rect. Tube | +| Arm Crossbeam Tube | T3 | 1 | 2"×6"×1/4" Rect. Tube | +| Main Arm Tubes | T4 | 2 | 2"×6"×1/4" Rect. Tube | +| Arm Leg Spacer Tubes | T5 | 2 | 2"×6"×1/4" Rect. Tube | +| **TOTAL** | **15 unique** | **77** | | + +--- + +## Angle Iron Parts (A1-A10) + +All angle iron uses 2"×2"×1/4" (50.8mm × 6.35mm) standard stock. + +### A1: Back Stiffener - Outer Wall Vertical +- **File:** `angle_iron_a1_back_outer_vertical.scad` +- **Quantity:** 2 pieces +- **Location:** Back stiffener plate, left and right outer walls +- **Height:** Parametric (~600mm, calculated from `FRAME_Z_OFFSET + 350` to `FRAME_Z_OFFSET + MACHINE_HEIGHT`) +- **Holes:** 3/8" diameter bolts + - Leg A (against wall): Multiple holes at 150mm spacing + - Leg B (against plate): Multiple holes at 150mm spacing, offset +- **Assembly Integration:** ✅ `part_a1_back_outer_vertical()` in `back_stiffener_plate()` + +### A2: Back Stiffener - Inner Wall Vertical +- **File:** `angle_iron_a2_back_inner_vertical.scad` +- **Quantity:** 4 pieces (2 per inner panel) +- **Location:** Back stiffener plate, both sides of each inner wall panel +- **Height:** Same as A1 (~600mm) +- **Holes:** Same pattern as A1 +- **Assembly Integration:** ✅ `part_a2_back_inner_vertical()` in `back_stiffener_plate()` + +### A3: Front Stiffener - Outer Section Vertical +- **File:** `angle_iron_a3_front_outer_vertical.scad` +- **Quantity:** 6 pieces (outer walls + inner wall faces) +- **Location:** Front stiffener plate outer sections (5" tall sections) +- **Height:** ~120.65mm (4.75") - `127mm - 2×3.175mm trim` +- **Holes:** 3/8" diameter, 2 per leg at 3" spacing +- **Assembly Integration:** Available but front_stiffener_plate uses A10 for these locations + +### A4: Frame Tube Mount Angles +- **File:** `angle_iron_a4_frame_tube_mount.scad` +- **Quantity:** 16 pieces (2 tubes × 4 walls × 2 faces) +- **Location:** At each 2×6 frame tube, mounting to wall panels +- **Height:** ~146.05mm (5.75") - `152.4mm - 2×3.175mm trim` +- **Holes:** 1/2" diameter + - Leg A (against wall): 2 holes at 4" (101.6mm) spacing + - Leg B (against tube): 2 holes at 2" (50.8mm) spacing +- **Assembly Integration:** ✅ `part_a4_frame_tube_mount()` in `frame_tube_angle_iron()`, `front_stiffener_plate()` + +### A5: Arm Crossbeam Mount Angles +- **File:** `angle_iron_a5_arm_crossbeam_mount.scad` +- **Quantity:** 4 pieces (2 per arm) +- **Location:** Connecting arm side plates to cross beam +- **Height:** ~146.05mm (5.75") +- **Holes:** Same pattern as A4 +- **Assembly Integration:** ✅ `part_a5_arm_crossbeam_mount()` in `loader_arm_v2.scad` + +### A6: Bottom Stiffener - Horizontal (Split into 3 Segments) +- **File:** `angle_iron_a6_bottom_horizontal.scad` +- **Quantity:** 24 pieces total (3 segments × 8 locations) +- **Location:** Bottom stiffener plate, all wall faces + motor plate faces +- **Orientation:** Horizontal along Y axis, X-leg flat on plate, Z-leg vertical against wall +- **Segments:** Split into 3 segments with 8" gaps at wheel axes + - Segment 1 (rear): `ANGLE_SEGMENT_1_LENGTH` + - Segment 2 (middle): `ANGLE_SEGMENT_2_LENGTH` + - Segment 3 (front): `ANGLE_SEGMENT_3_LENGTH` +- **Holes:** 3/8" diameter at ~150mm spacing +- **Assembly Integration:** ✅ `part_a6_split_horizontal_angle_iron()` in `bottom_stiffener_plate()`, motor plate angles + +### A7: Platform Side Angle Iron +- **File:** `angle_iron_a7_platform_side.scad` +- **Quantity:** 2 pieces (left and right, mirrored) +- **Location:** Folding standing platform side arms +- **Length:** Calculated from platform geometry (~400mm) +- **Holes:** 4 total (2 pivot bracket, 2 deck attachment) +- **Assembly Integration:** Part available, assembly uses `platform_angle_iron()` due to complex positioning + +### A8: Platform Transverse Angle Iron +- **File:** `angle_iron_a8_platform_transverse.scad` +- **Quantity:** 2 pieces (front and rear) +- **Location:** Platform transverse bracing (left-right across deck) +- **Length:** Platform width minus clearances +- **Holes:** 3 per piece for deck mounting +- **Assembly Integration:** Part available, assembly uses `platform_transverse_angle()` due to complex positioning + +### A9: Front Stiffener - Center Section Vertical +- **File:** `angle_iron_a9_front_center.scad` +- **Quantity:** 2 pieces (motor plate inner faces) +- **Location:** Front stiffener plate center section (10" tall) +- **Height:** ~146.05mm (5.75") - same as A4 +- **Holes:** 1/2" diameter, 2 per leg at 4" spacing +- **Note:** Same dimensions as A4, could use A4 in assembly +- **Assembly Integration:** A4 is used in assembly at these locations + +### A10: Front Stiffener - Outer Section (Motor Plate Sides) +- **File:** `angle_iron_a10_front_outer.scad` +- **Quantity:** 8 pieces (outer walls + motor plate outer faces) +- **Location:** Front stiffener plate outer sections (5" sections) +- **Height:** ~120.65mm (4.75") +- **Holes:** 3/8" diameter, 2 per leg at 3" spacing +- **Assembly Integration:** ✅ `part_a10_front_outer_angle()` in `front_stiffener_plate()` + +--- + +## Tubing Parts (T1-T5) + +All tubing uses 2"×6"×1/4" (50.8mm × 152.4mm × 6.35mm wall) rectangular tube. + +### T1: Front Cross Frame Tube +- **File:** `tube_t1_front_frame.scad` +- **Quantity:** 1 piece +- **Location:** Front frame cross tube, behind front wheels +- **Length:** `FRAME_TUBE_LENGTH` (~1133mm with 1/2" extensions past outer panels) +- **Features:** Rounded corners per actual tube profile +- **Assembly Integration:** ✅ `part_t1_front_frame_tube()` in `cross_frame_tubes()` + +### T2: Rear Cross Frame Tube +- **File:** `tube_t2_rear_frame.scad` +- **Quantity:** 1 piece +- **Location:** Rear frame cross tube, in front of rear wheels +- **Length:** Same as T1 +- **Assembly Integration:** ✅ `part_t2_rear_frame_tube()` in `cross_frame_tubes()` + +### T3: Arm Crossbeam Tube +- **File:** `tube_t3_arm_crossbeam.scad` +- **Quantity:** 1 piece +- **Location:** Connects left and right loader arms at crossbeam position +- **Length:** `ARM_SPACING` (~900mm) +- **Orientation:** 6" wide, 2" tall +- **Holes:** 4 mounting holes at each end for angle iron attachment +- **Assembly Integration:** ✅ `part_t3_arm_crossbeam()` in `loader_arms()` + +### T4: Main Arm Tube +- **File:** `tube_t4_arm_main.scad` +- **Quantity:** 2 pieces (left and right) +- **Location:** Loader arm main section (pivot to elbow) +- **Length:** Calculated from arm geometry +- **Holes:** 16 total (8 rear for pivot mount, 8 front for elbow) +- **Assembly Integration:** Part available, not yet called in assembly + +### T5: Arm Leg Spacer Tube +- **File:** `tube_t5_arm_leg_spacer.scad` +- **Quantity:** 2 pieces (one per arm) +- **Location:** Spacer tube at elbow of each loader arm +- **Length:** ~150mm stock, plasma cut to tapered profile +- **Features:** + - `part_t5_arm_leg_spacer_raw()` - raw stock for ordering + - `part_t5_arm_leg_spacer_cut()` - final tapered shape +- **Assembly Integration:** Part available, not yet called in assembly + +--- + +## Material Requirements Summary + +### 2"×2"×1/4" Angle Iron +- Total unique parts: 10 (A1-A10) +- Total pieces: 70 +- Estimated total length: ~45 meters (varies by configuration) + +### 2"×6"×1/4" Rectangular Tube +- Total unique parts: 5 (T1-T5) +- Total pieces: 7 +- Estimated total length: ~6.5 meters + +--- + +## Usage + +### In OpenSCAD Assembly Files + +```openscad +// Include the master structural parts file +use + +// Then call any part module: +part_a1_back_outer_vertical(); // Back stiffener outer angle +part_a4_frame_tube_mount(); // Frame tube mount angle +part_t1_front_frame_tube(); // Front frame cross tube +part_a6_split_horizontal_angle_iron(); // All 3 bottom plate segments +``` + +### Individual Part Preview + +Open any individual part file directly in OpenSCAD to see that specific part rendered with dimensions echoed to the console. + +--- + +## Notes + +1. All dimensions are parametric and calculated from `lifetrac_v25_params.scad` +2. Hole positions are generated by helper functions for consistent spacing +3. Bottom plate angles (A6) use split pattern with 8" gaps at wheel positions +4. Frame tube angles (A4) have matching bolt patterns for tube-to-wall mounting +5. Platform angles (A7, A8) have complex positioning - individual files available but assembly uses local modules +6. Parts T4 and T5 are defined but not yet integrated into assembly + +## File Naming Convention + +- Angle Iron: `angle_iron_a[N]_[description].scad` +- Tubing: `tube_t[N]_[description].scad` +- Master Include: `structural_parts.scad` + +Where [N] is the part number and [description] is a brief descriptive name. diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a10_front_outer.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a10_front_outer.scad new file mode 100644 index 0000000..0548395 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a10_front_outer.scad @@ -0,0 +1,94 @@ +// Part A10: Front Stiffener Outer Section Angle Iron +// Material: 2"×2"×1/4" Angle Iron (50.8mm leg × 6.35mm thickness) +// Quantity: 8 pieces total +// - 2 on outer walls (left and right) +// - 4 on inner walls (2 per side, both faces) +// - 2 on motor plates, outer side (left and right) +// Location: Front stiffener plate, 5" outer sections +// +// Shorter than center angles: ~4.75" (120.65mm) +// Used in the 5" tall outer sections between motor plates and outer walls +// Bolt pattern: 3/8" bolts, 3" spacing + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +_leg = 50.8; // 2" angle leg +_thick = 6.35; // 1/4" thickness +_trim = 3.175; // 1/8" trimmed from each end +_outer_height = 127.0; // 5" outer section height + +PART_A10_HEIGHT = _outer_height - 2*_trim; // ~120.65mm (4.75") +PART_A10_LEG = _leg; +PART_A10_THICK = _thick; + +// Bolt parameters (smaller pattern for outer sections) +_bolt_dia = is_undef(BOLT_DIA_3_8) ? 9.525 : BOLT_DIA_3_8; // 3/8" bolts +_outer_bolt_offset = 38.1; // 1.5" from center (3" total spacing) +_hole_offset = _leg / 2; // Center of angle leg + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A10: Front Stiffener Outer Section Angle Iron + * Vertical angle iron for 5" outer sections of front stiffener + * show_holes: if true, renders bolt holes + */ +module _part_a10_front_outer_angle(show_holes=true) { + height = PART_A10_HEIGHT; + leg = PART_A10_LEG; + thick = PART_A10_THICK; + + color("Silver") + difference() { + // L-profile + linear_extrude(height=height) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Vertical leg holes (through thickness, bolt to plate) + // 2 holes at 3" spacing centered on height + _center_z = height / 2; + for (z_off = [-_outer_bolt_offset, _outer_bolt_offset]) { + translate([_hole_offset, -1, _center_z + z_off]) + rotate([-90, 0, 0]) + cylinder(d=_bolt_dia, h=thick + 2, $fn=32); + } + + // Horizontal leg holes (through thickness, bolt to stiffener plate) + for (z_off = [-_outer_bolt_offset, _outer_bolt_offset]) { + translate([-1, _hole_offset, _center_z + z_off]) + rotate([0, 90, 0]) + cylinder(d=_bolt_dia, h=thick + 2, $fn=32); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a10_front_outer_angle(show_holes=true); + +echo("=== PART A10: Front Stiffener Outer Section Angle Iron ==="); +echo("Height:", PART_A10_HEIGHT, "mm (", PART_A10_HEIGHT/25.4, "in)"); +echo("Leg:", PART_A10_LEG, "mm (2 inches)"); +echo("Thickness:", PART_A10_THICK, "mm (1/4 inch)"); +echo("Bolt pattern: 2 holes per leg, 3\" spacing, 3/8\" dia"); +echo("Quantity needed: 8 pieces total"); +echo(" - 2 at outer walls (left, right)"); +echo(" - 4 at inner walls (2 per side)"); +echo(" - 2 at motor plates outer face (left, right)"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a1_back_outer_vertical.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a1_back_outer_vertical.scad new file mode 100644 index 0000000..905a19b --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a1_back_outer_vertical.scad @@ -0,0 +1,118 @@ +// Part A1: Back Stiffener - Outer Wall Vertical Angle Iron +// Material: 2"×2"×1/4" Angle Iron (50.8mm × 6.35mm) +// Quantity: 2 pieces +// Location: Back stiffener plate, left and right outer walls +// +// This file defines a single structural steel part used in the LifeTrac v25 assembly. +// The angle iron runs vertically, connecting the back stiffener plate to the outer wall panels. + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS (calculated from assembly parameters) +// ============================================================================= + +// Back stiffener plate vertical extent +_back_y_pos = 25.4; // 1 inch from rear +_back_z_start = FRAME_Z_OFFSET + 350; +_back_z_end = FRAME_Z_OFFSET + MACHINE_HEIGHT; + +// Part dimensions +PART_A1_HEIGHT = _back_z_end - _back_z_start; +PART_A1_LEG = 50.8; // 2" leg +PART_A1_THICK = 6.35; // 1/4" thickness +PART_A1_BOLT_DIA = 9.525; // 3/8" bolts +PART_A1_HOLE_OFFSET = PART_A1_LEG * 0.6; // Offset from corner for tool clearance + +// Hole spacing pattern (parametric based on height) +function get_stiffener_holes_a_local(h) = + let( + start_offset = 50, + end_offset = 50, + spacing = 150, + count = floor((h - start_offset - end_offset) / spacing) + 1 + ) + [for (i = [0:count-1]) start_offset + i * spacing]; + +function get_stiffener_holes_b_local(h) = + let( + start_offset = 100, + end_offset = 50, + spacing = 150, + count = floor((h - start_offset - end_offset) / spacing) + 1 + ) + [for (i = [0:count-1]) start_offset + i * spacing]; + +// Generate hole position arrays +PART_A1_HOLES_LEG_A = get_stiffener_holes_a_local(PART_A1_HEIGHT); +PART_A1_HOLES_LEG_B = get_stiffener_holes_b_local(PART_A1_HEIGHT); + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A1: Back Stiffener Outer Wall Vertical Angle Iron + * + * Orientation: + * - Extrudes along Z axis (vertical) + * - Corner at origin [0,0,0] + * - Leg A extends along +X (against wall panel) + * - Leg B extends along +Y (against stiffener plate) + * + * @param show_holes If true, drill bolt holes; if false, show solid angle + */ +module _part_a1_back_outer_vertical(show_holes=true) { + height = PART_A1_HEIGHT; + leg = PART_A1_LEG; + thick = PART_A1_THICK; + bolt_dia = PART_A1_BOLT_DIA; + hole_offset = PART_A1_HOLE_OFFSET; + + color("DarkGray") + difference() { + // L-profile extruded along Z + // Origin at corner, legs extend along +X and +Y + linear_extrude(height=height) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Holes on Leg A (X-leg) - bolts through in Y direction + for (z = PART_A1_HOLES_LEG_A) { + translate([hole_offset, 0, z]) + rotate([90, 0, 0]) + cylinder(d=bolt_dia, h=40, center=true, $fn=16); + } + + // Holes on Leg B (Y-leg) - bolts through in X direction + for (z = PART_A1_HOLES_LEG_B) { + translate([0, hole_offset, z]) + rotate([0, 90, 0]) + cylinder(d=bolt_dia, h=40, center=true, $fn=16); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +// Render the part when this file is opened directly +_part_a1_back_outer_vertical(show_holes=true); + +// Echo part specifications +echo("=== PART A1: Back Stiffener Outer Wall Vertical ==="); +echo("Height:", PART_A1_HEIGHT, "mm"); +echo("Leg size:", PART_A1_LEG, "mm"); +echo("Thickness:", PART_A1_THICK, "mm"); +echo("Bolt diameter:", PART_A1_BOLT_DIA, "mm"); +echo("Holes Leg A:", len(PART_A1_HOLES_LEG_A)); +echo("Holes Leg B:", len(PART_A1_HOLES_LEG_B)); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a2_back_inner_vertical.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a2_back_inner_vertical.scad new file mode 100644 index 0000000..4982b35 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a2_back_inner_vertical.scad @@ -0,0 +1,86 @@ +// Part A2: Back Stiffener - Inner Wall Vertical Angle Iron +// Material: 2"×2"×1/4" Angle Iron (50.8mm × 6.35mm) +// Quantity: 4 pieces (2 per inner panel, both sides) +// Location: Back stiffener plate, adjacent to inner wall panels +// +// This file defines a single structural steel part used in the LifeTrac v25 assembly. + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +_back_z_start = FRAME_Z_OFFSET + 350; +_back_z_end = FRAME_Z_OFFSET + MACHINE_HEIGHT; + +PART_A2_HEIGHT = _back_z_end - _back_z_start; +PART_A2_LEG = 50.8; +PART_A2_THICK = 6.35; +PART_A2_BOLT_DIA = 9.525; +PART_A2_HOLE_OFFSET = PART_A2_LEG * 0.6; + +// Hole patterns (same as A1) +function _get_holes_a(h) = + let(start=50, end=50, spacing=150, count=floor((h-start-end)/spacing)+1) + [for (i=[0:count-1]) start + i*spacing]; + +function _get_holes_b(h) = + let(start=100, end=50, spacing=150, count=floor((h-start-end)/spacing)+1) + [for (i=[0:count-1]) start + i*spacing]; + +PART_A2_HOLES_LEG_A = _get_holes_a(PART_A2_HEIGHT); +PART_A2_HOLES_LEG_B = _get_holes_b(PART_A2_HEIGHT); + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A2: Back Stiffener Inner Wall Vertical Angle Iron + * Identical dimensions to A1, used in 4 locations on inner panels + */ +module _part_a2_back_inner_vertical(show_holes=true) { + height = PART_A2_HEIGHT; + leg = PART_A2_LEG; + thick = PART_A2_THICK; + bolt_dia = PART_A2_BOLT_DIA; + hole_offset = PART_A2_HOLE_OFFSET; + + color("DarkGray") + difference() { + linear_extrude(height=height) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + for (z = PART_A2_HOLES_LEG_A) { + translate([hole_offset, 0, z]) + rotate([90, 0, 0]) + cylinder(d=bolt_dia, h=40, center=true, $fn=16); + } + + for (z = PART_A2_HOLES_LEG_B) { + translate([0, hole_offset, z]) + rotate([0, 90, 0]) + cylinder(d=bolt_dia, h=40, center=true, $fn=16); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a2_back_inner_vertical(show_holes=true); + +echo("=== PART A2: Back Stiffener Inner Wall Vertical ==="); +echo("Height:", PART_A2_HEIGHT, "mm"); +echo("Quantity needed: 4 pieces"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a2_frame_mount.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a2_frame_mount.scad new file mode 100644 index 0000000..df3cafe --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a2_frame_mount.scad @@ -0,0 +1,60 @@ +// angle_iron_a2_frame_mount.scad +// Frame Tube Mounting Angle - Part A2 +// 2" × 2" × 1/4" Angle Iron +// Used to mount frame tubes to side panels +// Quantity needed: 16 (2 per tube × 2 tubes × 4 panels) + +include <../../lifetrac_v25_params.scad> +use <../../modules/structural_steel.scad> + +/** + * Frame tube mounting angle iron with holes + * Length: 146.05mm (5.75") - 6" stock minus 2×1/8" trim + * Material: 2" × 2" × 1/4" angle iron + */ +module angle_iron_a2_frame_mount(show_holes=true) { + leg = 50.8; // 2" angle iron leg + thick = 6.35; // 1/4" thickness + length = 146.05; // 5.75" (6" stock - 2×trim) + + // Hole specifications + hole_dia = 12.7; // 1/2" (12.7mm) + hole1_pos = 22.86; // First hole position from end + hole2_pos = 123.19; // Second hole position from end + + difference() { + // Main angle iron shape + color("Gray") + rotate([90, 0, 0]) + linear_extrude(height=length) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Holes through horizontal leg (for plate mounting) + translate([leg/2, -hole1_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + translate([leg/2, -hole2_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + // Holes through vertical leg (for tube mounting) + translate([thick/2, -hole1_pos, leg/2]) + rotate([0, 90, 0]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + translate([thick/2, -hole2_pos, leg/2]) + rotate([0, 90, 0]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + } + } +} + +// Render the part +angle_iron_a2_frame_mount(show_holes=true); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a3_front_outer_vertical.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a3_front_outer_vertical.scad new file mode 100644 index 0000000..230f453 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a3_front_outer_vertical.scad @@ -0,0 +1,84 @@ +// Part A3: Front Stiffener - Outer Section Vertical Angle Iron +// Material: 2"×2"×1/4" Angle Iron (50.8mm × 6.35mm) +// Quantity: 6 pieces (outer walls + inner wall faces) +// Location: Front stiffener plate outer sections (5" tall sections) +// +// These are shorter angle irons used in the 5" tall outer sections of the front stiffener plate. + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +// Front stiffener outer section is 5" (127mm) tall +_outer_height = 127.0; +_angle_trim = 3.175; // 1/8" trimmed from each end + +PART_A3_HEIGHT = _outer_height - 2 * _angle_trim; // ~120.65mm (4.75") +PART_A3_LEG = 50.8; +PART_A3_THICK = 6.35; +PART_A3_BOLT_DIA = 9.525; // 3/8" bolts for outer sections +PART_A3_HOLE_OFFSET = PART_A3_LEG / 2; // Center of leg + +// 2 holes spaced 3" (76.2mm) apart, centered on angle +_bolt_spacing = 38.1; // 1.5" from center (3" total) +PART_A3_HOLES_LEG_A = [PART_A3_HEIGHT/2 - _bolt_spacing, PART_A3_HEIGHT/2 + _bolt_spacing]; +PART_A3_HOLES_LEG_B = [PART_A3_HEIGHT/2 - _bolt_spacing, PART_A3_HEIGHT/2 + _bolt_spacing]; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A3: Front Stiffener Outer Section Vertical Angle Iron + * Shorter angle for 5" tall outer sections of front stiffener plate + */ +module _part_a3_front_outer_vertical(show_holes=true) { + height = PART_A3_HEIGHT; + leg = PART_A3_LEG; + thick = PART_A3_THICK; + bolt_dia = PART_A3_BOLT_DIA; + hole_offset = PART_A3_HOLE_OFFSET; + + color("DarkGray") + difference() { + linear_extrude(height=height) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Holes on Leg A + for (z = PART_A3_HOLES_LEG_A) { + translate([hole_offset, 0, z]) + rotate([90, 0, 0]) + cylinder(d=bolt_dia, h=40, center=true, $fn=16); + } + + // Holes on Leg B + for (z = PART_A3_HOLES_LEG_B) { + translate([0, hole_offset, z]) + rotate([0, 90, 0]) + cylinder(d=bolt_dia, h=40, center=true, $fn=16); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a3_front_outer_vertical(show_holes=true); + +echo("=== PART A3: Front Stiffener Outer Section Vertical ==="); +echo("Height:", PART_A3_HEIGHT, "mm (", PART_A3_HEIGHT/25.4, "inches)"); +echo("Bolt diameter:", PART_A3_BOLT_DIA, "mm (3/8\")"); +echo("Holes per leg: 2"); +echo("Quantity needed: 6 pieces"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a3_loader_mount.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a3_loader_mount.scad new file mode 100644 index 0000000..99f4340 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a3_loader_mount.scad @@ -0,0 +1,64 @@ +// angle_iron_a3_loader_mount.scad +// Loader Arm Mounting Angle - Part A3 +// 2" × 2" × 1/4" Angle Iron +// Connects loader arm to side panels +// Quantity needed: 4 (2 per arm × 2 arms) + +include <../../lifetrac_v25_params.scad> +use <../../modules/structural_steel.scad> + +/** + * Loader arm mounting angle iron with holes + * Length: 146.05mm (5.75") - same as frame mount + * Material: 2" × 2" × 1/4" angle iron + */ +module angle_iron_a3_loader_mount(show_holes=true) { + leg = 50.8; // 2" angle iron leg + thick = 6.35; // 1/4" thickness + length = 146.05; // 5.75" + + // Hole specifications + hole_dia = 12.7; // 1/2" (12.7mm) + // Holes through horizontal leg (for plate mounting) + plate_hole1_pos = 22.86; + plate_hole2_pos = 123.19; + // Holes through vertical leg (for beam mounting - different positions) + beam_hole1_pos = 48.26; + beam_hole2_pos = 97.79; + + difference() { + // Main angle iron shape + color("Gray") + rotate([90, 0, 0]) + linear_extrude(height=length) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Holes through horizontal leg (for plate mounting) + translate([leg/2, -plate_hole1_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + translate([leg/2, -plate_hole2_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + // Holes through vertical leg (for beam mounting) + translate([thick/2, -beam_hole1_pos, leg/2]) + rotate([0, 90, 0]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + translate([thick/2, -beam_hole2_pos, leg/2]) + rotate([0, 90, 0]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + } + } +} + +// Render the part +angle_iron_a3_loader_mount(show_holes=true); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a4_frame_tube_mount.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a4_frame_tube_mount.scad new file mode 100644 index 0000000..255c8e4 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a4_frame_tube_mount.scad @@ -0,0 +1,100 @@ +// Part A4: Frame Tube Mount Angle Iron +// Material: 2"×2"×1/4" Angle Iron (50.8mm × 6.35mm) +// Quantity: 16 pieces (2 tubes × 4 walls × 2 faces) +// Location: Mounting 2×6 cross frame tubes to wall panels +// +// This is the standard frame tube mounting angle with asymmetric hole spacing: +// - Wall side (Leg A): 4" spacing (2" from center) with 1/2" bolts +// - Tube side (Leg B): 2" spacing (1" from center) with 1/2" bolts + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +// Frame tube height is 6" (152.4mm), angles are 1/8" shorter on each end +_frame_tube_height = 152.4; +_angle_trim = 3.175; // 1/8" trim from each end + +PART_A4_HEIGHT = _frame_tube_height - 2 * _angle_trim; // 146.05mm (5.75") +PART_A4_LEG = 50.8; // 2" +PART_A4_THICK = 6.35; // 1/4" +PART_A4_BOLT_DIA = 12.7; // 1/2" bolts + +// Hole positions (from bottom of angle) +_plate_bolt_offset = 50.8; // 2" from center for wall side (4" total spacing) +_tube_bolt_offset = 25.4; // 1" from center for tube side (2" total spacing) +_hole_inset = 25.4; // 1" from corner for hole placement on tube leg + +// Centered on height for both +_center_z = PART_A4_HEIGHT / 2; + +// Leg A holes (against wall) - 4" spacing +PART_A4_HOLES_LEG_A = [_center_z - _plate_bolt_offset, _center_z + _plate_bolt_offset]; + +// Leg B holes (against tube) - 2" spacing, inset from corner +PART_A4_HOLES_LEG_B = [_center_z - _tube_bolt_offset, _center_z + _tube_bolt_offset]; +PART_A4_TUBE_HOLE_INSET = _hole_inset; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A4: Frame Tube Mount Angle Iron + * + * Orientation when installed: + * - Vertical leg against wall panel (bolts in X direction) + * - Horizontal leg against frame tube (bolts in Y direction) + * - Extrusion runs vertically along Z + * + * Hole pattern: + * - Leg A (wall): 2 holes, 4" apart, 1/2" bolts + * - Leg B (tube): 2 holes, 2" apart, 1/2" bolts, 1" from corner + */ +module _part_a4_frame_tube_mount(show_holes=true) { + height = PART_A4_HEIGHT; + leg = PART_A4_LEG; + thick = PART_A4_THICK; + bolt_dia = PART_A4_BOLT_DIA; + + color("DarkGray") + difference() { + union() { + // Vertical leg (against wall panel) - runs full height in Z, thin in X + cube([thick, leg, height]); + // Horizontal leg (toward tube) - extends in +X direction + cube([leg, thick, height]); + } + + if (show_holes) { + // Holes in vertical leg (bolts through to wall plate in X direction) + for (z = PART_A4_HOLES_LEG_A) { + translate([-1, leg/2, z]) + rotate([0, 90, 0]) + cylinder(d=bolt_dia, h=thick+2, $fn=32); + } + + // Holes in horizontal leg (bolts through in Y direction into tube side) + for (z = PART_A4_HOLES_LEG_B) { + translate([PART_A4_TUBE_HOLE_INSET, -1, z]) + rotate([-90, 0, 0]) + cylinder(d=bolt_dia, h=thick+2, $fn=32); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a4_frame_tube_mount(show_holes=true); + +echo("=== PART A4: Frame Tube Mount Angle Iron ==="); +echo("Height:", PART_A4_HEIGHT, "mm (", PART_A4_HEIGHT/25.4, "inches)"); +echo("Bolt diameter:", PART_A4_BOLT_DIA, "mm (1/2\")"); +echo("Wall side spacing: 4\" (101.6mm)"); +echo("Tube side spacing: 2\" (50.8mm)"); +echo("Quantity needed: 16 pieces"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a4_side_panel_vertical.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a4_side_panel_vertical.scad new file mode 100644 index 0000000..588a607 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a4_side_panel_vertical.scad @@ -0,0 +1,51 @@ +// angle_iron_a4_side_panel_vertical.scad +// Side Panel Vertical Angle - Part A4 +// 2" × 2" × 1/4" Angle Iron +// Vertical stiffeners for side panels +// Quantity needed: 8 + +include <../../lifetrac_v25_params.scad> +use <../../modules/structural_steel.scad> + +/** + * Side panel vertical stiffener angle iron with holes + * Length: 550mm (21.65") + * Material: 2" × 2" × 1/4" angle iron + */ +module angle_iron_a4_side_panel_vertical(show_holes=true) { + leg = 50.8; // 2" angle iron leg + thick = 6.35; // 1/4" thickness + length = 550.0; // 550mm + + // Hole specifications + hole_dia = 9.525; // 3/8" (9.525mm) + hole1_pos = 60.0; // First hole + hole2_pos = 490.0; // Second hole + + difference() { + // Main angle iron shape + color("Gray") + rotate([90, 0, 0]) + linear_extrude(height=length) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Holes through horizontal leg (for panel mounting) + translate([leg/2, -hole1_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + translate([leg/2, -hole2_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + } + } +} + +// Render the part +angle_iron_a4_side_panel_vertical(show_holes=true); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a5_arm_crossbeam_mount.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a5_arm_crossbeam_mount.scad new file mode 100644 index 0000000..189f8db --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a5_arm_crossbeam_mount.scad @@ -0,0 +1,86 @@ +// Part A5: Arm Crossbeam Mount Angle Iron +// Material: 2"×2"×1/4" Angle Iron (50.8mm × 6.35mm) +// Quantity: 4 pieces (2 per arm, top and bottom) +// Location: Connecting arm side plates to cross beam +// +// Mounts the 2×6 cross beam to the loader arm side plates. +// Same dimensions as frame tube mount (A4) but used on the arms. + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +// Crossbeam is 6" (152.4mm) in one dimension +_angle_trim = 3.175; // 1/8" trim from each end + +PART_A5_HEIGHT = 152.4 - 2 * _angle_trim; // 146.05mm (5.75") +PART_A5_LEG = 50.8; +PART_A5_THICK = 6.35; +PART_A5_BOLT_DIA = 12.7; // 1/2" bolts + +// Hole spacing +_plate_bolt_offset = 50.8; // 2" from center for plate side +_beam_bolt_offset = 25.4; // 1" from center for beam side +_center_z = PART_A5_HEIGHT / 2; + +PART_A5_HOLES_LEG_A = [_center_z - _plate_bolt_offset, _center_z + _plate_bolt_offset]; +PART_A5_HOLES_LEG_B = [_center_z - _beam_bolt_offset, _center_z + _beam_bolt_offset]; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A5: Arm Crossbeam Mount Angle Iron + * + * Same hole pattern as frame tube mount but used on loader arms. + * Installed with: + * - Vertical leg against arm side plate + * - Horizontal leg against cross beam + */ +module _part_a5_arm_crossbeam_mount(show_holes=true) { + height = PART_A5_HEIGHT; + leg = PART_A5_LEG; + thick = PART_A5_THICK; + bolt_dia = PART_A5_BOLT_DIA; + + color("DarkGray") + difference() { + union() { + // Vertical leg (against plate) + cube([height, thick, leg]); + // Horizontal leg (against beam) + cube([height, leg, thick]); + } + + if (show_holes) { + // Holes in vertical leg (plate bolts) + for (x = PART_A5_HOLES_LEG_A) { + translate([x, thick + 1, leg/2]) + rotate([90, 0, 0]) + cylinder(d=bolt_dia, h=thick+2, $fn=32); + } + + // Holes in horizontal leg (beam bolts) + for (x = PART_A5_HOLES_LEG_B) { + translate([x, leg/2, -1]) + cylinder(d=bolt_dia, h=thick+2, $fn=32); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a5_arm_crossbeam_mount(show_holes=true); + +echo("=== PART A5: Arm Crossbeam Mount Angle Iron ==="); +echo("Height:", PART_A5_HEIGHT, "mm"); +echo("Bolt diameter:", PART_A5_BOLT_DIA, "mm (1/2\")"); +echo("Plate side spacing: 4\" (101.6mm)"); +echo("Beam side spacing: 2\" (50.8mm)"); +echo("Quantity needed: 4 pieces"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a5_bottom_plate_horizontal.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a5_bottom_plate_horizontal.scad new file mode 100644 index 0000000..19af310 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a5_bottom_plate_horizontal.scad @@ -0,0 +1,51 @@ +// angle_iron_a5_bottom_plate_horizontal.scad +// Bottom Plate Horizontal Angle - Part A5 +// 2" × 2" × 1/4" Angle Iron +// Bottom plate stiffeners (split pattern to clear wheel axles) +// Quantity needed: 8 + +include <../../lifetrac_v25_params.scad> +use <../../modules/structural_steel.scad> + +/** + * Bottom plate horizontal stiffener angle iron with holes + * Length: 400mm (15.75") + * Material: 2" × 2" × 1/4" angle iron + */ +module angle_iron_a5_bottom_plate_horizontal(show_holes=true) { + leg = 50.8; // 2" angle iron leg + thick = 6.35; // 1/4" thickness + length = 400.0; // 400mm + + // Hole specifications + hole_dia = 9.525; // 3/8" (9.525mm) + hole1_pos = 60.0; // First hole + hole2_pos = 340.0; // Second hole + + difference() { + // Main angle iron shape + color("Gray") + rotate([90, 0, 0]) + linear_extrude(height=length) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Holes through horizontal leg (for plate mounting) + translate([leg/2, -hole1_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + + translate([leg/2, -hole2_pos, thick/2]) + cylinder(d=hole_dia, h=thick+2, center=true, $fn=24); + } + } +} + +// Render the part +angle_iron_a5_bottom_plate_horizontal(show_holes=true); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a6_bottom_horizontal.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a6_bottom_horizontal.scad new file mode 100644 index 0000000..4c6cc7e --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a6_bottom_horizontal.scad @@ -0,0 +1,174 @@ +// Part A6: Bottom Stiffener Plate - Horizontal Angle Iron (Full Assembly Module) +// Material: 2"×2"×1/4" Angle Iron (50.8mm × 6.35mm) +// Quantity: 24 total pieces (6 locations × 3 segments + motor plate segments) +// Location: Bottom stiffener plate, all four wall panel faces +// +// This is a HORIZONTAL angle iron that runs along the Y axis (forward/backward). +// It sits on top of the bottom plate with: +// - Horizontal leg flat ON the plate surface (extends toward center) +// - Vertical leg going UP against the wall panel +// +// NOTE: Due to wheel clearance requirements, each run is split into 3 segments +// with 8" (203.2mm) gaps centered on each wheel axis. + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// SEGMENT LENGTH CALCULATIONS +// ============================================================================= + +// These values match lifetrac_v25.scad calculations +// The actual lengths are calculated from wheel positions in the main assembly + +// Default segment lengths (actual values come from params at render time) +// Segment 1: Rear section (from plate start to rear wheel gap) +// Segment 2: Middle section (between wheel gaps) +// Segment 3: Front section (from front wheel gap to plate end) + +PART_A6_LEG = 50.8; // 2" leg +PART_A6_THICK = 6.35; // 1/4" thickness +PART_A6_BOLT_DIA = 9.525; // 3/8" bolts +PART_A6_HOLE_OFFSET = PART_A6_LEG * 0.6; + +// ============================================================================= +// HOLE PATTERN FUNCTIONS +// ============================================================================= + +// Horizontal leg holes (bolts go through in Z direction - into plate) +function get_horizontal_holes_a(length) = + let( + start_offset = 50, + end_offset = 50, + spacing = 150, + count = max(1, floor((length - start_offset - end_offset) / spacing) + 1) + ) + [for (i = [0:count-1]) start_offset + i * spacing]; + +// Vertical leg holes (bolts go through in X direction - into wall) +function get_horizontal_holes_b(length) = + let( + start_offset = 75, + end_offset = 50, + spacing = 150, + count = max(1, floor((length - start_offset - end_offset) / spacing) + 1) + ) + [for (i = [0:count-1]) start_offset + i * spacing]; + +// ============================================================================= +// SINGLE SEGMENT MODULE +// ============================================================================= + +/** + * Creates a single horizontal angle iron segment + * + * Orientation: + * - Runs along Y axis (length) + * - Origin at corner [0, 0, 0] + * - Horizontal leg extends in +X direction (toward center, flat on plate) + * - Vertical leg extends in +Z direction (up against wall) + * + * @param length Length of this segment + * @param show_holes If true, drill bolt holes + */ +module _bottom_angle_segment(length, show_holes=true) { + leg = PART_A6_LEG; + thick = PART_A6_THICK; + bolt_dia = PART_A6_BOLT_DIA; + hole_offset = PART_A6_HOLE_OFFSET; + + if (length > 0) { + color("DarkGray") + difference() { + union() { + // Horizontal leg - flat on plate, extends in +X direction + cube([leg, length, thick]); + // Vertical leg - extends upward in +Z direction + cube([thick, length, leg]); + } + + if (show_holes) { + // Holes on horizontal leg (bolts go through in Z direction) + for (y = get_horizontal_holes_a(length)) { + translate([hole_offset, y, thick/2]) + cylinder(d=bolt_dia, h=thick+2, center=true, $fn=16); + } + + // Holes on vertical leg (bolts go through in X direction) + for (y = get_horizontal_holes_b(length)) { + translate([thick/2, y, hole_offset]) + rotate([0, 90, 0]) + cylinder(d=bolt_dia, h=thick+2, center=true, $fn=16); + } + } + } + } +} + +// ============================================================================= +// INDIVIDUAL SEGMENT PARTS (for fabrication) +// ============================================================================= + +// Segment 1 (Rear) - from plate start to rear wheel gap +module _part_a6_bottom_segment_1(show_holes=true) { + // Use the calculated segment length from params + _seg1_len = is_undef(ANGLE_SEGMENT_1_LENGTH) ? 200 : ANGLE_SEGMENT_1_LENGTH; + _bottom_angle_segment(_seg1_len, show_holes); +} + +// Segment 2 (Middle) - between wheel gaps +module _part_a6_bottom_segment_2(show_holes=true) { + _seg2_len = is_undef(ANGLE_SEGMENT_2_LENGTH) ? 300 : ANGLE_SEGMENT_2_LENGTH; + _bottom_angle_segment(_seg2_len, show_holes); +} + +// Segment 3 (Front) - from front wheel gap to plate end +module _part_a6_bottom_segment_3(show_holes=true) { + _seg3_len = is_undef(ANGLE_SEGMENT_3_LENGTH) ? 250 : ANGLE_SEGMENT_3_LENGTH; + _bottom_angle_segment(_seg3_len, show_holes); +} + +// ============================================================================= +// COMPLETE SPLIT ASSEMBLY (for visualization) +// ============================================================================= + +/** + * Creates all 3 segments in their correct positions + * Matches the split_horizontal_angle_iron module in lifetrac_v25.scad + */ +module _part_a6_split_horizontal_angle_iron(show_holes=true) { + _gap = is_undef(ANGLE_IRON_GAP) ? 203.2 : ANGLE_IRON_GAP; // 8" gap + + _seg1_len = is_undef(ANGLE_SEGMENT_1_LENGTH) ? 200 : ANGLE_SEGMENT_1_LENGTH; + _seg2_len = is_undef(ANGLE_SEGMENT_2_LENGTH) ? 300 : ANGLE_SEGMENT_2_LENGTH; + _seg3_len = is_undef(ANGLE_SEGMENT_3_LENGTH) ? 250 : ANGLE_SEGMENT_3_LENGTH; + + _seg1_start = is_undef(ANGLE_SEGMENT_1_START) ? 0 : ANGLE_SEGMENT_1_START; + _seg2_start = is_undef(ANGLE_SEGMENT_2_START) ? _seg1_len + _gap : ANGLE_SEGMENT_2_START; + _seg3_start = is_undef(ANGLE_SEGMENT_3_START) ? _seg2_start + _seg2_len + _gap : ANGLE_SEGMENT_3_START; + + // Segment 1 (rear) + translate([0, _seg1_start, 0]) + _bottom_angle_segment(_seg1_len, show_holes); + + // Segment 2 (middle) + translate([0, _seg2_start, 0]) + _bottom_angle_segment(_seg2_len, show_holes); + + // Segment 3 (front) + translate([0, _seg3_start, 0]) + _bottom_angle_segment(_seg3_len, show_holes); +} + +// ============================================================================= +// RENDER +// ============================================================================= + +// Show segment 2 (middle, typically the longest) for preview +_part_a6_bottom_segment_2(show_holes=true); + +echo("=== PART A6: Bottom Stiffener Horizontal Angle (Split) ==="); +echo("Total unique segments: 3 per run"); +echo("Total runs: 8 (4 walls × outer + inner sides)"); +echo("Total segment pieces: 24"); +echo("Leg size:", PART_A6_LEG, "mm (2\")"); +echo("Bolt diameter:", PART_A6_BOLT_DIA, "mm (3/8\")"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a7_platform_side.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a7_platform_side.scad new file mode 100644 index 0000000..30b2f99 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a7_platform_side.scad @@ -0,0 +1,113 @@ +// Part A7: Platform Side Angle Iron (Left and Right) +// Material: 2"×2"×1/4" Angle Iron (50.8mm leg × 6.35mm thickness) +// Quantity: 2 pieces (1 left, 1 right - mirror) +// Location: Folding standing platform, side arms connecting deck to pivot brackets +// +// These are the structural arms that rotate about the pivot point +// They connect the deck plate to the pivot bracket plates + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +_leg = is_undef(PLATFORM_ANGLE_LEG) ? 50.8 : PLATFORM_ANGLE_LEG; // 2" = 50.8mm +_thick = is_undef(PLATFORM_ANGLE_THICK) ? 6.35 : PLATFORM_ANGLE_THICK; // 1/4" = 6.35mm +_arm_len = is_undef(PLATFORM_ARM_LENGTH) ? 500 : PLATFORM_ARM_LENGTH; +_bracket_width = is_undef(PLATFORM_BRACKET_WIDTH) ? 101.6 : PLATFORM_BRACKET_WIDTH; +_depth = is_undef(PLATFORM_DEPTH) ? 400 : PLATFORM_DEPTH; +_edge_margin = is_undef(PLATFORM_EDGE_MARGIN) ? 12.7 : PLATFORM_EDGE_MARGIN; +_bolt_dia = is_undef(PLATFORM_BOLT_DIA) ? 9.525 : PLATFORM_BOLT_DIA; +_clearance = is_undef(PLATFORM_BOLT_CLEARANCE) ? 0.5 : PLATFORM_BOLT_CLEARANCE; + +// Calculate length like in main assembly +_deck_pivot_y = _bracket_width / 2; +_deck_far_y = -(_depth - _bracket_width/2); +_front_corner_y = (_deck_pivot_y - _edge_margin) - _leg; +_rear_corner_y = (_deck_far_y + _edge_margin) + _leg; +_gap = 12.7; +_side_start_y = _front_corner_y - _gap; +_side_end_y = _rear_corner_y + _gap; + +PART_A7_LENGTH = _side_start_y - _side_end_y; // Calculated from geometry +PART_A7_LEG = _leg; +PART_A7_THICK = _thick; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A7: Platform Side Angle Iron + * L-profile angle iron for platform side arms + * show_holes: if true, renders bolt holes + */ +module _part_a7_platform_side_angle(show_holes=true) { + length = PART_A7_LENGTH; + leg = PART_A7_LEG; + thick = PART_A7_THICK; + hole_dia = _bolt_dia + _clearance; + + color("DimGray") + difference() { + // L-profile extrusion + linear_extrude(height=length) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Pivot bracket attachment holes (through vertical leg) + // 2 holes near pivot end (start of extrusion) + _lock_offset = is_undef(PLATFORM_LOCK_OFFSET) ? 130 : PLATFORM_LOCK_OFFSET; + _side_bolt_y = is_undef(PLATFORM_SIDE_BOLT_Y_OFFSET) ? 25.4 : PLATFORM_SIDE_BOLT_Y_OFFSET; + _bracket_len = is_undef(PLATFORM_BRACKET_LENGTH) ? 200 : PLATFORM_BRACKET_LENGTH; + + _pivot_bolt_offset = (_bracket_len - _bracket_width/2) * 0.30; + _leg2_len = _bracket_len - _bracket_width/2; + _bx_1 = _pivot_bolt_offset; + _bx_2 = _leg2_len - _pivot_bolt_offset; + + _corner_y = -_lock_offset + _side_bolt_y; + _side_angle_start_y_calc = _side_start_y; + + z_pos_1 = _side_angle_start_y_calc + _bx_1; + z_pos_2 = _side_angle_start_y_calc + _bx_2; + x_pos = leg / 2; + + for (z_pos = [z_pos_1, z_pos_2]) { + translate([x_pos, thick/2, z_pos]) + rotate([90, 0, 0]) + cylinder(d=hole_dia, h=thick + 10, center=true, $fn=32); + } + + // Deck attachment holes (through horizontal leg) + // 2 holes for deck bolts + _deck_bolt_offset = max(length * 0.08, 2 * _bolt_dia); + for (z_pos = [_deck_bolt_offset, length - _deck_bolt_offset]) { + translate([thick/2, leg/2, z_pos]) + rotate([0, 90, 0]) + cylinder(d=hole_dia, h=thick + 10, center=true, $fn=32); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a7_platform_side_angle(show_holes=true); + +echo("=== PART A7: Platform Side Angle Iron ==="); +echo("Length:", PART_A7_LENGTH, "mm (", PART_A7_LENGTH/25.4, "in)"); +echo("Leg:", PART_A7_LEG, "mm (2 inches)"); +echo("Thickness:", PART_A7_THICK, "mm (1/4 inch)"); +echo("Holes: 4 total (2 pivot bracket, 2 deck attachment)"); +echo("Quantity needed: 2 pieces (left and right, mirrored in assembly)"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a8_platform_transverse.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a8_platform_transverse.scad new file mode 100644 index 0000000..ac35ba9 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a8_platform_transverse.scad @@ -0,0 +1,83 @@ +// Part A8: Platform Transverse Angle Iron (Front and Rear) +// Material: 2"×2"×1/4" Angle Iron (50.8mm leg × 6.35mm thickness) +// Quantity: 2 pieces (1 front near pivot, 1 rear at far end) +// Location: Folding standing platform, cross-bracing under deck plate +// +// These run transversely (left-right) across the platform deck, +// spaced 1/4" from the side angle mounting brackets + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +_leg = is_undef(PLATFORM_ANGLE_LEG) ? 50.8 : PLATFORM_ANGLE_LEG; // 2" = 50.8mm +_thick = is_undef(PLATFORM_ANGLE_THICK) ? 6.35 : PLATFORM_ANGLE_THICK; // 1/4" = 6.35mm +_track_width = is_undef(TRACK_WIDTH) ? 900 : TRACK_WIDTH; +_sandwich = is_undef(SANDWICH_SPACING) ? 120 : SANDWICH_SPACING; +_panel_thick = is_undef(PANEL_THICKNESS) ? 12.7 : PANEL_THICKNESS; +_pivot_x = is_undef(PLATFORM_PIVOT_X) ? 300 : PLATFORM_PIVOT_X; +_side_gap = is_undef(PLATFORM_SIDE_GAP) ? 6.35 : PLATFORM_SIDE_GAP; +_bolt_dia = is_undef(PLATFORM_BOLT_DIA) ? 9.525 : PLATFORM_BOLT_DIA; +_clearance = is_undef(PLATFORM_BOLT_CLEARANCE) ? 0.5 : PLATFORM_BOLT_CLEARANCE; +_end_offset = is_undef(PLATFORM_TRANSVERSE_BOLT_END_OFFSET) ? 50.8 : PLATFORM_TRANSVERSE_BOLT_END_OFFSET; + +// Calculate length: spans between side angle brackets with 1/4" clearance each side +_angle_x = _pivot_x - _side_gap; // X position of side angles from center +_clearance_gap = 6.35; // 1/4" clearance from L brackets + +PART_A8_LENGTH = 2 * (_angle_x - _clearance_gap); +PART_A8_LEG = _leg; +PART_A8_THICK = _thick; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A8: Platform Transverse Angle Iron + * L-profile angle iron running left-right across platform deck + * show_holes: if true, renders deck mounting holes + */ +module _part_a8_platform_transverse_angle(show_holes=true) { + length = PART_A8_LENGTH; + leg = PART_A8_LEG; + thick = PART_A8_THICK; + hole_dia = _bolt_dia + _clearance; + + color("DimGray") + difference() { + union() { + // Vertical Leg (full length, face at Y=0) + translate([-length/2, 0, -leg]) + cube([length, thick, leg]); + + // Horizontal Leg (face at Z=0) + translate([-length/2, 0, -thick]) + cube([length, leg, thick]); + } + + if (show_holes) { + // Deck Mounting Holes (through horizontal leg) + // 3 holes: near each end and center + for (x_pos = [-length/2 + _end_offset, 0, length/2 - _end_offset]) { + translate([x_pos, leg/2, 0]) + cylinder(d=hole_dia, h=thick*3, center=true, $fn=32); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a8_platform_transverse_angle(show_holes=true); + +echo("=== PART A8: Platform Transverse Angle Iron ==="); +echo("Length:", PART_A8_LENGTH, "mm (", PART_A8_LENGTH/25.4, "in)"); +echo("Leg:", PART_A8_LEG, "mm (2 inches)"); +echo("Thickness:", PART_A8_THICK, "mm (1/4 inch)"); +echo("Holes: 3 per piece (deck mounting, 3/8\" dia)"); +echo("Quantity needed: 2 pieces (front and rear)"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a9_front_center.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a9_front_center.scad new file mode 100644 index 0000000..a54e42a --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/angle_iron_a9_front_center.scad @@ -0,0 +1,92 @@ +// Part A9: Front Stiffener Center Section Angle Iron +// Material: 2"×2"×1/4" Angle Iron (50.8mm leg × 6.35mm thickness) +// Quantity: 2 pieces (left motor plate, right motor plate) +// Location: Front stiffener plate, 10" center section between motor plates +// +// These are the same height as frame tube angles (5.75" / 146.05mm) +// Used to connect the 10" tall center section to the motor mount plates +// Bolt pattern: 1/2" bolts, 4" spacing (same as frame tube angles) + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +_leg = 50.8; // 2" angle leg +_thick = 6.35; // 1/4" thickness +_trim = 3.175; // 1/8" trimmed from each end + +// Height matches frame tube angle: 6" tube - 2×(1/8" trim) = 5.75" +_frame_tube_height = is_undef(FRAME_TUBE_HEIGHT) ? 152.4 : FRAME_TUBE_HEIGHT; + +PART_A9_HEIGHT = _frame_tube_height - 2*_trim; // 146.05mm (5.75") +PART_A9_LEG = _leg; +PART_A9_THICK = _thick; + +// Bolt parameters (match frame tube angle pattern) +_bolt_dia = is_undef(BOLT_DIA_1_2) ? 12.7 : BOLT_DIA_1_2; // 1/2" bolts +_plate_bolt_offset = 50.8; // 2" from center (4" total spacing) +_hole_offset = _leg / 2; // Center of angle leg + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part A9: Front Stiffener Center Section Angle Iron + * Vertical angle iron for 10" center section of front stiffener + * Same dimensions as frame tube mounting angles + * show_holes: if true, renders bolt holes + */ +module _part_a9_front_center_angle(show_holes=true) { + height = PART_A9_HEIGHT; + leg = PART_A9_LEG; + thick = PART_A9_THICK; + + color("Silver") + difference() { + // L-profile + linear_extrude(height=height) + polygon([ + [0, 0], + [leg, 0], + [leg, thick], + [thick, thick], + [thick, leg], + [0, leg] + ]); + + if (show_holes) { + // Vertical leg holes (through thickness, bolt to plate) + // 2 holes at 4" spacing centered on height + _center_z = height / 2; + for (z_off = [-_plate_bolt_offset, _plate_bolt_offset]) { + translate([_hole_offset, -1, _center_z + z_off]) + rotate([-90, 0, 0]) + cylinder(d=_bolt_dia, h=thick + 2, $fn=32); + } + + // Horizontal leg holes (through thickness, bolt to stiffener plate) + for (z_off = [-_plate_bolt_offset, _plate_bolt_offset]) { + translate([-1, _hole_offset, _center_z + z_off]) + rotate([0, 90, 0]) + cylinder(d=_bolt_dia, h=thick + 2, $fn=32); + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_a9_front_center_angle(show_holes=true); + +echo("=== PART A9: Front Stiffener Center Section Angle Iron ==="); +echo("Height:", PART_A9_HEIGHT, "mm (", PART_A9_HEIGHT/25.4, "in)"); +echo("Leg:", PART_A9_LEG, "mm (2 inches)"); +echo("Thickness:", PART_A9_THICK, "mm (1/4 inch)"); +echo("Bolt pattern: 2 holes per leg, 4\" spacing, 1/2\" dia"); +echo("Quantity needed: 2 pieces (left and right motor plate faces)"); +echo("Note: Identical dimensions to frame tube mount angles (A4)"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/frame_tube_t1_front.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/frame_tube_t1_front.scad new file mode 100644 index 0000000..e0e86e5 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/frame_tube_t1_front.scad @@ -0,0 +1,42 @@ +// frame_tube_t1_front.scad +// Front Frame Tube - Part T1 +// 2" × 6" × 1/4" Rectangular Tubing +// Front cross frame tube passing through side panels +// Quantity needed: 1 + +include <../../lifetrac_v25_params.scad> +use <../../modules/structural_steel.scad> + +/** + * Front frame tube - 2" × 6" rectangular tubing + * Length: Track width (distance between side panels) + * Material: 2" × 6" × 1/4" rectangular tubing + */ +module frame_tube_t1_front(show_holes=false) { + // Tube dimensions from params + width = 50.8; // 2" (Y direction) + height = 152.4; // 6" (Z direction) + wall = 6.35; // 1/4" wall thickness + + // Length spans the full track width between panels + // Using approximate value - actual would be TRACK_WIDTH + length = 1200.0; // Approximate track width in mm + + // Inner dimensions + inner_width = width - 2*wall; + inner_height = height - 2*wall; + + color("DarkGray") + translate([0, 0, 0]) + rotate([0, 90, 0]) // Tube runs along X axis + difference() { + // Outer tube + cube([height, width, length], center=true); + + // Inner hollow + cube([inner_height, inner_width, length+2], center=true); + } +} + +// Render the part +frame_tube_t1_front(show_holes=false); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/frame_tube_t2_rear.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/frame_tube_t2_rear.scad new file mode 100644 index 0000000..2eb33d9 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/frame_tube_t2_rear.scad @@ -0,0 +1,41 @@ +// frame_tube_t2_rear.scad +// Rear Frame Tube - Part T2 +// 2" × 6" × 1/4" Rectangular Tubing +// Rear cross frame tube passing through side panels +// Quantity needed: 1 + +include <../../lifetrac_v25_params.scad> +use <../../modules/structural_steel.scad> + +/** + * Rear frame tube - 2" × 6" rectangular tubing + * Length: Track width (distance between side panels) + * Material: 2" × 6" × 1/4" rectangular tubing + */ +module frame_tube_t2_rear(show_holes=false) { + // Tube dimensions from params + width = 50.8; // 2" (Y direction) + height = 152.4; // 6" (Z direction) + wall = 6.35; // 1/4" wall thickness + + // Length spans the full track width between panels + length = 1200.0; // Approximate track width in mm + + // Inner dimensions + inner_width = width - 2*wall; + inner_height = height - 2*wall; + + color("DarkGray") + translate([0, 0, 0]) + rotate([0, 90, 0]) // Tube runs along X axis + difference() { + // Outer tube + cube([height, width, length], center=true); + + // Inner hollow + cube([inner_height, inner_width, length+2], center=true); + } +} + +// Render the part +frame_tube_t2_rear(show_holes=false); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/structural_parts.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/structural_parts.scad new file mode 100644 index 0000000..753dfa8 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/structural_parts.scad @@ -0,0 +1,180 @@ +// ============================================================================= +// LifeTrac v25 Structural Steel Parts - Master Include File +// ============================================================================= +// +// This file provides access to all structural steel parts for the LifeTrac v25 +// assembly. Use this file to gain access to all individual part modules. +// +// USAGE: +// use +// +// Then call any part module, for example: +// part_a1_back_outer_vertical(); +// part_t1_front_frame_tube(); +// +// NOTE: OpenSCAD 'use' statements don't re-export modules, so we define +// wrapper modules here that call the modules from individual part files. +// ============================================================================= + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// ANGLE IRON PARTS (A1-A10) - Wrapper modules +// ============================================================================= + +// A1: Back Stiffener - Outer Wall Vertical (2 pcs) +use +module part_a1_back_outer_vertical(show_holes=true) { + _part_a1_back_outer_vertical(show_holes); +} + +// A2: Back Stiffener - Inner Wall Vertical (4 pcs) +use +module part_a2_back_inner_vertical(show_holes=true) { + _part_a2_back_inner_vertical(show_holes); +} + +// A3: Front Stiffener - Outer Section Vertical (6 pcs) +use +module part_a3_front_outer_vertical(show_holes=true) { + _part_a3_front_outer_vertical(show_holes); +} + +// A4: Frame Tube Mount Angles (16 pcs) +use +module part_a4_frame_tube_mount(show_holes=true) { + _part_a4_frame_tube_mount(show_holes); +} + +// A5: Arm Crossbeam Mount Angles (4 pcs) +use +module part_a5_arm_crossbeam_mount(show_holes=true) { + _part_a5_arm_crossbeam_mount(show_holes); +} + +// A6: Bottom Stiffener Horizontal Angles - Split (24 pcs total) +use +module part_a6_bottom_segment_1(show_holes=true) { _part_a6_bottom_segment_1(show_holes); } +module part_a6_bottom_segment_2(show_holes=true) { _part_a6_bottom_segment_2(show_holes); } +module part_a6_bottom_segment_3(show_holes=true) { _part_a6_bottom_segment_3(show_holes); } +module part_a6_split_horizontal_angle_iron(show_holes=true) { _part_a6_split_horizontal_angle_iron(show_holes); } + +// A7: Platform Side Angle Irons (2 pcs) +use +module part_a7_platform_side_angle(show_holes=true) { + _part_a7_platform_side_angle(show_holes); +} + +// A8: Platform Transverse Angle Irons (2 pcs) +use +module part_a8_platform_transverse_angle(show_holes=true) { + _part_a8_platform_transverse_angle(show_holes); +} + +// A9: Front Stiffener Center Section Angles (2 pcs) +use +module part_a9_front_center_angle(show_holes=true) { + _part_a9_front_center_angle(show_holes); +} + +// A10: Front Stiffener Outer Section Angles (8 pcs) +use +module part_a10_front_outer_angle(show_holes=true) { + _part_a10_front_outer_angle(show_holes); +} + +// ============================================================================= +// TUBING PARTS (T1-T5) - Wrapper modules +// ============================================================================= + +// T1: Front Cross Frame Tube (1 pc) +use +module part_t1_front_frame_tube(show_cutaway=false, show_holes=true) { + _part_t1_front_frame_tube(show_cutaway, show_holes); +} + +// T2: Rear Cross Frame Tube (1 pc) +use +module part_t2_rear_frame_tube(show_cutaway=false, show_holes=true) { + _part_t2_rear_frame_tube(show_cutaway, show_holes); +} + +// T3: Arm Crossbeam Tube (1 pc) +use +module part_t3_arm_crossbeam(show_holes=true) { + _part_t3_arm_crossbeam(show_holes); +} + +// T4: Main Arm Tubes (2 pcs) +use +module part_t4_arm_main(show_holes=true) { + _part_t4_arm_main(show_holes); +} + +// T5: Arm Leg Spacer Tubes (2 pcs) +use +module part_t5_arm_leg_spacer_raw() { _part_t5_arm_leg_spacer_raw(); } +module part_t5_arm_leg_spacer_cut(show_holes=true) { _part_t5_arm_leg_spacer_cut(show_holes); } + +// ============================================================================= +// INVENTORY SUMMARY +// ============================================================================= +// Total unique parts: 15 +// Total pieces needed: 71 +// +// ANGLE IRON (2"×2"×1/4"): +// A1: 2 pcs - Back stiffener outer walls +// A2: 4 pcs - Back stiffener inner walls +// A3: 6 pcs - Front stiffener outer (4.75") +// A4: 16 pcs - Frame tube mounts +// A5: 4 pcs - Arm crossbeam mounts +// A6: 24 pcs - Bottom stiffener (3 segments × 8 locations) +// A7: 2 pcs - Platform side arms +// A8: 2 pcs - Platform transverse bracing +// A9: 2 pcs - Front stiffener center (5.75") +// A10: 8 pcs - Front stiffener outer motor plate sides +// ----------- +// Subtotal: 70 angle iron pieces +// +// RECTANGULAR TUBING (2"×6"×1/4"): +// T1: 1 pc - Front frame cross tube +// T2: 1 pc - Rear frame cross tube +// T3: 1 pc - Arm crossbeam +// T4: 2 pcs - Main arm tubes +// T5: 2 pcs - Arm leg spacer tubes +// ----------- +// Subtotal: 7 tubing pieces +// ============================================================================= + +// Display all parts for preview +module show_all_structural_parts() { + // Arrange parts in a row for visualization + spacing = 250; + + // Angle irons + translate([0*spacing, 0, 0]) part_a1_back_outer_vertical(); + translate([1*spacing, 0, 0]) part_a2_back_inner_vertical(); + translate([2*spacing, 0, 0]) part_a3_front_outer_vertical(); + translate([3*spacing, 0, 0]) part_a4_frame_tube_mount(); + translate([4*spacing, 0, 0]) part_a5_arm_crossbeam_mount(); + translate([5*spacing, 0, 0]) part_a6_bottom_segment_1(); + translate([6*spacing, 0, 0]) part_a7_platform_side_angle(); + translate([7*spacing, 0, 0]) part_a8_platform_transverse_angle(); + translate([8*spacing, 0, 0]) part_a9_front_center_angle(); + translate([9*spacing, 0, 0]) part_a10_front_outer_angle(); + + // Tubing - offset in Y + translate([0*spacing, 500, 0]) part_t1_front_frame_tube(); + translate([2*spacing, 500, 0]) part_t2_rear_frame_tube(); + translate([4*spacing, 500, 0]) part_t3_arm_crossbeam(); + translate([6*spacing, 500, 0]) part_t4_arm_main(); + translate([8*spacing, 500, 0]) part_t5_arm_leg_spacer_raw(); +} + +// Uncomment to preview all parts: +// show_all_structural_parts(); + +echo("=== STRUCTURAL PARTS LOADED ==="); +echo("Angle iron parts: A1-A10 (70 pieces total)"); +echo("Tubing parts: T1-T5 (7 pieces total)"); +echo("See STRUCTURAL_PARTS_CATALOG.md for details"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t1_front_frame.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t1_front_frame.scad new file mode 100644 index 0000000..0fc29a1 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t1_front_frame.scad @@ -0,0 +1,174 @@ +// Part T1: Front Cross Frame Tube +// Material: 2"×6"×1/4" Rectangular Tubing (50.8mm × 152.4mm × 6.35mm wall) +// Quantity: 1 piece +// Location: Front frame cross tube, behind front wheels +// +// This tube passes through all four wall panels, extending 1/2" past each outer panel. +// CNC-friendly corner radius on cutout profile. +// Bolt holes for angle iron mounts on front and rear faces. + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +// Frame tube dimensions +_tube_width = 152.4; // 6" (in Y direction when installed) +_tube_height = 50.8; // 2" (in Z direction when installed) +_wall_thick = 6.35; // 1/4" wall thickness +_corner_radius = 12.7; // 1/2" corner radius on tube profile + +// Length calculation (matches FRAME_TUBE_LENGTH in main assembly) +_extension = 12.7; // 1/2" extension past outer panels +_track_width = is_undef(TRACK_WIDTH) ? 900 : TRACK_WIDTH; +_sandwich_spacing = is_undef(SANDWICH_SPACING) ? 120 : SANDWICH_SPACING; +_panel_thick = is_undef(PANEL_THICKNESS) ? 12.7 : PANEL_THICKNESS; + +PART_T1_LENGTH = _track_width + _sandwich_spacing + 2*_panel_thick + 2*_extension; +PART_T1_WIDTH = _tube_width; +PART_T1_HEIGHT = _tube_height; +PART_T1_WALL = _wall_thick; +PART_T1_RADIUS = _corner_radius; + +// ============================================================================= +// BOLT HOLE PARAMETERS +// ============================================================================= + +_bolt_dia = is_undef(BOLT_DIA_1_2) ? 12.7 : BOLT_DIA_1_2; // 1/2" bolts +_angle_leg = 50.8; // 2" angle leg +_angle_thick = 6.35; // 1/4" angle thickness +_hole_inset = 25.4; // 1" from panel face (where angle hole is) + +// Angle iron hole positions (from angle bottom) +// Angle is 5.75" tall, holes are 2" apart centered = at 1.875" and 3.875" from bottom +_angle_height = 146.05; // 5.75" +_angle_hole_spacing = 50.8; // 2" spacing on tube leg +_angle_trim = 3.175; // 1/8" trim from tube edge + +// In part coords, Y becomes world Z after rotate([90,0,0]) +// Angle holes at 47.625mm and 98.425mm from angle bottom +// Angle bottom is at 3.175mm above tube bottom (in world Z = part Y) +_hole_y_1 = _angle_trim + (_angle_height/2 - _angle_hole_spacing/2); // ~50.8mm +_hole_y_2 = _angle_trim + (_angle_height/2 + _angle_hole_spacing/2); // ~101.6mm + +// X positions for panel faces (tube centered at X=0) +// Left outer panel inner face +_left_outer_x = -(_track_width/2 + _sandwich_spacing/2); +// Left inner panel inner face +_left_inner_x = -(_track_width/2 - _sandwich_spacing/2) + _panel_thick; +// Right inner panel inner face +_right_inner_x = (_track_width/2 - _sandwich_spacing/2) - _panel_thick; +// Right outer panel inner face +_right_outer_x = (_track_width/2 + _sandwich_spacing/2); + +// Motor plate positions (6" inboard from inner wall plates) +_motor_plate_inboard = 152.4; // 6" +_motor_plate_thick = 6.35; // 1/4" motor plate thickness +// Motor plate X is at: (TRACK_WIDTH/2 - SANDWICH_SPACING/2) - MOTOR_PLATE_INBOARD +// Angle is offset by plate_thickness/2 from motor plate centerline +_left_motor_x = -(_track_width/2 - _sandwich_spacing/2) + _motor_plate_inboard + _motor_plate_thick/2; +_right_motor_x = (_track_width/2 - _sandwich_spacing/2) - _motor_plate_inboard - _motor_plate_thick/2; + +// Hole X positions (1" inset from panel/plate face toward center) +// For left side angles, hole is at panel_x + angle_thick + hole_inset +// For right side angles (mirrored), hole is at panel_x - angle_thick - hole_inset +// Motor plates: angle extends toward center, so left motor gets + inset, right gets - inset +PART_T1_HOLE_X = [ + _left_outer_x + _angle_thick + _hole_inset, + _left_inner_x + _hole_inset, + _left_motor_x + _hole_inset, // Left motor plate + _right_motor_x - _hole_inset, // Right motor plate + _right_inner_x - _hole_inset, + _right_outer_x - _angle_thick - _hole_inset +]; + +// Y positions for holes (become Z height positions after rotation) +PART_T1_HOLE_Y = [_hole_y_1, _hole_y_2]; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part T1: Front Cross Frame Tube + * + * Orientation: + * - Length runs along X axis (side to side) + * - Width (6") is in Y direction + * - Height (2") is in Z direction + * - Origin at center-bottom of tube at X=0 + * + * Bolt holes: 16 total (4 positions × 2 faces × 2 holes per face) + */ +module _part_t1_front_frame_tube(show_cutaway=false, show_holes=true) { + length = PART_T1_LENGTH; + width = PART_T1_WIDTH; + height = PART_T1_HEIGHT; + wall = PART_T1_WALL; + rad = PART_T1_RADIUS; + + color("SteelBlue") + difference() { + translate([-length/2, 0, 0]) + difference() { + // Outer profile with rounded corners + rotate([0, 90, 0]) + rotate([0, 0, 90]) + linear_extrude(height=length) + offset(r=rad) + offset(r=-rad) + square([width, height]); + + // Inner cutout (hollow tube) + translate([wall, wall, wall]) + rotate([0, 90, 0]) + rotate([0, 0, 90]) + linear_extrude(height=length - 2*wall) + offset(r=rad - wall/2) + offset(r=-(rad - wall/2)) + square([width - 2*wall, height - 2*wall]); + + // Optional cutaway for visualization + if (show_cutaway) { + translate([length/2, -1, -1]) + cube([length, width + 2, height + 2]); + } + } + + // Bolt holes for angle iron mounts + // After rotate([90,0,0]) in assembly: part Y→world Z, part Z→world -Y + // Holes go through Z faces (front/rear after rotation), at Y positions (height after rotation) + if (show_holes) { + // Holes in top face (part Z = height, becomes rear Y face after rotation) + for (x_pos = PART_T1_HOLE_X) { + for (y_pos = PART_T1_HOLE_Y) { + translate([x_pos, y_pos, height - wall - 1]) + cylinder(d=_bolt_dia, h=wall + 2, $fn=32); + } + } + + // Holes in bottom face (part Z = 0, becomes front Y face after rotation) + for (x_pos = PART_T1_HOLE_X) { + for (y_pos = PART_T1_HOLE_Y) { + translate([x_pos, y_pos, -1]) + cylinder(d=_bolt_dia, h=wall + 2, $fn=32); + } + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_t1_front_frame_tube(show_cutaway=false, show_holes=true); + +echo("=== PART T1: Front Cross Frame Tube ==="); +echo("Length:", PART_T1_LENGTH, "mm (", PART_T1_LENGTH/25.4, "inches)"); +echo("Width:", PART_T1_WIDTH, "mm (6\")"); +echo("Height:", PART_T1_HEIGHT, "mm (2\")"); +echo("Wall thickness:", PART_T1_WALL, "mm (1/4\")"); +echo("Bolt holes: 24 (6 positions × 2 faces × 2 per face)"); +echo("Quantity needed: 1 piece"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t2_rear_frame.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t2_rear_frame.scad new file mode 100644 index 0000000..9578989 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t2_rear_frame.scad @@ -0,0 +1,149 @@ +// Part T2: Rear Cross Frame Tube +// Material: 2"×6"×1/4" Rectangular Tubing (50.8mm × 152.4mm × 6.35mm wall) +// Quantity: 1 piece +// Location: Rear frame cross tube, in front of rear wheels +// +// Identical dimensions to T1, different Y position in assembly. +// Bolt holes for angle iron mounts on front and rear faces. + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS (same as T1) +// ============================================================================= + +_tube_width = 152.4; +_tube_height = 50.8; +_wall_thick = 6.35; +_corner_radius = 12.7; +_extension = 12.7; +_track_width = is_undef(TRACK_WIDTH) ? 900 : TRACK_WIDTH; +_sandwich_spacing = is_undef(SANDWICH_SPACING) ? 120 : SANDWICH_SPACING; +_panel_thick = is_undef(PANEL_THICKNESS) ? 12.7 : PANEL_THICKNESS; + +PART_T2_LENGTH = _track_width + _sandwich_spacing + 2*_panel_thick + 2*_extension; +PART_T2_WIDTH = _tube_width; +PART_T2_HEIGHT = _tube_height; +PART_T2_WALL = _wall_thick; +PART_T2_RADIUS = _corner_radius; + +// ============================================================================= +// BOLT HOLE PARAMETERS (same as T1) +// ============================================================================= + +_bolt_dia = is_undef(BOLT_DIA_1_2) ? 12.7 : BOLT_DIA_1_2; // 1/2" bolts +_angle_leg = 50.8; // 2" angle leg +_angle_thick = 6.35; // 1/4" angle thickness +_hole_inset = 25.4; // 1" from panel face (where angle hole is) + +// Angle iron hole positions (from angle bottom) +_angle_height = 146.05; // 5.75" +_angle_hole_spacing = 50.8; // 2" spacing on tube leg +_angle_trim = 3.175; // 1/8" trim from tube edge + +// In part coords, Y becomes world Z after rotate([90,0,0]) +_hole_y_1 = _angle_trim + (_angle_height/2 - _angle_hole_spacing/2); // ~50.8mm +_hole_y_2 = _angle_trim + (_angle_height/2 + _angle_hole_spacing/2); // ~101.6mm + +// X positions for panel faces (tube centered at X=0) +_left_outer_x = -(_track_width/2 + _sandwich_spacing/2); +_left_inner_x = -(_track_width/2 - _sandwich_spacing/2) + _panel_thick; +_right_inner_x = (_track_width/2 - _sandwich_spacing/2) - _panel_thick; +_right_outer_x = (_track_width/2 + _sandwich_spacing/2); + +// Motor plate positions (6" inboard from inner wall plates) +_motor_plate_inboard = 152.4; // 6" +_motor_plate_thick = 6.35; // 1/4" motor plate thickness +// Angle is offset by plate_thickness/2 from motor plate centerline +_left_motor_x = -(_track_width/2 - _sandwich_spacing/2) + _motor_plate_inboard + _motor_plate_thick/2; +_right_motor_x = (_track_width/2 - _sandwich_spacing/2) - _motor_plate_inboard - _motor_plate_thick/2; + +// Hole X positions (1" inset from panel/plate face toward center) +// Motor plates: angle extends toward center, so left motor gets + inset, right gets - inset +PART_T2_HOLE_X = [ + _left_outer_x + _angle_thick + _hole_inset, + _left_inner_x + _hole_inset, + _left_motor_x + _hole_inset, // Left motor plate + _right_motor_x - _hole_inset, // Right motor plate + _right_inner_x - _hole_inset, + _right_outer_x - _angle_thick - _hole_inset +]; + +// Y positions for holes (become Z height positions after rotation) +PART_T2_HOLE_Y = [_hole_y_1, _hole_y_2]; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part T2: Rear Cross Frame Tube + * Identical to T1, positioned at rear wheel location in assembly + * Bolt holes: 16 total (4 positions × 2 faces × 2 holes per face) + */ +module _part_t2_rear_frame_tube(show_cutaway=false, show_holes=true) { + length = PART_T2_LENGTH; + width = PART_T2_WIDTH; + height = PART_T2_HEIGHT; + wall = PART_T2_WALL; + rad = PART_T2_RADIUS; + + color("SteelBlue") + difference() { + translate([-length/2, 0, 0]) + difference() { + rotate([0, 90, 0]) + rotate([0, 0, 90]) + linear_extrude(height=length) + offset(r=rad) + offset(r=-rad) + square([width, height]); + + translate([wall, wall, wall]) + rotate([0, 90, 0]) + rotate([0, 0, 90]) + linear_extrude(height=length - 2*wall) + offset(r=rad - wall/2) + offset(r=-(rad - wall/2)) + square([width - 2*wall, height - 2*wall]); + + if (show_cutaway) { + translate([length/2, -1, -1]) + cube([length, width + 2, height + 2]); + } + } + + // Bolt holes for angle iron mounts + // After rotate([90,0,0]) in assembly: part Y→world Z, part Z→world -Y + // Holes go through Z faces (front/rear after rotation), at Y positions (height after rotation) + if (show_holes) { + // Holes in top face (part Z = height, becomes rear Y face after rotation) + for (x_pos = PART_T2_HOLE_X) { + for (y_pos = PART_T2_HOLE_Y) { + translate([x_pos, y_pos, height - wall - 1]) + cylinder(d=_bolt_dia, h=wall + 2, $fn=32); + } + } + + // Holes in bottom face (part Z = 0, becomes front Y face after rotation) + for (x_pos = PART_T2_HOLE_X) { + for (y_pos = PART_T2_HOLE_Y) { + translate([x_pos, y_pos, -1]) + cylinder(d=_bolt_dia, h=wall + 2, $fn=32); + } + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_t2_rear_frame_tube(show_cutaway=false, show_holes=true); + +echo("=== PART T2: Rear Cross Frame Tube ==="); +echo("Length:", PART_T2_LENGTH, "mm"); +echo("Bolt holes: 24 (6 positions × 2 faces × 2 per face)"); +echo("Identical dimensions to T1"); +echo("Quantity needed: 1 piece"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t3_arm_crossbeam.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t3_arm_crossbeam.scad new file mode 100644 index 0000000..7ffd44f --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t3_arm_crossbeam.scad @@ -0,0 +1,102 @@ +// Part T3: Arm Crossbeam Tube +// Material: 2"×6"×1/4" Rectangular Tubing (50.8mm × 152.4mm × 6.35mm wall) +// Quantity: 1 piece +// Location: Connects left and right loader arms at elbow position +// +// Length = ARM_SPACING (same as TRACK_WIDTH) +// Mounting holes for angle irons at each end, top and bottom + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +_tube_width = 152.4; // 6 inches (along arm Y direction) +_tube_height = 50.8; // 2 inches (Z direction) +_wall_thick = 6.35; // 1/4 inch wall +_corner_radius = 12.7; // 1/2 inch radius + +// Length calculated from params +// Crossbeam extends through inner arm CNC plates +_arm_spacing = is_undef(ARM_SPACING) ? 900 : ARM_SPACING; +_tube_w = is_undef(TUBE_2X6_1_4) ? 50.8 : TUBE_2X6_1_4[0]; // Arm tube width (2") + +// Ends flush with inner face of inner arm CNC plates +PART_T3_LENGTH = _arm_spacing - _tube_w; +PART_T3_WIDTH = _tube_width; // 152.4mm (6") +PART_T3_HEIGHT = _tube_height; // 50.8mm (2") +PART_T3_WALL = _wall_thick; +PART_T3_RADIUS = _corner_radius; + +// Mounting hole parameters (for angle iron mounts) +_arm_plate_thick = is_undef(ARM_PLATE_THICKNESS) ? 6.35 : ARM_PLATE_THICKNESS; +_angle_hole_offset = 25.4; // 1 inch from center of angle leg +_hole_x_offset = _arm_spacing/2 - (25.4 + _arm_plate_thick + _angle_hole_offset); +_hole_y_spacing = 25.4; // 1 inch from beam center (2 inch total spacing) +_bolt_dia = is_undef(BOLT_DIA_1_2) ? 12.7 : BOLT_DIA_1_2; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part T3: Arm Crossbeam Tube + * Cross tube connecting loader arms at elbow position + * show_holes: if true, renders mounting holes for visualization + */ +module _part_t3_arm_crossbeam(show_holes=true) { + length = PART_T3_LENGTH; + width = PART_T3_WIDTH; + height = PART_T3_HEIGHT; + wall = PART_T3_WALL; + rad = PART_T3_RADIUS; + + color("DarkGray") + difference() { + // Main tube - centered on X axis + rotate([0, 90, 0]) + linear_extrude(height=length, center=true) + hull() { + translate([-height/2 + rad, -width/2 + rad]) circle(r=rad); + translate([height/2 - rad, -width/2 + rad]) circle(r=rad); + translate([height/2 - rad, width/2 - rad]) circle(r=rad); + translate([-height/2 + rad, width/2 - rad]) circle(r=rad); + } + + // Hollow interior + rotate([0, 90, 0]) + linear_extrude(height=length - 2*wall, center=true) + hull() { + translate([-height/2 + rad + wall, -width/2 + rad + wall]) circle(r=rad); + translate([height/2 - rad - wall, -width/2 + rad + wall]) circle(r=rad); + translate([height/2 - rad - wall, width/2 - rad - wall]) circle(r=rad); + translate([-height/2 + rad + wall, width/2 - rad - wall]) circle(r=rad); + } + + // Mounting Holes for Angle Irons + // Located at ends of beam, top and bottom + if (show_holes) { + for (x_pos = [-_hole_x_offset, _hole_x_offset]) { + for (y_pos = [-_hole_y_spacing, _hole_y_spacing]) { + translate([x_pos, y_pos, 0]) + cylinder(d=_bolt_dia, h=100, center=true, $fn=32); + } + } + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_t3_arm_crossbeam(show_holes=true); + +echo("=== PART T3: Arm Crossbeam Tube ==="); +echo("Length:", PART_T3_LENGTH, "mm (", PART_T3_LENGTH/25.4, "in)"); +echo("Width (6\" face):", PART_T3_WIDTH, "mm"); +echo("Height (2\" face):", PART_T3_HEIGHT, "mm"); +echo("Wall thickness:", PART_T3_WALL, "mm"); +echo("Mounting holes: 8 total (4 per end, 1/2\" dia)"); +echo("Quantity needed: 1 piece"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t4_arm_main.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t4_arm_main.scad new file mode 100644 index 0000000..c5098c0 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t4_arm_main.scad @@ -0,0 +1,106 @@ +// Part T4: Main Arm Tube (Left and Right are identical) +// Material: 2"×6"×1/4" Rectangular Tubing (50.8mm × 152.4mm × 6.35mm wall) +// Quantity: 2 pieces (1 left, 1 right - mirror in assembly) +// Location: Main loader arm tube from pivot to elbow +// +// Length = ARM_MAIN_LEN (calculated from kinematic parameters) +// 2" face is vertical (Z), 6" face is horizontal (Y) + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS +// ============================================================================= + +_tube_w = is_undef(TUBE_2X6_1_4) ? 152.4 : TUBE_2X6_1_4[0]; // 6 inches (horizontal) +_tube_h = is_undef(TUBE_2X6_1_4) ? 50.8 : TUBE_2X6_1_4[1]; // 2 inches (vertical) +_wall_thick = 6.35; // 1/4 inch wall +_tube_rad = 12.7; // 1/2 inch radius for rounded corners + +// Length calculated from kinematic parameters +_arm_main_len = is_undef(ARM_MAIN_LEN) ? 1500 : ARM_MAIN_LEN; + +PART_T4_LENGTH = _arm_main_len; +PART_T4_WIDTH = _tube_w; // 152.4mm (6") - horizontal +PART_T4_HEIGHT = _tube_h; // 50.8mm (2") - vertical +PART_T4_WALL = _wall_thick; +PART_T4_RADIUS = _tube_rad; + +// Bolt hole parameters +_bolt_spacing_x = 100; // X spacing between bolt pairs +_bolt_spacing_y = 80; // Y spacing between bolts (through-tube) +_tube_bolt_inset = is_undef(TUBE_BOLT_INSET) ? 50.8 : TUBE_BOLT_INSET; // 2" from tube end +_bolt_dia = is_undef(BOLT_DIA_1_2) ? 12.7 : BOLT_DIA_1_2; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part T4: Main Arm Tube + * Loader arm main section from pivot to elbow + * Oriented with origin at rear end (pivot side), extends in +X + * show_holes: if true, renders mounting holes for plate attachment + */ +module _part_t4_arm_main(show_holes=true) { + length = PART_T4_LENGTH; + width = PART_T4_WIDTH; + height = PART_T4_HEIGHT; + wall = PART_T4_WALL; + rad = PART_T4_RADIUS; + + color("DarkGray") + difference() { + // Hollow Rounded Tube + translate([0, 0, height]) + rotate([0, 90, 0]) + linear_extrude(height=length) + difference() { + // Outer profile + hull() { + translate([rad, rad]) circle(r=rad); + translate([height - rad, rad]) circle(r=rad); + translate([height - rad, width - rad]) circle(r=rad); + translate([rad, width - rad]) circle(r=rad); + } + // Inner cutout + offset(delta=-wall) + hull() { + translate([rad, rad]) circle(r=rad); + translate([height - rad, rad]) circle(r=rad); + translate([height - rad, width - rad]) circle(r=rad); + translate([rad, width - rad]) circle(r=rad); + } + } + + if (show_holes) { + // Rear Holes (near pivot mount) + for (x = [_tube_bolt_inset, _tube_bolt_inset + _bolt_spacing_x]) + for (z = [height/2 - _bolt_spacing_y/2, height/2 + _bolt_spacing_y/2]) + translate([x, width/2, z]) + rotate([90, 0, 0]) + cylinder(d=_bolt_dia, h=width + 10, center=true, $fn=32); + + // Front Holes (near elbow) + for (x = [length - _tube_bolt_inset - _bolt_spacing_x, length - _tube_bolt_inset]) + for (z = [height/2 - _bolt_spacing_y/2, height/2 + _bolt_spacing_y/2]) + translate([x, width/2, z]) + rotate([90, 0, 0]) + cylinder(d=_bolt_dia, h=width + 10, center=true, $fn=32); + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +_part_t4_arm_main(show_holes=true); + +echo("=== PART T4: Main Arm Tube ==="); +echo("Length:", PART_T4_LENGTH, "mm (", PART_T4_LENGTH/25.4, "in)"); +echo("Width (6\" face):", PART_T4_WIDTH, "mm"); +echo("Height (2\" face):", PART_T4_HEIGHT, "mm"); +echo("Wall thickness:", PART_T4_WALL, "mm"); +echo("Mounting holes: 16 total (8 rear, 8 front, 1/2\" dia)"); +echo("Quantity needed: 2 pieces (left and right arms)"); diff --git a/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t5_arm_leg_spacer.scad b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t5_arm_leg_spacer.scad new file mode 100644 index 0000000..78fb0f1 --- /dev/null +++ b/LifeTrac-v25/mechanical_design/openscad/parts/structural/tube_t5_arm_leg_spacer.scad @@ -0,0 +1,154 @@ +// Part T5: Arm Leg Spacer Tube +// Material: 2"×6"×1/4" Rectangular Tubing (50.8mm × 152.4mm × 6.35mm wall) +// Quantity: 2 pieces (1 left, 1 right) +// Location: Elbow section between main arm tube and bucket pivot +// +// This piece is tapered/cut to match the arm plate profile +// Full length before cutting: ~150mm +// Positioned at elbow angle (ARM_ANGLE, typically ~150 degrees) + +include <../../lifetrac_v25_params.scad> + +// ============================================================================= +// PART DIMENSIONS (Pre-cut stock length) +// ============================================================================= + +_tube_w = is_undef(TUBE_2X6_1_4) ? 152.4 : TUBE_2X6_1_4[0]; // 6 inches +_tube_h = is_undef(TUBE_2X6_1_4) ? 50.8 : TUBE_2X6_1_4[1]; // 2 inches +_wall_thick = 6.35; +_tube_rad = 12.7; + +// Spacer length before tapering +PART_T5_LENGTH_STOCK = 150; // 150mm stock length +PART_T5_WIDTH = _tube_w; +PART_T5_HEIGHT = _tube_h; +PART_T5_WALL = _wall_thick; +PART_T5_RADIUS = _tube_rad; + +// Taper parameters from arm geometry +_drop_len = is_undef(ARM_DROP_LEN) ? 300 : ARM_DROP_LEN; +_drop_ext = is_undef(ARM_DROP_EXT) ? 80 : ARM_DROP_EXT; +_pivot_hole_x = is_undef(PIVOT_HOLE_X_FROM_FRONT) ? 25.4 : PIVOT_HOLE_X_FROM_FRONT; +_pivot_hole_z = is_undef(PIVOT_HOLE_Z_FROM_BOTTOM) ? 127 : PIVOT_HOLE_Z_FROM_BOTTOM; + +_plate_cx = _drop_ext - _pivot_hole_x; +_boss_x = _drop_len + _plate_cx; +_boss_z = _pivot_hole_z; +_boss_r = _pivot_hole_x; +_cut_start_x = _drop_len - 100; + +_bolt_dia = is_undef(BOLT_DIA_1_2) ? 12.7 : BOLT_DIA_1_2; + +// ============================================================================= +// MAIN MODULE +// ============================================================================= + +/** + * Part T5: Arm Leg Spacer Tube (Raw Stock) + * This shows the uncut stock piece. + * In fabrication, this will be plasma cut to match arm plate profile. + * show_profile: if true, shows the cut profile envelope + */ +module _part_t5_arm_leg_spacer_raw() { + spacer_len = PART_T5_LENGTH_STOCK; + tube_w = PART_T5_WIDTH; + tube_h = PART_T5_HEIGHT; + wall = PART_T5_WALL; + rad = PART_T5_RADIUS; + + color("DarkGray") + translate([0, 0, tube_h]) + rotate([0, 90, 0]) + linear_extrude(height=spacer_len) + difference() { + // Outer profile + hull() { + translate([rad, rad]) circle(r=rad); + translate([tube_h - rad, rad]) circle(r=rad); + translate([tube_h - rad, tube_w - rad]) circle(r=rad); + translate([rad, tube_w - rad]) circle(r=rad); + } + // Inner cutout + offset(delta=-wall) + hull() { + translate([rad, rad]) circle(r=rad); + translate([tube_h - rad, rad]) circle(r=rad); + translate([tube_h - rad, tube_w - rad]) circle(r=rad); + translate([rad, tube_w - rad]) circle(r=rad); + } + } +} + +/** + * Part T5: Arm Leg Spacer Tube (Cut to Profile) + * Shows the final tapered shape matching arm plate + * show_holes: if true, renders mounting holes + */ +module _part_t5_arm_leg_spacer_cut(show_holes=true) { + spacer_len = PART_T5_LENGTH_STOCK; + tube_w = PART_T5_WIDTH; + tube_h = PART_T5_HEIGHT; + wall = PART_T5_WALL; + rad = PART_T5_RADIUS; + + color("DarkGray") + difference() { + intersection() { + // Raw tube section + translate([0, 0, tube_h]) + rotate([0, 90, 0]) + linear_extrude(height=spacer_len) + difference() { + offset(r=rad) square([tube_h - rad*2, tube_w - rad*2], center=true); + hull() { + translate([-tube_h/2 + rad, -tube_w/2 + rad]) circle(r=rad); + translate([tube_h/2 - rad, -tube_w/2 + rad]) circle(r=rad); + translate([tube_h/2 - rad, tube_w/2 - rad]) circle(r=rad); + translate([-tube_h/2 + rad, tube_w/2 - rad]) circle(r=rad); + } + } + + // Profile envelope (tapers toward pivot) + union() { + // Full width portion + cube([_cut_start_x, 100 + tube_w, tube_h]); + // Taper portion + hull() { + translate([_cut_start_x - 1, -50, 0]) + cube([1, 100 + tube_w, tube_h]); + translate([_boss_x, -50, _boss_z]) + rotate([-90, 0, 0]) + cylinder(r=_boss_r, h=100 + tube_w); + } + } + } + + // Mounting holes (4 bolts) + if (show_holes) { + for (xb = [spacer_len/3, spacer_len*2/3]) + for (zb = [tube_h*0.3, tube_h*0.7]) + translate([xb, tube_w/2, zb]) + rotate([90, 0, 0]) + cylinder(d=_bolt_dia, h=tube_w + 50, center=true, $fn=32); + } + } +} + +// ============================================================================= +// RENDER +// ============================================================================= + +// Show the raw stock piece for ordering/cutting +_part_t5_arm_leg_spacer_raw(); + +// Uncomment to see the cut profile: +// translate([0, 200, 0]) _part_t5_arm_leg_spacer_cut(show_holes=true); + +echo("=== PART T5: Arm Leg Spacer Tube ==="); +echo("Stock Length:", PART_T5_LENGTH_STOCK, "mm (", PART_T5_LENGTH_STOCK/25.4, "in)"); +echo("Width (6\" face):", PART_T5_WIDTH, "mm"); +echo("Height (2\" face):", PART_T5_HEIGHT, "mm"); +echo("Wall thickness:", PART_T5_WALL, "mm"); +echo("Note: Cut to profile matching arm_plate.scad taper"); +echo("Mounting holes: 8 total (1/2\" dia, through-tube)"); +echo("Quantity needed: 2 pieces (left and right arms)"); diff --git a/LifeTrac-v25/mechanical_design/requirements.txt b/LifeTrac-v25/mechanical_design/requirements.txt new file mode 100644 index 0000000..1319b5c --- /dev/null +++ b/LifeTrac-v25/mechanical_design/requirements.txt @@ -0,0 +1,2 @@ +reportlab>=4.0.0 +Pillow>=9.0.0 diff --git a/LifeTrac-v25/mechanical_design/structural_steel_cut_list.pdf b/LifeTrac-v25/mechanical_design/structural_steel_cut_list.pdf new file mode 100644 index 0000000..80f3c44 Binary files /dev/null and b/LifeTrac-v25/mechanical_design/structural_steel_cut_list.pdf differ