Skip to content

Commit 27a7cef

Browse files
committed
Merge pull request #8744 from LeoValque/PMP_triangle_soup_rounding-GF
Add do_snap parameter to PMP::autorefine_triangle_soup
2 parents 78cfeac + bd0e60e commit 27a7cef

35 files changed

Lines changed: 2231 additions & 86 deletions

Documentation/doc/biblio/geom.bib

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152087,6 +152087,18 @@ @article{ledoux2014triangulation
152087152087
publisher={Elsevier}
152088152088
}
152089152089

152090+
@unpublished{lazard:hal-04907149,
152091+
TITLE = {{Removing self-intersections in 3D meshes while preserving floating-point coordinates}},
152092+
AUTHOR = {Lazard, Sylvain and Valque, Leo},
152093+
URL = {https://inria.hal.science/hal-04907149},
152094+
NOTE = {working paper or preprint},
152095+
YEAR = {2025},
152096+
MONTH = Jan,
152097+
KEYWORDS = {Snap rounding ; mesh intersection ; robustness},
152098+
PDF = {https://inria.hal.science/hal-04907149v1/file/Snap-HAL.pdf},
152099+
HAL_ID = {hal-04907149},
152100+
HAL_VERSION = {v1},
152101+
}
152090152102

152091152103
@inproceedings{si2005meshing,
152092152104
title={Meshing piecewise linear complexes by constrained {Delaunay} tetrahedralizations},

Installation/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
- New implementation of `CGAL::Polygon_mesh_processing::clip()` with a plane as clipper that is much faster and is now able to handle non-triangulated surface meshes.
4040
- New implementation of `CGAL::Polygon_mesh_processing::split()` with a plane as clipper that is much faster and is now able to handle non-triangulated surface meshes.
4141
- Added the function `CGAL::Polygon_mesh_processing::refine_with_plane()`, which enables users to refine a mesh with their intersection with a plane.
42+
- Added the parameter `apply_iterative_snap_rounding` to the function `CGAL::Polygon_mesh_processing::autorefine_triangle_soup()`. When set to `true`, the coordinates are rounded to fit in double and may perform additional subdivisions to ensure the output is free of self-intersections.
4243

4344
### [Point Set Processing](https://doc.cgal.org/6.1/Manual/packages.html#PkgPointSetProcessing3)
4445
- Added `poisson_eliminate()` to downsample a point cloud to a target size while providing Poisson disk property, i.e., a larger minimal distance between points.

Polygon_mesh_processing/benchmark/Polygon_mesh_processing/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,21 @@ else()
3737
endif()
3838

3939
create_single_source_cgal_program("fast.cpp")
40+
create_single_source_cgal_program("rotated_cubes_autorefinement.cpp")
41+
create_single_source_cgal_program("coplanar_cubes_autorefinement.cpp")
42+
43+
create_single_source_cgal_program("Performance/performance_snap_polygon_soup.cpp")
44+
create_single_source_cgal_program("Robustness/robustness_snap_polygon_soup.cpp")
45+
create_single_source_cgal_program("Quality/quality_snap_polygon_soup.cpp")
4046

4147
create_single_source_cgal_program("polygon_mesh_slicer.cpp")
4248
target_link_libraries(polygon_mesh_slicer PRIVATE CGAL::Eigen3_support)
4349

50+
find_package(TBB QUIET)
51+
include(CGAL_TBB_support)
52+
if(TARGET CGAL::TBB_support)
53+
target_link_libraries(rotated_cubes_autorefinement PRIVATE CGAL::TBB_support)
54+
target_link_libraries(coplanar_cubes_autorefinement PRIVATE CGAL::TBB_support)
55+
else()
56+
message(STATUS "NOTICE: Intel TBB was not found. Sequential code will be used.")
57+
endif()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
2+
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
3+
#include <CGAL/Polygon_mesh_processing/autorefinement.h>
4+
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
5+
#include <CGAL/IO/polygon_soup_io.h>
6+
7+
#include <boost/container/small_vector.hpp>
8+
9+
#include <iostream>
10+
11+
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
12+
typedef typename Kernel::Point_3 Point_3;
13+
namespace PMP = CGAL::Polygon_mesh_processing;
14+
15+
enum EXIT_CODES { VALID_OUTPUT=0,
16+
INVALID_INPUT=1,
17+
ROUNDING_FAILED=2,
18+
SIGSEGV=10,
19+
SIGSABRT=11,
20+
SIGFPE=12,
21+
TIMEOUT=13
22+
};
23+
24+
int main(int argc, char** argv)
25+
{
26+
if(argc<4){
27+
std::cout << "Invalid argument" << std::endl;
28+
return 1;
29+
}
30+
31+
const std::string filename = std::string(argv[1]);
32+
const int grid_size = std::stoi(std::string(argv[2]));
33+
const bool erase_duplicate = std::stoi(argv[3])==1;
34+
35+
std::vector<Point_3> points;
36+
std::vector<boost::container::small_vector<std::size_t, 3>> triangles;
37+
38+
if (!CGAL::IO::read_polygon_soup(filename, points, triangles))
39+
{
40+
std::cerr << "Cannot read " << filename << "\n";
41+
return 1;
42+
}
43+
44+
PMP::repair_polygon_soup(points, triangles);
45+
PMP::triangulate_polygons(points, triangles);
46+
47+
PMP::autorefine_triangle_soup(points, triangles, CGAL::parameters::apply_iterative_snap_rounding(true).erase_all_duplicates(erase_duplicate).concurrency_tag(CGAL::Parallel_if_available_tag()).snap_grid_size(grid_size).number_of_iterations(15));
48+
49+
return 0;
50+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
if [ "$#" -lt 4 ]; then
6+
echo "Usage: $0 <input_file> <timeout> [component_params...]"
7+
exit 1
8+
fi
9+
10+
INPUT_FILE=$1
11+
TIMEOUT=$2
12+
GRID_SIZE=$3
13+
ERASE_ALL_DUPLICATE=$4
14+
15+
# Use /usr/bin/time for memory usage (maximum resident set size in KB)
16+
TMP_LOG=$(mktemp)
17+
18+
# Run the benchmarked command
19+
/usr/bin/time -f "TIME:%e\nMEM:%M" timeout "$TIMEOUT"s performance_snap_polygon_soup "$INPUT_FILE" "$GRID_SIZE" "$ERASE_ALL_DUPLICATE" 2> "$TMP_LOG"
20+
21+
# Parse time and memory
22+
SECONDS=$(grep "TIME" "$TMP_LOG" | cut -d':' -f2)
23+
MEMORY=$(grep "MEM" "$TMP_LOG" | cut -d':' -f2)
24+
25+
rm -f "$TMP_LOG"
26+
27+
# Output JSON
28+
echo "{\"seconds\": \"$SECONDS\", \"memory_peaks\": \"$MEMORY\"}"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
2+
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
3+
#include <CGAL/Polygon_mesh_processing/autorefinement.h>
4+
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
5+
#include <CGAL/Polygon_mesh_processing/distance.h>
6+
#include <CGAL/IO/polygon_soup_io.h>
7+
8+
#include <CGAL/Bbox_3.h>
9+
#include <CGAL/boost/graph/helpers.h>
10+
11+
#include <CGAL/Surface_mesh.h>
12+
13+
#include <boost/container/small_vector.hpp>
14+
#include <CGAL/Polygon_mesh_processing/orientation.h>
15+
16+
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
17+
typedef typename Kernel::Point_3 Point_3;
18+
namespace PMP = CGAL::Polygon_mesh_processing;
19+
20+
int main(int argc, char** argv)
21+
{
22+
if(argc<4){
23+
std::cout << "Invalid argument" << std::endl;
24+
return 1;
25+
}
26+
const std::string filename = std::string(argv[1]);
27+
const int grid_size = std::stoi(std::string(argv[2]));
28+
const bool erase_duplicate = std::stoi(argv[3])==1;
29+
30+
std::vector<Point_3> points;
31+
std::vector<boost::container::small_vector<std::size_t, 3>> triangles;
32+
33+
CGAL::Bbox_3 bb = CGAL::bbox_3(points.begin(), points.end());
34+
double diag_length=std::sqrt((bb.xmax()-bb.xmin())*(bb.xmax()-bb.xmin()) + (bb.ymax()-bb.ymin())*(bb.ymax()-bb.ymin()) + (bb.zmax()-bb.zmin())*(bb.zmax()-bb.zmin()));
35+
if (!CGAL::IO::read_polygon_soup(filename, points, triangles))
36+
{
37+
std::cerr << "Cannot read " << filename << "\n";
38+
return 1;
39+
}
40+
41+
std::vector<Point_3> input_points(points.begin(), points.end());
42+
43+
PMP::autorefine_triangle_soup(points, triangles, CGAL::parameters::apply_iterative_snap_rounding(true).erase_all_duplicates(erase_duplicate).concurrency_tag(CGAL::Parallel_if_available_tag()).snap_grid_size(grid_size).number_of_iterations(15));
44+
45+
46+
std::cout << "{" <<
47+
"\"Nb_output_points\": \"" << points.size() << "\",\n" <<
48+
"\"Nb_output_triangles\": \"" << triangles.size() << "\",\n" <<
49+
"\"Is_2_manifold\": \"" << (PMP::orient_polygon_soup(points, triangles)?"True":"False") << "\",\n";
50+
CGAL::Surface_mesh<Point_3> sm;
51+
PMP::polygon_soup_to_polygon_mesh(points, triangles, sm);
52+
53+
std::cout << std::setprecision(17) <<
54+
"\"Hausdorff_distance_output_to_input_(divide_by_bbox_diag)\": \"" << PMP::max_distance_to_triangle_mesh<CGAL::Parallel_if_available_tag>(input_points, sm) / diag_length << "\",\n" <<
55+
"\"Closed_output\": \"" << (CGAL::is_closed(sm)?"True":"False") << "\",\n" <<
56+
"\"Ouput_bound_a_volume\": \"" << (PMP::does_bound_a_volume(sm)?"True":"False") << "\"\n}"
57+
<< std::endl;
58+
59+
return 0;
60+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
if [ "$#" -lt 4 ]; then
6+
echo "Usage: $0 <input_file> <timeout> [component_params...]"
7+
exit 1
8+
fi
9+
10+
INPUT_FILE=$1
11+
TIMEOUT=$2
12+
GRID_SIZE=$3
13+
ERASE_ALL_DUPLICATE=$4
14+
15+
TMP_LOG=$(mktemp)
16+
timeout "$TIMEOUT"s quality_snap_polygon_soup "$INPUT_FILE" "$GRID_SIZE" "$ERASE_ALL_DUPLICATE" > "$TMP_LOG"
17+
18+
cat $TMP_LOG
19+
rm -f "$TMP_LOG"
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
2+
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
3+
#include <CGAL/Polygon_mesh_processing/autorefinement.h>
4+
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
5+
#include <CGAL/IO/polygon_soup_io.h>
6+
7+
#include <boost/container/small_vector.hpp>
8+
9+
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
10+
typedef typename Kernel::Point_3 Point_3;
11+
namespace PMP = CGAL::Polygon_mesh_processing;
12+
13+
enum EXIT_CODES { VALID_OUTPUT=0,
14+
INVALID_INPUT=1,
15+
ROUNDING_FAILED=2,
16+
SELF_INTERSECTING_OUTPUT=3,
17+
SIGSEGV=10,
18+
SIGSABRT=11,
19+
SIGFPE=12,
20+
TIMEOUT=13
21+
};
22+
23+
int main(int argc, char** argv)
24+
{
25+
if(argc<4){
26+
std::cout << "Invalid argument" << std::endl;
27+
return 1;
28+
}
29+
30+
const std::string filename = std::string(argv[1]);
31+
const int grid_size = std::stoi(std::string(argv[2]));
32+
const bool erase_duplicate = std::stoi(argv[3])==1;
33+
34+
std::vector<Point_3> points;
35+
std::vector<boost::container::small_vector<std::size_t, 3>> triangles;
36+
37+
if (!CGAL::IO::read_polygon_soup(filename, points, triangles) || points.size()==0 || triangles.size()==0)
38+
{
39+
return INVALID_INPUT;
40+
}
41+
42+
PMP::repair_polygon_soup(points, triangles);
43+
PMP::triangulate_polygons(points, triangles);
44+
45+
bool success=PMP::autorefine_triangle_soup(points, triangles, CGAL::parameters::apply_iterative_snap_rounding(true).erase_all_duplicates(erase_duplicate).concurrency_tag(CGAL::Parallel_if_available_tag()).snap_grid_size(grid_size).number_of_iterations(15));
46+
47+
if(!success)
48+
return ROUNDING_FAILED;
49+
if( PMP::does_triangle_soup_self_intersect<CGAL::Parallel_if_available_tag>(points, triangles) )
50+
return SELF_INTERSECTING_OUTPUT;
51+
52+
return VALID_OUTPUT;
53+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
if [ "$#" -lt 4 ]; then
4+
echo "Usage: $0 <input_file> <timeout> [component_params...]"
5+
exit 1
6+
fi
7+
8+
timeout_bis() {
9+
timeout 5 sleep 10
10+
}
11+
12+
INPUT_FILE=$1
13+
TIMEOUT=$2
14+
GRID_SIZE=$3
15+
ERASE_ALL_DUPLICATE=$4
16+
17+
# Run with timeout, capture exit code
18+
timeout "--foreground" "$TIMEOUT"s robustness_snap_polygon_soup "$INPUT_FILE" "$GRID_SIZE" "$ERASE_ALL_DUPLICATE"
19+
EXIT_CODE=$?
20+
21+
# Interpret exit codes
22+
declare -A TAGS
23+
TAGS[0]="VALID_OUTPUT"
24+
TAGS[1]="INPUT_IS_INVALID"
25+
TAGS[2]="ROUNDING_FAILED"
26+
TAGS[3]="SELF_INTERSECTING_OUTPUT"
27+
TAGS[139]="SIGSEGV"
28+
TAGS[11]="SIGSEGV"
29+
TAGS[6]="SIGABRT"
30+
TAGS[8]="SIGFPE"
31+
TAGS[132]="SIGILL"
32+
TAGS[124]="TIMEOUT"
33+
34+
TAG_NAME=${TAGS[$EXIT_CODE]:-UNKNOWN}
35+
TAG_DESC=$([[ "$EXIT_CODE" -eq 0 ]] && echo "OK" || echo "Error")
36+
37+
# Output JSON
38+
echo "{\"TAG_NAME\": \"$TAG_NAME\", \"TAG\": \"$TAG_DESC\"}"

0 commit comments

Comments
 (0)