Refactor lift cylinder mount calculations for leverage #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Generate Assembly PNG | |
| on: | |
| push: | |
| branches: [ main, master ] | |
| paths: | |
| - 'LifeTrac-v25/mechanical_design/openscad/lifetrac_v25.scad' | |
| - 'LifeTrac-v25/mechanical_design/openscad/**/*.scad' | |
| pull_request: | |
| branches: [ main, master ] | |
| paths: | |
| - 'LifeTrac-v25/mechanical_design/openscad/lifetrac_v25.scad' | |
| - 'LifeTrac-v25/mechanical_design/openscad/**/*.scad' | |
| workflow_dispatch: | |
| jobs: | |
| generate-assembly-png: | |
| 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: Install OpenSCAD | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y openscad xvfb x11-utils | |
| openscad --version | |
| - name: Start Xvfb | |
| run: | | |
| Xvfb :99 -screen 0 1024x768x16 & | |
| XVFB_PID=$! | |
| echo "XVFB_PID=$XVFB_PID" >> $GITHUB_ENV | |
| # Wait for Xvfb to be ready (max 10 seconds) | |
| for i in {1..10}; do | |
| if xdpyinfo -display :99 >/dev/null 2>&1; then | |
| echo "Xvfb is ready on display :99" | |
| break | |
| fi | |
| if [ $i -eq 10 ]; then | |
| echo "Timeout waiting for Xvfb to start" | |
| exit 1 | |
| fi | |
| sleep 1 | |
| done | |
| echo "DISPLAY=:99" >> $GITHUB_ENV | |
| - name: Calculate camera position | |
| run: | | |
| cd LifeTrac-v25/mechanical_design | |
| # Calculate camera position dynamically based on machine dimensions | |
| # Extract parameters from OpenSCAD file | |
| MACHINE_HEIGHT=$(grep -E "^MACHINE_HEIGHT = " openscad/lifetrac_v25.scad | grep -oE "[0-9]+\.?[0-9]*" || echo "1000") | |
| WHEEL_BASE=$(grep -E "^WHEEL_BASE = " openscad/lifetrac_v25.scad | grep -oE "[0-9]+\.?[0-9]*" || echo "1400") | |
| GROUND_CLEARANCE=$(grep -E "^GROUND_CLEARANCE = " openscad/lifetrac_v25.scad | grep -oE "[0-9]+\.?[0-9]*" || echo "150") | |
| ARM_MAX_ANGLE=$(grep -E "^ARM_MAX_ANGLE = " openscad/lifetrac_v25.scad | grep -oE "[0-9]+\.?[0-9]*" || echo "60") | |
| BUCKET_HEIGHT=$(grep -E "^BUCKET_HEIGHT = " openscad/lifetrac_v25.scad | grep -oE "[0-9]+\.?[0-9]*" || echo "450") | |
| # Validate all extracted parameters | |
| if [ -z "$MACHINE_HEIGHT" ] || [ -z "$WHEEL_BASE" ] || [ -z "$GROUND_CLEARANCE" ] || [ -z "$ARM_MAX_ANGLE" ] || [ -z "$BUCKET_HEIGHT" ]; then | |
| echo "ERROR: Failed to extract required parameters from OpenSCAD file" | |
| echo " MACHINE_HEIGHT: $MACHINE_HEIGHT" | |
| echo " WHEEL_BASE: $WHEEL_BASE" | |
| echo " GROUND_CLEARANCE: $GROUND_CLEARANCE" | |
| echo " ARM_MAX_ANGLE: $ARM_MAX_ANGLE" | |
| echo " BUCKET_HEIGHT: $BUCKET_HEIGHT" | |
| exit 1 | |
| fi | |
| # Calculate key dimensions | |
| FRAME_Z_OFFSET=$GROUND_CLEARANCE | |
| ARM_PIVOT_Z=$((FRAME_Z_OFFSET + MACHINE_HEIGHT - 50)) | |
| WHEEL_MIDPOINT_Y=$((WHEEL_BASE / 2)) | |
| # Calculate maximum height when arms are raised to ARM_MAX_ANGLE | |
| ARM_LENGTH_APPROX=1600 | |
| # Max height = ARM_PIVOT_Z + ARM_LENGTH * sin(ARM_MAX_ANGLE) + BUCKET_HEIGHT | |
| MAX_ARM_TIP_HEIGHT=$(awk "BEGIN {pi=3.14159; print int($ARM_PIVOT_Z + $ARM_LENGTH_APPROX * sin($ARM_MAX_ANGLE * pi / 180) + $BUCKET_HEIGHT)}") | |
| # Calculate camera position to frame the scene properly | |
| SCENE_HEIGHT=$MAX_ARM_TIP_HEIGHT | |
| MARGIN_FACTOR="1.75" | |
| # Center point: halfway between ground and max height, at wheel midpoint Y | |
| CENTER_Y=$WHEEL_MIDPOINT_Y | |
| CENTER_Z=$(awk "BEGIN {print int($SCENE_HEIGHT / 2)}") | |
| # Camera distance | |
| CAMERA_DISTANCE=$(awk "BEGIN {print int($SCENE_HEIGHT * $MARGIN_FACTOR * 1.414)}") | |
| echo "=== Camera Calculation ===" | |
| echo "Camera position: --camera=$CAMERA_DISTANCE,$CAMERA_DISTANCE,$CAMERA_DISTANCE,0,$CENTER_Y,$CENTER_Z" | |
| echo "==========================" | |
| # Export calculated values as environment variables for subsequent steps | |
| echo "CAMERA_DISTANCE=$CAMERA_DISTANCE" >> $GITHUB_ENV | |
| echo "CENTER_Y=$CENTER_Y" >> $GITHUB_ENV | |
| echo "CENTER_Z=$CENTER_Z" >> $GITHUB_ENV | |
| - name: Generate assembly.png | |
| run: | | |
| cd LifeTrac-v25/mechanical_design | |
| # Generate assembly.png (720x720 square as requested) | |
| echo "Generating assembly.png..." | |
| openscad -o assembly.png \ | |
| --camera=$CAMERA_DISTANCE,$CAMERA_DISTANCE,$CAMERA_DISTANCE,0,$CENTER_Y,$CENTER_Z \ | |
| --imgsize=720,720 \ | |
| --projection=p \ | |
| --colorscheme=Nature \ | |
| openscad/lifetrac_v25.scad | |
| echo "✓ assembly.png generated successfully" | |
| ls -lh assembly.png | |
| - name: Upload assembly PNG artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: assembly-png | |
| path: LifeTrac-v25/mechanical_design/assembly.png | |
| retention-days: 90 | |
| - name: Commit and push assembly.png | |
| 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 assembly.png || true | |
| if git diff --staged --quiet; then | |
| echo "ℹ️ No changes to commit" | |
| else | |
| echo "📝 Committing changes..." | |
| git commit -m "Update assembly.png render [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 | |
| else | |
| echo "⚠️ Could not fetch branch ${BRANCH_NAME} from origin. This may be a PR from a fork. Skipping fetch/rebase." | |
| 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 | |
| fi | |
| - name: Cleanup Xvfb | |
| if: always() | |
| run: | | |
| if [ -n "$XVFB_PID" ]; then | |
| echo "Stopping Xvfb process (PID: $XVFB_PID)" | |
| # Try graceful shutdown first | |
| kill -TERM $XVFB_PID 2>/dev/null || true | |
| # Wait up to 5 seconds for process to terminate | |
| for i in {1..5}; do | |
| if ! kill -0 $XVFB_PID 2>/dev/null; then | |
| echo "Xvfb stopped gracefully" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| # Force kill if still running | |
| kill -KILL $XVFB_PID 2>/dev/null || true | |
| echo "Xvfb force stopped" | |
| fi |