feat: Implement is_valid algorithm for polyhedral surfaces
This PR introduces support for the is_valid algorithm for polyhedral surface geometries. A polyhedral_surface is a 3D composite geometry formed by a collection of polygonal faces that meet at shared edges. The implementation follows the OGC Simple Features Specification and introduces structural and topological checks for polyhedral surfaces.
The following properties should hold (following OGC standard):
- Contiguity: All polygon patches must form a connected surface via shared boundary segments.
- Shared Edges: Shared boundaries must be finite LineStrings used in at most two polygons.
- Orientation: Shared boundaries must be traversed in opposite directions by adjacent polygons; global orientation must be consistent.
Six new validity failure types has been introduced that reflect the description of the standard.
Below there is a matrix with valid and invalid cases with visualizations. Those cases (and a few more) are included in the unit tests of this PR.
| Name | Geometry | Figure |
|---|---|---|
| valid_surface | POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 1,0 1 1,0 0 1,1 0 1,1 1 1)),((1 1 1,1 0 1,1 0 0,1 1 0,1 1 1)),((1 1 1,1 1 0,0 1 0,0 1 1,1 1 1))) | |
| valid_surface_non_convex | POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 1,0 1 1,0 0 1,1 0 1,1 1 1)),((1 1 1,1 0 1,1 0 0,1 1 0,1 .5 .5,1 1 1))) | |
| valid_surface_holes | POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0),(0.2 0.2 0,0.2 0.5 0,0.5 0.5 0,0.5 0.2 0,0.2 0.2 0)),((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 1,0 1 1,0 0 1,1 0 1,1 1 1)),((1 1 1,1 0 1,1 0 0,1 1 0,1 1 1)),((1 1 1,1 1 0,0 1 0,0 1 1,1 1 1))) | |
| invalid_surface_non_planar_face | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 1 0,0 0 1,0 0 0)),((1 0 0,1 1 0,0 1 0,1 0 0))) | |
| invalid_surface_collinear_points | POLYHEDRALSURFACE(((1 0 0,1 1 0,0 1 0,1 0 0)),((0 0 0,1 0 0,2 0 0,0 0 0))) | |
| invalid_surface_few_points | POLYHEDRALSURFACE(((1 0 0,1 1 0,0 1 0,1 0 0)),((0 0 0,1 0 0,0 0 0))) | |
| invalid_surface_invalid_intersection_boundary_interior | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 0 1,0 0 0)),((0 1 0,0.5 -0.5 0.5,1 1 0,0 1 0))) | |
| invalid_surface_invalid_intersection_boundary_boundary | POLYHEDRALSURFACE(((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((0 1 0,0 -1 1,1 -1 1,1 1 0,0 1 0))) | |
| invalid_surface_invalid_intersection_in_common_vertex | POLYHEDRALSURFACE(((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((0 1 0,1 0 1,1 1 0,0 1 0))) | |
| invalid_surface_invalid_intersection_partial_edge | POLYHEDRALSURFACE(((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((0 1 0,0.5 0 1,1 0 1,1 1 0,0 1 0))) | |
| invalid_surface_disconnected_polygons | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 1 0,0 0 0)),((1 1 0,2 1 0,2 2 0,1 2 0,1 1 0))) | |
| invalid_surface_invalid_intersection_vertex_edge | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 1 0,0 0 0)),((0.5 0.5 0,2 1 0,2 2 0,1 2 0,0.5 0.5 0))) | |
| invalid_surface_invalid_intersection_vertex_vertex | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 1 0,0 0 0)),((1 0 0,2 1 0,2 2 0,1 2 0,1 0 0))) | |
| invalid_surface_invalid_intersection_parallel_edges | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 1 0,0 0 0)),((0 1 0,0.5 0.5 0,1 0 0,2 1 0,2 2 0,1 2 0,0 1 0))) | |
| invalid_surface_invalid_intersection_operlapping_faces | POLYHEDRALSURFACE(((0 0 0,1 0.5 0,0.5 1 0,0 0 0)),((0 1 0,1 0 0,2 1 0,2 2 0,1 2 0,0 1 0))) | |
| invalid_surface_invalid_intersection_operlapping_faces2 | POLYHEDRALSURFACE(((0 0 0,1 0 0,0.5 1 0,0 0 0)),((0 1 0,1 0 0,2 1 0,2 2 0,1 2 0,0 1 0))) | |
| invalid_surface_invalid_face | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 1 0,0 0 0)),((0 1 0,1 0 0,2 2 0,2 1 0,1 2 0,0 1 0))) | |
| invalid_surface_non_manifold_edge | POLYHEDRALSURFACE(((0 0 0,1 0 0,0 1 0,0 0 0)),((0 1 0,1 0 0,2 1 0,2 2 0,1 2 0,0 1 0)),((0 1 0,1 0 0,1 1 1,0 1 0))) |
Two unit tests are failing and are commented out. Those are dependent on https://github.com/boostorg/geometry/issues/1406
Looks great! I will review it next Wednesday or Thursday.
@tinko92 thanks for the comments. I fixed all but the ones about tolerance and the strategy. I need to think of a unified and consistent solution for those.
Looks great! I will review it next Wednesday or Thursday.
I will continue tomorrow. It looks good but I had many suggestions already...
Looks great! I will review it next Wednesday or Thursday.
I will continue tomorrow. It looks good but I had many suggestions already...
So I didn't continue tomorrow - but great that you processed our comments! Thanks! I plan now to continue this weekend.
I refactor the code addressing almost all of the comments. Some things are left as todo for next PR(s). What is missing from this PR is the predicates (places in the code that use tolerance) that should be written as orientation checks implemented in strategies. I will work on it in the next days.
@barendgehrels thanks for you comments! I addressed most of them. I need to work more on this PR. Apart from the orient3d some other simplifications can be implemented (the unaddressed comments of Barend are related to those simplifications). I will work on it next week.
I have updated this PR. Now strategies are used (with 2d and 3d orientation predicates) and there are no constructions on new geometries apart from coordinate projections (that are not that vulnerable to numerical errors). Also a new algorithm for segment-polygon intersection in 3D that only uses orientation predicates is implemented.
Cool changes, thanks for the splits - I'll try to finish my review this weekend
Thanks for your reviews! I addressed all the comments. (However, I think @barendgehrels didn't finish his review yet)
Thanks for the quick response. The latest force push broke b2 test, looks like a missing git add test/geometries/polyhedral_surface_fail.cpp.