feat(meshing): trimesh_remesh — protect_boundary + sharp-feature edge constraints - #72
Conversation
|
Pssst @petrasvestartas && || @tomvanmele |
|
I have not seen it:) I will check now. |
|
@jf--- please provide documentation example, does it make sense to modify this example to show the difference: |
|
@jf--- would it be possible to make these little adjustments so that we could merge this? |
|
yes I'm on it |
|
@jf--- claude can take screenshots from the compas_viewer so that docs examples can be generated more easy :) |
…s_angle_deg
Expose CGAL's edge_is_constrained_map + protect_constraints for
isotropic_remeshing. Two new optional kwargs (both default-disabled,
backward-compatible):
- protect_boundary=True → all boundary edges constrained; preserves
boundary curves verbatim including sharp corners that the default
smoothing pass otherwise rounds.
- protect_sharp_edges_angle_deg=>0 → also constrain interior edges
with dihedral angle >= threshold (via PMP::detect_sharp_edges).
Pattern matches existing edge_is_constrained_map usage in
src/{isolines,geodesics,booleans}.cpp.
…_sharp_edges_angle_deg - default mode: boundary IS subdivided on annular layer - protect_boundary=True: all 8 original corners survive verbatim - protect_boundary=True: boundary-vertex count stays exactly 8 (no inserts) - protect_sharp_edges_angle_deg=0.0: no behavioural change vs unset
star disk remeshed OFF vs ON, false-colored by vertex density, labelled.
ac5d001 to
92826a5
Compare
|
Done — rebased onto current It remeshes a dense star disk to a coarse target two ways, side by side and false-colored by local vertex density:
A star rim (rather than the convex RhinoVault shell) makes the density difference legible. Ready for another look. |
|
shoot, the example now is a remeshing specific example. |
Keep the original RhinoVault shell geometry and enhance the existing example
in place, instead of replacing it with a synthetic star disk. It now remeshes
the shell to a coarse target two ways, side by side over the faint original:
- protect_boundary=False (default): the open perimeter is coarsened, corners
rounded (~9 boundary verts at target=3).
- protect_boundary=True: every boundary edge is constrained, so all 41
perimeter verts and the 4 corners survive while the interior coarsens.
Restores docs/examples/example_meshing.{py,md} + image to the RhinoVault
example and augments it; no separate example file, no nav change.
…coordinate Add an optional keep_points (Nx3) argument to trimesh_remesh. Each point is snapped to its coincident mesh vertex (within 1e-6) and marked in a vertex_is_constrained_map, so isotropic_remeshing neither moves nor removes it while the boundary edges between such points are still re-sampled. This is the coordinate-snap behaviour already used by trimesh_remesh_dual, now exposed on the plain remesh. The vertex-constrained map is attached only when points are supplied, leaving the default and protect_boundary paths byte-identical. Also rework example_meshing to show all three boundary modes side by side (default | protect_boundary | keep_points = 4 corners) and update its image.
|
perfect, thanks for the review & really do enjoy working with you on |

Summary
Expose CGAL's
edge_is_constrained_map+protect_constraintsparameters fromPolygon_mesh_processing::isotropic_remeshingvia two new optional kwargs ontrimesh_remesh. Both default to disabled, so the change is backward-compatible.protect_boundary=True— every boundary edge is constrained, preserving the input boundary curve (including sharp corners) verbatim. Without this, CGAL's default smoothing pass re-samples boundary edges totarget_edge_lengthand rounds sharp corners.protect_sharp_edges_angle_deg=>0— additionally constrain interior edges whose adjacent faces meet at a dihedral angle ≥ threshold (viaPMP::detect_sharp_edges).Motivation
Discovered while remeshing curved 3D-printing layers (annular cross-sections of a torus). With the default
trimesh_remesh, sharp boundary corners get visibly rounded because CGAL smooths them along with interior vertices — there was no way from Python to mark those edges as features.Implementation
Follows the existing
edge_is_constrained_mappattern already used insrc/isolines.cpp,src/geodesics.cpp, andsrc/booleans.cpp: a boolean property map is built on the mesh, marked for the protected edges, then passed to PMP viaparameters().edge_is_constrained_map(ecm).protect_constraints(True).PMP::detect_sharp_edgesis also imported (one extra header —CGAL/Polygon_mesh_processing/detect_features.h).Tests
Added under
tests/test_meshing.py(4 new cases, all passing alongside the existing 2):test_remesh_default_subdivides_boundary— default mode (no protection) subdivides boundary verts on an annular layer, as before.test_remesh_protect_boundary_keeps_corners— every original corner of an outer + inner square boundary survives verbatim underprotect_boundary=True.test_remesh_protect_boundary_keeps_boundary_vertex_count— boundary-vertex count stays exactly 8 (4 outer + 4 inner) withprotect_boundary=True; no inserts.test_remesh_protect_sharp_edges_default_disabled—protect_sharp_edges_angle_deg=0.0(default) produces identical output to omitting the kwarg, confirming backward compatibility.Backward compatibility
Both new params default to their old behaviour (
protect_boundary=False,protect_sharp_edges_angle_deg=0.0). No existing call sites change.