trenchfoot 0.1.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of trenchfoot might be problematic. Click here for more details.

Files changed (120) hide show
  1. trenchfoot-0.2.2/.env +1 -0
  2. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/.github/workflows/ci.yml +17 -0
  3. trenchfoot-0.2.2/CHANGELOG.md +40 -0
  4. trenchfoot-0.2.2/CLAUDE.md +68 -0
  5. trenchfoot-0.2.2/PKG-INFO +111 -0
  6. trenchfoot-0.2.2/README.md +93 -0
  7. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/docs/scenario_gallery.md +1 -0
  8. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/generate_scenarios.py +104 -27
  9. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/gmsh_sloped_trench_mesher.py +133 -28
  10. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S01_straight_vwalls/metrics.json +4 -4
  11. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S01_straight_vwalls/preview_oblique.png +0 -0
  12. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S01_straight_vwalls/preview_side.png +0 -0
  13. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S01_straight_vwalls/preview_top.png +0 -0
  14. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S01_straight_vwalls/scene.json +2 -2
  15. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S01_straight_vwalls/trench_scene.obj +49 -0
  16. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S01_straight_vwalls/volumetric/trench_volume.msh +755 -0
  17. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S02_straight_slope_pipe/metrics.json +10 -10
  18. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_oblique.png +0 -0
  19. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_side.png +0 -0
  20. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_top.png +0 -0
  21. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S02_straight_slope_pipe/scene.json +3 -3
  22. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S02_straight_slope_pipe/trench_scene.obj +14407 -0
  23. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S02_straight_slope_pipe/volumetric/trench_volume.msh +2114 -0
  24. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/metrics.json +42 -0
  25. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_oblique.png +0 -0
  26. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_side.png +0 -0
  27. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_top.png +0 -0
  28. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/scene.json +4 -4
  29. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/trench_scene.obj +10547 -10540
  30. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/volumetric/trench_volume.msh +3797 -0
  31. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/metrics.json +46 -0
  32. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png +0 -0
  33. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png +0 -0
  34. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png +0 -0
  35. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/scene.json +8 -7
  36. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/trench_scene.obj +17999 -17988
  37. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/volumetric/trench_volume.msh +6095 -0
  38. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S05_wide_slope_pair/metrics.json +42 -0
  39. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png +0 -0
  40. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png +0 -0
  41. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png +0 -0
  42. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S05_wide_slope_pair/scene.json +6 -6
  43. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S05_wide_slope_pair/trench_scene.obj +28810 -0
  44. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S05_wide_slope_pair/volumetric/trench_volume.msh +5081 -0
  45. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/metrics.json +43 -0
  46. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png +0 -0
  47. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png +0 -0
  48. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png +0 -0
  49. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/scene.json +6 -6
  50. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/trench_scene.obj +12812 -12793
  51. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/volumetric/trench_volume.msh +10402 -0
  52. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S07_circular_well/metrics.json +48 -0
  53. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S07_circular_well/preview_oblique.png +0 -0
  54. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S07_circular_well/preview_side.png +0 -0
  55. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S07_circular_well/preview_top.png +0 -0
  56. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S07_circular_well/scene.json +203 -0
  57. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S07_circular_well/trench_scene.obj +64401 -0
  58. trenchfoot-0.2.2/packages/trenchfoot/scenarios/S07_circular_well/volumetric/trench_volume.msh +22370 -0
  59. trenchfoot-0.2.2/packages/trenchfoot/scenarios/SUMMARY.json +470 -0
  60. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/trench_scene_generator_v3.py +323 -46
  61. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/pyproject.toml +1 -1
  62. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/tests/test_trenchfoot_generation.py +127 -3
  63. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/uv.lock +1 -1
  64. trenchfoot-0.1.0/.env +0 -1
  65. trenchfoot-0.1.0/PKG-INFO +0 -104
  66. trenchfoot-0.1.0/README.md +0 -86
  67. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S01_straight_vwalls/preview_oblique.png +0 -0
  68. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S01_straight_vwalls/preview_side.png +0 -0
  69. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S01_straight_vwalls/preview_top.png +0 -0
  70. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S01_straight_vwalls/trench_scene.obj +0 -46
  71. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S01_straight_vwalls/volumetric/trench_volume.msh +0 -1017
  72. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_oblique.png +0 -0
  73. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_side.png +0 -0
  74. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_top.png +0 -0
  75. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S02_straight_slope_pipe/trench_scene.obj +0 -14404
  76. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S02_straight_slope_pipe/volumetric/trench_volume.msh +0 -2389
  77. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/metrics.json +0 -42
  78. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_oblique.png +0 -0
  79. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_side.png +0 -0
  80. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_top.png +0 -0
  81. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/volumetric/trench_volume.msh +0 -4463
  82. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/metrics.json +0 -46
  83. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png +0 -0
  84. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png +0 -0
  85. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png +0 -0
  86. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/volumetric/trench_volume.msh +0 -7610
  87. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S05_wide_slope_pair/metrics.json +0 -42
  88. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png +0 -0
  89. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png +0 -0
  90. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png +0 -0
  91. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S05_wide_slope_pair/trench_scene.obj +0 -28803
  92. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/metrics.json +0 -43
  93. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png +0 -0
  94. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png +0 -0
  95. trenchfoot-0.1.0/packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png +0 -0
  96. trenchfoot-0.1.0/packages/trenchfoot/scenarios/SUMMARY.json +0 -159
  97. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/.github_token.env +0 -0
  98. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/LICENSE +0 -0
  99. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/STATUS.md +0 -0
  100. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/__init__.py +0 -0
  101. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/Dockerfile +0 -0
  102. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/README.md +0 -0
  103. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/__init__.py +0 -0
  104. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/plot_mesh.py +0 -0
  105. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/render_colors.py +0 -0
  106. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S01_straight_vwalls/meshes/trench_scene_culled.obj +0 -0
  107. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S01_straight_vwalls/point_clouds/culled/resolution0p050.pth +0 -0
  108. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S01_straight_vwalls/point_clouds/full/resolution0p050.pth +0 -0
  109. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S01_straight_vwalls/preview.png +0 -0
  110. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S02_straight_slope_pipe/meshes/trench_scene_culled.obj +0 -0
  111. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S02_straight_slope_pipe/point_clouds/culled/resolution0p050.pth +0 -0
  112. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S02_straight_slope_pipe/point_clouds/full/resolution0p050.pth +0 -0
  113. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview.png +0 -0
  114. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/meshes/trench_scene_culled.obj +0 -0
  115. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/point_clouds/culled/resolution0p050.pth +0 -0
  116. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/point_clouds/full/resolution0p050.pth +0 -0
  117. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview.png +0 -0
  118. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/meshes/trench_scene_culled.obj +0 -0
  119. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview.png +0 -0
  120. {trenchfoot-0.1.0 → trenchfoot-0.2.2}/packages/trenchfoot/scene_spec_example.json +0 -0
