trenchfoot 0.2.7__py3-none-any.whl → 0.3.0__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.
- trenchfoot/trench_scene_generator_v3.py +125 -4
- {trenchfoot-0.2.7.dist-info → trenchfoot-0.3.0.dist-info}/METADATA +1 -1
- {trenchfoot-0.2.7.dist-info → trenchfoot-0.3.0.dist-info}/RECORD +6 -6
- {trenchfoot-0.2.7.dist-info → trenchfoot-0.3.0.dist-info}/WHEEL +0 -0
- {trenchfoot-0.2.7.dist-info → trenchfoot-0.3.0.dist-info}/entry_points.txt +0 -0
- {trenchfoot-0.2.7.dist-info → trenchfoot-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -425,6 +425,7 @@ class SurfaceMeshFiles:
|
|
|
425
425
|
obj_path: Path
|
|
426
426
|
metrics_path: Path
|
|
427
427
|
preview_paths: Tuple[Path, ...]
|
|
428
|
+
sdf_metadata_path: Optional[Path] = None
|
|
428
429
|
|
|
429
430
|
|
|
430
431
|
@dataclass
|
|
@@ -435,7 +436,73 @@ class SurfaceMeshResult:
|
|
|
435
436
|
metrics: Dict[str, Any]
|
|
436
437
|
previews: Dict[str, bytes]
|
|
437
438
|
|
|
438
|
-
def
|
|
439
|
+
def _build_sdf_metadata(self) -> Dict[str, Any]:
|
|
440
|
+
"""Build SDF metadata for downstream consumers.
|
|
441
|
+
|
|
442
|
+
This metadata enables generic mesh-to-SDF pipelines to correctly
|
|
443
|
+
interpret the mesh geometry without trenchfoot-specific heuristics.
|
|
444
|
+
"""
|
|
445
|
+
# Extract trench opening polygon from the trench cap geometry
|
|
446
|
+
trench_opening_vertices = None
|
|
447
|
+
if "trench_cap_for_volume" in self.groups:
|
|
448
|
+
V_cap, F_cap = self.groups["trench_cap_for_volume"]
|
|
449
|
+
if V_cap.size > 0:
|
|
450
|
+
# Get unique vertices at z ≈ ground level (the top cap boundary)
|
|
451
|
+
# These form the trench opening polygon
|
|
452
|
+
z_level = float(np.median(V_cap[:, 2]))
|
|
453
|
+
xy_coords = V_cap[:, :2]
|
|
454
|
+
# Use convex hull to get ordered boundary vertices
|
|
455
|
+
try:
|
|
456
|
+
from scipy.spatial import ConvexHull
|
|
457
|
+
hull = ConvexHull(xy_coords)
|
|
458
|
+
boundary_indices = hull.vertices
|
|
459
|
+
trench_opening_vertices = xy_coords[boundary_indices].tolist()
|
|
460
|
+
except ImportError:
|
|
461
|
+
# Fallback: just use unique xy coords (unordered)
|
|
462
|
+
trench_opening_vertices = xy_coords.tolist()
|
|
463
|
+
|
|
464
|
+
# Determine geometry type
|
|
465
|
+
is_closed = _is_path_closed(self.spec.path_xy)
|
|
466
|
+
geometry_type = "closed_well" if is_closed else "open_trench"
|
|
467
|
+
|
|
468
|
+
# Build surface group info
|
|
469
|
+
surface_groups = {}
|
|
470
|
+
for name in self.groups:
|
|
471
|
+
if name in _INTERNAL_GROUPS:
|
|
472
|
+
continue
|
|
473
|
+
if "bottom" in name:
|
|
474
|
+
surface_groups[name] = {"normal_direction": "up", "surface_type": "floor"}
|
|
475
|
+
elif "wall" in name:
|
|
476
|
+
surface_groups[name] = {"normal_direction": "inward", "surface_type": "wall"}
|
|
477
|
+
elif "ground" in name:
|
|
478
|
+
surface_groups[name] = {"normal_direction": "up", "surface_type": "ground"}
|
|
479
|
+
elif "pipe" in name:
|
|
480
|
+
surface_groups[name] = {"normal_direction": "outward", "surface_type": "embedded_object"}
|
|
481
|
+
elif "box" in name or "sphere" in name:
|
|
482
|
+
surface_groups[name] = {"normal_direction": "outward", "surface_type": "embedded_object"}
|
|
483
|
+
else:
|
|
484
|
+
surface_groups[name] = {"normal_direction": "unknown", "surface_type": "other"}
|
|
485
|
+
|
|
486
|
+
return {
|
|
487
|
+
"sdf_metadata": {
|
|
488
|
+
"version": "1.0",
|
|
489
|
+
"normal_convention": "into_void",
|
|
490
|
+
"geometry_type": geometry_type,
|
|
491
|
+
"trench_opening": {
|
|
492
|
+
"type": "polygon",
|
|
493
|
+
"vertices_xy": trench_opening_vertices,
|
|
494
|
+
"z_level": self.spec.ground.z0 if self.spec.ground else 0.0,
|
|
495
|
+
},
|
|
496
|
+
"surface_groups": surface_groups,
|
|
497
|
+
"embedded_objects": {
|
|
498
|
+
"pipes": self.object_counts.get("pipes", 0),
|
|
499
|
+
"boxes": self.object_counts.get("boxes", 0),
|
|
500
|
+
"spheres": self.object_counts.get("spheres", 0),
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
def persist(self, out_dir: str | Path, *, include_previews: bool = False, include_sdf_metadata: bool = True) -> SurfaceMeshFiles:
|
|
439
506
|
out_path = Path(out_dir)
|
|
440
507
|
out_path.mkdir(parents=True, exist_ok=True)
|
|
441
508
|
obj_path = out_path / "trench_scene.obj"
|
|
@@ -445,13 +512,27 @@ class SurfaceMeshResult:
|
|
|
445
512
|
metrics_path = out_path / "metrics.json"
|
|
446
513
|
with metrics_path.open("w") as fh:
|
|
447
514
|
json.dump(self.metrics, fh, indent=2)
|
|
515
|
+
|
|
516
|
+
# Export SDF metadata
|
|
517
|
+
sdf_metadata_path = None
|
|
518
|
+
if include_sdf_metadata:
|
|
519
|
+
sdf_metadata = self._build_sdf_metadata()
|
|
520
|
+
sdf_metadata_path = out_path / "sdf_metadata.json"
|
|
521
|
+
with sdf_metadata_path.open("w") as fh:
|
|
522
|
+
json.dump(sdf_metadata, fh, indent=2)
|
|
523
|
+
|
|
448
524
|
preview_paths: List[Path] = []
|
|
449
525
|
if include_previews and self.previews:
|
|
450
526
|
for name, data in self.previews.items():
|
|
451
527
|
target = out_path / f"preview_{name}.png"
|
|
452
528
|
target.write_bytes(data)
|
|
453
529
|
preview_paths.append(target)
|
|
454
|
-
return SurfaceMeshFiles(
|
|
530
|
+
return SurfaceMeshFiles(
|
|
531
|
+
obj_path=obj_path,
|
|
532
|
+
metrics_path=metrics_path,
|
|
533
|
+
preview_paths=tuple(preview_paths),
|
|
534
|
+
sdf_metadata_path=sdf_metadata_path,
|
|
535
|
+
)
|
|
455
536
|
|
|
456
537
|
def _ground_fn(g: GroundSpec):
|
|
457
538
|
sx, sy = g.slope
|
|
@@ -620,7 +701,8 @@ def make_trench_from_path_sloped(path_xy: List[Tuple[float,float]], width_top: f
|
|
|
620
701
|
|
|
621
702
|
bot_verts, bot_faces = _triangulate_annulus(outer_bot, inner_bot[::-1])
|
|
622
703
|
V_bottom = np.column_stack([bot_verts, np.concatenate([z_outer_bot, z_inner_bot[::-1]])])
|
|
623
|
-
|
|
704
|
+
# Floor normals point UP (+z) into the trench void for correct SDF sign
|
|
705
|
+
F_bottom = _ensure_upward_normals(V_bottom, bot_faces)
|
|
624
706
|
|
|
625
707
|
# Outer wall: connects outer_top to outer_bot (facing outward from trench)
|
|
626
708
|
n_outer = len(outer_top)
|
|
@@ -680,7 +762,8 @@ def make_trench_from_path_sloped(path_xy: List[Tuple[float,float]], width_top: f
|
|
|
680
762
|
V_cap = np.column_stack([poly_top, z_top])
|
|
681
763
|
V_bottom = np.column_stack([poly_bot, z_bot])
|
|
682
764
|
F_cap = tris_top
|
|
683
|
-
|
|
765
|
+
# Floor normals point UP (+z) into the trench void for correct SDF sign
|
|
766
|
+
F_bottom = _ensure_upward_normals(V_bottom, tris_bot)
|
|
684
767
|
|
|
685
768
|
# Walls: connect corresponding indices
|
|
686
769
|
N = len(poly_top)
|
|
@@ -715,6 +798,38 @@ def make_trench_from_path_sloped(path_xy: List[Tuple[float,float]], width_top: f
|
|
|
715
798
|
|
|
716
799
|
return groups, poly_top, poly_bot, extra
|
|
717
800
|
|
|
801
|
+
def _ensure_upward_normals(V: np.ndarray, F: np.ndarray) -> np.ndarray:
|
|
802
|
+
"""Ensure all faces have upward-pointing normals (+z).
|
|
803
|
+
|
|
804
|
+
For horizontal surfaces like trench floors, normals should point UP
|
|
805
|
+
into the void for correct SDF computation. This function flips any
|
|
806
|
+
faces with downward-pointing normals.
|
|
807
|
+
|
|
808
|
+
Parameters
|
|
809
|
+
----------
|
|
810
|
+
V : np.ndarray
|
|
811
|
+
Vertices (n, 3)
|
|
812
|
+
F : np.ndarray
|
|
813
|
+
Faces (m, 3) - indices into V
|
|
814
|
+
|
|
815
|
+
Returns
|
|
816
|
+
-------
|
|
817
|
+
np.ndarray
|
|
818
|
+
Faces with consistent upward normals (may have winding flipped)
|
|
819
|
+
"""
|
|
820
|
+
F_out = F.copy()
|
|
821
|
+
p0 = V[F[:, 0]]
|
|
822
|
+
p1 = V[F[:, 1]]
|
|
823
|
+
p2 = V[F[:, 2]]
|
|
824
|
+
normals = np.cross(p1 - p0, p2 - p0)
|
|
825
|
+
|
|
826
|
+
# Flip faces with negative z-component normals
|
|
827
|
+
down_mask = normals[:, 2] < 0
|
|
828
|
+
F_out[down_mask] = F_out[down_mask, ::-1]
|
|
829
|
+
|
|
830
|
+
return F_out
|
|
831
|
+
|
|
832
|
+
|
|
718
833
|
def _triangulate_annulus(outer: np.ndarray, inner: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
719
834
|
"""Triangulate the annular region between outer and inner polygons.
|
|
720
835
|
|
|
@@ -822,6 +937,9 @@ def make_ground_surface_plane(path_xy: List[Tuple[float,float]], width_top: floa
|
|
|
822
937
|
combined_xy, tris = _triangulate_annulus(ground_outer, trench_outer)
|
|
823
938
|
Vg = np.array([[x, y, gfun(x, y)] for (x, y) in combined_xy], float)
|
|
824
939
|
|
|
940
|
+
# Ground normals should point UP (+z) into the air
|
|
941
|
+
tris = _ensure_upward_normals(Vg, tris)
|
|
942
|
+
|
|
825
943
|
return {"ground_surface": (Vg, tris)}
|
|
826
944
|
else:
|
|
827
945
|
# Open paths: ground forms annulus with extensions past trench endpoints.
|
|
@@ -844,6 +962,9 @@ def make_ground_surface_plane(path_xy: List[Tuple[float,float]], width_top: floa
|
|
|
844
962
|
# Apply ground elevation to get 3D vertices
|
|
845
963
|
Vg = np.array([[x, y, gfun(x, y)] for (x, y) in combined_xy], float)
|
|
846
964
|
|
|
965
|
+
# Ground normals should point UP (+z) into the air
|
|
966
|
+
tris = _ensure_upward_normals(Vg, tris)
|
|
967
|
+
|
|
847
968
|
return {"ground_surface": (Vg, tris)}
|
|
848
969
|
|
|
849
970
|
def _half_width_at_depth(half_top: float, slope: float, top_z: float, z: float) -> float:
|
|
@@ -6,7 +6,7 @@ trenchfoot/gmsh_sloped_trench_mesher.py,sha256=D7EL6V0wkE6tvDARmm006yZE6KEzCl25O
|
|
|
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=
|
|
9
|
+
trenchfoot/trench_scene_generator_v3.py,sha256=9d93JK1rl61iq4fPxjiENEiFdX0Zhw306UQISeWnnvg,49277
|
|
10
10
|
trenchfoot/scenarios/SUMMARY.json,sha256=uylEzgzIqk5pGBfWVchVFnwwIDGBjNTDY_E23L_iakI,9372
|
|
11
11
|
trenchfoot/scenarios/S01_straight_vwalls/metrics.json,sha256=7VDscjZdxNPgNZaPHzRHYBJ1a5amNgJ7XYKCezVJJKQ,691
|
|
12
12
|
trenchfoot/scenarios/S01_straight_vwalls/preview.png,sha256=adsx3-6oMu9WaixggXRhuXi_KM-q5s2RNSBLR_stq80,199624
|
|
@@ -71,8 +71,8 @@ trenchfoot/scenarios/S07_circular_well/preview_top.png,sha256=X1P0g8kblJLsxOpYiZ
|
|
|
71
71
|
trenchfoot/scenarios/S07_circular_well/scene.json,sha256=bvror2YX6aNbsEc25-N7JO3ysH2dTLGyEE6zGzZysXQ,3146
|
|
72
72
|
trenchfoot/scenarios/S07_circular_well/trench_scene.obj,sha256=leTTT0i5xE-fvFSzHLNf_JBsU0AN3YqadDx4HmNmFhU,1618101
|
|
73
73
|
trenchfoot/scenarios/S07_circular_well/volumetric/trench_volume.msh,sha256=dqhtd3SFKj5RLT_BcWIIvVGCbAqvOx7RX25-K7NKX10,615212
|
|
74
|
-
trenchfoot-0.
|
|
75
|
-
trenchfoot-0.
|
|
76
|
-
trenchfoot-0.
|
|
77
|
-
trenchfoot-0.
|
|
78
|
-
trenchfoot-0.
|
|
74
|
+
trenchfoot-0.3.0.dist-info/METADATA,sha256=_N5N5_BZvcb3JmbEKrtaosI3BEe1DIh0fOzIunUNF88,5292
|
|
75
|
+
trenchfoot-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
76
|
+
trenchfoot-0.3.0.dist-info/entry_points.txt,sha256=5TejAGmc4GnNYLn7MhhLtSCNz9240RvzcNaetF4IHfg,119
|
|
77
|
+
trenchfoot-0.3.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
78
|
+
trenchfoot-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|