Skip to content

Commit

Permalink
Add validation and repair for TriangleMesh with inward-facing normals
Browse files Browse the repository at this point in the history
  • Loading branch information
caseyflex authored and momchil-flex committed Sep 25, 2024
1 parent 0ea2e3f commit 0ec17a9
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`ComponentModeler.batch_data` convenience property to access the `BatchData` corresponding to the component modeler run.
- Added optimization methods to the Design plugin. The plugin has been expanded to include Bayesian optimization, genetic algorithms and particle swarm optimization. Explanations of these methods are available in new and updated notebooks.
- Added new support functions for the Design plugin: automated batching of `Simulation` objects, and summary functions with `DesignSpace.estimate_cost` and `DesignSpace.summarize`.
- Added validation and repair methods for `TriangleMesh` with inward-facing normals.

### Changed
- Priority is given to `snapping_points` in `GridSpec` when close to structure boundaries, which reduces the chance of them being skipped.
Expand Down
15 changes: 12 additions & 3 deletions tests/test_components/test_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,9 +836,9 @@ def test_custom_surface_geometry(tmp_path, log_capture):

# test inconsistent winding
vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])
faces = np.array([[2, 1, 3], [0, 3, 2], [0, 1, 3], [0, 2, 1]])
faces = np.array([[2, 3, 1], [0, 2, 3], [0, 3, 1], [0, 1, 2]])
tetrahedron = trimesh.Trimesh(vertices, faces)
with AssertLogLevel(log_capture, "WARNING"):
with AssertLogLevel(log_capture, "WARNING", contains_str="face orientations"):
geom = td.TriangleMesh.from_trimesh(tetrahedron)
with AssertLogLevel(log_capture, None):
geom = geom.fix_winding()
Expand All @@ -847,11 +847,20 @@ def test_custom_surface_geometry(tmp_path, log_capture):
vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])
faces = np.array([[0, 3, 2], [0, 1, 3], [0, 2, 1]])
tetrahedron = trimesh.Trimesh(vertices, faces)
with AssertLogLevel(log_capture, "WARNING"):
with AssertLogLevel(log_capture, "WARNING", contains_str="watertight"):
geom = td.TriangleMesh.from_trimesh(tetrahedron)
with AssertLogLevel(log_capture, None):
geom = geom.fill_holes()

# test inward normals
vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])
faces = np.array([[2, 1, 3], [0, 3, 2], [0, 1, 3], [0, 2, 1]])
tetrahedron = trimesh.Trimesh(vertices, faces)
with AssertLogLevel(log_capture, "WARNING", contains_str="outward"):
geom = td.TriangleMesh.from_trimesh(tetrahedron)
with AssertLogLevel(log_capture, None):
geom = geom.fix_normals()

# test zero area triangles
vertices = np.array([[1, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]])
faces = np.array([[1, 2, 3], [0, 3, 2], [0, 1, 3], [0, 2, 1]])
Expand Down
23 changes: 22 additions & 1 deletion tidy3d/components/geometry/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,23 @@ def _check_mesh(cls, val: TriangleMeshDataset) -> TriangleMeshDataset:
if not mesh.is_winding_consistent:
log.warning(
"The provided mesh does not have consistent winding (face orientations). "
"This can lead to incorrect permittivity distributions. "
"This can lead to incorrect permittivity distributions, "
"and can also cause problems with plotting and mesh validation. "
"You can try 'TriangleMesh.fix_winding', which attempts to repair the mesh. "
"Otherwise, the mesh may require manual repair. You can use a "
"'PermittivityMonitor' to check if the permittivity distribution is correct. "
)
if not mesh.is_volume:
log.warning(
"The provided mesh does not represent a valid volume, possibly due to "
"incorrect normal vector orientation. "
"This can lead to incorrect permittivity distributions, "
"and can also cause problems with plotting and mesh validation. "
"You can try 'TriangleMesh.fix_normals', "
"which attempts to fix the normals to be consistent and outward-facing. "
"Otherwise, the mesh may require manual repair. You can use a "
"'PermittivityMonitor' to check if the permittivity distribution is correct."
)

return val

Expand All @@ -123,6 +135,15 @@ def fill_holes(self) -> TriangleMesh:
trimesh.repair.fill_holes(mesh)
return TriangleMesh.from_trimesh(mesh)

@verify_packages_import(["trimesh"])
def fix_normals(self) -> TriangleMesh:
"""Try to fix normals to be consistent and outward-facing."""
import trimesh

mesh = TriangleMesh._triangles_to_trimesh(self.mesh_dataset.surface_mesh)
trimesh.repair.fix_normals(mesh)
return TriangleMesh.from_trimesh(mesh)

@classmethod
@verify_packages_import(["trimesh"])
def from_stl(
Expand Down

0 comments on commit 0ec17a9

Please sign in to comment.