trenchfoot-0.2.2/.env ADDED
@@ -0,0 +1 @@
1
+ PYPI_API_TOKEN=pypi-AgEIcHlwaS5vcmcCJGZjZTM1MzQwLTFiZGQtNGQ5MS1hNzIyLTc1MmU1YTJhYzE1NQACGFsxLFsicHl0ZXN0LWNocm9uaWNsZSJdXQACLFsyLFsiMzg4ZTYxZTMtMDk2NC00ZjNiLWE5ODItYzc4NTMxNDlhM2FjIl1dAAAGID45Pzb1Xm05EgS5M8C2_5BbgSEULSUwP3GpDCia6vTP
@@ -15,6 +15,9 @@ jobs:
15
15
  steps:
16
16
  - uses: actions/checkout@v4
17
17
 
18
+ - name: Install system dependencies
19
+ run: sudo apt-get update && sudo apt-get install -y libglu1-mesa
20
+
18
21
  - name: Set up uv
19
22
  uses: astral-sh/setup-uv@v3
20
23
  with:
@@ -38,9 +41,13 @@ jobs:
38
41
  env:
39
42
  UV_PROJECT_ENVIRONMENT: .venv
40
43
  PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
44
+ PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
41
45
  steps:
42
46
  - uses: actions/checkout@v4
43
47
 
48
+ - name: Install system dependencies
49
+ run: sudo apt-get update && sudo apt-get install -y libglu1-mesa
50
+
44
51
  - name: Set up uv
45
52
  uses: astral-sh/setup-uv@v3
46
53
  with:
@@ -49,6 +56,16 @@ jobs:
49
56
  - name: Create virtual environment
50
57
  run: uv venv --python 3.12
51
58
 
59
+ - name: Ensure PyPI token
60
+ shell: bash
61
+ run: |
62
+ TOKEN="${PYPI_API_TOKEN:-${PYPI_TOKEN:-}}"
63
+ if [ -z "$TOKEN" ]; then
64
+ echo "PYPI_API_TOKEN or PYPI_TOKEN secret not provided" >&2
65
+ exit 1
66
+ fi
67
+ echo "PYPI_API_TOKEN=$TOKEN" >> "$GITHUB_ENV"
68
+
52
69
  - name: Load .env if present
53
70
  if: hashFiles('.env') != ''
54
71
  shell: bash
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ ## [0.2.2] - 2025-12-20
4
+
5
+ ### Fixed
6
+ - **Closed path handling for S07 circular well**: Explicitly close the circular path by repeating the first point. The previous heuristic was incorrectly detecting L-shaped and U-shaped paths as closed.
7
+ - **Volumetric meshing for closed paths**: Added proper annular geometry support to the gmsh mesher for closed circular paths with outer and inner walls.
8
+
9
+ ## [0.2.1] - 2025-12-20
10
+
11
+ ### Fixed
12
+ - **Annular triangulation bug**: Ground plane was incorrectly filling in the trench opening with triangles instead of leaving it as a hole. Replaced bridge+ear-clipping algorithm with proper annular triangulation that "zips" around both polygon boundaries.
13
+
14
+ ## [0.2.0] - 2025-12-19
15
+
16
+ ### Added
17
+ - **S07 circular well scenario**: Deep cylindrical well with 4 criss-crossing pipes at different elevations and diameters
18
+ - **Offset polygon ground plane**: Ground surface now follows trench outline instead of axis-aligned bounding box, reducing wasted space for L-shaped, U-shaped, and curved trenches
19
+ - **Open-topped trenches**: Trench cap (`trench_cap_for_volume`) is kept internally for metrics calculations but excluded from OBJ export and preview renders
20
+ - New tests for cap exclusion, offset ground, and circular well scenario
21
+
22
+ ### Changed
23
+ - **Shallower trench depths**: All scenarios adjusted to be less extreme (S01: 0.6m, S02: 0.9m, S03: 1.1m, S04: 1.2m, S05: 0.7m, S06: 0.85m)
24
+ - Reduced ground `size_margin` values to work better with offset polygon ground
25
+ - Pipe z-positions adjusted proportionally to fit within shallower trenches
26
+
27
+ ### Fixed
28
+ - Ground plane no longer wastes space on flat areas away from the trench path
29
+
30
+ ## [0.1.1] - 2025-10-30
31
+
32
+ - CI hardening for PyPI token handling
33
+ - Initial PyPI release
34
+
35
+ ## [0.1.0] - 2025-10-30
36
+
37
+ - Initial release with surface and volumetric mesh generation
38
+ - Scenarios S01-S06 with increasing complexity
39
+ - Plotly HTML viewer support
40
+ - Python SDK with `generate_surface_mesh()` and `generate_trench_volume()`
@@ -0,0 +1,68 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Trenchfoot is a synthetic trench mesh generator that produces surface meshes (OBJ) and volumetric meshes (via Gmsh). It generates semi-realistic trench scenes with embedded pipes, boxes, and spheres, useful for creating synthetic datasets.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Install with uv (development)
13
+ uv pip install -e ".[dev,preview,mesher,viz]"
14
+
15
+ # Run tests
16
+ uv run pytest -rs
17
+
18
+ # Run a single test
19
+ uv run pytest tests/test_trenchfoot_generation.py::test_build_scene_produces_surface -v
20
+
21
+ # Generate scenario previews (writes to TRENCHFOOT_SCENARIO_OUT_ROOT or packages/trenchfoot/scenarios/)
22
+ uv run python -m trenchfoot.generate_scenarios --preview --skip-volumetric
23
+
24
+ # Generate with volumetric meshes (requires gmsh)
25
+ uv run python -m trenchfoot.generate_scenarios --volumetric --lc 0.4
26
+
27
+ # Generate gallery markdown
28
+ uv run python -m trenchfoot.generate_scenarios --preview --skip-volumetric --gallery docs/scenario_gallery.md
29
+
30
+ # Interactive Plotly mesh viewer (requires viz extra)
31
+ trenchfoot-plot path/to/trench_scene.obj --open
32
+ ```
33
+
34
+ Set `TRENCHFOOT_SCENARIO_OUT_ROOT=/tmp/trench-previews` to keep generated assets out of the repository.
35
+
36
+ ## Architecture
37
+
38
+ The package lives in `packages/trenchfoot/` with these core modules:
39
+
40
+ - **`trench_scene_generator_v3.py`**: Surface mesh generator. Produces OBJ files with polyline trenches, sloped walls, ground planes, and embedded geometry (pipes/boxes/spheres). Handles noise application and preview rendering. Key types: `SceneSpec`, `SurfaceMeshResult`.
41
+
42
+ - **`gmsh_sloped_trench_mesher.py`**: Volumetric mesher using Gmsh. Creates tetrahedral meshes with physical groups (`TrenchAir`, `Pipe0`, etc.). Has adaptive clearance logic that scales guard bands based on pipe radius and wall slope. Key types: `VolumeMeshResult`.
43
+
44
+ - **`generate_scenarios.py`**: CLI and scenario runner. Defines preset scenarios (S01-S06) and orchestrates both surface and volumetric generation. Handles gallery markdown output and SUMMARY.json diagnostics.
45
+
46
+ - **`__init__.py`**: Public API exports. Gracefully handles missing gmsh dependency.
47
+
48
+ ## Scene Specification
49
+
50
+ Trenches are defined by JSON specs with:
51
+ - `path_xy`: 2D polyline defining trench centerline
52
+ - `width`, `depth`, `wall_slope`: Cross-section parameters
53
+ - `ground`: Elevation, slope, and margin for ground plane
54
+ - `pipes`, `boxes`, `spheres`: Embedded objects positioned along the trench
55
+ - `noise`: Perlin-style surface perturbation settings
56
+
57
+ Objects use `s` or `s_center` (0-1 arc-length parameter) to position along the trench path.
58
+
59
+ ## Optional Dependencies
60
+
61
+ - `[preview]` - matplotlib for PNG previews
62
+ - `[mesher]` - gmsh for volumetric mesh generation
63
+ - `[viz]` - plotly for interactive HTML viewers
64
+ - `[dev]` - pytest
65
+
66
+ ## Testing Notes
67
+
68
+ Tests skip volumetric tests if gmsh runtime prerequisites (e.g., libGLU) are unavailable. The `_require_gmsh_runtime()` helper handles this gracefully.
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: trenchfoot
3
+ Version: 0.2.2
4
+ Summary: Synthetic trench scenario generator bundle (surfaces + volumetrics).
5
+ Author: Liam Moore
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.12
8
+ Requires-Dist: numpy>=1.26
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8.3; extra == 'dev'
11
+ Provides-Extra: mesher
12
+ Requires-Dist: gmsh>=4.11; extra == 'mesher'
13
+ Provides-Extra: preview
14
+ Requires-Dist: matplotlib>=3.8; extra == 'preview'
15
+ Provides-Extra: viz
16
+ Requires-Dist: plotly>=5.24; extra == 'viz'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # Trenchfoot
20
+
21
+ Surface and volumetric trench mesh generator with shipped presets, Plotly previews, and a lightweight Python SDK.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install trenchfoot
27
+ ```
28
+
29
+ Want volumetrics or visualisations? Install extras as needed:
30
+ - `pip install "trenchfoot[mesher]"` for gmsh-powered volume meshes.
31
+ - `pip install "trenchfoot[preview]"` for matplotlib snapshot renders.
32
+ - `pip install "trenchfoot[viz]"` for Plotly HTML viewers.
33
+
34
+ ## Scenario Gallery
35
+
36
+ Color key: trench surfaces use warm soil tones; embedded geometry is colour-coded per group.
37
+
38
+ | Scenario | Top | Side | Oblique |
39
+ | --- | --- | --- | --- |
40
+ | S01_straight_vwalls | ![S01 top](packages/trenchfoot/scenarios/S01_straight_vwalls/preview_top.png) | ![S01 side](packages/trenchfoot/scenarios/S01_straight_vwalls/preview_side.png) | ![S01 oblique](packages/trenchfoot/scenarios/S01_straight_vwalls/preview_oblique.png) |
41
+ | S02_straight_slope_pipe | ![S02 top](packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_top.png) | ![S02 side](packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_side.png) | ![S02 oblique](packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_oblique.png) |
42
+ | S03_L_slope_two_pipes_box | ![S03 top](packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_top.png) | ![S03 side](packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_side.png) | ![S03 oblique](packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_oblique.png) |
43
+ | S04_U_slope_multi_noise | ![S04 top](packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png) | ![S04 side](packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png) | ![S04 oblique](packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png) |
44
+ | S05_wide_slope_pair | ![S05 top](packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png) | ![S05 side](packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png) | ![S05 oblique](packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png) |
45
+ | S06_bumpy_wide_loop | ![S06 top](packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png) | ![S06 side](packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png) | ![S06 oblique](packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png) |
46
+ | S07_circular_well | ![S07 top](packages/trenchfoot/scenarios/S07_circular_well/preview_top.png) | ![S07 side](packages/trenchfoot/scenarios/S07_circular_well/preview_side.png) | ![S07 oblique](packages/trenchfoot/scenarios/S07_circular_well/preview_oblique.png) |
47
+
48
+ ### S07 circular well preset
49
+
50
+ A deep cylindrical well with criss-crossing pipes at different elevations:
51
+
52
+ ```json
53
+ {
54
+ "path_xy": "<<32-vertex circle approximation, radius=1.5>>",
55
+ "width": 2.0,
56
+ "depth": 2.5,
57
+ "wall_slope": 0.05,
58
+ "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 2.0},
59
+ "pipes": [
60
+ {"radius": 0.20, "length": 4.0, "angle_deg": 0, "s_center": 0.25, "z": -0.5},
61
+ {"radius": 0.15, "length": 3.5, "angle_deg": 45, "s_center": 0.5, "z": -1.2},
62
+ {"radius": 0.10, "length": 3.0, "angle_deg": -60, "s_center": 0.75, "z": -1.8},
63
+ {"radius": 0.12, "length": 3.2, "angle_deg": 90, "s_center": 0.0, "z": -2.2}
64
+ ],
65
+ "spheres": [{"radius": 0.25, "s": 0.4, "z": -1.5}],
66
+ "noise": {"enable": true, "amplitude": 0.02, "corr_length": 0.4, "octaves": 2, "gain": 0.5}
67
+ }
68
+ ```
69
+
70
+ ## CLI quick start
71
+
72
+ ```bash
73
+ trenchfoot-generate --help
74
+ trenchfoot-generate --preview --skip-volumetric --gallery docs/scenario_gallery.md
75
+ trenchfoot-plot packages/trenchfoot/scenarios/S05_wide_slope_pair/trench_scene.obj --open
76
+ ```
77
+
78
+ Set `TRENCHFOOT_SCENARIO_OUT_ROOT=/tmp/trench-previews` (or another writable path) to keep generated assets out of your checkout.
79
+
80
+ ## Python API
81
+
82
+ ```python
83
+ from trenchfoot import scene_spec_from_dict, generate_surface_mesh, generate_trench_volume, gmsh_available
84
+
85
+ spec_dict = {
86
+ "path_xy": [[0.0, 0.0], [5.0, 0.0]],
87
+ "width": 1.0,
88
+ "depth": 1.2,
89
+ "pipes": [{"radius": 0.1, "length": 1.8, "angle_deg": 0.0, "s_center": 0.5}],
90
+ "boxes": [],
91
+ "spheres": [],
92
+ "noise": {"enable": False},
93
+ }
94
+
95
+ scene = scene_spec_from_dict(spec_dict)
96
+ surface = generate_surface_mesh(scene, make_preview=True)
97
+ surface.persist("./surface")
98
+
99
+ if gmsh_available():
100
+ volume = generate_trench_volume(spec_dict, lc=0.4, persist_path="./volume/trench_volume.msh")
101
+ ```
102
+
103
+ `SurfaceMeshResult` keeps per-group faces, metrics, and optional preview PNG bytes; call `.persist(...)` when you need files. `VolumeMeshResult` exposes node coordinates, elements, and physical groups while still letting you stay in memory.
104
+
105
+ ## Testing
106
+
107
+ ```bash
108
+ pytest -rs
109
+ ```
110
+
111
+ The suite exercises each preset (surface + volumetric), the gallery helpers, and the SDK smoke paths.
@@ -0,0 +1,93 @@
1
+ # Trenchfoot
2
+
3
+ Surface and volumetric trench mesh generator with shipped presets, Plotly previews, and a lightweight Python SDK.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install trenchfoot
9
+ ```
10
+
11
+ Want volumetrics or visualisations? Install extras as needed:
12
+ - `pip install "trenchfoot[mesher]"` for gmsh-powered volume meshes.
13
+ - `pip install "trenchfoot[preview]"` for matplotlib snapshot renders.
14
+ - `pip install "trenchfoot[viz]"` for Plotly HTML viewers.
15
+
16
+ ## Scenario Gallery
17
+
18
+ Color key: trench surfaces use warm soil tones; embedded geometry is colour-coded per group.
19
+
20
+ | Scenario | Top | Side | Oblique |
21
+ | --- | --- | --- | --- |
22
+ | S01_straight_vwalls | ![S01 top](packages/trenchfoot/scenarios/S01_straight_vwalls/preview_top.png) | ![S01 side](packages/trenchfoot/scenarios/S01_straight_vwalls/preview_side.png) | ![S01 oblique](packages/trenchfoot/scenarios/S01_straight_vwalls/preview_oblique.png) |
23
+ | S02_straight_slope_pipe | ![S02 top](packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_top.png) | ![S02 side](packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_side.png) | ![S02 oblique](packages/trenchfoot/scenarios/S02_straight_slope_pipe/preview_oblique.png) |
24
+ | S03_L_slope_two_pipes_box | ![S03 top](packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_top.png) | ![S03 side](packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_side.png) | ![S03 oblique](packages/trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_oblique.png) |
25
+ | S04_U_slope_multi_noise | ![S04 top](packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png) | ![S04 side](packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png) | ![S04 oblique](packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png) |
26
+ | S05_wide_slope_pair | ![S05 top](packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png) | ![S05 side](packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png) | ![S05 oblique](packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png) |
27
+ | S06_bumpy_wide_loop | ![S06 top](packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png) | ![S06 side](packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png) | ![S06 oblique](packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png) |
28
+ | S07_circular_well | ![S07 top](packages/trenchfoot/scenarios/S07_circular_well/preview_top.png) | ![S07 side](packages/trenchfoot/scenarios/S07_circular_well/preview_side.png) | ![S07 oblique](packages/trenchfoot/scenarios/S07_circular_well/preview_oblique.png) |
29
+
30
+ ### S07 circular well preset
31
+
32
+ A deep cylindrical well with criss-crossing pipes at different elevations:
33
+
34
+ ```json
35
+ {
36
+ "path_xy": "<<32-vertex circle approximation, radius=1.5>>",
37
+ "width": 2.0,
38
+ "depth": 2.5,
39
+ "wall_slope": 0.05,
40
+ "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 2.0},
41
+ "pipes": [
42
+ {"radius": 0.20, "length": 4.0, "angle_deg": 0, "s_center": 0.25, "z": -0.5},
43
+ {"radius": 0.15, "length": 3.5, "angle_deg": 45, "s_center": 0.5, "z": -1.2},
44
+ {"radius": 0.10, "length": 3.0, "angle_deg": -60, "s_center": 0.75, "z": -1.8},
45
+ {"radius": 0.12, "length": 3.2, "angle_deg": 90, "s_center": 0.0, "z": -2.2}
46
+ ],
47
+ "spheres": [{"radius": 0.25, "s": 0.4, "z": -1.5}],
48
+ "noise": {"enable": true, "amplitude": 0.02, "corr_length": 0.4, "octaves": 2, "gain": 0.5}
49
+ }
50
+ ```
51
+
52
+ ## CLI quick start
53
+
54
+ ```bash
55
+ trenchfoot-generate --help
56
+ trenchfoot-generate --preview --skip-volumetric --gallery docs/scenario_gallery.md
57
+ trenchfoot-plot packages/trenchfoot/scenarios/S05_wide_slope_pair/trench_scene.obj --open
58
+ ```
59
+
60
+ Set `TRENCHFOOT_SCENARIO_OUT_ROOT=/tmp/trench-previews` (or another writable path) to keep generated assets out of your checkout.
61
+
62
+ ## Python API
63
+
64
+ ```python
65
+ from trenchfoot import scene_spec_from_dict, generate_surface_mesh, generate_trench_volume, gmsh_available
66
+
67
+ spec_dict = {
68
+ "path_xy": [[0.0, 0.0], [5.0, 0.0]],
69
+ "width": 1.0,
70
+ "depth": 1.2,
71
+ "pipes": [{"radius": 0.1, "length": 1.8, "angle_deg": 0.0, "s_center": 0.5}],
72
+ "boxes": [],
73
+ "spheres": [],
74
+ "noise": {"enable": False},
75
+ }
76
+
77
+ scene = scene_spec_from_dict(spec_dict)
78
+ surface = generate_surface_mesh(scene, make_preview=True)
79
+ surface.persist("./surface")
80
+
81
+ if gmsh_available():
82
+ volume = generate_trench_volume(spec_dict, lc=0.4, persist_path="./volume/trench_volume.msh")
83
+ ```
84
+
85
+ `SurfaceMeshResult` keeps per-group faces, metrics, and optional preview PNG bytes; call `.persist(...)` when you need files. `VolumeMeshResult` exposes node coordinates, elements, and physical groups while still letting you stay in memory.
86
+
87
+ ## Testing
88
+
89
+ ```bash
90
+ pytest -rs
91
+ ```
92
+
93
+ The suite exercises each preset (surface + volumetric), the gallery helpers, and the SDK smoke paths.
@@ -6,3 +6,4 @@
6
6
  | S04_U_slope_multi_noise | ![S04_U_slope_multi_noise top](../packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png) | ![S04_U_slope_multi_noise side](../packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png) | ![S04_U_slope_multi_noise oblique](../packages/trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png) |
7
7
  | S05_wide_slope_pair | ![S05_wide_slope_pair top](../packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png) | ![S05_wide_slope_pair side](../packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png) | ![S05_wide_slope_pair oblique](../packages/trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png) |
8
8
  | S06_bumpy_wide_loop | ![S06_bumpy_wide_loop top](../packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png) | ![S06_bumpy_wide_loop side](../packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png) | ![S06_bumpy_wide_loop oblique](../packages/trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png) |
9
+ | S07_circular_well | ![S07_circular_well top](../packages/trenchfoot/scenarios/S07_circular_well/preview_top.png) | ![S07_circular_well side](../packages/trenchfoot/scenarios/S07_circular_well/preview_side.png) | ![S07_circular_well oblique](../packages/trenchfoot/scenarios/S07_circular_well/preview_oblique.png) |
@@ -135,18 +135,35 @@ def gmsh_available() -> bool:
135
135
  return _gmsh_mesher is not None
136
136
 
137
137
 
138
+ def _generate_circular_path(
139
+ center: tuple[float, float], radius: float, n_vertices: int = 32
140
+ ) -> List[List[float]]:
141
+ """Generate vertices for a closed circular polyline.
142
+
143
+ The path is explicitly closed by repeating the first point at the end.
144
+ This ensures proper handling as a closed loop in mesh generation.
145
+ """
146
+ import math
147
+ cx, cy = center
148
+ angles = [2 * math.pi * i / n_vertices for i in range(n_vertices)]
149
+ points = [[cx + radius * math.cos(a), cy + radius * math.sin(a)] for a in angles]
150
+ # Close the path by repeating the first point
151
+ points.append(points[0].copy())
152
+ return points
153
+
154
+
138
155
  def default_scenarios() -> List[ScenarioDefinition]:
139
- """Built-in scenario presets."""
156
+ """Built-in scenario presets with shallower depths and offset polygon ground."""
140
157
  return [
141
158
  ScenarioDefinition(
142
159
  "S01_straight_vwalls",
143
160
  {
144
161
  "path_xy": [[0, 0], [5, 0]],
145
162
  "width": 1.0,
146
- "depth": 1.0,
163
+ "depth": 0.6,
147
164
  "wall_slope": 0.0,
148
165
  "ground_margin": 0.5,
149
- "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 3.0},
166
+ "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 1.0},
150
167
  "pipes": [],
151
168
  "boxes": [],
152
169
  "spheres": [],
@@ -158,17 +175,17 @@ def default_scenarios() -> List[ScenarioDefinition]:
158
175
  {
159
176
  "path_xy": [[0, 0], [6, 0]],
160
177
  "width": 1.2,
161
- "depth": 1.5,
178
+ "depth": 0.9,
162
179
  "wall_slope": 0.2,
163
180
  "ground_margin": 0.5,
164
- "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 3.0},
181
+ "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 1.0},
165
182
  "pipes": [
166
183
  {
167
184
  "radius": 0.15,
168
185
  "length": 7.0,
169
186
  "angle_deg": 0,
170
187
  "s_center": 0.5,
171
- "z": -0.7,
188
+ "z": -0.45,
172
189
  "offset_u": 0.0,
173
190
  }
174
191
  ],
@@ -182,17 +199,17 @@ def default_scenarios() -> List[ScenarioDefinition]:
182
199
  {
183
200
  "path_xy": [[0, 0], [6, 0], [6, 4]],
184
201
  "width": 1.2,
185
- "depth": 1.8,
202
+ "depth": 1.1,
186
203
  "wall_slope": 0.15,
187
204
  "ground_margin": 1.0,
188
- "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 4.0},
205
+ "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 1.2},
189
206
  "pipes": [
190
207
  {
191
208
  "radius": 0.15,
192
209
  "length": 8.0,
193
210
  "angle_deg": 0,
194
211
  "s_center": 0.35,
195
- "z": -1.0,
212
+ "z": -0.6,
196
213
  "offset_u": 0.0,
197
214
  },
198
215
  {
@@ -200,7 +217,7 @@ def default_scenarios() -> List[ScenarioDefinition]:
200
217
  "length": 5.0,
201
218
  "angle_deg": 90,
202
219
  "s_center": 0.75,
203
- "z": -0.9,
220
+ "z": -0.55,
204
221
  "offset_u": 0.2,
205
222
  },
206
223
  ],
@@ -230,17 +247,17 @@ def default_scenarios() -> List[ScenarioDefinition]:
230
247
  {
231
248
  "path_xy": [[0, 0], [6, 0], [6, 4], [0, 4]],
232
249
  "width": 1.4,
233
- "depth": 2.0,
250
+ "depth": 1.2,
234
251
  "wall_slope": 0.25,
235
252
  "ground_margin": 1.2,
236
- "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 5.0},
253
+ "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 1.5},
237
254
  "pipes": [
238
255
  {
239
256
  "radius": 0.18,
240
257
  "length": 9.0,
241
258
  "angle_deg": 0,
242
259
  "s_center": 0.25,
243
- "z": -1.0,
260
+ "z": -0.6,
244
261
  "offset_u": 0.0,
245
262
  },
246
263
  {
@@ -248,7 +265,7 @@ def default_scenarios() -> List[ScenarioDefinition]:
248
265
  "length": 5.0,
249
266
  "angle_deg": 45,
250
267
  "s_center": 0.55,
251
- "z": -1.1,
268
+ "z": -0.65,
252
269
  "offset_u": -0.2,
253
270
  },
254
271
  {
@@ -256,12 +273,12 @@ def default_scenarios() -> List[ScenarioDefinition]:
256
273
  "length": 3.5,
257
274
  "angle_deg": -60,
258
275
  "s_center": 0.75,
259
- "z": -1.3,
276
+ "z": -0.8,
260
277
  "offset_u": 0.25,
261
278
  },
262
279
  ],
263
280
  "boxes": [],
264
- "spheres": [{"radius": 0.3, "s": 0.85, "offset_u": -0.2}],
281
+ "spheres": [{"radius": 0.25, "s": 0.85, "offset_u": -0.2, "z": -0.7}],
265
282
  "noise": {
266
283
  "enable": True,
267
284
  "amplitude": 0.02,
@@ -278,17 +295,17 @@ def default_scenarios() -> List[ScenarioDefinition]:
278
295
  {
279
296
  "path_xy": [[0, 0], [9, 0], [9, 3]],
280
297
  "width": 2.4,
281
- "depth": 1.2,
298
+ "depth": 0.7,
282
299
  "wall_slope": 0.08,
283
300
  "ground_margin": 1.5,
284
- "ground": {"z0": 0.0, "slope": [0.02, -0.015], "size_margin": 6.0},
301
+ "ground": {"z0": 0.0, "slope": [0.02, -0.015], "size_margin": 1.5},
285
302
  "pipes": [
286
303
  {
287
304
  "radius": 0.2,
288
305
  "length": 5.5,
289
306
  "angle_deg": 10,
290
307
  "s_center": 0.35,
291
- "z": -0.7,
308
+ "z": -0.4,
292
309
  "offset_u": 0.3,
293
310
  "clearance_scale": 0.9,
294
311
  },
@@ -297,7 +314,7 @@ def default_scenarios() -> List[ScenarioDefinition]:
297
314
  "length": 4.2,
298
315
  "angle_deg": -15,
299
316
  "s_center": 0.7,
300
- "z": -0.8,
317
+ "z": -0.45,
301
318
  "offset_u": -0.4,
302
319
  "clearance_scale": 1.1,
303
320
  },
@@ -306,10 +323,10 @@ def default_scenarios() -> List[ScenarioDefinition]:
306
323
  {
307
324
  "along": 1.2,
308
325
  "across": 0.9,
309
- "height": 0.5,
326
+ "height": 0.35,
310
327
  "s": 0.55,
311
328
  "offset_u": -0.25,
312
- "z": -0.6,
329
+ "z": -0.35,
313
330
  }
314
331
  ],
315
332
  "spheres": [],
@@ -329,17 +346,17 @@ def default_scenarios() -> List[ScenarioDefinition]:
329
346
  {
330
347
  "path_xy": [[0, 0], [4, -1], [8, 0], [8, 5], [2, 5], [-1, 2]],
331
348
  "width": 2.6,
332
- "depth": 1.4,
349
+ "depth": 0.85,
333
350
  "wall_slope": 0.12,
334
351
  "ground_margin": 2.0,
335
- "ground": {"z0": 0.2, "slope": [0.015, 0.03], "size_margin": 7.0},
352
+ "ground": {"z0": 0.2, "slope": [0.015, 0.03], "size_margin": 1.8},
336
353
  "pipes": [
337
354
  {
338
355
  "radius": 0.18,
339
356
  "length": 6.0,
340
357
  "angle_deg": 35,
341
358
  "s_center": 0.3,
342
- "z": -0.9,
359
+ "z": -0.55,
343
360
  "offset_u": 0.35,
344
361
  "clearance_scale": 1.0,
345
362
  },
@@ -348,14 +365,14 @@ def default_scenarios() -> List[ScenarioDefinition]:
348
365
  "length": 4.8,
349
366
  "angle_deg": -40,
350
367
  "s_center": 0.6,
351
- "z": -0.95,
368
+ "z": -0.6,
352
369
  "offset_u": -0.45,
353
370
  "clearance_scale": 0.85,
354
371
  },
355
372
  ],
356
373
  "boxes": [],
357
374
  "spheres": [
358
- {"radius": 0.35, "s": 0.82, "offset_u": 0.3, "z": -0.65}
375
+ {"radius": 0.3, "s": 0.82, "offset_u": 0.3, "z": -0.4}
359
376
  ],
360
377
  "noise": {
361
378
  "enable": True,
@@ -368,6 +385,66 @@ def default_scenarios() -> List[ScenarioDefinition]:
368
385
  },
369
386
  },
370
387
  ),
388
+ ScenarioDefinition(
389
+ "S07_circular_well",
390
+ {
391
+ "path_xy": _generate_circular_path((0.0, 0.0), radius=1.5, n_vertices=32),
392
+ "width": 2.0,
393
+ "depth": 2.5,
394
+ "wall_slope": 0.05,
395
+ "ground_margin": 1.0,
396
+ "ground": {"z0": 0.0, "slope": [0.0, 0.0], "size_margin": 2.0},
397
+ "pipes": [
398
+ # Upper pipe - large diameter, horizontal
399
+ {
400
+ "radius": 0.20,
401
+ "length": 4.0,
402
+ "angle_deg": 0,
403
+ "s_center": 0.25,
404
+ "z": -0.5,
405
+ "offset_u": 0.0,
406
+ },
407
+ # Middle pipe - medium, angled
408
+ {
409
+ "radius": 0.15,
410
+ "length": 3.5,
411
+ "angle_deg": 45,
412
+ "s_center": 0.5,
413
+ "z": -1.2,
414
+ "offset_u": 0.1,
415
+ },
416
+ # Lower pipe - small, opposite angle
417
+ {
418
+ "radius": 0.10,
419
+ "length": 3.0,
420
+ "angle_deg": -60,
421
+ "s_center": 0.75,
422
+ "z": -1.8,
423
+ "offset_u": -0.15,
424
+ },
425
+ # Deep pipe - crossing at bottom
426
+ {
427
+ "radius": 0.12,
428
+ "length": 3.2,
429
+ "angle_deg": 90,
430
+ "s_center": 0.0,
431
+ "z": -2.2,
432
+ "offset_u": 0.0,
433
+ },
434
+ ],
435
+ "boxes": [],
436
+ "spheres": [{"radius": 0.25, "s": 0.4, "offset_u": 0.0, "z": -1.5}],
437
+ "noise": {
438
+ "enable": True,
439
+ "amplitude": 0.02,
440
+ "corr_length": 0.4,
441
+ "octaves": 2,
442
+ "gain": 0.5,
443
+ "seed": 37,
444
+ "apply_to": ["trench_walls", "trench_bottom"],
445
+ },
446
+ },
447
+ ),
371
448
  ]
372
449
 
373
450