Closed f-p-b closed 1 year ago
I can reproduce. There is an issue with the polylines you pass to domain.add_features(...)
: they do not comply with the expectations. I will try to find a fix for you.
Here is a new version of the code. It replaces one of your piece of code with a use of
CGAL::split_graph_into_polylines
.
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/STL.h>
#include <CGAL/boost/graph/copy_face_graph.h>
#include <CGAL/Polygon_mesh_processing/detect_features.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/boost/graph/split_graph_into_polylines.h>
// 3D volume mesh
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/make_mesh_3.h>
// 3D volume mesh (polyhedral domain with features)
#include <CGAL/Polyhedral_mesh_domain_with_features_3.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Surface_mesh<Point_3> SurfaceMesh;
typedef boost::graph_traits<SurfaceMesh>::edge_descriptor edge_descriptor;
typedef boost::graph_traits<SurfaceMesh>::vertex_descriptor vertex_descriptor;
// 3D volume meshing (from polyhedron domain with features)
typedef CGAL::Polyhedral_mesh_domain_with_features_3<Kernel, SurfaceMesh> F_Polyhedron_domain;
typedef CGAL::Mesh_triangulation_3<F_Polyhedron_domain, CGAL::Default, CGAL::Parallel_if_available_tag>::type F_VTr;
typedef CGAL::Mesh_complex_3_in_triangulation_3<F_VTr, F_Polyhedron_domain::Corner_index, F_Polyhedron_domain::Curve_index> F_C3t3;
typedef CGAL::Mesh_criteria_3<F_VTr> F_Mesh_criteria;
struct Polyline_visitor
{
typedef std::vector<Point_3> Polyline;
std::vector<Polyline>& polylines;
const SurfaceMesh& mesh;
void start_new_polyline()
{
polylines.emplace_back();
}
void add_node(vertex_descriptor vd)
{
polylines.back().push_back(mesh.point(vd));
}
void end_polyline(){}
};
bool polehedral2volume (const SurfaceMesh& surfaceMesh, F_C3t3 &c3t3) {
// Create domain
F_Polyhedron_domain domain(surfaceMesh);
// Specify edges to protect
std::vector<std::vector<Point_3>> protected_features;
Polyline_visitor visitor{protected_features, surfaceMesh};
CGAL::split_graph_into_polylines(surfaceMesh, visitor);
int count = 0;
std::ofstream polylines_file("protected_features.polylines.txt");
polylines_file.precision(17);
for (const std::vector<Point_3>& polyline : protected_features) { //
polylines_file << polyline.size() << " ";
count += 1;
std::cout << count << ": " << polyline.size() << " >> ";
for ( auto ee : polyline) {
std::cout << ee << " " << std::flush;
polylines_file << ee << " ";
}
polylines_file << std::endl;
std::cout << std::endl;
}
// domain.add_features(protected_features.begin(), protected_features.end());
domain.detect_features(10);
// Mesh criteria
F_Mesh_criteria criteria(CGAL::parameters::edge_size = 0.1,
CGAL::parameters::facet_angle = 30.0,
CGAL::parameters::facet_size = 0.1,
CGAL::parameters::facet_distance = 0.015,
CGAL::parameters::cell_radius_edge_ratio = 3.0,
CGAL::parameters::cell_size = 0.1);
c3t3 = CGAL::make_mesh_3<F_C3t3>(domain, criteria);
return EXIT_SUCCESS;
}
int main(int, char**) {
//
std::vector<Point_3> points_A, points_B;
std::vector<std::vector<std::size_t>> polygons_A, polygons_B;
SurfaceMesh cube_A, cube_B, cube_OUT;
SurfaceMesh::Property_map<edge_descriptor, bool> ecmap_A = cube_A.add_property_map<edge_descriptor, bool>("e:is_constrained", false).first;
SurfaceMesh::Property_map<edge_descriptor, bool> ecmap_B = cube_B.add_property_map<edge_descriptor, bool>("e:is_constrained", false).first;
SurfaceMesh::Property_map<edge_descriptor, bool> ecmap_OUT = cube_OUT.add_property_map<edge_descriptor, bool>("e:is_constrained", false).first;
std::cout << " Opening " << "cube.stl" << std::endl;
std::ifstream stl_file_A("cube.stl", std::ios::binary);
CGAL::IO::read_STL("cube.stl", points_A, polygons_A);
stl_file_A.close();
std::cout << " Opening " << "cube_b.stl" << std::endl;
std::ifstream stl_file_B("cube_b.stl", std::ios::binary);
CGAL::IO::read_STL("cube_b.stl", points_B, polygons_B);
stl_file_B.close();
//
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points_A, polygons_A, cube_A);
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points_B, polygons_B, cube_B);
// Detect sharp edges
CGAL::Polygon_mesh_processing::detect_sharp_edges(cube_A, 60, ecmap_A);
CGAL::Polygon_mesh_processing::detect_sharp_edges(cube_B, 60, ecmap_B);
if(CGAL::Polygon_mesh_processing::corefine_and_compute_intersection(cube_B, cube_A, cube_OUT, CGAL::parameters::edge_is_constrained_map(ecmap_B), CGAL::parameters::default_values(), CGAL::parameters::edge_is_constrained_map(ecmap_OUT)) == false)
std::cout << "\n WARNING: corefine_and_compute_intersection output may have only been corefined!" << std::endl;
std::ofstream("cube_OUT.off") << std::setprecision(17) << cube_OUT;
F_C3t3 c3t3;
polehedral2volume(cube_OUT, c3t3);
// Write output to .mesh file
std::ofstream medit_file("cube_OUT.mesh");
c3t3.output_to_medit(medit_file);
medit_file.close();
}
And then the result is fine.
I have tested the new code and the issue seems to still be there. If I run it the code as posted it works since the line "domain.add_features(protected_features.begin(), protected_features.end())" is commented out and domain.detect_features(10) is used instead. But if I uncomment add_features() and comment the detect_features() it gets gets stuck like before (waited for 3 minutes and then killed it)
I am sorry. You are right. Here is the second fixed version.
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/STL.h>
#include <CGAL/boost/graph/copy_face_graph.h>
#include <CGAL/Polygon_mesh_processing/detect_features.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/boost/graph/split_graph_into_polylines.h>
// 3D volume mesh
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/make_mesh_3.h>
// 3D volume mesh (polyhedral domain with features)
#include <CGAL/Polyhedral_mesh_domain_with_features_3.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Surface_mesh<Point_3> SurfaceMesh;
typedef boost::graph_traits<SurfaceMesh>::edge_descriptor edge_descriptor;
typedef boost::graph_traits<SurfaceMesh>::vertex_descriptor vertex_descriptor;
template <typename ECMap>
struct Is_constrained {
ECMap ecmap;
template <typename Edge>
bool operator()(const Edge& e) const {
return get(ecmap, e);
}
};
template <typename VPMap>
struct Is_feature {
VPMap vpmap;
template <typename Vertex>
bool operator()(const Vertex& v) const {
return get(vpmap, v);
}
};
// 3D volume meshing (from polyhedron domain with features)
typedef CGAL::Polyhedral_mesh_domain_with_features_3<Kernel, SurfaceMesh> F_Polyhedron_domain;
typedef CGAL::Mesh_triangulation_3<F_Polyhedron_domain, CGAL::Default, CGAL::Parallel_if_available_tag>::type F_VTr;
typedef CGAL::Mesh_complex_3_in_triangulation_3<F_VTr, F_Polyhedron_domain::Corner_index, F_Polyhedron_domain::Curve_index> F_C3t3;
typedef CGAL::Mesh_criteria_3<F_VTr> F_Mesh_criteria;
struct Polyline_visitor
{
typedef std::vector<Point_3> Polyline;
std::vector<Polyline>& polylines;
const SurfaceMesh& mesh;
void start_new_polyline()
{
polylines.emplace_back();
}
void add_node(vertex_descriptor vd)
{
polylines.back().push_back(mesh.point(vd));
}
void end_polyline(){}
};
bool polehedral2volume (SurfaceMesh& surfaceMesh,
SurfaceMesh::Property_map<edge_descriptor, bool> ecmap,
F_C3t3 &c3t3) {
// Create domain
F_Polyhedron_domain domain(surfaceMesh);
auto v_pmap = surfaceMesh.add_property_map<vertex_descriptor, bool>("v:on_feature_curve", false).first;
for(auto e : edges(surfaceMesh)) {
auto v1 = source(e, surfaceMesh);
auto v2 = target(e, surfaceMesh);
if (get(ecmap, e)) {
put(v_pmap, v1, true);
put(v_pmap, v2, true);
}
}
typedef SurfaceMesh::Property_map<edge_descriptor, bool> ECMap;
typedef SurfaceMesh::Property_map<vertex_descriptor, bool> VPMap;
typedef boost::filtered_graph<SurfaceMesh,
Is_constrained<ECMap>,
Is_feature<VPMap>> FG;
// Specify edges to protect
std::vector<std::vector<Point_3>> protected_features;
Polyline_visitor visitor{protected_features, surfaceMesh};
Is_constrained<ECMap> is_constrained{ecmap};
Is_feature<VPMap> is_feature{v_pmap};
FG constrained_edges{surfaceMesh, is_constrained, is_feature};
CGAL::split_graph_into_polylines(constrained_edges, visitor);
int count = 0;
std::ofstream polylines_file("protected_features.polylines.txt");
polylines_file.precision(17);
for (const std::vector<Point_3>& polyline : protected_features) { //
polylines_file << polyline.size() << " ";
count += 1;
std::cout << count << ": " << polyline.size() << " >> ";
for ( auto ee : polyline) {
std::cout << ee << " " << std::flush;
polylines_file << ee << " ";
}
polylines_file << std::endl;
std::cout << std::endl;
}
polylines_file.close();
domain.add_features(protected_features.begin(), protected_features.end());
// Mesh criteria
F_Mesh_criteria criteria(CGAL::parameters::edge_size = 0.1,
CGAL::parameters::facet_angle = 30.0,
CGAL::parameters::facet_size = 0.1,
CGAL::parameters::facet_distance = 0.015,
CGAL::parameters::cell_radius_edge_ratio = 3.0,
CGAL::parameters::cell_size = 0.1);
c3t3 = CGAL::make_mesh_3<F_C3t3>(domain, criteria);
return EXIT_SUCCESS;
}
int main(int, char**) {
//
std::vector<Point_3> points_A, points_B;
std::vector<std::vector<std::size_t>> polygons_A, polygons_B;
SurfaceMesh cube_A, cube_B, cube_OUT;
SurfaceMesh::Property_map<edge_descriptor, bool> ecmap_A = cube_A.add_property_map<edge_descriptor, bool>("e:is_constrained", false).first;
SurfaceMesh::Property_map<edge_descriptor, bool> ecmap_B = cube_B.add_property_map<edge_descriptor, bool>("e:is_constrained", false).first;
SurfaceMesh::Property_map<edge_descriptor, bool> ecmap_OUT = cube_OUT.add_property_map<edge_descriptor, bool>("e:is_constrained", false).first;
std::cout << " Opening " << "cube.stl" << std::endl;
std::ifstream stl_file_A("cube.stl", std::ios::binary);
CGAL::IO::read_STL("cube.stl", points_A, polygons_A);
stl_file_A.close();
std::cout << " Opening " << "cube_b.stl" << std::endl;
std::ifstream stl_file_B("cube_b.stl", std::ios::binary);
CGAL::IO::read_STL("cube_b.stl", points_B, polygons_B);
stl_file_B.close();
//
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points_A, polygons_A, cube_A);
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points_B, polygons_B, cube_B);
// Detect sharp edges
CGAL::Polygon_mesh_processing::detect_sharp_edges(cube_A, 60, ecmap_A);
CGAL::Polygon_mesh_processing::detect_sharp_edges(cube_B, 60, ecmap_B);
if(CGAL::Polygon_mesh_processing::corefine_and_compute_intersection(cube_B, cube_A, cube_OUT, CGAL::parameters::edge_is_constrained_map(ecmap_B), CGAL::parameters::default_values(), CGAL::parameters::edge_is_constrained_map(ecmap_OUT)) == false)
std::cout << "\n WARNING: corefine_and_compute_intersection output may have only been corefined!" << std::endl;
CGAL::Polygon_mesh_processing::remove_isolated_vertices(cube_OUT);
std::ofstream("cube_OUT.off") << std::setprecision(17) << cube_OUT;
F_C3t3 c3t3;
polehedral2volume(cube_OUT, ecmap_OUT, c3t3);
// Write output to .mesh file
std::ofstream medit_file("cube_OUT.mesh");
c3t3.output_to_medit(medit_file);
medit_file.close();
}
Thanks, it working very nicely now!
I have one follow up question. Even though its doing exactly what it is supposed to, sometimes the result is not ideal. In corners were the cut from compute intersection results in a small edge the mesher tends to use more elements than ideal, in the example bellow it is visible how it subdivided the 3 small edges in multiple edges while not subdividing them would have been preferred. Is there any way to ‘encourage’ the mesher a little towards the later?
I see. You have a small triangular face in the input, with small edges, and it is meshed with even smaller edges. Unfortunately, that behavior is mandatory to ensure that the triangular face is well represented in the weighted Delaunay mesh.
As you are probably not interested by the Delaunay property, you may want to try remesh the result using Tetrahedral Remeshing. Please share the two input data meshes corresponding to that picture, and we will experiment.
Cc: @janetournois (author of Tetrahedral Remeshing in CGAL).
I have shared them though the following link. However, to avoid wasting your time for no reason a bit more context since I think Tetrahedral Remeshing may not offer an ideal solution.
Although for the meshes I just shared it’s not necessary since they are a trivial example using a cube, in general I pass on sizing fields (for cell, edge and facet size) when calling make_mesh_3
Under this conditions, can Tetrahedral Remeshing still offer some fix or would a different strategy be requried?
Hello, you are right about tetrahedral remeshing. It is only working for uniform remeshing, though we are planning to add a sizing field parameter in the future. The best solution for now would be to detect these small undesired features and fix them a priori with a "real" corner.
Issue Details
I am trying to generate a volume mesh from a surface obtained by performing a corefine_and_compute_intersection(). The mesh should preserve the relevant edges of the original geometry as well as those resulting from the intersection. While the code works if I don’t pass the edges of the original inputs, as soon as I pass the CGAL::parameters::edge_is_constrained_map() detected with detect_sharp_edges() for one of the original geometries the code either gets stuck or I get a segmentation fault (if there are edges from the corresponding map to preserve in the final geometry) . The code bellow and linked files (https://we.tl/t-3B38kjyeYw) should reproduce the issue.
Source Code
Environment