trenchfoot 0.1.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

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 (58) hide show
  1. trenchfoot/generate_scenarios.py +104 -27
  2. trenchfoot/gmsh_sloped_trench_mesher.py +125 -25
  3. trenchfoot/scenarios/S01_straight_vwalls/metrics.json +4 -4
  4. trenchfoot/scenarios/S01_straight_vwalls/preview_oblique.png +0 -0
  5. trenchfoot/scenarios/S01_straight_vwalls/preview_side.png +0 -0
  6. trenchfoot/scenarios/S01_straight_vwalls/preview_top.png +0 -0
  7. trenchfoot/scenarios/S01_straight_vwalls/scene.json +2 -2
  8. trenchfoot/scenarios/S01_straight_vwalls/trench_scene.obj +30 -27
  9. trenchfoot/scenarios/S01_straight_vwalls/volumetric/trench_volume.msh +540 -802
  10. trenchfoot/scenarios/S02_straight_slope_pipe/metrics.json +10 -10
  11. trenchfoot/scenarios/S02_straight_slope_pipe/preview_oblique.png +0 -0
  12. trenchfoot/scenarios/S02_straight_slope_pipe/preview_side.png +0 -0
  13. trenchfoot/scenarios/S02_straight_slope_pipe/preview_top.png +0 -0
  14. trenchfoot/scenarios/S02_straight_slope_pipe/scene.json +3 -3
  15. trenchfoot/scenarios/S02_straight_slope_pipe/trench_scene.obj +4977 -4974
  16. trenchfoot/scenarios/S02_straight_slope_pipe/volumetric/trench_volume.msh +1694 -1969
  17. trenchfoot/scenarios/S03_L_slope_two_pipes_box/metrics.json +14 -14
  18. trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_oblique.png +0 -0
  19. trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_side.png +0 -0
  20. trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_top.png +0 -0
  21. trenchfoot/scenarios/S03_L_slope_two_pipes_box/scene.json +4 -4
  22. trenchfoot/scenarios/S03_L_slope_two_pipes_box/trench_scene.obj +10547 -10540
  23. trenchfoot/scenarios/S03_L_slope_two_pipes_box/volumetric/trench_volume.msh +3101 -3767
  24. trenchfoot/scenarios/S04_U_slope_multi_noise/metrics.json +17 -17
  25. trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png +0 -0
  26. trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png +0 -0
  27. trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png +0 -0
  28. trenchfoot/scenarios/S04_U_slope_multi_noise/scene.json +8 -7
  29. trenchfoot/scenarios/S04_U_slope_multi_noise/trench_scene.obj +17999 -17988
  30. trenchfoot/scenarios/S04_U_slope_multi_noise/volumetric/trench_volume.msh +4991 -6506
  31. trenchfoot/scenarios/S05_wide_slope_pair/metrics.json +14 -14
  32. trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png +0 -0
  33. trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png +0 -0
  34. trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png +0 -0
  35. trenchfoot/scenarios/S05_wide_slope_pair/scene.json +6 -6
  36. trenchfoot/scenarios/S05_wide_slope_pair/trench_scene.obj +10547 -10540
  37. trenchfoot/scenarios/S05_wide_slope_pair/volumetric/trench_volume.msh +5081 -0
  38. trenchfoot/scenarios/S06_bumpy_wide_loop/metrics.json +16 -16
  39. trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png +0 -0
  40. trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png +0 -0
  41. trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png +0 -0
  42. trenchfoot/scenarios/S06_bumpy_wide_loop/scene.json +6 -6
  43. trenchfoot/scenarios/S06_bumpy_wide_loop/trench_scene.obj +12812 -12793
  44. trenchfoot/scenarios/S06_bumpy_wide_loop/volumetric/trench_volume.msh +10402 -0
  45. trenchfoot/scenarios/S07_circular_well/metrics.json +48 -0
  46. trenchfoot/scenarios/S07_circular_well/preview_oblique.png +0 -0
  47. trenchfoot/scenarios/S07_circular_well/preview_side.png +0 -0
  48. trenchfoot/scenarios/S07_circular_well/preview_top.png +0 -0
  49. trenchfoot/scenarios/S07_circular_well/scene.json +203 -0
  50. trenchfoot/scenarios/S07_circular_well/trench_scene.obj +64401 -0
  51. trenchfoot/scenarios/S07_circular_well/volumetric/trench_volume.msh +22370 -0
  52. trenchfoot/scenarios/SUMMARY.json +343 -32
  53. trenchfoot/trench_scene_generator_v3.py +311 -46
  54. {trenchfoot-0.1.1.dist-info → trenchfoot-0.2.3.dist-info}/METADATA +16 -18
  55. {trenchfoot-0.1.1.dist-info → trenchfoot-0.2.3.dist-info}/RECORD +58 -49
  56. {trenchfoot-0.1.1.dist-info → trenchfoot-0.2.3.dist-info}/WHEEL +1 -1
  57. {trenchfoot-0.1.1.dist-info → trenchfoot-0.2.3.dist-info}/entry_points.txt +0 -0
  58. {trenchfoot-0.1.1.dist-info → trenchfoot-0.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -33,6 +33,9 @@ except Exception:
33
33
  plt = None
34
34
  Poly3DCollection = None
35
35
 
36
+ # Groups kept for internal metrics but excluded from OBJ export and previews
37
+ _INTERNAL_GROUPS = frozenset({"trench_cap_for_volume"})
38
+
36
39
  # ---------------- Geometry helpers ----------------
37
40
 
38
41
  def _normalize(v: np.ndarray) -> np.ndarray:
@@ -74,6 +77,19 @@ def _sample_polyline_at_s(path: List[Tuple[float,float]], s: float):
74
77
  t = seg / L; u = (s_abs - cum[i]) / L; pos = (1-u)*P[i] + u*P[i+1]
75
78
  return pos, t
76
79
 
80
+ def _is_path_closed(path: List[Tuple[float,float]], threshold: float = 0.01) -> bool:
81
+ """Detect if a path is explicitly closed (first and last points nearly identical).
82
+
83
+ Returns True only if the first and last points are within threshold distance.
84
+ Use a small threshold (default 0.01) to only catch truly closed paths where
85
+ the endpoint is repeated.
86
+ """
87
+ if len(path) < 3:
88
+ return False
89
+ P = np.array(path, float)
90
+ first_last_dist = np.linalg.norm(P[0] - P[-1])
91
+ return first_last_dist < threshold
92
+
77
93
  def _offset_polyline(path: List[Tuple[float,float]], offset: float):
78
94
  P = np.array(path, float); n = len(P)
79
95
  if n < 2: raise ValueError("Polyline needs at least 2 points")
@@ -100,6 +116,44 @@ def _offset_polyline(path: List[Tuple[float,float]], offset: float):
100
116
  right_pts.append(P[-1] - offset * normals[-1])
101
117
  return left_pts, right_pts
102
118
 
119
+ def _offset_closed_polyline(path: List[Tuple[float,float]], offset: float) -> List[np.ndarray]:
120
+ """Offset a closed polyline, returning a single closed ring.
121
+
122
+ Unlike _offset_polyline which returns left/right sides for open paths,
123
+ this returns a single continuous closed ring for paths where first ≈ last point.
124
+ """
125
+ P = np.array(path, float)
126
+ n = len(P)
127
+ if n < 3:
128
+ raise ValueError("Closed polyline needs at least 3 points")
129
+
130
+ # Compute tangents treating path as closed loop
131
+ # For CCW-oriented polygons, use CW rotation to get outward-pointing normals
132
+ tangents = []
133
+ normals = []
134
+ for i in range(n):
135
+ t = _normalize(P[(i+1) % n] - P[i])
136
+ if np.linalg.norm(t) < 1e-12:
137
+ t = np.array([1.0, 0.0])
138
+ tangents.append(t)
139
+ normals.append(_rotate_cw(t)) # CW rotation gives outward normal for CCW polygon
140
+
141
+ # Compute offset points with proper miter at each vertex
142
+ offset_pts = []
143
+ for k in range(n):
144
+ t_prev, n_prev = tangents[(k-1) % n], normals[(k-1) % n]
145
+ t_next, n_next = tangents[k], normals[k]
146
+ L1_p = P[k] + offset * n_prev
147
+ L1_d = t_prev
148
+ L2_p = P[k] + offset * n_next
149
+ L2_d = t_next
150
+ pt = _line_intersection_2d(L1_p, L1_d, L2_p, L2_d)
151
+ if pt is None:
152
+ pt = 0.5 * (L1_p + L2_p)
153
+ offset_pts.append(pt)
154
+
155
+ return offset_pts
156
+
103
157
  def _polygon_area_2d(poly_xy: np.ndarray) -> float:
104
158
  x = poly_xy[:,0]; y = poly_xy[:,1]
105
159
  return 0.5 * float(np.dot(x, np.roll(y, -1)) - np.dot(y, np.roll(x, -1)))
@@ -262,9 +316,14 @@ def _compute_surface_metrics(
262
316
  return metrics
263
317
 
264
318
 
265
- def _render_surface_previews(groups: Dict[str, Tuple[np.ndarray, np.ndarray]]) -> Dict[str, bytes]:
319
+ def _render_surface_previews(
320
+ groups: Dict[str, Tuple[np.ndarray, np.ndarray]],
321
+ exclude_groups: Optional[frozenset] = None,
322
+ ) -> Dict[str, bytes]:
266
323
  if plt is None or not groups:
267
324
  return {}
325
+ if exclude_groups:
326
+ groups = {k: v for k, v in groups.items() if k not in exclude_groups}
268
327
  all_vertices = [V for (V, F) in groups.values() if V.size > 0]
269
328
  if not all_vertices:
270
329
  return {}
@@ -380,7 +439,9 @@ class SurfaceMeshResult:
380
439
  out_path = Path(out_dir)
381
440
  out_path.mkdir(parents=True, exist_ok=True)
382
441
  obj_path = out_path / "trench_scene.obj"
383
- write_obj_with_groups(obj_path.as_posix(), self.groups)
442
+ # Exclude internal groups (like trench_cap_for_volume) from OBJ export
443
+ export_groups = {k: v for k, v in self.groups.items() if k not in _INTERNAL_GROUPS}
444
+ write_obj_with_groups(obj_path.as_posix(), export_groups)
384
445
  metrics_path = out_path / "metrics.json"
385
446
  with metrics_path.open("w") as fh:
386
447
  json.dump(self.metrics, fh, indent=2)
@@ -491,60 +552,263 @@ def make_trench_from_path_sloped(path_xy: List[Tuple[float,float]], width_top: f
491
552
  half_top = width_top/2.0
492
553
  shrink = max(0.0, wall_slope * depth)
493
554
  half_bot = max(1e-3, half_top - shrink)
494
- L_top, R_top = _offset_polyline(path_xy, half_top)
495
- L_bot, R_bot = _offset_polyline(path_xy, half_bot)
496
- poly_top = _ensure_ccw(_ring_from_LR(L_top, R_top))
497
- poly_bot = _ensure_ccw(_ring_from_LR(L_bot, R_bot))
498
555
 
499
- gfun = _ground_fn(ground)
500
- # Top and bottom rings lie on the ground plane and ground-depth respectively
501
- z_top = np.array([gfun(x,y) for x,y in poly_top]); z_bot = np.array([gfun(x,y) - depth for x,y in poly_bot])
502
- tris_top = _ear_clipping_triangulation(poly_top)
503
- tris_bot = _ear_clipping_triangulation(poly_bot)
504
- V_cap = np.column_stack([poly_top, z_top])
505
- V_bottom = np.column_stack([poly_bot, z_bot])
506
- F_cap = tris_top
507
- F_bottom = tris_bot[:, ::-1] # outward
508
-
509
- # Walls: connect corresponding indices
510
- N = len(poly_top); assert N == len(poly_bot)
511
- walls_V = []; walls_F = []
512
- for i in range(N):
513
- j=(i+1)%N
514
- A_top = np.array([poly_top[i,0], poly_top[i,1], z_top[i]])
515
- B_top = np.array([poly_top[j,0], poly_top[j,1], z_top[j]])
516
- A_bot = np.array([poly_bot[i,0], poly_bot[i,1], z_bot[i]])
517
- B_bot = np.array([poly_bot[j,0], poly_bot[j,1], z_bot[j]])
518
- base=len(walls_V)
519
- walls_V.extend([A_top, B_top, B_bot, A_bot])
520
- walls_F.extend([[base, base+1, base+2], [base, base+2, base+3]])
521
- V_walls = np.array(walls_V,float); F_walls = np.array(walls_F,int)
556
+ is_closed = _is_path_closed(path_xy)
557
+
558
+ if is_closed:
559
+ # For closed paths (like circles), create outer/inner rings
560
+ # Outer ring: centerline offset outward (positive)
561
+ # Inner ring: centerline offset inward (negative)
562
+ outer_top = np.array(_offset_closed_polyline(path_xy, half_top), float)
563
+ inner_top = np.array(_offset_closed_polyline(path_xy, -half_top), float)
564
+ outer_bot = np.array(_offset_closed_polyline(path_xy, half_bot), float)
565
+ inner_bot = np.array(_offset_closed_polyline(path_xy, -half_bot), float)
566
+
567
+ # Ensure CCW orientation (outer should be CCW, inner CW for proper normals)
568
+ outer_top = _ensure_ccw(outer_top)
569
+ outer_bot = _ensure_ccw(outer_bot)
570
+ # Inner rings should go opposite direction
571
+ if _polygon_area_2d(inner_top) > 0:
572
+ inner_top = inner_top[::-1].copy()
573
+ if _polygon_area_2d(inner_bot) > 0:
574
+ inner_bot = inner_bot[::-1].copy()
575
+
576
+ gfun = _ground_fn(ground)
577
+
578
+ # For closed trenches, we need outer wall, inner wall, and bottom (no cap for annular trench)
579
+ # Actually, for annular trench, the "bottom" is an annulus and the "cap" is also an annulus
580
+ z_outer_top = np.array([gfun(x,y) for x,y in outer_top])
581
+ z_inner_top = np.array([gfun(x,y) for x,y in inner_top])
582
+ z_outer_bot = np.array([gfun(x,y) - depth for x,y in outer_bot])
583
+ z_inner_bot = np.array([gfun(x,y) - depth for x,y in inner_bot])
584
+
585
+ # Triangulate annular cap and bottom
586
+ cap_verts, cap_faces = _triangulate_annulus(outer_top, inner_top[::-1]) # reverse inner for CCW
587
+ V_cap = np.column_stack([cap_verts, np.concatenate([z_outer_top, z_inner_top[::-1]])])
588
+ F_cap = cap_faces
589
+
590
+ bot_verts, bot_faces = _triangulate_annulus(outer_bot, inner_bot[::-1])
591
+ V_bottom = np.column_stack([bot_verts, np.concatenate([z_outer_bot, z_inner_bot[::-1]])])
592
+ F_bottom = bot_faces[:, ::-1] # flip for outward normals
593
+
594
+ # Outer wall: connects outer_top to outer_bot (facing outward from trench)
595
+ n_outer = len(outer_top)
596
+ walls_V = []
597
+ walls_F = []
598
+ for i in range(n_outer):
599
+ j = (i + 1) % n_outer
600
+ A_top = np.array([outer_top[i,0], outer_top[i,1], z_outer_top[i]])
601
+ B_top = np.array([outer_top[j,0], outer_top[j,1], z_outer_top[j]])
602
+ A_bot = np.array([outer_bot[i,0], outer_bot[i,1], z_outer_bot[i]])
603
+ B_bot = np.array([outer_bot[j,0], outer_bot[j,1], z_outer_bot[j]])
604
+ base = len(walls_V)
605
+ walls_V.extend([A_top, B_top, B_bot, A_bot])
606
+ # Winding for outward-facing (away from center)
607
+ walls_F.extend([[base, base+1, base+2], [base, base+2, base+3]])
608
+
609
+ # Inner wall: connects inner_top to inner_bot (facing inward toward center)
610
+ n_inner = len(inner_top)
611
+ for i in range(n_inner):
612
+ j = (i + 1) % n_inner
613
+ A_top = np.array([inner_top[i,0], inner_top[i,1], z_inner_top[i]])
614
+ B_top = np.array([inner_top[j,0], inner_top[j,1], z_inner_top[j]])
615
+ A_bot = np.array([inner_bot[i,0], inner_bot[i,1], z_inner_bot[i]])
616
+ B_bot = np.array([inner_bot[j,0], inner_bot[j,1], z_inner_bot[j]])
617
+ base = len(walls_V)
618
+ walls_V.extend([A_top, B_top, B_bot, A_bot])
619
+ # Winding for inward-facing (toward center) - reverse winding
620
+ walls_F.extend([[base, base+2, base+1], [base, base+3, base+2]])
621
+
622
+ V_walls = np.array(walls_V, float)
623
+ F_walls = np.array(walls_F, int)
624
+
625
+ # For closed path, poly_top is the outer ring (used for ground plane hole)
626
+ poly_top = outer_top
627
+ poly_bot = outer_bot
628
+
629
+ extra = {
630
+ "width_top": width_top,
631
+ "width_bottom": 2.0*half_bot,
632
+ "area_top": abs(_polygon_area_2d(outer_top)) - abs(_polygon_area_2d(inner_top)),
633
+ "area_bottom": abs(_polygon_area_2d(outer_bot)) - abs(_polygon_area_2d(inner_bot)),
634
+ "is_closed_path": True
635
+ }
636
+ else:
637
+ # Original logic for open paths
638
+ L_top, R_top = _offset_polyline(path_xy, half_top)
639
+ L_bot, R_bot = _offset_polyline(path_xy, half_bot)
640
+ poly_top = _ensure_ccw(_ring_from_LR(L_top, R_top))
641
+ poly_bot = _ensure_ccw(_ring_from_LR(L_bot, R_bot))
642
+
643
+ gfun = _ground_fn(ground)
644
+ # Top and bottom rings lie on the ground plane and ground-depth respectively
645
+ z_top = np.array([gfun(x,y) for x,y in poly_top])
646
+ z_bot = np.array([gfun(x,y) - depth for x,y in poly_bot])
647
+ tris_top = _ear_clipping_triangulation(poly_top)
648
+ tris_bot = _ear_clipping_triangulation(poly_bot)
649
+ V_cap = np.column_stack([poly_top, z_top])
650
+ V_bottom = np.column_stack([poly_bot, z_bot])
651
+ F_cap = tris_top
652
+ F_bottom = tris_bot[:, ::-1] # outward
653
+
654
+ # Walls: connect corresponding indices
655
+ N = len(poly_top)
656
+ assert N == len(poly_bot)
657
+ walls_V = []
658
+ walls_F = []
659
+ for i in range(N):
660
+ j = (i+1) % N
661
+ A_top = np.array([poly_top[i,0], poly_top[i,1], z_top[i]])
662
+ B_top = np.array([poly_top[j,0], poly_top[j,1], z_top[j]])
663
+ A_bot = np.array([poly_bot[i,0], poly_bot[i,1], z_bot[i]])
664
+ B_bot = np.array([poly_bot[j,0], poly_bot[j,1], z_bot[j]])
665
+ base = len(walls_V)
666
+ walls_V.extend([A_top, B_top, B_bot, A_bot])
667
+ walls_F.extend([[base, base+1, base+2], [base, base+2, base+3]])
668
+ V_walls = np.array(walls_V, float)
669
+ F_walls = np.array(walls_F, int)
670
+
671
+ extra = {
672
+ "width_top": width_top,
673
+ "width_bottom": 2.0*half_bot,
674
+ "area_top": abs(_polygon_area_2d(poly_top)),
675
+ "area_bottom": abs(_polygon_area_2d(poly_bot)),
676
+ "is_closed_path": False
677
+ }
522
678
 
523
679
  groups = {
524
680
  "trench_bottom": (V_bottom, F_bottom),
525
681
  "trench_cap_for_volume": (V_cap, F_cap),
526
682
  "trench_walls": (V_walls, F_walls)
527
683
  }
528
- extra = {
529
- "width_top": width_top,
530
- "width_bottom": 2.0*half_bot,
531
- "area_top": abs(_polygon_area_2d(poly_top)),
532
- "area_bottom": abs(_polygon_area_2d(poly_bot))
533
- }
684
+
534
685
  return groups, poly_top, poly_bot, extra
535
686
 
687
+ def _triangulate_annulus(outer: np.ndarray, inner: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
688
+ """Triangulate the annular region between outer and inner polygons.
689
+
690
+ Creates triangles that fill ONLY the region between the two polygons,
691
+ leaving the inner polygon area as an open hole.
692
+
693
+ Both polygons should be CCW oriented. Returns (vertices, faces) where
694
+ vertices is the concatenation of outer and inner, and faces index into it.
695
+ """
696
+ n_outer = len(outer)
697
+ n_inner = len(inner)
698
+
699
+ # Vertices: outer first, then inner
700
+ verts = np.vstack([outer, inner])
701
+
702
+ # Create triangles by "zipping" around the two polygons
703
+ # This works well when both polygons have similar vertex counts
704
+ # For different counts, we need to handle the ratio
705
+
706
+ tris = []
707
+
708
+ # Use a marching approach: for each outer edge, connect to nearest inner vertices
709
+ # and vice versa. This creates a proper triangulated annulus.
710
+
711
+ # Simple approach: interpolate around both polygons simultaneously
712
+ # treating them as having a common parameter t in [0, 1]
713
+
714
+ i_outer = 0 # current outer vertex index
715
+ i_inner = 0 # current inner vertex index
716
+ t_outer = 0.0 # parameter position on outer polygon
717
+ t_inner = 0.0 # parameter position on inner polygon
718
+
719
+ outer_step = 1.0 / n_outer
720
+ inner_step = 1.0 / n_inner
721
+
722
+ # March around creating triangles
723
+ while i_outer < n_outer or i_inner < n_inner:
724
+ # Current vertices
725
+ o_curr = i_outer % n_outer
726
+ o_next = (i_outer + 1) % n_outer
727
+ i_curr = i_inner % n_inner
728
+ i_next = (i_inner + 1) % n_inner
729
+
730
+ # Indices in combined vertex array
731
+ vo_curr = o_curr
732
+ vo_next = o_next
733
+ vi_curr = n_outer + i_curr
734
+ vi_next = n_outer + i_next
735
+
736
+ if i_outer >= n_outer:
737
+ # Finished outer, just advance inner
738
+ tris.append([vo_curr, vi_next, vi_curr])
739
+ i_inner += 1
740
+ t_inner += inner_step
741
+ elif i_inner >= n_inner:
742
+ # Finished inner, just advance outer
743
+ tris.append([vo_curr, vo_next, vi_curr])
744
+ i_outer += 1
745
+ t_outer += outer_step
746
+ elif t_outer + outer_step <= t_inner + inner_step:
747
+ # Advance outer - create triangle: o_curr, o_next, i_curr
748
+ tris.append([vo_curr, vo_next, vi_curr])
749
+ i_outer += 1
750
+ t_outer += outer_step
751
+ else:
752
+ # Advance inner - create triangle: o_curr, i_next, i_curr
753
+ tris.append([vo_curr, vi_next, vi_curr])
754
+ i_inner += 1
755
+ t_inner += inner_step
756
+
757
+ return verts, np.array(tris, dtype=int)
758
+
759
+
536
760
  def make_ground_surface_plane(path_xy: List[Tuple[float,float]], width_top: float, ground) -> Dict[str,Tuple[np.ndarray,np.ndarray]]:
537
- # single rectangular plane covering the trench projection + margin
538
- half_top = width_top/2.0
539
- L, R = _offset_polyline(path_xy, half_top)
540
- ring = _ensure_ccw(_ring_from_LR(L, R))
541
- minx, miny = ring.min(axis=0); maxx, maxy = ring.max(axis=0)
542
- m = float(max(1.0, ground.size_margin))
761
+ """Create ground surface as an offset polygon around the trench opening.
762
+
763
+ The ground surface forms an annulus (ring) around the trench, leaving
764
+ the trench opening as an open hole. This creates a natural shape that
765
+ hugs L-shaped, U-shaped, and curved trenches.
766
+
767
+ For closed paths (like circles), the ground surface is a ring around the
768
+ outer edge of the trench, with the trench opening left completely open.
769
+ """
770
+ half_top = width_top / 2.0
771
+ m = float(max(0.5, ground.size_margin))
543
772
  gfun = _ground_fn(ground)
544
- corners_xy = np.array([[minx-m,miny-m],[maxx+m,miny-m],[maxx+m,maxy+m],[minx-m,maxy+m]], float)
545
- Vg = np.array([[x,y,gfun(x,y)] for (x,y) in corners_xy], float)
546
- Fg = np.array([[0,1,2],[0,2,3]], int) if _polygon_area_2d(corners_xy)>0 else np.array([[0,2,1],[0,3,2]], int)
547
- return {"ground_surface": (Vg, Fg)}
773
+ is_closed = _is_path_closed(path_xy)
774
+
775
+ if is_closed:
776
+ # For closed paths (like circular wells), the ground is an annulus
777
+ # from the outer ground boundary to the outer edge of the trench opening.
778
+ # The center (inside the trench) is left completely open.
779
+
780
+ # Trench outer boundary (edge of trench opening)
781
+ trench_outer = np.array(_offset_closed_polyline(path_xy, half_top), float)
782
+
783
+ # Ground outer boundary (edge of ground surface)
784
+ ground_outer = np.array(_offset_closed_polyline(path_xy, half_top + m), float)
785
+
786
+ # Ensure proper orientations
787
+ trench_outer = _ensure_ccw(trench_outer)
788
+ ground_outer = _ensure_ccw(ground_outer)
789
+
790
+ # Ground annulus: from ground_outer to trench_outer
791
+ combined_xy, tris = _triangulate_annulus(ground_outer, trench_outer)
792
+ Vg = np.array([[x, y, gfun(x, y)] for (x, y) in combined_xy], float)
793
+
794
+ return {"ground_surface": (Vg, tris)}
795
+ else:
796
+ # Original logic for open paths
797
+ # Inner boundary: the trench opening (same as trench top ring)
798
+ L_inner, R_inner = _offset_polyline(path_xy, half_top)
799
+ inner_ring = _ensure_ccw(_ring_from_LR(L_inner, R_inner))
800
+
801
+ # Outer boundary: offset by additional margin
802
+ L_outer, R_outer = _offset_polyline(path_xy, half_top + m)
803
+ outer_ring = _ensure_ccw(_ring_from_LR(L_outer, R_outer))
804
+
805
+ # Triangulate the annular region (leaves hole open)
806
+ combined_xy, tris = _triangulate_annulus(outer_ring, inner_ring)
807
+
808
+ # Apply ground elevation to get 3D vertices
809
+ Vg = np.array([[x, y, gfun(x, y)] for (x, y) in combined_xy], float)
810
+
811
+ return {"ground_surface": (Vg, tris)}
548
812
 
549
813
  def _half_width_at_depth(half_top: float, slope: float, top_z: float, z: float) -> float:
550
814
  return max(1e-6, half_top - slope * (top_z - z))
@@ -731,7 +995,8 @@ def _build_surface_groups(
731
995
  def generate_surface_mesh(spec: SceneSpec, *, make_preview: bool = False) -> SurfaceMeshResult:
732
996
  groups, object_counts, extra = _build_surface_groups(spec)
733
997
  metrics = _compute_surface_metrics(groups, extra, spec)
734
- previews = _render_surface_previews(groups) if make_preview else {}
998
+ # Exclude internal groups (like cap) from previews to show open-topped trenches
999
+ previews = _render_surface_previews(groups, exclude_groups=_INTERNAL_GROUPS) if make_preview else {}
735
1000
  return SurfaceMeshResult(
736
1001
  spec=spec,
737
1002
  groups=groups,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trenchfoot
3
- Version: 0.1.1
3
+ Version: 0.2.3
4
4
  Summary: Synthetic trench scenario generator bundle (surfaces + volumetrics).
5
5
  Author: Liam Moore
6
6
  License-File: LICENSE
@@ -43,29 +43,27 @@ Color key: trench surfaces use warm soil tones; embedded geometry is colour-code
43
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
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
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) |
46
47
 
47
- ### S06 preset at a glance
48
+ ### S07 circular well preset
49
+
50
+ A deep cylindrical well with criss-crossing pipes at different elevations:
48
51
 
49
52
  ```json
50
53
  {
51
- "path_xy": [[0, 0], [4, -1], [8, 0], [8, 5], [2, 5], [-1, 2]],
52
- "width": 2.6,
53
- "depth": 1.4,
54
- "wall_slope": 0.12,
55
- "ground": {"z0": 0.2, "slope": [0.015, 0.03], "size_margin": 7.0},
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},
56
59
  "pipes": [
57
- {"radius": 0.18, "length": 6.0, "angle_deg": 35, "s_center": 0.3, "z": -0.9, "offset_u": 0.35},
58
- {"radius": 0.14, "length": 4.8, "angle_deg": -40, "s_center": 0.6, "z": -0.95, "offset_u": -0.45}
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}
59
64
  ],
60
- "spheres": [{"radius": 0.35, "s": 0.82, "offset_u": 0.3, "z": -0.65}],
61
- "noise": {
62
- "enable": true,
63
- "amplitude": 0.035,
64
- "corr_length": 0.5,
65
- "octaves": 4,
66
- "gain": 0.55,
67
- "apply_to": ["trench_walls", "trench_bottom", "pipe*_pipe_side"]
68
- }
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}
69
67
  }
70
68
  ```
71
69
 
@@ -1,69 +1,78 @@
1
1
  trenchfoot/Dockerfile,sha256=TjwHTNOZF6eHxpDjw29uruEmvJ4FWQEv1XRDE3qMJvY,210
2
2
  trenchfoot/README.md,sha256=PGpBwprT0ZgR-ASx7bDLm9SvYR8yCbIzML7amq3oJ-k,5483
3
3
  trenchfoot/__init__.py,sha256=jI30sofuw13hesvaShlAaatpswxY8wRFVsTJq06TKo8,1672
4
- trenchfoot/generate_scenarios.py,sha256=drQ6a2bKnkcyIDXP4EKHe4m6CtLHW9NjJzXSIgbTQso,22561
5
- trenchfoot/gmsh_sloped_trench_mesher.py,sha256=GJz8acg80dZbO8s6iW71X7aLg3qvOjlgW0ImPG7mKFE,21211
4
+ trenchfoot/generate_scenarios.py,sha256=1vIBEZ0qK1NFnzNQvNpmA0ZnDAcz82MH4Ux-bO8cYuI,25506
5
+ trenchfoot/gmsh_sloped_trench_mesher.py,sha256=D7EL6V0wkE6tvDARmm006yZE6KEzCl25OlZQviArBRo,25675
6
6
  trenchfoot/plot_mesh.py,sha256=26dOlVfaM1WsUfr_sXVqA7axtY9qjY3WCNM7cUBTS7Q,3810
7
7
  trenchfoot/render_colors.py,sha256=EfldvoshgaZ6iZFk-UDL67-tvwGYaiZFS2NQZBMwhDM,1596
8
8
  trenchfoot/scene_spec_example.json,sha256=UcV25ku422UO0ZZPDrJwrT1zwmjoOIpnBdLuEdh-AZA,1028
9
- trenchfoot/trench_scene_generator_v3.py,sha256=7bJTd00d9dA3-sa_iH-3nuREbPfTe24D6HmrZIAGmzg,31938
10
- trenchfoot/scenarios/SUMMARY.json,sha256=h_mpOK_6QOaCek_qBDSrorJa9QUbOKyqRhVNFjdEjHY,8093
11
- trenchfoot/scenarios/S01_straight_vwalls/metrics.json,sha256=_bg0EIfTZ8rp_rDwL5cT9J3gmIiOqCtI11q7pBTZ7zs,722
9
+ trenchfoot/trench_scene_generator_v3.py,sha256=AbTQ90yOtEtVaKysItDRYQ595vIPZVQqpFGkU850Opc,43031
10
+ trenchfoot/scenarios/SUMMARY.json,sha256=PUT7ID4nl5BJ6A0M3MhbOkx5bm2HAy0Gwzq8cc6JBAE,18492
11
+ trenchfoot/scenarios/S01_straight_vwalls/metrics.json,sha256=_AoK-WCgTKKdu_zUJL-HiSnsxMJvr6GDxlet7Ojmtk4,691
12
12
  trenchfoot/scenarios/S01_straight_vwalls/preview.png,sha256=adsx3-6oMu9WaixggXRhuXi_KM-q5s2RNSBLR_stq80,199624
13
- trenchfoot/scenarios/S01_straight_vwalls/preview_oblique.png,sha256=d0qtG_lEFFZRF9ek9wi__77wBENOiHGHU6BOwzFM0r0,123576
14
- trenchfoot/scenarios/S01_straight_vwalls/preview_side.png,sha256=vcG8nctwbFLs9I3vblLvWK5qEDOLLmwHonHTh_-YSoM,32354
15
- trenchfoot/scenarios/S01_straight_vwalls/preview_top.png,sha256=22xocTRqTLPg8SjX8G_XzDSInt9hAdTcSZwJjWuaimI,27816
16
- trenchfoot/scenarios/S01_straight_vwalls/scene.json,sha256=H2u4YTL6IpYzDkrD1jGJ9dwgLt3jsoT3oU87dOVdGAg,343
17
- trenchfoot/scenarios/S01_straight_vwalls/trench_scene.obj,sha256=6-usCEpAfhhZcdCxAy7NcXrkBwv-mEh40A6zN50k0Jo,519
13
+ trenchfoot/scenarios/S01_straight_vwalls/preview_oblique.png,sha256=bHALyBDHkywF6pKzOPDvK6VNo6nk7s87SqqHhVAx_YU,121709
14
+ trenchfoot/scenarios/S01_straight_vwalls/preview_side.png,sha256=A6nUvAfLky-dqX2SWYSCAzVO7BP7HCq-sfq_ay6AS8M,33157
15
+ trenchfoot/scenarios/S01_straight_vwalls/preview_top.png,sha256=qjXvYOvdc_D1tei09VjRzm-0_Da2MCs-6FOc7ClmBo8,33305
16
+ trenchfoot/scenarios/S01_straight_vwalls/scene.json,sha256=kbHRC9zMVDGHbuLh2wRsvchfVfcbdDEVvxsMwHHBoHw,343
17
+ trenchfoot/scenarios/S01_straight_vwalls/trench_scene.obj,sha256=QeAtXExlXsoGCBT5uDa7fap5YgXfkxN-obAiQTOxpeI,561
18
18
  trenchfoot/scenarios/S01_straight_vwalls/meshes/trench_scene_culled.obj,sha256=6-usCEpAfhhZcdCxAy7NcXrkBwv-mEh40A6zN50k0Jo,519
19
19
  trenchfoot/scenarios/S01_straight_vwalls/point_clouds/culled/resolution0p050.pth,sha256=w4AKFbud2V0NhAtudGXXheLynAq-NLxUFSPxLczK3T0,1352093
20
20
  trenchfoot/scenarios/S01_straight_vwalls/point_clouds/full/resolution0p050.pth,sha256=w4AKFbud2V0NhAtudGXXheLynAq-NLxUFSPxLczK3T0,1352093
21
- trenchfoot/scenarios/S01_straight_vwalls/volumetric/trench_volume.msh,sha256=xkgqmM4w3ZrtjD_ckHE7t-IKAXEgiI1TQrcuF9wRVHQ,20788
22
- trenchfoot/scenarios/S02_straight_slope_pipe/metrics.json,sha256=mxTbcXvkQVQsPHDbwAzrcqwINWixIhSLzb65D0V5t4c,915
21
+ trenchfoot/scenarios/S01_straight_vwalls/volumetric/trench_volume.msh,sha256=mpKNhdAPEogyDQrE9IGTgg535ZBNYXiLpIcmvl6QYPA,15007
22
+ trenchfoot/scenarios/S02_straight_slope_pipe/metrics.json,sha256=D6Rq9lQ1WXsUPkuYdgB9QkET2k0iX3QLnWEyPknaesk,940
23
23
  trenchfoot/scenarios/S02_straight_slope_pipe/preview.png,sha256=Aj5XpJMSdRNqnrPzL4oe-d0RjLdFT2V7OFrrZxwlsx8,251134
24
- trenchfoot/scenarios/S02_straight_slope_pipe/preview_oblique.png,sha256=ofiefdnRf3i-s16_i-bHHRNMcHGljJQMXkBZLcuoChk,142980
25
- trenchfoot/scenarios/S02_straight_slope_pipe/preview_side.png,sha256=KdD2hIh680nNK-7ebwehnXb3rBtKEK7j1cJJwAvGlZE,41581
26
- trenchfoot/scenarios/S02_straight_slope_pipe/preview_top.png,sha256=bnmECCqfHZ40LZj8H65mSN8Iir9KDHtZFfIWD2AfmSc,29953
27
- trenchfoot/scenarios/S02_straight_slope_pipe/scene.json,sha256=8jIKAeLbkjuhnJSvnKa0PVvwR7VRpFINrCuzVc0YZZo,485
28
- trenchfoot/scenarios/S02_straight_slope_pipe/trench_scene.obj,sha256=uGbQ3d288W4snGktGLC5D4YKG2giXTwfiVvCDOOkXuU,332970
24
+ trenchfoot/scenarios/S02_straight_slope_pipe/preview_oblique.png,sha256=0LqiBa__OrJi8m5sxj9buy6kiU3jdQ_uxDf3DTOGu_w,176300
25
+ trenchfoot/scenarios/S02_straight_slope_pipe/preview_side.png,sha256=0b8wYSgRusl1RbilJGdAAi2zz4vZH_BQO31HdyTQvTM,56757
26
+ trenchfoot/scenarios/S02_straight_slope_pipe/preview_top.png,sha256=EvTsUFRCSvD6kRqPY64x1sdSn9HMu_1a-J_WOQMrgag,59470
27
+ trenchfoot/scenarios/S02_straight_slope_pipe/scene.json,sha256=W1ApzLHEnHQn-7AdWxh2xiEinj6Yb7830sQXNxmmmDw,486
28
+ trenchfoot/scenarios/S02_straight_slope_pipe/trench_scene.obj,sha256=7MmtkwmSsJivS5j4O_Bfd1j1Xs27ZGwq1fZJrgKGtcw,333051
29
29
  trenchfoot/scenarios/S02_straight_slope_pipe/meshes/trench_scene_culled.obj,sha256=OOiw80NuCBejzeN3MWAip9Haw8yQsLZEdlMQdDIgEMU,847
30
30
  trenchfoot/scenarios/S02_straight_slope_pipe/point_clouds/culled/resolution0p050.pth,sha256=Ug9hTIsm6iQKRs9cucXJyJpxpSvQ5f2GS1ytZmxi1mE,1623389
31
31
  trenchfoot/scenarios/S02_straight_slope_pipe/point_clouds/full/resolution0p050.pth,sha256=JgIRni8xWKtz2mm0gpSdmavGe-FF71mDervavM4Rqw8,1718877
32
- trenchfoot/scenarios/S02_straight_slope_pipe/volumetric/trench_volume.msh,sha256=6VHq7Yn1HZYkvpuW1om1Pf9XBTceiZF5TQEy8_dbOUY,54172
33
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/metrics.json,sha256=0JFNeS_6lSOOsUnqFSc7zR451liyeCvOdEmY7xxAUsg,1124
32
+ trenchfoot/scenarios/S02_straight_slope_pipe/volumetric/trench_volume.msh,sha256=kNFAqjr7dM-KX4JQV1u5GbHbWWFIfiEur1wV1fJCePA,47084
33
+ trenchfoot/scenarios/S03_L_slope_two_pipes_box/metrics.json,sha256=Do1FCPkBfEznb3eop45DjjlgIJlwX9LjzDhDTHfl9c4,1124
34
34
  trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview.png,sha256=q0V_2HSLUl2ovWLc4INKV87aLVh3vzqabMyKr8XEWEg,224248
35
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_oblique.png,sha256=Pephmcu_6LKxTUCXJQKhbJ7vfxzICUaHnIWRx5vmeYE,161433
36
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_side.png,sha256=a8B_BomBigRC7sAhRfuc5i1jL5lFwuCkZ9ALwnpK9ZI,48340
37
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_top.png,sha256=BQ6WuTiYk4d99uwBFE8tWrG-TJ_hMZw_VxtXluKZ8Wc,33449
38
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/scene.json,sha256=vOibsPWlhYYvpUKNz3NZwtEj583j16BBCJga716L5mo,939
39
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/trench_scene.obj,sha256=FVIaGr7bvVnQ6EjU4_h3hWjbTo9qXbWq8UjxkjG4GYI,652943
35
+ trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_oblique.png,sha256=dooBp08mEnjQyNhAZQlaJ6YMMnl9CEBAbcUUz3HT8d8,164130
36
+ trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_side.png,sha256=CDROHUpFRgdvdgkY3guWLEkZKyJ-jL2WPmH5QXi4bhg,52200
37
+ trenchfoot/scenarios/S03_L_slope_two_pipes_box/preview_top.png,sha256=YKjUA6H3YKRAAz3-Adza5NSrQLrQt0vzLXZCwvtmHYs,51812
38
+ trenchfoot/scenarios/S03_L_slope_two_pipes_box/scene.json,sha256=10azlgelNGCDG6dyWsAKVviXXZaoYnAMvJxvvj-HyS8,940
39
+ trenchfoot/scenarios/S03_L_slope_two_pipes_box/trench_scene.obj,sha256=KkZxHtJaGRSDwkATtwpIoA6J2VpknjQrOdlHj8ZsUIs,655847
40
40
  trenchfoot/scenarios/S03_L_slope_two_pipes_box/meshes/trench_scene_culled.obj,sha256=At4lipowr_3MY1rxmcKa479F_mQ-jJDVQJe0yHTrFSc,1835
41
41
  trenchfoot/scenarios/S03_L_slope_two_pipes_box/point_clouds/culled/resolution0p050.pth,sha256=KqbuoZ1tUPDhlnDgmhd-7WBb92TABsEmOzEsB__KtlA,2456029
42
42
  trenchfoot/scenarios/S03_L_slope_two_pipes_box/point_clouds/full/resolution0p050.pth,sha256=4fXvfu0TMO9FJaoqYrW21SCGe3eKuSLkSUl9CdE14rw,2622685
43
- trenchfoot/scenarios/S03_L_slope_two_pipes_box/volumetric/trench_volume.msh,sha256=Z0YBZmu0SctYbC2mkEcy2rdLULZvRLkjSq7SZK91XEA,103364
44
- trenchfoot/scenarios/S04_U_slope_multi_noise/metrics.json,sha256=RLn5RA1P6IiYJ5KGe-gzp1WaKOH0CcVHmGSp1nC5YBA,1294
43
+ trenchfoot/scenarios/S03_L_slope_two_pipes_box/volumetric/trench_volume.msh,sha256=cPgHKHlzPPtWX8H1yaxAYsEgYba3fr2JjfPvgg2a000,87383
44
+ trenchfoot/scenarios/S04_U_slope_multi_noise/metrics.json,sha256=mRIbV_Lk0UfgifamGky6WpIsHWZgpfCDJZ-2Cdsfiyg,1294
45
45
  trenchfoot/scenarios/S04_U_slope_multi_noise/preview.png,sha256=ZbLgrL0nRtUX1y77i7b3LCxw4w_j5Ivmmh0i2YzrBDs,238958
46
- trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png,sha256=bwMWv-D0DpMgp2HjRpZc-U12aGI0jDD7DET72j2lW7k,160927
47
- trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png,sha256=QlbK7UNlgNjNDb8nClZ1w6pUXqlp0kXqItq89Ezretg,58382
48
- trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png,sha256=fRh0fWdVscG4TTJ0nRDHB7cblmbp9WPE1JO3Dxjgkgg,41779
49
- trenchfoot/scenarios/S04_U_slope_multi_noise/scene.json,sha256=23nlervPUKUD3LassBLjGoV1f5RDjcDGJdlLZHZ1f80,1100
50
- trenchfoot/scenarios/S04_U_slope_multi_noise/trench_scene.obj,sha256=z6E6rozOgsDTVQnWGoejTgYYCuO0sdafDPPAD28KH0c,1173145
46
+ trenchfoot/scenarios/S04_U_slope_multi_noise/preview_oblique.png,sha256=2KxDuzcT3Ej910RVDUTAUWD-aj5vgr4LU6Vb6_vPUw4,172384
47
+ trenchfoot/scenarios/S04_U_slope_multi_noise/preview_side.png,sha256=M2TsuqEUj_x1iWUw2eaYRAtiW62qsmYn9uS59PhVclY,76544
48
+ trenchfoot/scenarios/S04_U_slope_multi_noise/preview_top.png,sha256=510pmJTQBHWsyB18l4hIoHUd70J1yEeljn-nubq2MzU,67892
49
+ trenchfoot/scenarios/S04_U_slope_multi_noise/scene.json,sha256=ePFijY2uoZt6P5bVW2GSI8GKSA3CUMupIiQ1RnJkLxM,1119
50
+ trenchfoot/scenarios/S04_U_slope_multi_noise/trench_scene.obj,sha256=IJuwTxx5Xs4s_8IfF9OorJ_f_AMJATTVWZJytDhOL5U,1188160
51
51
  trenchfoot/scenarios/S04_U_slope_multi_noise/meshes/trench_scene_culled.obj,sha256=ykw_tzhXknXt3DbmSk3cBzbOL91Jm4zxjthp799YkWw,3206
52
- trenchfoot/scenarios/S04_U_slope_multi_noise/volumetric/trench_volume.msh,sha256=-9-uvLcTnYmZIgB8aDtczvqqNbrrjAqUk1dz39FU1Gw,190282
53
- trenchfoot/scenarios/S05_wide_slope_pair/metrics.json,sha256=mJ5X7VHLi0SnkffktbS3QsNlBSsfwSyV4tKNg1t1CJU,1115
54
- trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png,sha256=4M9OhkmWmWk13CB2Dp12PYgNIQ8JbliQATJH22JuJ4E,162455
55
- trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png,sha256=TJ_PcwHahCOJSt5aTHYNBGgU9oRlDKAGAoqlgS5Sktw,56373
56
- trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png,sha256=7k7vYFzSPUvsiZ5hn8Z3MGm8d-SipL7EE_F9cbFkzrs,42495
57
- trenchfoot/scenarios/S05_wide_slope_pair/scene.json,sha256=AJHPTeuQWrUBa108ok6CWh12DGSRNCdEdZ4ynimM4No,1027
58
- trenchfoot/scenarios/S05_wide_slope_pair/trench_scene.obj,sha256=7mH0l1MutP8a5-BnVSVD2niAcgrmy3pnsJiG4izEJUc,689443
59
- trenchfoot/scenarios/S06_bumpy_wide_loop/metrics.json,sha256=sRFALxQH8rdAa3AqY9Y7J0zwVtfEthDoRNSlLj2nntg,1149
60
- trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png,sha256=ko6SRl9h8TU_TCZVwkRwws3g3-5eDGZNdZGdn1OpVKI,158032
61
- trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png,sha256=OoBdaEeiFIV9pOYOPOWq6hQftQok26804ITC8WESxZs,73294
62
- trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png,sha256=rVHFPNicx98ja0oaZf_9dG5v7tEmjBSVzRHgan4sgu4,48554
63
- trenchfoot/scenarios/S06_bumpy_wide_loop/scene.json,sha256=jy00g_SHAQzHg7N4wY0nM-WUMRSsP3dF1NzscqM1BMk,1106
64
- trenchfoot/scenarios/S06_bumpy_wide_loop/trench_scene.obj,sha256=u8ME1nB1jXSRydtvXtg8_wiod_YxXb_QydCQ-mjHV68,842349
65
- trenchfoot-0.1.1.dist-info/METADATA,sha256=fxDjnkJr7i8bLvVYhkCOrpfNnbHjIAllItrztjlhAjU,4941
66
- trenchfoot-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
67
- trenchfoot-0.1.1.dist-info/entry_points.txt,sha256=5TejAGmc4GnNYLn7MhhLtSCNz9240RvzcNaetF4IHfg,119
68
- trenchfoot-0.1.1.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
69
- trenchfoot-0.1.1.dist-info/RECORD,,
52
+ trenchfoot/scenarios/S04_U_slope_multi_noise/volumetric/trench_volume.msh,sha256=7KfGkJAlv19rzbaPhzbmy9UWo42Db_4VhAtisn6aMak,144623
53
+ trenchfoot/scenarios/S05_wide_slope_pair/metrics.json,sha256=koOpLE6SxamT7mzenj73YbG2I9ca-xFj0WcwenZAHGo,1113
54
+ trenchfoot/scenarios/S05_wide_slope_pair/preview_oblique.png,sha256=poBHAotvcDGV9D43D_ZvDk4b4Bt5p45kfjM5jlg9TdM,139451
55
+ trenchfoot/scenarios/S05_wide_slope_pair/preview_side.png,sha256=YqBIKLmoNwakWjI6vVrXrJc0huU3dPIyf2-mO5RF6eM,73917
56
+ trenchfoot/scenarios/S05_wide_slope_pair/preview_top.png,sha256=VeHyLYWaytKpfm0sPwOgjHi253oejmWFfCSbpZll_yA,57690
57
+ trenchfoot/scenarios/S05_wide_slope_pair/scene.json,sha256=5JMFXLxjjkT3_xrmJU7SX3FYSHM2rgL63nehik0c_JY,1030
58
+ trenchfoot/scenarios/S05_wide_slope_pair/trench_scene.obj,sha256=alQlzZAZ7zclj2z4drAN0oHx_NEoa9ISvLPd_4ogf6o,691090
59
+ trenchfoot/scenarios/S05_wide_slope_pair/volumetric/trench_volume.msh,sha256=pHrj2dJgpSklpxiHHThiZINJQWpUdvkfhUnVBpKASEM,123235
60
+ trenchfoot/scenarios/S06_bumpy_wide_loop/metrics.json,sha256=lj_ptCbIJa9Iplq8nROviIiUnwx6l1ToOg-TwHG31K4,1135
61
+ trenchfoot/scenarios/S06_bumpy_wide_loop/preview_oblique.png,sha256=6z054QlAdvF4Cy4dSr9BKvT5A4X-DETeac3jQQhokrk,178063
62
+ trenchfoot/scenarios/S06_bumpy_wide_loop/preview_side.png,sha256=-17uqFyIZFoEZCPg1gEdWCITnX2HoPFHlWlL8F2AX8c,85737
63
+ trenchfoot/scenarios/S06_bumpy_wide_loop/preview_top.png,sha256=Yen_fICekSBLtAJscZ7409o07LRfPrNLqhDGW5a6o5I,84580
64
+ trenchfoot/scenarios/S06_bumpy_wide_loop/scene.json,sha256=SzjQgccYU1KRIiXVp-LwnLpf7pLkBotQ9_baHljd46g,1105
65
+ trenchfoot/scenarios/S06_bumpy_wide_loop/trench_scene.obj,sha256=7RiSRxvUHrvVnk7-M7YSoOZKKPcMp0DoN_WL9cIbmkU,846803
66
+ trenchfoot/scenarios/S06_bumpy_wide_loop/volumetric/trench_volume.msh,sha256=r8KJMR5TxykP1_oeYbPnsckStGZDZ2-7e8KCAHPBh5I,279971
67
+ trenchfoot/scenarios/S07_circular_well/metrics.json,sha256=-dKjI-UIPWrlHXaFqQVydh_sc8fmX808dndzzNyubkg,1386
68
+ trenchfoot/scenarios/S07_circular_well/preview_oblique.png,sha256=_jraTOyoKC-PUzz0bT1lfC07Jmy9nmMn6Ymlkr_QDb0,217118
69
+ trenchfoot/scenarios/S07_circular_well/preview_side.png,sha256=mA67PfZT79ft1buRoCwnYOqKNjJ863FI1_gU4zZPmlg,92326
70
+ trenchfoot/scenarios/S07_circular_well/preview_top.png,sha256=XW7DQLDEYmSV6Lnfo1F-Cep4wh0HEkuE9ae1jCvlzAs,142139
71
+ trenchfoot/scenarios/S07_circular_well/scene.json,sha256=bvror2YX6aNbsEc25-N7JO3ysH2dTLGyEE6zGzZysXQ,3146
72
+ trenchfoot/scenarios/S07_circular_well/trench_scene.obj,sha256=1MAsyGkJ4-tCRXLDWi_7ruumgpCpcl_Ca5poRLtTua0,1619846
73
+ trenchfoot/scenarios/S07_circular_well/volumetric/trench_volume.msh,sha256=dqhtd3SFKj5RLT_BcWIIvVGCbAqvOx7RX25-K7NKX10,615212
74
+ trenchfoot-0.2.3.dist-info/METADATA,sha256=kbpaAePYuk_SrbL1xgQGn3lT-gZpFhX1G9Lb62Y1krI,5292
75
+ trenchfoot-0.2.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
76
+ trenchfoot-0.2.3.dist-info/entry_points.txt,sha256=5TejAGmc4GnNYLn7MhhLtSCNz9240RvzcNaetF4IHfg,119
77
+ trenchfoot-0.2.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
78
+ trenchfoot-0.2.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any