Skip to content

feat(meshing): trimesh_remesh — protect_boundary + sharp-feature edge constraints#72

Open
jf--- wants to merge 2 commits into
compas-dev:mainfrom
jf---:jf/trimesh-remesh-protect-features
Open

feat(meshing): trimesh_remesh — protect_boundary + sharp-feature edge constraints#72
jf--- wants to merge 2 commits into
compas-dev:mainfrom
jf---:jf/trimesh-remesh-protect-features

Conversation

@jf---
Copy link
Copy Markdown
Collaborator

@jf--- jf--- commented May 19, 2026

Summary

Expose CGAL's edge_is_constrained_map + protect_constraints parameters from Polygon_mesh_processing::isotropic_remeshing via two new optional kwargs on trimesh_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 to target_edge_length and rounds sharp corners.
  • protect_sharp_edges_angle_deg=>0 — additionally constrain interior edges whose adjacent faces meet at a dihedral angle ≥ threshold (via PMP::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_map pattern already used in src/isolines.cpp, src/geodesics.cpp, and src/booleans.cpp: a boolean property map is built on the mesh, marked for the protected edges, then passed to PMP via parameters().edge_is_constrained_map(ecm).protect_constraints(True).

PMP::detect_sharp_edges is 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 under protect_boundary=True.
  • test_remesh_protect_boundary_keeps_boundary_vertex_count — boundary-vertex count stays exactly 8 (4 outer + 4 inner) with protect_boundary=True; no inserts.
  • test_remesh_protect_sharp_edges_default_disabledprotect_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.

jf--- added 2 commits May 19, 2026 17:11
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant