diff --git a/BGL/include/CGAL/boost/graph/boost_parameters_interface.h b/BGL/include/CGAL/boost/graph/boost_parameters_interface.h index f2e1939871c..8af0898910d 100644 --- a/BGL/include/CGAL/boost/graph/boost_parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/boost_parameters_interface.h @@ -20,4 +20,4 @@ // List the named parameters imported from boost we are using in CGAL CGAL_add_named_parameter(vertex_index_t, vertex_index, vertex_index_map) -CGAL_add_named_parameter(graph_visitor_t, visitor, visitor) +CGAL_add_named_parameter(graph_visitor_t, graph_visitor, visitor) diff --git a/BGL/include/CGAL/boost/graph/named_function_params.h b/BGL/include/CGAL/boost/graph/named_function_params.h index 095b122381a..205269a856d 100644 --- a/BGL/include/CGAL/boost/graph/named_function_params.h +++ b/BGL/include/CGAL/boost/graph/named_function_params.h @@ -88,7 +88,7 @@ enum all_default_t { all_default }; //cannot use macro because it takes no argum using boost::vertex_index_t; using boost::vertex_index; using boost::graph_visitor_t; -using boost::visitor; +using boost::graph_visitor; // define enum types and values for new named parameters #define CGAL_add_named_parameter(X, Y, Z) \ diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index d152be492c8..959f0fcd812 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -66,6 +66,9 @@ CGAL_add_named_parameter(outward_orientation_t, outward_orientation, outward_ori CGAL_add_named_parameter(overlap_test_t, overlap_test, do_overlap_test_of_bounded_sides) CGAL_add_named_parameter(preserve_genus_t, preserve_genus, preserve_genus) CGAL_add_named_parameter(projection_functor_t, projection_functor, projection_functor) +CGAL_add_named_parameter(throw_on_self_intersection_t, throw_on_self_intersection, throw_on_self_intersection) +CGAL_add_named_parameter(clip_volume_t, clip_volume, clip_volume) +CGAL_add_named_parameter(use_compact_clipper_t, use_compact_clipper, use_compact_clipper) // List of named parameters that we use in the package 'Surface Mesh Simplification' CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index fd1f631ea6b..5f0224806de 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -73,6 +73,9 @@ void test(const NamedParameters& np) assert(get_param(np, CGAL::internal_np::number_of_points_on_edges).v == 31); assert(get_param(np, CGAL::internal_np::nb_points_per_area_unit).v == 32); assert(get_param(np, CGAL::internal_np::nb_points_per_distance_unit).v == 33); + assert(get_param(np, CGAL::internal_np::throw_on_self_intersection).v == 43); + assert(get_param(np, CGAL::internal_np::clip_volume).v == 44); + assert(get_param(np, CGAL::internal_np::use_compact_clipper).v == 45); // Named parameters that we use in the package 'Surface Mesh Simplification' assert(get_param(np, CGAL::internal_np::get_cost_policy).v == 34); @@ -139,6 +142,9 @@ void test(const NamedParameters& np) check_same_type<31>(get_param(np, CGAL::internal_np::number_of_points_on_edges)); check_same_type<32>(get_param(np, CGAL::internal_np::nb_points_per_area_unit)); check_same_type<33>(get_param(np, CGAL::internal_np::nb_points_per_distance_unit)); + check_same_type<43>(get_param(np, CGAL::internal_np::throw_on_self_intersection)); + check_same_type<44>(get_param(np, CGAL::internal_np::clip_volume)); + check_same_type<45>(get_param(np, CGAL::internal_np::use_compact_clipper)); // Named parameters that we use in the package 'Surface Mesh Simplification' check_same_type<34>(get_param(np, CGAL::internal_np::get_cost_policy)); @@ -205,6 +211,9 @@ int main() .preserve_genus(A<40>(40)) .verbosity_level(A<41>(41)) .projection_functor(A<42>(42)) + .throw_on_self_intersection(A<43>(43)) + .clip_volume(A<44>(44)) + .use_compact_clipper(A<45>(45)) ); return EXIT_SUCCESS; diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index adc36f63d8f..5edfd16daa8 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -53,6 +53,17 @@ Release date: September 2018 ### Polygon Mesh Processing - Added a function to apply a transformation to a mesh: - `CGAL::Polygon_mesh_processing::transform()` +- Added in corefinement-related functions a new named parameter `visitor` + that makes it possible to pass a visitor to the function in order to track + the creation of new faces. +- Added in all corefinement-related functions a named parameter `throw_on_self_intersection` + (that replaces the `bool` parameter in `corefine()`) that enables to check for + self-intersecting faces involved in the intersection before trying to corefine the + input meshes. +- Added the function `corefine_and_compute_boolean_operations()` that can be used to + compute the result of several Boolean operations between 2 volumes at the same time. +- Added the function `clip()` that can be used to clip a triangulated surface mesh + by a plane or a clipping volume. - Fix a bug in `isotropic_remeshing()` making constrained vertices missing in the output - Guarantee that constrained vertices are kept in the mesh after calling `isotropic_remeshing()` diff --git a/Kernel_23/include/CGAL/Kernel/function_objects.h b/Kernel_23/include/CGAL/Kernel/function_objects.h index ec0681875e5..ef3224824ba 100644 --- a/Kernel_23/include/CGAL/Kernel/function_objects.h +++ b/Kernel_23/include/CGAL/Kernel/function_objects.h @@ -2049,6 +2049,20 @@ namespace CommonKernelFunctors { CGAL_assertion(e_pt!=NULL); return *e_pt; } + + Point + operator()(const Plane& plane, + const Point& l1, const Point& l2) const + { + Line line = construct_line( l1, l2 ); + + typename cpp11::result_of::type + res = typename K::Intersect_3()(plane,line); + CGAL_assertion(res!=boost::none); + const Point* e_pt = boost::get(&(*res)); + CGAL_assertion(e_pt!=NULL); + return *e_pt; + } }; template diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Concepts/PMPCorefinementVisitor.h b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Concepts/PMPCorefinementVisitor.h new file mode 100644 index 00000000000..7d95997ff80 --- /dev/null +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Concepts/PMPCorefinementVisitor.h @@ -0,0 +1,45 @@ +/// \ingroup PkgPolygonMeshProcessingConcepts +/// \cgalConcept +/// +/// The concept `PMPCorefinementVisitor` defines the requirements for the visitor +/// used in \link PMP_corefinement_grp corefinement-related functions \endlink to track +/// the creation of new faces. +/// +/// \cgalRefines `CopyConstructible` +/// \cgalHasModel `CGAL::Polygon_mesh_processing::Corefinement::Default_visitor`. + + +class PMPCorefinementVisitor{ +public: +/// Mesh type +typedef unspecified_type Triangle_mesh; +/// Face decriptor type +typedef unspecified_type face_descriptor; + +/// @name Functions used by corefine() +/// @{ + /// called before the triangulation of `f_split` in `tm`. Note that `f_split` + /// will be one of the faces of the triangulation. Each subsequent call to + /// `before_subface_created()`/`after_subface_created()` will correspond to + /// the creation of a new face of the triangulation of `f_split`. + void before_subface_creations(face_descriptor f_split, Triangle_mesh& tm); + /// called when the triangulation of a face in `tm` is finished + void after_subface_creations(Triangle_mesh& tm); + /// called before creating a new triangle face in `tm` to triangulate the face passed to `before_subface_creations()` + void before_subface_created(Triangle_mesh& tm); + /// called after creating a new triangle face `f_new` in `tm` to triangulate the face passed to `before_subface_creations()`. + /// Note that the call is placed just after a call to `add_face()` so the halfedge pointer is not set yet. + void after_subface_created(face_descriptor f_new, Triangle_mesh& tm); +/// @} + +/// @name Functions used by Boolean operations functions using corefinement. +/// These functions are not needed if you only call `corefine()`. +/// @{ + /// called before importing the face `f_src` of `tm_src` in `tm_tgt` + void before_face_copy(face_descriptor f_src, Triangle_mesh& tm_src, Triangle_mesh& tm_tgt); + /// called after importing the face `f_src` of `tm_src` in `tm_tgt`. The new face is `f_tgt`. + /// Note that the call is placed just after a call to `add_face()` so the halfedge pointer is not set yet. + void after_face_copy(face_descriptor f_src, Triangle_mesh& tm_src, + face_descriptor f_tgt, Triangle_mesh& tm_tgt); +/// @} +}; diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 22101de1caf..ceae2c24118 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -326,6 +326,38 @@ Parameter used in `isotropic_remeshing()` to specify an alternative vertex proje Default: A function object projecting vertices on the input surface. \cgalNPEnd +\cgalNPBegin{visitor} \anchor PMP_visitor +Parameter used to pass a visitor class to a function. Its type and behavior depend on the visited function. +\n +\b Type : `A class` \n +\b Default Specific to the function visited +\cgalNPEnd + +\cgalNPBegin{throw_on_self_intersection} \anchor PMP_throw_on_self_intersection +Parameter used in corefinement-related functions to make the functions throw an exception in +case some faces involved in the intersection of the input are self-intersecting +and make the operation impossible with the current version of the code. +\n +\b Type : `bool` \n +\b Default value is `false` +\cgalNPEnd + +\cgalNPBegin{clip_volume} \anchor PMP_clip_volume +Parameter used in `clip()` functions to clip a volume rather than a surface. +\n +\b Type : `bool` \n +\b Default value is `false` +\cgalNPEnd + +\cgalNPBegin{use_compact_clipper} \anchor PMP_use_compact_clipper +Parameter used in `clip()` functions to indicate whether the boundary of the clipper +should be considered as part of the clipping volume or not. +\n +\b Type : `bool` \n +\b Default value is `true` +\cgalNPEnd + + \cgalNPTableEnd */ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt index 80e65018910..92f4874bca2 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt @@ -145,8 +145,10 @@ and provides a list of the parameters that are used in this package. - `CGAL::Polygon_mesh_processing::corefine_and_compute_union()` - `CGAL::Polygon_mesh_processing::corefine_and_compute_difference()` - `CGAL::Polygon_mesh_processing::corefine_and_compute_intersection()` +- `CGAL::Polygon_mesh_processing::corefine_and_compute_boolean_operations()` - `CGAL::Polygon_mesh_processing::corefine()` - `CGAL::Polygon_mesh_processing::surface_intersection()` +- `CGAL::Polygon_mesh_processing::clip()` - `CGAL::Polygon_mesh_processing::does_bound_a_volume()` ## Geometric Measure Functions ## @@ -178,6 +180,4 @@ and provides a list of the parameters that are used in this package. - `CGAL::Polygon_mesh_processing::border_halfedges()` - `CGAL::Polygon_mesh_processing::transform()` -\todo add `clip_triangle_mesh()` using the code of `Polyhedron_place_clipping.h` and `PMP::corefine()` -\todo fix and restore remove_degenerate_faces in the user and the reference manual */ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt index 6482ee54e73..e6f3c49f3b9 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt @@ -215,9 +215,10 @@ Considering two triangulated surface meshes, each bounding a volume, the functions `CGAL::Polygon_mesh_processing::corefine_and_compute_union()`, `CGAL::Polygon_mesh_processing::corefine_and_compute_intersection()` and `CGAL::Polygon_mesh_processing::corefine_and_compute_difference()` respectively compute the union, -the intersection and the difference of the two volumes. -Note that there is no restriction on the topology of the input volumes. +the intersection and the difference of the two volumes. If several Boolean operations must be +computed at the same time, the function `corefine_and_compute_boolean_operations()` should be used. +There is no restriction on the topology of the input volumes. However, there are some requirements on the input to guarantee that the operation is possible. First, the input meshes must not self-intersect. Second, the operation is possible only if the output @@ -252,6 +253,28 @@ edges will be split at each intersection with a triangle but the position of the intersection point might create self-intersections due to the limited precision of floating point numbers. +\subsection coref_clip Clipping + +As a natural extension, some clipping functionalities with a volume bounded by a closed mesh +and a halfspace (defined by the negative side of a plane to be consistent with +the outward normal convention) are offered. The functions `CGAL::Polygon_mesh_processing::clip()` +have some options to select whether the clipping should be done at the volume or surface +level, and also if the clipper should be considered as compact or not. This is illustrated on +\cgalFigureRef{coref_clip_close_open} and \cgalFigureRef{coref_clip_compact}. + +\cgalFigureBegin{coref_clip_close_open, clip_open_close.png} +Clipping a cube with a halfspace. From left to right: (i) initial cube and the plane +defining the clipping halfspace; (ii) `close_volumes=false`: clipping of the surface mesh (boundary edges +depicted in red); (iii) `close_volumes=true`: clipping of the volume bounded by the surface mesh. +\cgalFigureEnd + +\cgalFigureBegin{coref_clip_compact, clip_compact.png} +Clipping a cube with a halfspace: compacity of the clipper (`close_volumes=false` in both cases). +From left to right: (i) initial cube and the plane defining the clipping halfspace, +note that a whole face of the cube (2 triangles) is exactly contained in the plane; +(ii) `use_compact_clipper=true`: clipping of the surface mesh with a compact halfspace: coplanar faces are part of the output; +(iii) `use_compact_clipper=false`: clipping of the surface mesh with a non-compact halfspace: coplanar faces are not part of the output. +\cgalFigureEnd \subsection coref_ex_subsec Examples diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt index c7a96cf3ec8..2811a37ecef 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt @@ -18,6 +18,7 @@ \example Poisson_surface_reconstruction_3/poisson_reconstruction_example.cpp \example Polygon_mesh_processing/corefinement_difference_remeshed.cpp \example Polygon_mesh_processing/corefinement_mesh_union.cpp +\example Polygon_mesh_processing/corefinement_mesh_union_and_intersection.cpp \example Polygon_mesh_processing/corefinement_consecutive_bool_op.cpp \example Polygon_mesh_processing/detect_features_example.cpp */ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/clip_compact.png b/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/clip_compact.png new file mode 100644 index 00000000000..6640346dd57 Binary files /dev/null and b/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/clip_compact.png differ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/clip_open_close.png b/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/clip_open_close.png new file mode 100644 index 00000000000..caf998a2d7b Binary files /dev/null and b/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/clip_open_close.png differ diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 2cffcc102aa..ed6a9a6e8f9 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -97,6 +97,8 @@ create_single_source_cgal_program( "corefinement_SM.cpp") create_single_source_cgal_program( "corefinement_consecutive_bool_op.cpp" ) create_single_source_cgal_program( "corefinement_difference_remeshed.cpp" ) create_single_source_cgal_program( "corefinement_mesh_union.cpp" ) +create_single_source_cgal_program( "corefinement_mesh_union_and_intersection.cpp" ) +create_single_source_cgal_program( "corefinement_mesh_union_with_attributes.cpp" ) create_single_source_cgal_program( "corefinement_polyhedron_union.cpp" ) create_single_source_cgal_program( "random_perturbation_SM_example.cpp" ) create_single_source_cgal_program( "corefinement_LCC.cpp") diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/corefinement_mesh_union_and_intersection.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/corefinement_mesh_union_and_intersection.cpp new file mode 100644 index 00000000000..1f2756a0da5 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/corefinement_mesh_union_and_intersection.cpp @@ -0,0 +1,73 @@ +#include +#include + +#include + +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; + +namespace PMP = CGAL::Polygon_mesh_processing; +namespace params = CGAL::Polygon_mesh_processing::parameters; + +int main(int argc, char* argv[]) +{ + const char* filename1 = (argc > 1) ? argv[1] : "data/blobby.off"; + const char* filename2 = (argc > 2) ? argv[2] : "data/eight.off"; + std::ifstream input(filename1); + + Mesh mesh1, mesh2; + if (!input || !(input >> mesh1)) + { + std::cerr << "First mesh is not a valid off file." << std::endl; + return 1; + } + input.close(); + input.open(filename2); + if (!input || !(input >> mesh2)) + { + std::cerr << "Second mesh is not a valid off file." << std::endl; + return 1; + } + + Mesh out_union, out_intersection; + CGAL::cpp11::array, 4> output; + output[PMP::Corefinement::UNION] = &out_union; + output[PMP::Corefinement::INTERSECTION] = &out_intersection; + + // for the example, we explicit the named parameters, this is identical to + // PMP::corefine_and_compute_boolean_operations(mesh1, mesh2, output) + CGAL::cpp11::array res = + PMP::corefine_and_compute_boolean_operations( + mesh1, mesh2, + output, + params::all_default(), // mesh1 named parameters + params::all_default(), // mesh2 named parameters + CGAL::cpp11::make_tuple( + params::vertex_point_map(get(boost::vertex_point, out_union)), // named parameters for out_union + params::vertex_point_map(get(boost::vertex_point, out_intersection)), // named parameters for out_intersection + params::all_default(), // named parameters for mesh1-mesh2 not used + params::all_default() )// named parameters for mesh2-mesh1 not used) + ); + + if (res[PMP::Corefinement::UNION]) + { + std::cout << "Union was successfully computed\n"; + std::ofstream output("union.off"); + output << out_union; + } + else + std::cout << "Union could not be computed\n"; + + if (res[PMP::Corefinement::INTERSECTION]) + { + std::cout << "Intersection was successfully computed\n"; + std::ofstream output("intersection.off"); + output << out_intersection; + } + else + std::cout << "Intersection could not be computed\n"; + + return 0; +} diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/corefinement_mesh_union_with_attributes.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/corefinement_mesh_union_with_attributes.cpp new file mode 100644 index 00000000000..bc6488d2b12 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/corefinement_mesh_union_with_attributes.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; + +namespace PMP = CGAL::Polygon_mesh_processing; + +struct Visitor : + public PMP::Corefinement::Default_visitor +{ + typedef Mesh::Face_index face_descriptor; + + boost::container::flat_map > properties; + int face_id; + + Visitor() + { + properties.reserve(3); + face_id=-1; + } + +// visitor API overloaded + void before_subface_creations(face_descriptor f_split,Mesh& tm) + { + face_id = properties[&tm][f_split]; + } + + void after_subface_created(face_descriptor f_new,Mesh& tm) + { + properties[&tm][f_new] = face_id; + } + + void after_face_copy(face_descriptor f_src, Mesh& tm_src, + face_descriptor f_tgt, Mesh& tm_tgt) + { + properties[&tm_tgt][f_tgt] = properties[&tm_src][f_src]; + } +}; + +int main(int argc, char* argv[]) +{ + const char* filename1 = (argc > 1) ? argv[1] : "data/blobby.off"; + const char* filename2 = (argc > 2) ? argv[2] : "data/eight.off"; + std::ifstream input(filename1); + + Mesh mesh1, mesh2; + if (!input || !(input >> mesh1)) + { + std::cerr << "First mesh is not a valid off file." << std::endl; + return 1; + } + input.close(); + input.open(filename2); + if (!input || !(input >> mesh2)) + { + std::cerr << "Second mesh is not a valid off file." << std::endl; + return 1; + } + + Mesh out; + + // add a property in each mesh to track the parent mesh for each face of the output + Mesh::Property_map + mesh1_id = mesh1.add_property_map("f:id", -1).first, + mesh2_id = mesh2.add_property_map("f:id", -1).first, + out_id = out.add_property_map("f:id", -1).first; + + // init the face ids (for the purpose of the example but choosing 1 (2) as default value of the map would avoid the loop) + BOOST_FOREACH(Mesh::Face_index f, faces(mesh1)) + mesh1_id[f] = 1; + BOOST_FOREACH(Mesh::Face_index f, faces(mesh2)) + mesh2_id[f] = 2; + + Visitor visitor; + visitor.properties[&mesh1] = mesh1_id; + visitor.properties[&mesh2] = mesh2_id; + visitor.properties[&out] = out_id; + + bool valid_union = PMP::corefine_and_compute_union(mesh1, mesh2, out, PMP::parameters::visitor(visitor)); + + BOOST_FOREACH(Mesh::Face_index f, faces(mesh1)) + assert( mesh1_id[f] == 1 ); + BOOST_FOREACH(Mesh::Face_index f, faces(mesh2)) + assert( mesh2_id[f] == 2 ); + BOOST_FOREACH(Mesh::Face_index f, faces(out)) + assert( out_id[f]==1 || out_id[f]==2); + + if (valid_union) + { + std::cout << "Union was successfully computed\n"; + std::ofstream output("union.off"); + output << out; + return 0; + } + + std::cout << "Union could not be computed\n"; + return 1; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h new file mode 100644 index 00000000000..d33dde326a2 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h @@ -0,0 +1,589 @@ +// Copyright (c) 2016 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0+ +// +// +// Author(s) : Sebastien Loriot + +#ifndef CGAL_POLYGON_MESH_PROCESSING_CLIP_H +#define CGAL_POLYGON_MESH_PROCESSING_CLIP_H + +#include + + +#include +#include +#include +#include + +#include + +namespace CGAL{ +namespace Polygon_mesh_processing { + +namespace internal +{ +template +bool +clip_open_impl( TriangleMesh& tm, + TriangleMesh& clipper, + const NamedParameters1& np_tm, + const NamedParameters2& np_c) +{ + typedef typename GetVertexPointMap::type Vpm; + typedef typename GetGeomTraits::type GeomTraits; + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::face_descriptor face_descriptor; + +// First build an AABB-tree of the clipper triangles as it will be modified + typedef std::vector Clipper_triangles; + typedef typename Clipper_triangles::iterator Tr_iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits AABB_triangle_traits; + typedef CGAL::AABB_tree Clipper_tree; + + // vector of clipper triangles + Clipper_triangles clipper_triangles; + clipper_triangles.reserve( num_faces(clipper) ); + Vpm vpm_c = boost::choose_param(boost::get_param(np_c, internal_np::vertex_point), + get_property_map(vertex_point, clipper)); + BOOST_FOREACH(face_descriptor f, faces(clipper)) + { + halfedge_descriptor h = halfedge(f, clipper); + clipper_triangles.push_back( typename GeomTraits::Triangle_3( + get(vpm_c, source(h, clipper)), + get(vpm_c, target(h, clipper)), + get(vpm_c, target(next(h, clipper), clipper)) ) ); + } + // tree + Clipper_tree clipper_tree(clipper_triangles.begin(), clipper_triangles.end()); + // predicate functor + Side_of_triangle_mesh side_of(clipper_tree); + +// Second corefine the meshes + typedef CGAL::dynamic_edge_property_t Ecm_tag; + typedef typename boost::property_map::type Ecm; + Ecm ecm = get(Ecm_tag(), tm); + + corefine(tm, clipper, np_tm.edge_is_constrained_map(ecm), np_c); + +// Extract connected components + typedef typename GetFaceIndexMap::type Fid_map; + + Fid_map fid_map = boost::choose_param(boost::get_param(np_tm, internal_np::face_index), + get_property_map(boost::face_index, tm)); + Vpm vpm1 = boost::choose_param(boost::get_param(np_tm, internal_np::vertex_point), + get_property_map(vertex_point, tm)); + + typedef CGAL::dynamic_vertex_property_t Vid_tag; + typedef typename boost::property_map::type Vid_map; + Vid_map vid_map = get(Vid_tag(), tm); + + // init indices if needed + helpers::init_face_indices(tm, fid_map); + helpers::init_vertex_indices(tm, vid_map); + + // set the connected component id of each face + std::vector face_cc(num_faces(tm), std::size_t(-1)); + std::size_t nb_cc = + connected_components(tm, + bind_property_maps(fid_map, make_property_map(face_cc)), + parameters::face_index_map(fid_map). + edge_is_constrained_map(ecm)); + + + boost::dynamic_bitset<> cc_not_handled(nb_cc); + cc_not_handled.set(); + std::vector ccs_to_remove; + + BOOST_FOREACH(face_descriptor f, faces(tm)) + { + std::size_t cc_id = face_cc[ get(fid_map, f) ]; + if ( !cc_not_handled.test(cc_id) ) continue; + + halfedge_descriptor h=halfedge(f, tm); + for(int i=0;i<3;++i) + { + // look for a vertex not on a constrained edge + bool no_marked_edge=true; + BOOST_FOREACH(halfedge_descriptor h2, halfedges_around_target(h, tm)) + if ( get(ecm, edge(h2, tm)) ){ + no_marked_edge=false; + break; + } + if (no_marked_edge){ + if ( side_of( get(vpm1, target(h, tm) ) ) == ON_UNBOUNDED_SIDE ) + ccs_to_remove.push_back(cc_id); + cc_not_handled.reset(cc_id); + break; + } + h=next(h, tm); + } + if (!cc_not_handled.any()) break; + } + + if (cc_not_handled.any()) + { + // A patch without no vertex incident to a non-constrained edges + // is a coplanar patch: drop it or keep it! + if (!boost::choose_param(boost::get_param(np_tm, internal_np::use_compact_clipper), true)) + { + for (std::size_t cc_id = cc_not_handled.find_first(); + cc_id < cc_not_handled.npos; + cc_id = cc_not_handled.find_next(cc_id)) + { + ccs_to_remove.push_back(cc_id); + } + } + } +// Filter out the cc + remove_connected_components(tm, + ccs_to_remove, + bind_property_maps(fid_map, make_property_map(face_cc)), + parameters::vertex_index_map(vid_map)); + + return true; +} + +template +int +inter_pt_index(int i, int j, + const Plane_3& plane, + std::vector& points, + std::map, int>& id_map) +{ + std::pair, int>::iterator, bool> res = + id_map.insert(std::make_pair(make_sorted_pair(i,j), + static_cast (points.size()))); + if (res.second) + points.push_back( + typename Geom_traits::Construct_plane_line_intersection_point_3() + (plane, points[i], points[j]) ); + + return res.first->second; +} + +template +Oriented_side +clip_to_bbox(const Plane_3& plane, + const Bbox_3& bbox, + TriangleMesh& tm_out, + const NamedParameters& np ) +{ + typedef typename GetGeomTraits::type Geom_traits; + typedef typename Geom_traits::Point_3 Point_3; + typedef typename GetVertexPointMap::type Vpm; + + Vpm vpm_out = boost::choose_param(boost::get_param(np, internal_np::vertex_point), + get_property_map(boost::vertex_point, tm_out)); + + + std::vector corners(8); + corners[0] = Point_3(bbox.xmin(),bbox.ymin(),bbox.zmin()); + corners[1] = Point_3(bbox.xmin(),bbox.ymax(),bbox.zmin()); + corners[2] = Point_3(bbox.xmax(),bbox.ymax(),bbox.zmin()); + corners[3] = Point_3(bbox.xmax(),bbox.ymin(),bbox.zmin()); + corners[4] = Point_3(bbox.xmin(),bbox.ymin(),bbox.zmax()); + corners[5] = Point_3(bbox.xmin(),bbox.ymax(),bbox.zmax()); + corners[6] = Point_3(bbox.xmax(),bbox.ymax(),bbox.zmax()); + corners[7] = Point_3(bbox.xmax(),bbox.ymin(),bbox.zmax()); + + cpp11::array orientations = {{ + plane.oriented_side(corners[0]), + plane.oriented_side(corners[1]), + plane.oriented_side(corners[2]), + plane.oriented_side(corners[3]), + plane.oriented_side(corners[4]), + plane.oriented_side(corners[5]), + plane.oriented_side(corners[6]), + plane.oriented_side(corners[7]) + }}; + + // description of faces of the bbox + cpp11::array face_indices = + {{ 0, 1, 2, 3, + 2, 1, 5, 6, + 3, 2, 6, 7, + 1, 0, 4, 5, + 4, 0, 3, 7, + 6, 5, 4, 7 }}; + + std::map, int> id_map; + std::vector< std::vector > output_faces(6); + bool all_in = true; + bool all_out = true; + std::set in_point_ids; // to collect the set of points in the clipped bbox + + // for each face of the bbox, we look for intersection of the plane with its edges + for (int i=0; i<6; ++i) + { + for (int k=0; k< 4; ++k) + { + int current_id = face_indices[4*i + k]; + int next_id = face_indices[4*i + (k+1)%4]; + + if ( orientations[ current_id ] != ON_POSITIVE_SIDE ) + { + all_out=false; + // point on or on the negative side + output_faces[i].push_back( current_id ); + in_point_ids.insert( output_faces[i].back() ); + // check for intersection of the edge + if (orientations[ current_id ] == ON_NEGATIVE_SIDE && + orientations[ next_id ] == ON_POSITIVE_SIDE) + { + output_faces[i].push_back( + inter_pt_index(current_id, next_id, plane, corners, id_map) ); + in_point_ids.insert( output_faces[i].back() ); + } + } + else + { + all_in = false; + // check for intersection of the edge + if ( orientations[ next_id ] == ON_NEGATIVE_SIDE ) + { + output_faces[i].push_back( + inter_pt_index(current_id, next_id, plane, corners, id_map) ); + in_point_ids.insert( output_faces[i].back() ); + } + } + } + CGAL_assertion( output_faces[i].empty() || output_faces[i].size() >= 3 ); + } + + // the intersection is the full bbox + if (all_in) return ON_NEGATIVE_SIDE; + if (all_out) return ON_POSITIVE_SIDE; + + // build the clipped bbox + typedef boost::graph_traits graph_traits; + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + typedef typename graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename graph_traits::face_descriptor face_descriptor; + + std::map out_vertices; + BOOST_FOREACH(int i, in_point_ids) + { + vertex_descriptor v = add_vertex(tm_out); + out_vertices.insert( std::make_pair(i, v ) ); + put(vpm_out, v, corners[i]); + } + + std::map< std::pair, halfedge_descriptor> hedge_map; + const halfedge_descriptor null_hedge = graph_traits::null_halfedge(); + const face_descriptor null_fd = graph_traits::null_face(); + BOOST_FOREACH( const std::vector& findices, output_faces) + { + if (findices.empty()) continue; + const face_descriptor fd=add_face(tm_out); + int prev_id = findices.back(); + + // create of recover face boundary halfedges + std::vector hedges; + hedges.reserve(findices.size()); + BOOST_FOREACH( int current_id, findices) + { + vertex_descriptor src = out_vertices[prev_id], tgt = out_vertices[current_id]; + + std::pair, + halfedge_descriptor>::iterator, bool> res = + hedge_map.insert( std::make_pair(std::make_pair(prev_id, current_id), null_hedge) ); + if (res.second) + { + res.first->second = halfedge( add_edge(tm_out), tm_out); + hedge_map.insert( std::make_pair(std::make_pair(current_id, prev_id), + opposite(res.first->second, tm_out) ) ); + set_face(opposite(res.first->second, tm_out), null_fd, tm_out); + + } + hedges.push_back(res.first->second); + // set edge source and target + set_target(hedges.back(), tgt, tm_out); + set_target(opposite(hedges.back(), tm_out), src, tm_out); + // set face pointer of halfedges + set_face(hedges.back(), fd, tm_out); + // set vertex halfedge + set_halfedge(src, opposite(hedges.back(), tm_out), tm_out); + set_halfedge(tgt, hedges.back(), tm_out); + + if (current_id==findices.front()) + set_halfedge(fd, hedges.back(), tm_out); + + prev_id = current_id; + } + CGAL_assertion(hedges.size() == findices.size()); + + // set next/prev relationship + halfedge_descriptor prev_h=hedges.back(); + BOOST_FOREACH(halfedge_descriptor h, hedges) + { + set_next(prev_h, h, tm_out); + prev_h = h; + } + } + + // handle the face of the plane: + // look for a border halfedge and reconstruct the face of the plane + // by turning around vertices inside the mesh constructed above + // until we reach another border halfedge + BOOST_FOREACH(halfedge_descriptor h, halfedges(tm_out)) + { + if (face(h, tm_out) == null_fd) + { + face_descriptor fd = add_face(tm_out); + set_halfedge(fd, h, tm_out); + + halfedge_descriptor h_prev=h; + halfedge_descriptor h_curr=h; + do{ + h_curr=opposite(h_curr, tm_out); + do{ + h_curr=opposite(prev(h_curr, tm_out), tm_out); + } while(face(h_curr, tm_out) != null_fd && h_curr!=h); + set_face(h_prev, fd, tm_out); + set_next(h_prev, h_curr, tm_out); + if (h_curr==h) + break; + h_prev=h_curr; + } while(true); + break; + } + } + CGAL_assertion(is_valid_polygon_mesh(tm_out)); + + // triangulate the faces + CGAL::Polygon_mesh_processing::triangulate_faces(tm_out, np); + + return ON_ORIENTED_BOUNDARY; +} + +} // end of internal namespace + +/** + * \ingroup PMP_corefinement_grp + * clips `tm` by keeping the part that is inside the volume \link coref_def_subsec bounded \endlink + * by `clipper`. + * If `tm` is closed, the clipped part can be closed too if the named parameter `clip_volume` is set to `true`. + * See subsection \ref coref_clip for more details. + * \attention With the current implementation, `clipper` will be modified (refined with the intersection with `tm`). + * + * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm1)` \endlink + * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(clipper)` \endlink + * \pre \link CGAL::Polygon_mesh_processing::does_bound_a_volume() `CGAL::Polygon_mesh_processing::does_bound_a_volume(clipper)` \endlink + * + * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. + * If `TriangleMesh` has an internal property map for `CGAL::face_index_t`, + * as a named parameter, then it must be initialized. + * + * @tparam NamedParameters1 a sequence of \ref pmp_namedparameters "Named Parameters" + * @tparam NamedParameters2 a sequence of \ref pmp_namedparameters "Named Parameters" + * + * @param tm input triangulated surface mesh + * @param clipper triangulated surface mesh used to clip `tm` + * @param np_tm optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * @param np_c optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamBegin{vertex_point_map} + * the property map with the points associated to the vertices of `tm` (`clipper`). + * If this parameter is omitted, an internal property map for + * `CGAL::vertex_point_t` must be available in `TriangleMesh` + * \cgalParamEnd + * \cgalParamBegin{face_index_map} a property map containing the index of each face of `tm` (`clipper`). + * Note that if the property map is writable, the indices of the faces + * of `tm` and `clipper` will be set after the refining `tm` with the intersection with `plane`. + * \cgalParamEnd + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces. + * \cgalParamEnd + * \cgalParamBegin{throw_on_self_intersection} if `true`, + * the set of triangles closed to the intersection of `tm` and `clipper` will be + * checked for self-intersections and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` + * will be thrown if at least one is found. + * \cgalParamEnd + * \cgalParamBegin{clip_volume} if `true` and `tm` is closed, the clipping will be done on + * the volume \link coref_def_subsec bounded \endlink by `tm` rather than on its surface + * (i.e. `tm` will be kept closed). + * \cgalParamEnd + * \cgalParamBegin{use_compact_clipper} if `false` and `clip_volume` is `false` and `tm` is open, the parts of `tm` coplanar with `clipper` + * will not be part of the output. + * \cgalParamEnd + * \cgalNamedParamsEnd + * + * @return `true` if the output surface mesh is manifold. + * If `false` is returned `tm` and `clipper` are only corefined. + */ +template +bool +clip( TriangleMesh& tm, + TriangleMesh& clipper, + const NamedParameters1& np_tm, + const NamedParameters2& np_c) +{ + const bool close = + boost::choose_param(boost::get_param(np_tm, internal_np::clip_volume), false); + + if (close && is_closed(tm)) + return corefine_and_compute_intersection(tm, clipper, tm, np_tm, np_c); + + return internal::clip_open_impl(tm, clipper, np_tm, np_c); +} + +namespace internal{ +template +bool dispatch_clip_call(TriangleMesh& tm, TriangleMesh& clipper, + const NamedParameters& np, Tag_false) +{ + return clip(tm, clipper, + np.face_index_map(get(CGAL::dynamic_face_property_t(), tm)), + parameters::face_index_map(get(CGAL::dynamic_face_property_t(), clipper))); +} + +template +bool dispatch_clip_call(TriangleMesh& tm, TriangleMesh& clipper, + const NamedParameters& np, Tag_true) +{ + return clip(tm, clipper, + np.face_index_map(get(face_index, tm)), + parameters::face_index_map(get(face_index, clipper))); +} +} + +/** + * \ingroup PMP_corefinement_grp + * clips `tm` by keeping the part that is on the negative side of `plane` (side opposite to its normal vector). + * If `tm` is closed, the clipped part can be closed too if the named parameter `clip_volume` is set to `true`. + * See subsection \ref coref_clip for more details. + * + * \note In the current implementation it is not possible to set the vertex point map and the default will be used. + * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink + * + * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. + * If `TriangleMesh` has an internal property map for `CGAL::face_index_t`, + * as a named parameter, then it must be initialized. + * An internal property map for `CGAL::vertex_point_t` must be available. + * + * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" + * + * @param tm input triangulated surface mesh + * @param plane plane whose negative side defines the half-space to intersect `tm` with. + * `Plane_3` is the plane type for the same CGAL kernel as the point of the vertex point map of `tm`. + * @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces. + * \cgalParamEnd + * \cgalParamBegin{throw_on_self_intersection} if `true`, + * the set of triangles closed to the intersection of `tm` and `plane` will be + * checked for self-intersections and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` + * will be thrown if at least one is found. + * \cgalParamEnd + * \cgalParamBegin{clip_volume} if `true` and `tm` is closed, the clipping will be done on + * the volume \link coref_def_subsec bounded \endlink by `tm` rather than on its surface + * (i.e. `tm` will be kept closed). + * \cgalParamEnd + * \cgalParamBegin{use_compact_clipper} if `false` and `clip_volume` is `false` and `tm` is open, the parts of `tm` coplanar with `plane` + * will not be part of the output. + * \cgalNamedParamsEnd + * + * @return `true` if the output surface mesh is manifold. + * If `false` is returned `tm` is only refined by the intersection with `plane`. + */ +template +bool clip( TriangleMesh& tm, + #ifdef DOXYGEN_RUNNING + const Plane_3& plane, + #else + const typename GetGeomTraits::type::Plane_3& plane, + #endif + const NamedParameters& np) +{ + if( boost::begin(faces(tm))==boost::end(faces(tm)) ) return true; + + CGAL::Bbox_3 bbox = ::CGAL::Polygon_mesh_processing::bbox(tm); + + //extend the bbox a bit to avoid border cases + double xd=(bbox.xmax()-bbox.xmin())/100; + double yd=(bbox.ymax()-bbox.ymin())/100; + double zd=(bbox.zmax()-bbox.zmin())/100; + bbox=CGAL::Bbox_3(bbox.xmin()-xd, bbox.ymin()-yd, bbox.zmin()-zd, + bbox.xmax()+xd, bbox.ymax()+yd, bbox.zmax()+zd); + TriangleMesh clipper; + Oriented_side os = internal::clip_to_bbox(plane, bbox, clipper, parameters::all_default()); + + switch(os) + { + case ON_NEGATIVE_SIDE: + return true; // nothing to clip, the full mesh is on the negative side + case ON_POSITIVE_SIDE: + clear(tm); // clear the mesh that is fully on the positive side + return true; + default: + break; + } + // dispatch is needed because face index map for tm and clipper have to be of the same time + return internal::dispatch_clip_call(tm, clipper, + np, CGAL::graph_has_property()); +} + +/// \cond SKIP_IN_MANUAL + +// convenience overloads +template +bool clip( TriangleMesh& tm, + const typename GetGeomTraits::type::Plane_3& plane) +{ + return clip(tm, plane, parameters::all_default()); +} + +// convenience overload +template +bool +clip( TriangleMesh& tm, + TriangleMesh& clipper, + const NamedParameters1& np_tm) +{ + return clip(tm, clipper, np_tm, parameters::all_default()); +} + +// convenience overload +template +bool +clip( TriangleMesh& tm, + TriangleMesh& clipper) +{ + return clip(tm, clipper, parameters::all_default()); +} +/// \endcond + +} } //end of namespace CGAL::Polygon_mesh_processing + +#endif // CGAL_POLYGON_MESH_PROCESSING_CLIP_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index b92329c24d4..38c8fbb1058 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -34,6 +34,13 @@ #include namespace CGAL { + +#if !defined(CGAL_NO_DEPRECATED_CODE) && !defined(DOXYGEN_RUNNING) +namespace Corefinement { +using Polygon_mesh_processing::Corefinement::Self_intersection_exception; +} +#endif + namespace Polygon_mesh_processing { namespace internal { @@ -125,14 +132,32 @@ bool recursive_does_bound_a_volume(const TriangleMesh& tm, } //end of namespace internal +namespace Corefinement +{ +/** \ingroup PMP_corefinement_grp + * Default new-face visitor model of `PMPCorefinementVisitor`. + * All of its functions have an empty body. This class can be used as a + * base class if only some of the functions of the concept require to be + * overridden. + */ +template +struct Default_visitor; + +#ifdef DOXYGEN_RUNNING +/** \ingroup PMP_corefinement_grp + * Integer identifiers to refer to a particular Boolean operation in the function `corefine_and_compute_boolean_operations()`. + */ +enum Boolean_operation_type {UNION = 0, INTERSECTION=1, + TM1_MINUS_TM2=2, TM2_MINUS_TM1=3, NONE }; +#endif +} + /** \ingroup PMP_corefinement_grp * * indicates if `tm` bounds a volume. * See \ref coref_def_subsec for details. * * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. - * If `TriangleMesh` has an internal property map for `CGAL::face_index_t`, - * as a named parameter, then it must be initialized. * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" * * @param tm a closed triangulated surface mesh @@ -168,10 +193,10 @@ bool does_bound_a_volume(const TriangleMesh& tm, const NamedParameters& np) if (!is_closed(tm)) return false; if (!is_triangle_mesh(tm)) return false; - Vpm vpm = boost::choose_param(get_param(np, internal_np::vertex_point), + Vpm vpm = boost::choose_param(boost::get_param(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); - Fid_map fid_map = boost::choose_param(get_param(np, internal_np::face_index), + Fid_map fid_map = boost::choose_param(boost::get_param(np, internal_np::face_index), get_const_property_map(boost::face_index, tm)); std::vector face_cc(num_faces(tm), std::size_t(-1)); @@ -221,17 +246,7 @@ bool does_bound_a_volume(const TriangleMesh& tm) { return does_bound_a_volume(tm, parameters::all_default()); } - -#define CGAL_COREF_SET_OUTPUT_VERTEX_POINT_MAP(i) \ - if (desired_output[i]!=boost::none) \ - { \ - vpm_out.push_back( \ - boost::choose_param(get_param(cpp11::get(nps_out), internal_np::vertex_point), \ - get_property_map(boost::vertex_point, *(*desired_output[i])))); \ - output_vpms[i]=&vpm_out.back(); \ - } \ - else \ - output_vpms[i]=NULL; +/// \endcond #define CGAL_COREF_SET_OUTPUT_EDGE_MARK_MAP(I) \ typedef typename boost::lookup_named_param_def < \ @@ -240,13 +255,92 @@ bool does_bound_a_volume(const TriangleMesh& tm) Corefinement::No_mark \ > ::type Ecm_out_##I; \ Ecm_out_##I ecm_out_##I = \ - boost::choose_param( get_param(cpp11::get(nps_out), internal_np::edge_is_constrained), \ + boost::choose_param( boost::get_param(cpp11::get(nps_out), internal_np::edge_is_constrained), \ Corefinement::No_mark() ); /** - \todo document me - */ + * \ingroup PMP_corefinement_grp + * \link coref_def_subsec corefines \endlink `tm1` and `tm2` and for each triangle mesh `tm_out` passed + * as an optional in `output` different from `boost::none`, the triangulated surface mesh + * \link coref_def_subsec bounding \endlink the result of a particular Boolean operation + * between the volumes bounded by `tm1` and `tm2` will be put in the corresponding triangle mesh. + * The positions of the meshes in the array `output` are specific to the Boolean operation to compute + * and `Corefinement::Boolean_operation_type` encodes and describes the ordering. Constructing the default array + * means that no Boolean operation will be done. Overwriting a default value will trigger the corresponding + * operation. In such a case, the address to a valid surface mesh must be provided. + * The optional named parameters for all output meshes are provided as a `tuple` and follow the same + * order as the array `output`. A call to `corefine_and_compute_boolean_operations()` with optional + * named parameters passed for output meshes should be done using `make_tuple()` as the types of + * named parameters are unspecified. + * + * If `tm1` and/or `tm2` are part of the output surface meshes, they will be updated to + * contain the output (in-place operation), in any other case, the corresponding result will + * be inserted into the mesh without clearing it first. + * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm1)` \endlink + * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm2)` \endlink + * \pre \link CGAL::Polygon_mesh_processing::does_bound_a_volume() `CGAL::Polygon_mesh_processing::does_bound_a_volume(tm1)` \endlink + * \pre \link CGAL::Polygon_mesh_processing::does_bound_a_volume() `CGAL::Polygon_mesh_processing::does_bound_a_volume(tm2)` \endlink + * + * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph` + * @tparam NamedParameters1 a sequence of \ref pmp_namedparameters "Named Parameters" + * @tparam NamedParameters2 a sequence of \ref pmp_namedparameters "Named Parameters" + * @tparam NamedParametersOut0 a sequence of \ref pmp_namedparameters "Named Parameters" for computing the union of the volumes bounded by `tm1` and `tm2` + * @tparam NamedParametersOut1 a sequence of \ref pmp_namedparameters "Named Parameters" for computing the intersection of the volumes bounded by `tm1` and `tm2` + * @tparam NamedParametersOut2 a sequence of \ref pmp_namedparameters "Named Parameters" for computing the difference of the volumes bounded by `tm1` and `tm2` + * @tparam NamedParametersOut3 a sequence of \ref pmp_namedparameters "Named Parameters" for computing the difference of the volumes bounded by `tm2` and `tm1` + * + * @param tm1 first input triangulated surface mesh + * @param tm2 second input triangulated surface mesh + * @param output an array of output surface meshes + * @param np1 optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * @param np2 optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamBegin{vertex_point_map} + * the property map with the points associated to the vertices of `tm1` (`tm2`). + * If this parameter is omitted, an internal property map for + * `CGAL::vertex_point_t` should be available in `TriangleMesh` + * \cgalParamEnd + * \cgalParamBegin{edge_is_constrained_map} a property map containing the + * constrained-or-not status of each edge of `tm1` (`tm2`). + * \cgalParamEnd + * \cgalParamBegin{face_index_map} a property map containing the index of each face of `tm1` (`tm2`). + * Note that if the property map is writable, the indices of the faces + * of `tm1` and `tm2` will be set after the corefinement is done. + * \cgalParamEnd + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces (`np1` only) + * \cgalParamEnd + * \cgalParamBegin{throw_on_self_intersection} if `true`, for each input triangle mesh, + * the set of triangles close to the intersection of `tm1` and `tm2` will be + * checked for self-intersection and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` + * will be thrown if at least one is found (`np1` only). + * \cgalParamEnd + * \cgalNamedParamsEnd + * + * @param nps_out tuple of optional sequences of \ref pmp_namedparameters "Named Parameters" each among the ones listed below + * (`tm_out` being used to refer to the output surface mesh in `output` corresponding to a given named parameter sequence) + * + * \cgalNamedParamsBegin + * \cgalParamBegin{vertex_point_map} + * the property map with the points associated to the vertices of `tm_out`. + * If this parameter is omitted, an internal property map for + * `CGAL::vertex_point_t` must be available in `TriangleMesh` + * \cgalParamEnd + * \cgalParamBegin{edge_is_constrained_map} a property map containing the + * constrained-or-not status of each edge of `tm_out`. An edge of `tm_out` is constrained + * if it is on the intersection of `tm1` and `tm2`, or if the edge corresponds to a + * constrained edge in `tm1` or `tm2`. + * \cgalParamEnd + * \cgalNamedParamsEnd + * + * @return an array filled as follows: for each operation computed, the position in the array + * will contain `true` iff the output surface mesh is manifold, and it is put in the surface mesh + * at the same position as in `output`. Note that if an output surface mesh also was + * an input mesh but the output operation was generating a non-manifold mesh, the surface mesh + * will only be corefined. + */ template cpp11::array -boolean_operation( TriangleMesh& tm1, - TriangleMesh& tm2, - const cpp11::array< boost::optional,4>& desired_output, - const NamedParameters1& np1, - const NamedParameters2& np2, - const cpp11::tuple& nps_out, - const bool throw_on_self_intersection = false ) +corefine_and_compute_boolean_operations( + TriangleMesh& tm1, + TriangleMesh& tm2, + const cpp11::array< boost::optional,4>& output, + const NamedParameters1& np1, + const NamedParameters2& np2, + const cpp11::tuple& nps_out) { + const bool throw_on_self_intersection = + boost::choose_param(boost::get_param(np1, internal_np::throw_on_self_intersection), false); // Vertex point maps //for input meshes @@ -278,52 +374,74 @@ boolean_operation( TriangleMesh& tm1, static const bool same_vpm = (boost::is_same::value); ) CGAL_static_assertion(same_vpm); - Vpm vpm1 = boost::choose_param(get_param(np1, internal_np::vertex_point), + Vpm vpm1 = boost::choose_param(boost::get_param(np1, internal_np::vertex_point), get_property_map(boost::vertex_point, tm1)); - Vpm vpm2 = boost::choose_param(get_param(np2, internal_np::vertex_point), + Vpm vpm2 = boost::choose_param(boost::get_param(np2, internal_np::vertex_point), get_property_map(boost::vertex_point, tm2)); - //for output meshes - cpp11::array output_vpms; - std::vector vpm_out; - vpm_out.reserve(4); - CGAL_COREF_SET_OUTPUT_VERTEX_POINT_MAP(0) - CGAL_COREF_SET_OUTPUT_VERTEX_POINT_MAP(1) - CGAL_COREF_SET_OUTPUT_VERTEX_POINT_MAP(2) - CGAL_COREF_SET_OUTPUT_VERTEX_POINT_MAP(3) + typedef typename boost::property_traits::value_type Point_3; + + // for output meshes: here we have to use a trick so that if for a specific output + // that is not requested, the default vpm does not have the same value type as the + // input map, a dummy default vpm is used so that calls to get/put can be compiled + // (even if not used). + typedef cpp11::tuple< + Corefinement::TweakedGetVertexPointMap, + Corefinement::TweakedGetVertexPointMap, + Corefinement::TweakedGetVertexPointMap, + Corefinement::TweakedGetVertexPointMap + > Vpm_out_tuple_helper; + + typedef cpp11::tuple< + boost::optional< typename cpp11::tuple_element<0, Vpm_out_tuple_helper>::type::type >, + boost::optional< typename cpp11::tuple_element<1, Vpm_out_tuple_helper>::type::type >, + boost::optional< typename cpp11::tuple_element<2, Vpm_out_tuple_helper>::type::type >, + boost::optional< typename cpp11::tuple_element<3, Vpm_out_tuple_helper>::type::type > + > Vpm_out_tuple; + + Vpm_out_tuple vpm_out_tuple( + Corefinement::get_vpm(cpp11::get<0>(nps_out), output[0], + typename cpp11::tuple_element<0, Vpm_out_tuple_helper>::type::Use_default_tag()), + Corefinement::get_vpm(cpp11::get<1>(nps_out), output[1], + typename cpp11::tuple_element<1, Vpm_out_tuple_helper>::type::Use_default_tag()), + Corefinement::get_vpm(cpp11::get<2>(nps_out), output[2], + typename cpp11::tuple_element<2, Vpm_out_tuple_helper>::type::Use_default_tag()), + Corefinement::get_vpm(cpp11::get<3>(nps_out), output[3], + typename cpp11::tuple_element<3, Vpm_out_tuple_helper>::type::Use_default_tag()) + ); if (&tm1==&tm2) { // for now edges in a coplanar patch are not constrained so there is nothing to constrained here // \todo marked edges from input to output are not ported - if (desired_output[Corefinement::UNION] != boost::none) - if (&tm1 != *desired_output[Corefinement::UNION]) + if (output[Corefinement::UNION] != boost::none) + if (&tm1 != *output[Corefinement::UNION]) copy_face_graph(tm1, - *(*desired_output[Corefinement::UNION]), + *(*output[Corefinement::UNION]), Emptyset_iterator(), Emptyset_iterator(), Emptyset_iterator(), vpm1, - vpm_out[Corefinement::UNION]); + *cpp11::get(vpm_out_tuple)); - if (desired_output[Corefinement::INTER] != boost::none) - if (&tm1 != *desired_output[Corefinement::INTER]) + if (output[Corefinement::INTERSECTION] != boost::none) + if (&tm1 != *output[Corefinement::INTERSECTION]) copy_face_graph(tm1, - *(*desired_output[Corefinement::INTER]), + *(*output[Corefinement::INTERSECTION]), Emptyset_iterator(), Emptyset_iterator(), Emptyset_iterator(), vpm1, - vpm_out[Corefinement::INTER]); + *cpp11::get(vpm_out_tuple)); - if (desired_output[Corefinement::TM1_MINUS_TM2] != boost::none) - if (&tm1 == *desired_output[Corefinement::TM1_MINUS_TM2]) + if (output[Corefinement::TM1_MINUS_TM2] != boost::none) + if (&tm1 == *output[Corefinement::TM1_MINUS_TM2]) clear(tm1); - if (desired_output[Corefinement::TM2_MINUS_TM1] != boost::none) - if (&tm1 == *desired_output[Corefinement::TM2_MINUS_TM1]) + if (output[Corefinement::TM2_MINUS_TM1] != boost::none) + if (&tm1 == *output[Corefinement::TM2_MINUS_TM1]) clear(tm1); return CGAL::make_array(true, true, true, true); @@ -370,30 +488,38 @@ boolean_operation( TriangleMesh& tm1, static const bool same_fidmap = (boost::is_same::value);) CGAL_static_assertion(same_fidmap); - Fid_map fid_map1 = boost::choose_param(get_param(np1, internal_np::face_index), + Fid_map fid_map1 = boost::choose_param(boost::get_param(np1, internal_np::face_index), get_property_map(boost::face_index, tm1)); - Fid_map fid_map2 = boost::choose_param(get_param(np2, internal_np::face_index), + Fid_map fid_map2 = boost::choose_param(boost::get_param(np2, internal_np::face_index), get_property_map(boost::face_index, tm2)); +// User visitor + typedef typename boost::lookup_named_param_def < + internal_np::graph_visitor_t, + NamedParameters1, + Corefinement::Default_visitor//default + > ::type User_visitor; + User_visitor uv( boost::choose_param( boost::get_param(np1, internal_np::graph_visitor), + Corefinement::Default_visitor() ) ); + // surface intersection algorithm call - typedef Corefinement::Default_node_visitor Dnv; - typedef Corefinement::Default_face_visitor Dfv; typedef Corefinement::Face_graph_output_builder Ob; + Edge_mark_map_tuple, + User_visitor> Ob; - typedef Corefinement::Visitor Visitor; - Dnv dnv; - Dfv dfv; + typedef Corefinement::Surface_intersection_visitor_for_corefinement< + TriangleMesh, Vpm, Ob, Ecm_in, User_visitor> Algo_visitor; Ecm_in ecm_in(tm1,tm2,ecm1,ecm2); Edge_mark_map_tuple ecms_out(ecm_out_0, ecm_out_1, ecm_out_2, ecm_out_3); Ob ob(tm1, tm2, vpm1, vpm2, fid_map1, fid_map2, ecm_in, - output_vpms, ecms_out, desired_output); + vpm_out_tuple, ecms_out, uv, output); - Corefinement::Intersection_of_triangle_meshes - functor(tm1, tm2, vpm1, vpm2, Visitor(dnv,dfv,ob,ecm_in)); + Corefinement::Intersection_of_triangle_meshes + functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm_in)); functor(CGAL::Emptyset_iterator(), throw_on_self_intersection, true); @@ -405,22 +531,52 @@ boolean_operation( TriangleMesh& tm1, template cpp11::array -boolean_operation( TriangleMesh& tm1, - TriangleMesh& tm2, - const cpp11::array< boost::optional,4>& desired_output, - const bool throw_on_self_intersection = false ) +corefine_and_compute_boolean_operations( + TriangleMesh& tm1, + TriangleMesh& tm2, + const cpp11::array< boost::optional,4>& output) { using namespace CGAL::Polygon_mesh_processing::parameters; - return boolean_operation(tm1, tm2, desired_output, - all_default(), all_default(), - cpp11::make_tuple(all_default(), all_default(), - all_default(), all_default()), - throw_on_self_intersection); + return corefine_and_compute_boolean_operations(tm1, tm2, output, + all_default(), all_default(), + cpp11::make_tuple(all_default(), all_default(), + all_default(), all_default())); +} + +template +cpp11::array +corefine_and_compute_boolean_operations( + TriangleMesh& tm1, + TriangleMesh& tm2, + const cpp11::array< boost::optional,4>& output, + const NamedParameters1& np1) +{ + using namespace CGAL::Polygon_mesh_processing::parameters; + return corefine_and_compute_boolean_operations(tm1, tm2, output, + np1, all_default(), + cpp11::make_tuple(all_default(), all_default(), + all_default(), all_default())); +} + +template +cpp11::array +corefine_and_compute_boolean_operations( + TriangleMesh& tm1, + TriangleMesh& tm2, + const cpp11::array< boost::optional,4>& output, + const NamedParameters1& np1, + const NamedParameters2& np2) +{ + using namespace CGAL::Polygon_mesh_processing::parameters; + return corefine_and_compute_boolean_operations(tm1, tm2, output, + np1, np2, + cpp11::make_tuple(all_default(), all_default(), + all_default(), all_default())); } #undef CGAL_COREF_SET_OUTPUT_VERTEX_POINT_MAP #undef CGAL_COREF_SET_OUTPUT_EDGE_MARK_MAP -/// \endcond + /** * \ingroup PMP_corefinement_grp @@ -435,10 +591,7 @@ boolean_operation( TriangleMesh& tm1, * \pre \link CGAL::Polygon_mesh_processing::does_bound_a_volume() `CGAL::Polygon_mesh_processing::does_bound_a_volume(tm1)` \endlink * \pre \link CGAL::Polygon_mesh_processing::does_bound_a_volume() `CGAL::Polygon_mesh_processing::does_bound_a_volume(tm2)` \endlink * - * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. - * If `TriangleMesh` has an internal property map for `CGAL::face_index_t`, - * as a named parameter, then it must be initialized. - * + * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph` * @tparam NamedParameters1 a sequence of \ref pmp_namedparameters "Named Parameters" * @tparam NamedParameters2 a sequence of \ref pmp_namedparameters "Named Parameters" * @tparam NamedParametersOut a sequence of \ref pmp_namedparameters "Named Parameters" @@ -462,6 +615,14 @@ boolean_operation( TriangleMesh& tm1, * Note that if the property map is writable, the indices of the faces * of `tm1` and `tm2` will be set after the corefinement is done. * \cgalParamEnd + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces (`np1` only) + * \cgalParamEnd + * \cgalParamBegin{throw_on_self_intersection} if `true`, for each input triangle mesh, + * the set of triangles close to the intersection of `tm1` and `tm2` will be + * checked for self-intersection and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` + * will be thrown if at least one is found (`np1` only). + * \cgalParamEnd * \cgalNamedParamsEnd * * @param np_out optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below @@ -495,16 +656,16 @@ corefine_and_compute_union( TriangleMesh& tm1, const NamedParametersOut& np_out) { using namespace CGAL::Polygon_mesh_processing::parameters; - cpp11::array< boost::optional,4> desired_output; - desired_output[Corefinement::UNION]=&tm_out; + cpp11::array< boost::optional,4> output; + output[Corefinement::UNION]=&tm_out; return - boolean_operation(tm1, tm2, desired_output, np1, np2, - cpp11::make_tuple(np_out, - no_parameters(np_out), - no_parameters(np_out), - no_parameters(np_out))) - [Corefinement::UNION]; + corefine_and_compute_boolean_operations(tm1, tm2, output, np1, np2, + cpp11::make_tuple(np_out, + all_default(), + all_default(), + all_default())) + [Corefinement::UNION]; } /** @@ -527,16 +688,16 @@ corefine_and_compute_intersection( TriangleMesh& tm1, const NamedParametersOut& np_out) { using namespace CGAL::Polygon_mesh_processing::parameters; - cpp11::array< boost::optional,4> desired_output; - desired_output[Corefinement::INTER]=&tm_out; + cpp11::array< boost::optional,4> output; + output[Corefinement::INTERSECTION]=&tm_out; return - boolean_operation(tm1, tm2, desired_output, np1, np2, - cpp11::make_tuple(no_parameters(np_out), - np_out, - no_parameters(np_out), - no_parameters(np_out))) - [Corefinement::INTER]; + corefine_and_compute_boolean_operations(tm1, tm2, output, np1, np2, + cpp11::make_tuple(all_default(), + np_out, + all_default(), + all_default())) + [Corefinement::INTERSECTION]; } /** @@ -559,16 +720,17 @@ corefine_and_compute_difference( TriangleMesh& tm1, const NamedParametersOut& np_out) { using namespace CGAL::Polygon_mesh_processing::parameters; - using namespace CGAL::Corefinement; - cpp11::array< boost::optional,4> desired_output; - desired_output[TM1_MINUS_TM2]=&tm_out; + using namespace CGAL::Polygon_mesh_processing::Corefinement; + cpp11::array< boost::optional,4> output; + output[TM1_MINUS_TM2]=&tm_out; return - boolean_operation(tm1, tm2, desired_output, np1, np2, - cpp11::make_tuple(no_parameters(np_out), - no_parameters(np_out), - np_out, - no_parameters(np_out)))[TM1_MINUS_TM2]; + corefine_and_compute_boolean_operations(tm1, tm2, output, np1, np2, + cpp11::make_tuple(all_default(), + all_default(), + np_out, + all_default())) + [TM1_MINUS_TM2]; } /** @@ -589,10 +751,7 @@ corefine_and_compute_difference( TriangleMesh& tm1, * @param tm2 second input triangulated surface mesh * @param np1 optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below * @param np2 optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below - * @param throw_on_self_intersection if `true`, for each input triangle mesh, - * the set of triangles closed to the intersection of `tm1` and `tm2` will be - * checked for self-intersection and `CGAL::Corefinement::Self_intersection_exception` - * will be thrown if at least one is found. + * * \cgalNamedParamsBegin * \cgalParamBegin{vertex_point_map} * the property map with the points associated to the vertices of `tm1` (`tm2`). @@ -602,6 +761,14 @@ corefine_and_compute_difference( TriangleMesh& tm1, * \cgalParamBegin{edge_is_constrained_map} a property map containing the * constrained-or-not status of each edge of `tm1` (`tm2`) * \cgalParamEnd + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces (`np1` only) + * \cgalParamEnd + * \cgalParamBegin{throw_on_self_intersection} if `true`, for each input triangle mesh, + * the set of triangles close to the intersection of `tm1` and `tm2` will be + * checked for self-intersection and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` + * will be thrown if at least one is found (`np1` only). + * \cgalParamEnd * \cgalNamedParamsEnd * */ @@ -612,9 +779,11 @@ corefine_and_compute_difference( TriangleMesh& tm1, corefine( TriangleMesh& tm1, TriangleMesh& tm2, const NamedParameters1& np1, - const NamedParameters2& np2, - const bool throw_on_self_intersection = false) + const NamedParameters2& np2) { + const bool throw_on_self_intersection = + boost::choose_param(boost::get_param(np1, internal_np::throw_on_self_intersection), false); + // Vertex point maps typedef typename GetVertexPointMap::type Vpm; @@ -625,10 +794,10 @@ corefine_and_compute_difference( TriangleMesh& tm1, static const bool same_vpm = (boost::is_same::value);) CGAL_static_assertion(same_vpm); - Vpm vpm1 = boost::choose_param(get_param(np1, internal_np::vertex_point), + Vpm vpm1 = boost::choose_param(boost::get_param(np1, internal_np::vertex_point), get_property_map(boost::vertex_point, tm1)); - Vpm vpm2 = boost::choose_param(get_param(np2, internal_np::vertex_point), + Vpm vpm2 = boost::choose_param(boost::get_param(np2, internal_np::vertex_point), get_property_map(boost::vertex_point, tm2)); // Edge is-constrained maps @@ -644,9 +813,9 @@ corefine_and_compute_difference( TriangleMesh& tm1, Corefinement::No_mark//default > ::type Ecm2; - Ecm1 ecm1 = boost::choose_param( get_param(np1, internal_np::edge_is_constrained), + Ecm1 ecm1 = boost::choose_param( boost::get_param(np1, internal_np::edge_is_constrained), Corefinement::No_mark() ); - Ecm2 ecm2 = boost::choose_param( get_param(np2, internal_np::edge_is_constrained), + Ecm2 ecm2 = boost::choose_param( boost::get_param(np2, internal_np::edge_is_constrained), Corefinement::No_mark() ); typedef Corefinement::Ecm_bind Ecm; @@ -658,17 +827,23 @@ corefine_and_compute_difference( TriangleMesh& tm1, return; } + // User visitor + typedef typename boost::lookup_named_param_def < + internal_np::graph_visitor_t, + NamedParameters1, + Corefinement::Default_visitor//default + > ::type User_visitor; + User_visitor uv( boost::choose_param( boost::get_param(np1, internal_np::graph_visitor), + Corefinement::Default_visitor() ) ); + // surface intersection algorithm call - typedef Corefinement::Default_node_visitor Dnv; - typedef Corefinement::Default_face_visitor Dfv; typedef Corefinement::No_extra_output_from_corefinement Ob; - typedef Corefinement::Visitor Visitor; - Dnv dnv; - Dfv dfv; + typedef Corefinement::Surface_intersection_visitor_for_corefinement< + TriangleMesh, Vpm, Ob, Ecm, User_visitor> Algo_visitor; Ob ob; Ecm ecm(tm1,tm2,ecm1,ecm2); - Corefinement::Intersection_of_triangle_meshes - functor(tm1, tm2, vpm1, vpm2, Visitor(dnv,dfv,ob,ecm)); + Corefinement::Intersection_of_triangle_meshes + functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm)); functor(CGAL::Emptyset_iterator(), throw_on_self_intersection, true); } @@ -695,6 +870,9 @@ namespace experimental { * \cgalParamBegin{edge_is_constrained_map} a property map containing the * constrained-or-not status of each edge of `tm` * \cgalParamEnd + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces + * \cgalParamEnd * \cgalNamedParamsEnd * */ @@ -708,7 +886,7 @@ namespace experimental { typedef typename GetVertexPointMap::type Vpm; - Vpm vpm = boost::choose_param(get_param(np, internal_np::vertex_point), + Vpm vpm = boost::choose_param(boost::get_param(np, internal_np::vertex_point), get_property_map(boost::vertex_point, tm)); // Edge is-constrained maps @@ -719,21 +897,27 @@ namespace experimental { > ::type Ecm; - Ecm ecm = boost::choose_param( get_param(np, internal_np::edge_is_constrained), + Ecm ecm = boost::choose_param( boost::get_param(np, internal_np::edge_is_constrained), Corefinement::No_mark() ); +// User visitor + typedef typename boost::lookup_named_param_def < + internal_np::graph_visitor_t, + NamedParameters, + Corefinement::Default_visitor//default + > ::type User_visitor; + User_visitor uv( boost::choose_param( boost::get_param(np, internal_np::graph_visitor), + Corefinement::Default_visitor() ) ); + + // surface intersection algorithm call - typedef Corefinement::Default_node_visitor Dnv; - typedef Corefinement::Default_face_visitor Dfv; typedef Corefinement::No_extra_output_from_corefinement Ob; - typedef Default D; - typedef Corefinement::Visitor Visitor; - Dnv dnv; - Dfv dfv; + typedef Corefinement::Surface_intersection_visitor_for_corefinement< + TriangleMesh, Vpm, Ob, Ecm, User_visitor,true> Algo_visitor; Ob ob; - Corefinement::Intersection_of_triangle_meshes - functor(tm, vpm, Visitor(dnv,dfv,ob,ecm) ); + Corefinement::Intersection_of_triangle_meshes + functor(tm, vpm, Algo_visitor(uv,ob,ecm) ); functor(CGAL::Emptyset_iterator(), true); } @@ -761,7 +945,10 @@ namespace experimental { * \cgalParamBegin{edge_is_constrained_map} a property map containing the * constrained-or-not status of each edge of `tm` * \cgalParamEnd -* \cgalParamBegin{face_index_map} a property map containing the index of each face of `tm` \cgalParamEnd + * \cgalParamBegin{face_index_map} a property map containing the index of each face of `tm` \cgalParamEnd + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces + * \cgalParamEnd * \cgalNamedParamsEnd * */ @@ -774,12 +961,12 @@ namespace experimental { // Vertex point maps typedef typename GetVertexPointMap::type Vpm; - Vpm vpm = boost::choose_param(get_param(np, internal_np::vertex_point), + Vpm vpm = boost::choose_param(boost::get_param(np, internal_np::vertex_point), get_property_map(boost::vertex_point, tm)); // Face index map typedef typename GetFaceIndexMap::type Fid_map; - Fid_map fid_map = boost::choose_param(get_param(np, internal_np::face_index), + Fid_map fid_map = boost::choose_param(boost::get_param(np, internal_np::face_index), get_property_map(boost::face_index, tm)); // Edge is-constrained maps typedef typename boost::lookup_named_param_def < @@ -787,26 +974,30 @@ namespace experimental { NamedParameters, Corefinement::No_mark//default > ::type Ecm; - Ecm ecm = boost::choose_param( get_param(np, internal_np::edge_is_constrained), + Ecm ecm = boost::choose_param( boost::get_param(np, internal_np::edge_is_constrained), Corefinement::No_mark() ); +// User visitor + typedef typename boost::lookup_named_param_def < + internal_np::graph_visitor_t, + NamedParameters, + Corefinement::Default_visitor//default + > ::type User_visitor; + User_visitor uv( boost::choose_param( boost::get_param(np, internal_np::graph_visitor), + Corefinement::Default_visitor() ) ); // surface intersection algorithm call - typedef Corefinement::Default_node_visitor Dnv; - typedef Corefinement::Default_face_visitor Dfv; typedef Corefinement::Output_builder_for_autorefinement Ob; - typedef Default D; - typedef Corefinement::Visitor Visitor; - Dnv dnv; - Dfv dfv; + typedef Corefinement::Surface_intersection_visitor_for_corefinement< + TriangleMesh, Vpm, Ob, Ecm, User_visitor,true> Algo_visitor; Ob ob(tm, vpm, fid_map, ecm); - Corefinement::Intersection_of_triangle_meshes - functor(tm, vpm, Visitor(dnv,dfv,ob,ecm) ); + Corefinement::Intersection_of_triangle_meshes + functor(tm, vpm, Algo_visitor(uv,ob,ecm) ); functor(CGAL::Emptyset_iterator(), true); @@ -941,22 +1132,60 @@ template void corefine( TriangleMesh& tm1, TriangleMesh& tm2, - const NamedParameters1& np1, - const bool throw_on_self_intersection = false) + const NamedParameters1& np1) { using namespace CGAL::Polygon_mesh_processing::parameters; - corefine(tm1, tm2, np1, all_default(), throw_on_self_intersection); + corefine(tm1, tm2, np1, all_default()); +} + +template +void +corefine( TriangleMesh& tm1, + TriangleMesh& tm2) +{ + using namespace CGAL::Polygon_mesh_processing::parameters; + corefine(tm1, tm2, all_default(), all_default()); +} + +#ifndef CGAL_NO_DEPRECATED_CODE + template + void + corefine( TriangleMesh& tm1, + TriangleMesh& tm2, + const NamedParameters1& np1, + const NamedParameters2& np2, + const bool throw_on_self_intersection) +{ + corefine(tm1, tm2, np1.throw_on_self_intersection(throw_on_self_intersection), np2); +} + +template +void +corefine( TriangleMesh& tm1, + TriangleMesh& tm2, + const NamedParameters1& np1, + const bool throw_on_self_intersection) +{ + namespace params = CGAL::Polygon_mesh_processing::parameters; + corefine(tm1, tm2, + np1.throw_on_self_intersection(throw_on_self_intersection), + params::all_default()); } template void corefine( TriangleMesh& tm1, TriangleMesh& tm2, - const bool throw_on_self_intersection = false) + const bool throw_on_self_intersection) { - using namespace CGAL::Polygon_mesh_processing::parameters; - corefine(tm1, tm2, all_default(), all_default(), throw_on_self_intersection); + namespace params = CGAL::Polygon_mesh_processing::parameters; + corefine(tm1, tm2, + params::throw_on_self_intersection(throw_on_self_intersection), + params::all_default()); } +#endif ///// autorefine ///// namespace experimental { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h index b0c37510978..6fe903aa52b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h @@ -36,29 +36,44 @@ #include - -#define CGAL_COREF_SELECT_OUT_ECM(I) \ - (I == 0 ? cpp11::get<0>(out_edge_mark_maps) \ - : I == 1 ? cpp11::get<1>(out_edge_mark_maps) \ - : I == 2 ? cpp11::get<2>(out_edge_mark_maps) \ - : cpp11::get<3>(out_edge_mark_maps)) +// required to handle the multiple types of edge constrained maps +// for the different output types. CGAL_COREF_FUNCTION_CALL_DEF +// must be defined prior to using this macro. +#define CGAL_COREF_FUNCTION_CALL(BO_type) \ + switch(BO_type) \ + { \ + case UNION: \ + CGAL_COREF_FUNCTION_CALL_DEF(UNION); \ + break; \ + case INTERSECTION: \ + CGAL_COREF_FUNCTION_CALL_DEF(INTERSECTION); \ + break; \ + case TM1_MINUS_TM2: \ + CGAL_COREF_FUNCTION_CALL_DEF(TM1_MINUS_TM2); \ + break; \ + default: \ + CGAL_assertion( BO_type == TM2_MINUS_TM1 ); \ + CGAL_COREF_FUNCTION_CALL_DEF(TM2_MINUS_TM1); \ + } namespace CGAL { +namespace Polygon_mesh_processing { namespace Corefinement { -enum Boolean_operation {UNION = 0, INTER, - TM1_MINUS_TM2, TM2_MINUS_TM1, - NONE }; +enum Boolean_operation_type {UNION = 0, INTERSECTION, + TM1_MINUS_TM2, TM2_MINUS_TM1, NONE }; namespace PMP=Polygon_mesh_processing; namespace params=PMP::parameters; template + class EdgeMarkMapBind_ = Default, + class EdgeMarkMapTuple_ = Default, + class UserVisitor_ = Default> class Face_graph_output_builder { //Default typedefs @@ -75,6 +90,8 @@ class Face_graph_output_builder No_mark, No_mark, No_mark > >::type EdgeMarkMapTuple; + typedef typename Default::Get< + UserVisitor_, Default_visitor >::type UserVisitor; // graph_traits typedefs typedef TriangleMesh TM; @@ -107,10 +124,11 @@ class Face_graph_output_builder const FaceIdMap &fids1, &fids2; EdgeMarkMapBind& marks_on_input_edges; // property maps of output meshes - const cpp11::array& output_vpms; + const VpmOutTuple& output_vpms; EdgeMarkMapTuple& out_edge_mark_maps; + UserVisitor& user_visitor; // output meshes - const cpp11::array, 4>& desired_output; + const cpp11::array, 4>& requested_output; // input meshes closed ? /// \todo do we really need this? bool is_tm1_closed; @@ -251,14 +269,14 @@ class Face_graph_output_builder template void mark_edges(const EdgeMarkMap& edge_mark_map, - const std::vector& edges) + const std::vector& edges) { BOOST_FOREACH(edge_descriptor ed, edges) put(edge_mark_map, ed, true); } void mark_edges(const No_mark&, - const std::vector&) + const std::vector&) {} //nothing to do template @@ -336,17 +354,19 @@ public: const FaceIdMap& fids1, const FaceIdMap& fids2, EdgeMarkMapBind& marks_on_input_edges, - const cpp11::array& output_vpms, + const VpmOutTuple& output_vpms, EdgeMarkMapTuple& out_edge_mark_maps, + UserVisitor& user_visitor, const cpp11::array< - boost::optional, 4 >& desired_output) + boost::optional, 4 >& requested_output) : tm1(tm1), tm2(tm2) , vpm1(vpm1), vpm2(vpm2) , fids1(fids1), fids2(fids2) , marks_on_input_edges(marks_on_input_edges) , output_vpms(output_vpms) , out_edge_mark_maps(out_edge_mark_maps) - , desired_output(desired_output) + , user_visitor(user_visitor) + , requested_output(requested_output) , is_tm1_closed( is_closed(tm1)) , is_tm2_closed( is_closed(tm2)) , is_tm1_inside_out( is_tm1_closed && !PMP::is_outward_oriented(tm1) ) @@ -361,7 +381,7 @@ public: } bool intersection_is_valid() const { - return !impossible_operation[INTER]; + return !impossible_operation[INTERSECTION]; } bool tm1_minus_tm2_is_valid() const { @@ -852,7 +872,7 @@ public: // opposite( poly_first U poly_second ) = {O} is_patch_inside_tm2.set(patch_id_p1); is_patch_inside_tm2.set(patch_id_p2); - impossible_operation.set(INTER); // tm1 n tm2 is non-manifold + impossible_operation.set(INTERSECTION); // tm1 n tm2 is non-manifold } } else @@ -1103,7 +1123,7 @@ public: std::vector< boost::dynamic_bitset<> > patches_of_tm2_used(4); /// handle the bitset for the union - if ( !impossible_operation.test(UNION) && desired_output[UNION] ) + if ( !impossible_operation.test(UNION) && requested_output[UNION] ) { //define patches to import from P patches_of_tm1_used[UNION] = ~is_patch_inside_tm2 - coplanar_patches_of_tm1; @@ -1112,7 +1132,7 @@ public: //handle coplanar patches if (coplanar_patches_of_tm1.any()) { - if (desired_output[UNION]==&tm2) + if (requested_output[UNION]==&tm2) patches_of_tm2_used[UNION] |= coplanar_patches_of_tm2_for_union_and_intersection; else patches_of_tm1_used[UNION] |= coplanar_patches_of_tm1_for_union_and_intersection; @@ -1120,24 +1140,24 @@ public: } /// handle the bitset for the intersection - if ( !impossible_operation.test(INTER) && desired_output[INTER] ) + if ( !impossible_operation.test(INTERSECTION) && requested_output[INTERSECTION] ) { //define patches to import from P - patches_of_tm1_used[INTER] = is_patch_inside_tm2; + patches_of_tm1_used[INTERSECTION] = is_patch_inside_tm2; //define patches to import from Q - patches_of_tm2_used[INTER] = is_patch_inside_tm1; + patches_of_tm2_used[INTERSECTION] = is_patch_inside_tm1; //handle coplanar patches if (coplanar_patches_of_tm1.any()) { - if (desired_output[INTER]==&tm2) - patches_of_tm2_used[INTER] |= coplanar_patches_of_tm2_for_union_and_intersection; + if (requested_output[INTERSECTION]==&tm2) + patches_of_tm2_used[INTERSECTION] |= coplanar_patches_of_tm2_for_union_and_intersection; else - patches_of_tm1_used[INTER] |= coplanar_patches_of_tm1_for_union_and_intersection; + patches_of_tm1_used[INTERSECTION] |= coplanar_patches_of_tm1_for_union_and_intersection; } } /// handle the bitset for P-Q - if ( !impossible_operation.test(TM1_MINUS_TM2) && desired_output[TM1_MINUS_TM2] ) + if ( !impossible_operation.test(TM1_MINUS_TM2) && requested_output[TM1_MINUS_TM2] ) { //define patches to import from P patches_of_tm1_used[TM1_MINUS_TM2] = (~is_patch_inside_tm2 - coplanar_patches_of_tm1); @@ -1146,7 +1166,7 @@ public: //handle coplanar patches if (coplanar_patches_of_tm1.any()) { - if (desired_output[TM1_MINUS_TM2]==&tm2) + if (requested_output[TM1_MINUS_TM2]==&tm2) patches_of_tm2_used[TM1_MINUS_TM2] |= ~coplanar_patches_of_tm2_for_union_and_intersection & coplanar_patches_of_tm2; else patches_of_tm1_used[TM1_MINUS_TM2] |= ~coplanar_patches_of_tm1_for_union_and_intersection & coplanar_patches_of_tm1; @@ -1154,7 +1174,7 @@ public: } /// handle the bitset for Q-P - if ( !impossible_operation.test(TM2_MINUS_TM1) && desired_output[TM2_MINUS_TM1] ) + if ( !impossible_operation.test(TM2_MINUS_TM1) && requested_output[TM2_MINUS_TM1] ) { //define patches to import from P patches_of_tm1_used[TM2_MINUS_TM1] = is_patch_inside_tm2; @@ -1163,7 +1183,7 @@ public: //handle coplanar patches if (coplanar_patches_of_tm1.any()) { - if (desired_output[TM2_MINUS_TM1]==&tm2) + if (requested_output[TM2_MINUS_TM1]==&tm2) patches_of_tm2_used[TM2_MINUS_TM1] |= ~coplanar_patches_of_tm2_for_union_and_intersection & coplanar_patches_of_tm2; else patches_of_tm1_used[TM2_MINUS_TM1] |= ~coplanar_patches_of_tm1_for_union_and_intersection & coplanar_patches_of_tm1; @@ -1174,8 +1194,8 @@ public: #ifdef CGAL_COREFINEMENT_DEBUG std::cout << "patches_of_tm1_used[UNION] " << patches_of_tm1_used[UNION] << "\n"; std::cout << "patches_of_tm2_used[UNION] " << patches_of_tm2_used[UNION] << "\n"; - std::cout << "patches_of_tm1_used[INTER] " << patches_of_tm1_used[INTER] << "\n"; - std::cout << "patches_of_tm2_used[INTER] " << patches_of_tm2_used[INTER] << "\n"; + std::cout << "patches_of_tm1_used[INTERSECTION] " << patches_of_tm1_used[INTERSECTION] << "\n"; + std::cout << "patches_of_tm2_used[INTERSECTION] " << patches_of_tm2_used[INTERSECTION] << "\n"; std::cout << "patches_of_tm1_used[TM1_MINUS_TM2] " << patches_of_tm1_used[TM1_MINUS_TM2] << "\n"; std::cout << "patches_of_tm2_used[TM1_MINUS_TM2] " << patches_of_tm2_used[TM1_MINUS_TM2] << "\n"; std::cout << "patches_of_tm1_used[TM2_MINUS_TM1] " << patches_of_tm1_used[TM2_MINUS_TM1] << "\n"; @@ -1185,29 +1205,29 @@ public: // should be done. First operations are those filling meshes // different from tm1 and tm2, then the one modifying tm1 and // finally the one modifying tm2. - std::vector out_of_place_operations; - Boolean_operation inplace_operation_tm1=NONE, - inplace_operation_tm2=NONE; + std::vector out_of_place_operations; + Boolean_operation_type inplace_operation_tm1=NONE, + inplace_operation_tm2=NONE; for (int i=0;i<4;++i) { - Boolean_operation operation=enum_cast(i); + Boolean_operation_type operation=enum_cast(i); - if (!desired_output[operation] || impossible_operation.test(operation)) + if (!requested_output[operation] || impossible_operation.test(operation)) continue; - if (desired_output[operation]==&tm1) + if (requested_output[operation]==&tm1) inplace_operation_tm1=operation; else - if (desired_output[operation]==&tm2) + if (requested_output[operation]==&tm2) inplace_operation_tm2=operation; else out_of_place_operations.push_back(operation); } /// first handle operations in a mesh that is neither tm1 nor tm2 - BOOST_FOREACH(Boolean_operation operation, out_of_place_operations) + BOOST_FOREACH(Boolean_operation_type operation, out_of_place_operations) { - TriangleMesh& output = *(*desired_output[operation]); + TriangleMesh& output = *(*requested_output[operation]); CGAL_assertion(&tm1!=&output && &tm2!=&output); Intersection_polylines polylines(tm1_polylines, @@ -1222,19 +1242,24 @@ public: ); std::vector shared_edges; - fill_new_triangle_mesh( - output, - patches_of_tm1_used[operation], patches_of_tm2_used[operation], - patches_of_tm1, patches_of_tm2, - operation == TM2_MINUS_TM1, operation == TM1_MINUS_TM2, - polylines, - intersection_edges1, intersection_edges2, - vpm1, vpm2, *output_vpms[operation], - marks_on_input_edges.ecm1, - marks_on_input_edges.ecm2, - CGAL_COREF_SELECT_OUT_ECM(operation), - shared_edges - ); + + #define CGAL_COREF_FUNCTION_CALL_DEF(BO_type) \ + fill_new_triangle_mesh( \ + output, \ + patches_of_tm1_used[BO_type], patches_of_tm2_used[BO_type], \ + patches_of_tm1, patches_of_tm2, \ + BO_type == TM2_MINUS_TM1, BO_type == TM1_MINUS_TM2, \ + polylines, \ + intersection_edges1, intersection_edges2, \ + vpm1, vpm2, *cpp11::get(output_vpms), \ + marks_on_input_edges.ecm1, \ + marks_on_input_edges.ecm2, \ + cpp11::get(out_edge_mark_maps), \ + shared_edges, \ + user_visitor \ + ) + CGAL_COREF_FUNCTION_CALL(operation) + #undef CGAL_COREF_FUNCTION_CALL_DEF mark_edges(out_edge_mark_maps, shared_edges, operation); } @@ -1248,7 +1273,7 @@ public: mesh_to_intersection_edges[&tm1], inplace_operation_tm1); - CGAL_assertion( *desired_output[inplace_operation_tm1] == &tm1 ); + CGAL_assertion( *requested_output[inplace_operation_tm1] == &tm1 ); if ( inplace_operation_tm2!=NONE) { @@ -1299,34 +1324,44 @@ public: patches_of_tm1[i]; } // Operation in tm1: disconnect patches not use and append the one from tm2 - compute_inplace_operation_delay_removal_and_insideout( - tm1, - tm2, - patches_of_tm1_used[inplace_operation_tm1], - patches_of_tm2_used[inplace_operation_tm1], - patches_of_tm1, patches_of_tm2, - inplace_operation_tm1 == TM1_MINUS_TM2 || - inplace_operation_tm1 == TM2_MINUS_TM1, - polylines_in_tm1, - vpm1, vpm2, - marks_on_input_edges.ecm1, - marks_on_input_edges.ecm2, - CGAL_COREF_SELECT_OUT_ECM(inplace_operation_tm1), - disconnected_patches_edge_to_tm2_edge); + #define CGAL_COREF_FUNCTION_CALL_DEF(BO_type)\ + compute_inplace_operation_delay_removal_and_insideout( \ + tm1, \ + tm2, \ + patches_of_tm1_used[BO_type], \ + patches_of_tm2_used[BO_type], \ + patches_of_tm1, patches_of_tm2, \ + BO_type == TM1_MINUS_TM2 || \ + BO_type == TM2_MINUS_TM1, \ + polylines_in_tm1, \ + vpm1, vpm2, \ + marks_on_input_edges.ecm1, \ + marks_on_input_edges.ecm2, \ + cpp11::get(out_edge_mark_maps), \ + disconnected_patches_edge_to_tm2_edge, \ + user_visitor) + CGAL_COREF_FUNCTION_CALL(inplace_operation_tm1) + #undef CGAL_COREF_FUNCTION_CALL_DEF // Operation in tm2: discard patches and append the one from tm2 - CGAL_assertion( *desired_output[inplace_operation_tm2] == &tm2 ); - compute_inplace_operation( tm2, tm1, - patches_of_tm2_used[inplace_operation_tm2], - patches_of_tm1_used[inplace_operation_tm2], - patches_of_tm2, patches_of_tm1, - inplace_operation_tm2==TM1_MINUS_TM2, - inplace_operation_tm2==TM2_MINUS_TM1, - vpm2, - vpm1, - marks_on_input_edges.ecm2, - marks_on_input_edges.ecm1, - CGAL_COREF_SELECT_OUT_ECM(inplace_operation_tm2), - disconnected_patches_edge_to_tm2_edge); + CGAL_assertion( *requested_output[inplace_operation_tm2] == &tm2 ); + + #define CGAL_COREF_FUNCTION_CALL_DEF(BO_type)\ + compute_inplace_operation( tm2, tm1, \ + patches_of_tm2_used[BO_type], \ + patches_of_tm1_used[BO_type], \ + patches_of_tm2, patches_of_tm1, \ + BO_type==TM1_MINUS_TM2, \ + BO_type==TM2_MINUS_TM1, \ + vpm2, \ + vpm1, \ + marks_on_input_edges.ecm2, \ + marks_on_input_edges.ecm1, \ + cpp11::get(out_edge_mark_maps), \ + disconnected_patches_edge_to_tm2_edge, \ + user_visitor) + CGAL_COREF_FUNCTION_CALL(inplace_operation_tm2) + #undef CGAL_COREF_FUNCTION_CALL_DEF + // remove polylines only on the border of patches not kept in tm2 if (polylines_in_tm2.to_skip.any()) remove_unused_polylines(tm2, @@ -1339,8 +1374,12 @@ public: marks_on_input_edges.ecm1); // transfer marks of edges of patches kept to the output edge mark property - copy_edge_mark( - tm1, marks_on_input_edges.ecm1, CGAL_COREF_SELECT_OUT_ECM(inplace_operation_tm1)); + #define CGAL_COREF_FUNCTION_CALL_DEF(BO_type) \ + copy_edge_mark( \ + tm1, marks_on_input_edges.ecm1, \ + cpp11::get(out_edge_mark_maps)) + CGAL_COREF_FUNCTION_CALL(inplace_operation_tm1) + #undef CGAL_COREF_FUNCTION_CALL_DEF // remove polylines only on the border of patches not kept in tm1 if (polylines_in_tm1.to_skip.any()) @@ -1353,7 +1392,7 @@ public: } else{ /// handle the operation updating only tm1 - CGAL_assertion( *desired_output[inplace_operation_tm1] == &tm1 ); + CGAL_assertion( *requested_output[inplace_operation_tm1] == &tm1 ); Intersection_polylines polylines( tm1_polylines, tm2_polylines, polyline_lengths); fill_polylines_to_skip( @@ -1361,21 +1400,24 @@ public: patches_of_tm1_used[inplace_operation_tm1], patches_of_tm2_used[inplace_operation_tm1], fids1, fids2, tm1, tm2); - - compute_inplace_operation( - tm1, tm2, - patches_of_tm1_used[inplace_operation_tm1], - patches_of_tm2_used[inplace_operation_tm1], - patches_of_tm1, patches_of_tm2, - inplace_operation_tm1 == TM2_MINUS_TM1, - inplace_operation_tm1 == TM1_MINUS_TM2, - vpm1, - vpm2, - marks_on_input_edges.ecm1, - marks_on_input_edges.ecm2, - CGAL_COREF_SELECT_OUT_ECM(inplace_operation_tm1), - polylines - ); + #define CGAL_COREF_FUNCTION_CALL_DEF(BO_type) \ + compute_inplace_operation( \ + tm1, tm2, \ + patches_of_tm1_used[BO_type], \ + patches_of_tm2_used[BO_type], \ + patches_of_tm1, patches_of_tm2, \ + BO_type == TM2_MINUS_TM1, \ + BO_type == TM1_MINUS_TM2, \ + vpm1, \ + vpm2, \ + marks_on_input_edges.ecm1, \ + marks_on_input_edges.ecm2, \ + cpp11::get(out_edge_mark_maps), \ + polylines, \ + user_visitor \ + ) + CGAL_COREF_FUNCTION_CALL(inplace_operation_tm1) + #undef CGAL_COREF_FUNCTION_CALL_DEF // remove polylines only on the border of patches not kept if (polylines.to_skip.any()) remove_unused_polylines(tm1, @@ -1392,7 +1434,7 @@ public: inplace_operation_tm2); /// handle the operation updating only tm2 - CGAL_assertion( *desired_output[inplace_operation_tm2] == &tm2 ); + CGAL_assertion( *requested_output[inplace_operation_tm2] == &tm2 ); Intersection_polylines polylines( tm2_polylines, tm1_polylines, polyline_lengths); fill_polylines_to_skip( @@ -1401,20 +1443,22 @@ public: patches_of_tm1_used[inplace_operation_tm2], fids2, fids1, tm2, tm1 ); - - compute_inplace_operation( tm2, tm1, - patches_of_tm2_used[inplace_operation_tm2], - patches_of_tm1_used[inplace_operation_tm2], - patches_of_tm2, patches_of_tm1, - inplace_operation_tm2==TM1_MINUS_TM2, - inplace_operation_tm2==TM2_MINUS_TM1, - vpm2, - vpm1, - marks_on_input_edges.ecm2, - marks_on_input_edges.ecm1, - CGAL_COREF_SELECT_OUT_ECM(inplace_operation_tm2), - polylines); - + #define CGAL_COREF_FUNCTION_CALL_DEF(BO_type) \ + compute_inplace_operation( tm2, tm1, \ + patches_of_tm2_used[BO_type], \ + patches_of_tm1_used[BO_type], \ + patches_of_tm2, patches_of_tm1, \ + BO_type==TM1_MINUS_TM2, \ + BO_type==TM2_MINUS_TM1, \ + vpm2, \ + vpm1, \ + marks_on_input_edges.ecm2, \ + marks_on_input_edges.ecm1, \ + cpp11::get(out_edge_mark_maps), \ + polylines, \ + user_visitor); + CGAL_COREF_FUNCTION_CALL(inplace_operation_tm2) + #undef CGAL_COREF_FUNCTION_CALL_DEF // remove polylines only on the border of patches not kept if (polylines.to_skip.any()) remove_unused_polylines(tm2, @@ -1425,8 +1469,7 @@ public: }; -} } // CGAL::Corefinement - -#undef CGAL_COREF_SELECT_OUT_ECM +} } } // CGAL::Polygon_mesh_processing::Corefinement +#undef CGAL_COREF_FUNCTION_CALL #endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_FACE_GRAPH_OUTPUT_BUILDER_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Intersection_type.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Intersection_type.h index 280aadefbb4..79beefbdaa5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Intersection_type.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Intersection_type.h @@ -29,6 +29,7 @@ #include namespace CGAL{ +namespace Polygon_mesh_processing { namespace Corefinement{ enum Intersection_type {ON_FACE,ON_EDGE,ON_VERTEX,EMPTY,COPLANAR_TRIANGLES}; @@ -50,6 +51,6 @@ struct Coplanar_intersection{ }; -} } // CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif // CGAL_PMP_INTERNAL_COREFINEMENT_INTERSECTION_TYPE_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h index 5c72f1c5fbe..ba32b8948f2 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h @@ -40,6 +40,7 @@ #include namespace CGAL { +namespace Polygon_mesh_processing { namespace Corefinement { namespace PMP=Polygon_mesh_processing; @@ -1012,7 +1013,7 @@ public: }; -} } // CGAL::Corefinement +} } } // CGAL::Corefinement #include diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h index f6f4ebd128c..7b73309e1c9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h @@ -36,46 +36,11 @@ #include namespace CGAL{ +namespace Polygon_mesh_processing { namespace Corefinement{ // TODO option to ignore internal edges for patches of coplanar faces -template -struct Default_node_visitor{ - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::face_descriptor face_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - - void new_node_added( std::size_t /* node_id */, - Intersection_type /* type */, - halfedge_descriptor /* principal_edge */, - halfedge_descriptor /* additional_edge */, - bool /* is_target_coplanar */, - bool /* is_source_coplanar */ ) - {} - - void new_node_added_triple_face(std::size_t /* node_id */, - face_descriptor /* f1 */, - face_descriptor /* f2 */, - face_descriptor /* f3 */, - const TriangleMesh& /* tm */) - {} - - void new_vertex_added(std::size_t /* node_id */, - vertex_descriptor /* vh */, - TriangleMesh& /*tm*/){} -}; - -template -struct Default_face_visitor{ - typedef boost::graph_traits GT; - typedef typename GT::face_descriptor face_descriptor; - - void before_subface_creations(face_descriptor /*f_old*/,TriangleMesh&){} - void after_subface_created(face_descriptor /*f_new*/,TriangleMesh&){} -}; - //binds two edge constrained pmaps template struct Ecm_bind{ @@ -147,19 +112,16 @@ template< class TriangleMesh, class VertexPointMap, class OutputBuilder_ = Default, class EdgeMarkMapBind_ = Default, - class NewNodeVisitor_ = Default, - class NewFaceVisitor_ = Default, + class UserVisitor_ = Default, bool doing_autorefinement = false > -class Visitor{ +class Surface_intersection_visitor_for_corefinement{ //default template parameters typedef typename Default::Get > >::type EdgeMarkMapBind; typedef typename Default::Get >::type OutputBuilder; typedef typename Default::Get< - NewNodeVisitor_, Default_node_visitor >::type NewNodeVisitor; - typedef typename Default::Get< - NewFaceVisitor_, Default_face_visitor >::type NewFaceVisitor; + UserVisitor_, Default_visitor >::type UserVisitor; // config flags public: @@ -211,8 +173,7 @@ private: std::map< Node_id,std::set > coplanar_constraints; //data members that require initialization in the constructor - NewNodeVisitor& new_node_visitor; - NewFaceVisitor& new_face_visitor; + UserVisitor& user_visitor; OutputBuilder& output_builder; EdgeMarkMapBind marks_on_edges; bool input_with_coplanar_faces; @@ -245,10 +206,9 @@ private: // visitor public functions public: - Visitor(NewNodeVisitor& v, NewFaceVisitor& f, - OutputBuilder& o, const EdgeMarkMapBind& emm) - : new_node_visitor(v) - , new_face_visitor(f) + Surface_intersection_visitor_for_corefinement( + UserVisitor& uv, OutputBuilder& o, const EdgeMarkMapBind& emm) + : user_visitor(uv) , output_builder(o) , marks_on_edges(emm) , input_with_coplanar_faces(false) @@ -353,7 +313,7 @@ public: { CGAL_assertion(f1!=f2 && f1!=f3 && f2!=f3); TriangleMesh* tm_ptr = const_cast(&tm); - new_node_visitor.new_node_added_triple_face(node_id, f1, f2, f3, tm); +// user_visitor.new_node_added_triple_face(node_id, f1, f2, f3, tm); // NODE_VISITOR_TAG #ifdef CGAL_DEBUG_AUTOREFINEMENT std::cout << "adding node " << node_id << " " << f1 << " " << f2 << " " << f3 << "\n"; #endif @@ -377,7 +337,7 @@ public: TriangleMesh* tm2_ptr = const_cast(&tm2); //forward to the visitor - new_node_visitor.new_node_added(node_id, type, h_1, h_2, is_target_coplanar, is_source_coplanar); +// user_visitor.new_node_added(node_id, type, h_1, h_2, is_target_coplanar, is_source_coplanar); // NODE_VISITOR_TAG switch(type) { case ON_FACE: //Face intersected by an edge @@ -790,7 +750,7 @@ public: halfedge_descriptor hnew = Euler::split_edge(hedge, tm); CGAL_assertion(expected_src==source(hnew,tm)); vertex_descriptor vnew=target(hnew,tm); - new_node_visitor.new_vertex_added(node_id, vnew, tm); +// user_visitor.new_vertex_added(node_id, vnew, tm); // NODE_VISITOR_TAG nodes.call_put(vpm, vnew, node_id, tm); node_id_to_vertex[node_id]=vnew; @@ -1036,7 +996,7 @@ public: // import the triangle in `cdt` in the face `f` of `tm` triangulate_a_face(f, tm, nodes, node_ids, node_id_to_vertex, - edge_to_hedge, cdt, vpm, new_node_visitor, new_face_visitor); + edge_to_hedge, cdt, vpm, user_visitor); // TODO Here we do the update only for internal edges. // Update for border halfedges could be done during the split @@ -1080,7 +1040,7 @@ public: } }; -} } //end of namespace CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #include diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/face_graph_utils.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/face_graph_utils.h index b5bb6eaff81..be442040d31 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/face_graph_utils.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/face_graph_utils.h @@ -28,11 +28,14 @@ #include #include #include +#include #include +#include #include #include #include namespace CGAL { +namespace Polygon_mesh_processing { namespace Corefinement { template @@ -152,13 +155,115 @@ void copy_edge_mark(G&, No_mark&) {} // nothing to do +// Parts to get default property maps for output meshes based on the value type +// of input vertex point maps. +template +struct Dummy_default_vertex_point_map +{ + typedef vertex_descriptor key_type; + typedef Point_3 value_type; + typedef Point_3 reference; + typedef boost::read_write_property_map_tag category; + + inline friend + value_type + get(Dummy_default_vertex_point_map, key_type) + { + CGAL_assertion(false || + !"This property map should not be used." + "Check the value type of the output vpm vs that of input"); + return Point_3(); + } + + inline friend + void + put(Dummy_default_vertex_point_map, key_type, value_type) + { + CGAL_assertion(false || + !"This property map should not be used." + "Check the value type of the output vpm vs that of input"); + } +}; + +template +struct TweakedGetVertexPointMap +{ + typedef typename GetVertexPointMap::type Default_map; + typedef typename boost::is_same::value_type>::type Use_default_tag; + + typedef typename boost::mpl::if_< + Use_default_tag, + Default_map, + Dummy_default_vertex_point_map::vertex_descriptor > + >::type type; +}; + +template +boost::optional< typename TweakedGetVertexPointMap::type > +get_vpm(const NP& np, boost::optional opm, boost::true_type) +{ + if (boost::none == opm) return boost::none; + return boost::choose_param( + boost::get_param(np, internal_np::vertex_point), + get_property_map(boost::vertex_point, *(*opm)) ); +} + +template +boost::optional< typename TweakedGetVertexPointMap::type > +get_vpm(const NP&, boost::optional opm, boost::false_type) +{ + if (boost::none == opm) return boost::none; + return typename TweakedGetVertexPointMap::type(); +} +// + +template +struct Default_visitor{ + typedef boost::graph_traits GT; + typedef typename GT::face_descriptor face_descriptor; + + void before_subface_creations(face_descriptor /*f_old*/,TriangleMesh&){} + void after_subface_creations(TriangleMesh&){} + void before_subface_created(TriangleMesh&){} + void after_subface_created(face_descriptor /*f_new*/,TriangleMesh&){} + void before_face_copy(face_descriptor /*f_old*/, TriangleMesh&, TriangleMesh&){} + void after_face_copy(face_descriptor /*f_old*/, TriangleMesh&, + face_descriptor /* f_new */, TriangleMesh&){} + +// calls commented in the code and probably incomplete due to the migration +// see NODE_VISITOR_TAG +/* + void new_node_added( std::size_t node_id, + Intersection_type type, + halfedge_descriptor principal_edge, + halfedge_descriptor additional_edge, + bool is_target_coplanar, + bool is_source_coplanar) + {} + + // autorefinement only + void new_node_added_triple_face(std::size_t node_id, + face_descriptor f1, + face_descriptor f2, + face_descriptor f3, + const TriangleMesh& tm) + {} + + void new_vertex_added(std::size_tnode_id, + vertex_descriptor vh, + TriangleMesh& tm){} +*/ +}; + template < class TriangleMesh, class VertexPointMap, class Node_id, class Node_vector, class CDT, - class NewNodeVisitor, - class NewFaceVisitor > + class UserVisitor> void triangulate_a_face( typename boost::graph_traits::face_descriptor current_face, @@ -172,8 +277,7 @@ triangulate_a_face( ::halfedge_descriptor>& edge_to_hedge, const CDT& cdt, const VertexPointMap& vpm, - NewNodeVisitor& new_node_visitor, - NewFaceVisitor& new_face_visitor) + UserVisitor& user_visitor) { typedef boost::graph_traits GT; typedef typename GT::vertex_descriptor vertex_descriptor; @@ -186,7 +290,7 @@ triangulate_a_face( BOOST_FOREACH(Node_id node_id, node_ids) { vertex_descriptor v=add_vertex(tm); - new_node_visitor.new_vertex_added(node_id, v, tm); +// user_visitor.new_vertex_added(node_id, v, tm); // NODE_VISITOR_TAG nodes.call_put(vpm, v, node_id, tm); CGAL_assertion(node_id_to_vertex.size()>node_id); node_id_to_vertex[node_id]=v; @@ -226,7 +330,7 @@ triangulate_a_face( } //grab triangles. - new_face_visitor.before_subface_creations(current_face,tm); + user_visitor.before_subface_creations(current_face,tm); for (typename CDT::Finite_faces_iterator it=cdt.finite_faces_begin(), it_end=cdt.finite_faces_end();;) { @@ -262,12 +366,14 @@ triangulate_a_face( if ( ++it!=it_end ) { + user_visitor.before_subface_created(tm); current_face=add_face(tm); - new_face_visitor.before_subface_creations(current_face,tm); + user_visitor.after_subface_created(current_face,tm); } else break; } + user_visitor.after_subface_creations(tm); } template @@ -484,6 +590,7 @@ template void import_polyline( PolygonMesh& output, @@ -499,7 +606,7 @@ void import_polyline( const IntersectionEdgeMap& intersection_edges2, const VertexPointMap& vpm1, const VertexPointMap& /*vpm2*/, - const VertexPointMap& vpm_out, + const VertexPointMapOut& vpm_out, std::vector ::edge_descriptor>& output_shared_edges) { @@ -696,20 +803,23 @@ template < bool reverse_patch_orientation, class TriangleMesh, class PatchContainer, class VertexPointMap, + class VertexPointMapOut, class EdgeMarkMapOut, - class EdgeMarkMapIn > + class EdgeMarkMapIn , + class UserVisitor> void append_patches_to_triangle_mesh( TriangleMesh& output, const boost::dynamic_bitset<>& patches_to_append, PatchContainer& patches, - const VertexPointMap& vpm_out, + const VertexPointMapOut& vpm_out, const VertexPointMap& vpm_tm, EdgeMarkMapOut& edge_mark_map_out, const EdgeMarkMapIn& edge_mark_map_in, boost::unordered_map< typename boost::graph_traits::edge_descriptor, typename boost::graph_traits::edge_descriptor - >& tm_to_output_edges) + >& tm_to_output_edges, + UserVisitor& user_visitor) { typedef boost::graph_traits GT; typedef typename GT::halfedge_descriptor halfedge_descriptor; @@ -781,7 +891,9 @@ void append_patches_to_triangle_mesh( { cpp11::array hedges = helper.halfedges(f); + user_visitor.before_face_copy(f, patches.pm, output); face_descriptor new_f = add_face(output); + user_visitor.after_face_copy(f, patches.pm, new_f, output); set_halfedge(new_f, hedges[0], output); for (int i=0;i<3;++i) @@ -899,11 +1011,13 @@ void append_patches_to_triangle_mesh( template < class TriangleMesh, class IntersectionEdgeMap, class VertexPointMap, + class VertexPointMapOut, class EdgeMarkMap1, class EdgeMarkMap2, class EdgeMarkMapOut, class IntersectionPolylines, - class PatchContainer> + class PatchContainer, + class UserVisitor> void fill_new_triangle_mesh( TriangleMesh& output, const boost::dynamic_bitset<>& patches_of_tm1_to_import, @@ -917,12 +1031,13 @@ void fill_new_triangle_mesh( const IntersectionEdgeMap& intersection_edges2, const VertexPointMap& vpm1, const VertexPointMap& vpm2, - const VertexPointMap& vpm_out, + const VertexPointMapOut& vpm_out, const EdgeMarkMap1& edge_mark_map1, const EdgeMarkMap2& edge_mark_map2, EdgeMarkMapOut& edge_mark_map_out, std::vector< typename boost::graph_traits::edge_descriptor>& - output_shared_edges) + output_shared_edges, + UserVisitor& user_visitor) { typedef boost::graph_traits GT; typedef typename GT::vertex_descriptor vertex_descriptor; @@ -961,7 +1076,8 @@ void fill_new_triangle_mesh( vpm1, edge_mark_map_out, edge_mark_map1, - tm1_to_output_edges); + tm1_to_output_edges, + user_visitor); else append_patches_to_triangle_mesh(output, patches_of_tm1_to_import, @@ -970,7 +1086,8 @@ void fill_new_triangle_mesh( vpm1, edge_mark_map_out, edge_mark_map1, - tm1_to_output_edges); + tm1_to_output_edges, + user_visitor); //import patches from tm2 if (reverse_orientation_of_patches_from_tm2) @@ -981,7 +1098,8 @@ void fill_new_triangle_mesh( vpm2, edge_mark_map_out, edge_mark_map2, - tm2_to_output_edges); + tm2_to_output_edges, + user_visitor); else append_patches_to_triangle_mesh(output, patches_of_tm2_to_import, @@ -990,7 +1108,8 @@ void fill_new_triangle_mesh( vpm2, edge_mark_map_out, edge_mark_map2, - tm2_to_output_edges); + tm2_to_output_edges, + user_visitor); } template + class EdgeMarkMapOut, + class UserVisitor> void compute_inplace_operation_delay_removal_and_insideout( TriangleMesh& tm1, TriangleMesh& tm2, @@ -1164,7 +1284,8 @@ void compute_inplace_operation_delay_removal_and_insideout( EdgeMarkMapIn1&, const EdgeMarkMapIn2& edge_mark_map2, const EdgeMarkMapOut& edge_mark_map_out1, - EdgeMap& disconnected_patches_edge_to_tm2_edge) + EdgeMap& disconnected_patches_edge_to_tm2_edge, + UserVisitor& user_visitor) { typedef boost::graph_traits GT; typedef typename GT::edge_descriptor edge_descriptor; @@ -1214,7 +1335,8 @@ void compute_inplace_operation_delay_removal_and_insideout( vpm2, edge_mark_map_out1, edge_mark_map2, - tm2_edge_to_tm1_edge); + tm2_edge_to_tm1_edge, + user_visitor); else append_patches_to_triangle_mesh(tm1, patches_of_tm2_to_import, @@ -1223,7 +1345,8 @@ void compute_inplace_operation_delay_removal_and_insideout( vpm2, edge_mark_map_out1, edge_mark_map2, - tm2_edge_to_tm1_edge); + tm2_edge_to_tm1_edge, + user_visitor); } template + class EdgeMarkMapOut1, + class UserVisitor> void compute_inplace_operation( TriangleMesh& tm1, const TriangleMesh& /*tm2*/, @@ -1321,7 +1445,8 @@ void compute_inplace_operation( boost::unordered_map< typename boost::graph_traits::edge_descriptor, typename boost::graph_traits::edge_descriptor - >& tm2_edge_to_tm1_edge) + >& tm2_edge_to_tm1_edge, + UserVisitor& user_visitor) { typedef boost::unordered_map< typename boost::graph_traits::edge_descriptor, @@ -1350,7 +1475,8 @@ void compute_inplace_operation( vpm2, edge_mark_map_out1, edge_mark_map_in2, - tm2_edge_to_tm1_edge); + tm2_edge_to_tm1_edge, + user_visitor); else append_patches_to_triangle_mesh(tm1, patches_of_tm2_to_import, @@ -1359,7 +1485,8 @@ void compute_inplace_operation( vpm2, edge_mark_map_out1, edge_mark_map_in2, - tm2_edge_to_tm1_edge); + tm2_edge_to_tm1_edge, + user_visitor); } template + class EdgeMarkMapOut1, + class UserVisitor> void compute_inplace_operation( TriangleMesh& tm1, const TriangleMesh& tm2, @@ -1419,7 +1547,8 @@ void compute_inplace_operation( const EdgeMarkMapIn1& edge_mark_map_in1, const EdgeMarkMapIn2& edge_mark_map_in2, const EdgeMarkMapOut1& edge_mark_map_out1, - const IntersectionPolylines& polylines) + const IntersectionPolylines& polylines, + UserVisitor& user_visitor) { typedef boost::graph_traits GT; typedef typename GT::edge_descriptor edge_descriptor; @@ -1442,7 +1571,8 @@ void compute_inplace_operation( edge_mark_map_in1, edge_mark_map_in2, edge_mark_map_out1, - tm2_edge_to_tm1_edge); + tm2_edge_to_tm1_edge, + user_visitor); } // function used to remove polylines imported or kept that are incident only @@ -1539,6 +1669,6 @@ void remove_disconnected_patches( } } -} } // end of namespace CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_FACE_GRAPH_UTILS_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersect_triangle_and_segment_3.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersect_triangle_and_segment_3.h index 9aba776fc47..0bde009ee57 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersect_triangle_and_segment_3.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersect_triangle_and_segment_3.h @@ -31,6 +31,7 @@ #include namespace CGAL{ +namespace Polygon_mesh_processing { namespace Corefinement{ @@ -178,6 +179,6 @@ intersection_type( } } -}} //namespace CGAL::internal_IOP +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif //CGAL_INTERNAL_PMP_INTERSECT_TRIANGLE_AND_SEGMENT_3_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_callbacks.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_callbacks.h index ffc4e12b274..87453fe3da3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_callbacks.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_callbacks.h @@ -34,6 +34,7 @@ #include namespace CGAL { +namespace Polygon_mesh_processing { namespace Corefinement { template @@ -344,6 +345,6 @@ public: } }; -} } // end of namespace CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_COREFINEMENT_INTERSECTION_CALLBACK_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h index 741ffc2ae00..d66665c4a1b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h @@ -41,6 +41,7 @@ #include namespace CGAL{ +namespace Polygon_mesh_processing { namespace Corefinement { struct Self_intersection_exception{}; @@ -1364,6 +1365,6 @@ public: }; -} } // end of namespace CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif //CGAL_POLYGON_MESH_PROCESSING_INTERNAL_COREFINEMENT_INTERSECTION_IMPL_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_nodes.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_nodes.h index 85285e265b9..dae07420937 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_nodes.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_nodes.h @@ -30,6 +30,7 @@ #include namespace CGAL { +namespace Polygon_mesh_processing { namespace Corefinement { // A class responsible for storing the intersection nodes of the intersection @@ -418,6 +419,6 @@ public: }; // end specialization -} } // end of namespace CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_COREFINEMENT_INTERSECTION_NODES_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_of_coplanar_triangles_3.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_of_coplanar_triangles_3.h index 9c4fe4d3eb3..560a5b0cff4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_of_coplanar_triangles_3.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_of_coplanar_triangles_3.h @@ -33,6 +33,7 @@ #include namespace CGAL{ +namespace Polygon_mesh_processing { namespace Corefinement{ template @@ -320,7 +321,7 @@ void intersection_coplanar_faces( intersect_cpln.cutoff_face(next(next(h2,tm2),tm2),inter_pts,h1); } -} } //namespace CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif //CGAL_PMP_INTERNAL_COREFINEMENT_INTERSECTION_OF_COPLANAR_TRIANGLES_3_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/predicates.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/predicates.h index d427dbcbf4a..94d83d56857 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/predicates.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/predicates.h @@ -29,6 +29,7 @@ #include namespace CGAL { +namespace Polygon_mesh_processing { namespace Corefinement { @@ -176,6 +177,6 @@ bool sorted_around_edge( Node_id o_prime_index, } -} } // end of namespace CGAL::Corefinement +} } } // CGAL::Polygon_mesh_processing::Corefinement #endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_COREFINEMENT_PREDICATES_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/clip.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/clip.h deleted file mode 100644 index f8613723d5d..00000000000 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/clip.h +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright (c) 2016 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// You can redistribute it and/or modify it under the terms of the GNU -// General Public License as published by the Free Software Foundation, -// either version 3 of the License, or (at your option) any later version. -// -// Licensees holding a valid commercial license may use this file in -// accordance with the commercial license agreement provided with the software. -// -// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0+ -// -// -// Author(s) : Sebastien Loriot - -#ifndef CGAL_POLYGON_MESH_PROCESSING_CLIP_H -#define CGAL_POLYGON_MESH_PROCESSING_CLIP_H - -#include - - -#include -#include -#include - -#include - -namespace CGAL{ -namespace Polygon_mesh_processing { - -namespace internal -{ -template -bool -clip_open_impl( TriangleMesh& tm, - TriangleMesh& clipper, - Ecm ecm, - const NamedParameters1& np_tm, - const NamedParameters2& np_c) -{ - // first corefine the meshes - corefine(tm, clipper, np_tm, np_c); - - typedef typename GetVertexPointMap::type Vpm; - typedef typename GetFaceIndexMap::type Fid_map; - typedef typename GetVertexIndexMap::type Vid_map; - - typedef boost::graph_traits GT; - typedef typename GetGeomTraits::type GeomTraits; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::face_descriptor face_descriptor; - - Fid_map fid_map = boost::choose_param(get_param(np_tm, internal_np::face_index), - get_property_map(boost::face_index, tm)); - Vid_map vid_map = boost::choose_param(get_param(np_tm, internal_np::vertex_index), - get_property_map(boost::vertex_index, tm)); - Vpm vpm1 = boost::choose_param(get_param(np_tm, internal_np::vertex_point), - get_property_map(vertex_point, tm)); - Vpm vpm_c = boost::choose_param(get_param(np_c, internal_np::vertex_point), - get_property_map(vertex_point, clipper)); - - // init indices if needed - helpers::init_face_indices(tm, fid_map); - helpers::init_vertex_indices(tm, vid_map); - - // set the connected component id of each face - std::vector face_cc(num_faces(tm), std::size_t(-1)); - std::size_t nb_cc = - connected_components(tm, - bind_property_maps(fid_map, make_property_map(face_cc)), - parameters::face_index_map(fid_map). - edge_is_constrained_map(ecm)); - - - boost::dynamic_bitset<> cc_not_handled(nb_cc); - cc_not_handled.set(); - std::vector ccs_to_remove; - /// \todo clipper has been modified, this is not robust if inexact constructions are used - CGAL::Side_of_triangle_mesh - side_of(clipper, vpm_c); - BOOST_FOREACH(face_descriptor f, faces(tm)) - { - std::size_t cc_id = face_cc[ get(fid_map, f) ]; - if ( !cc_not_handled.test(cc_id) ) continue; - - halfedge_descriptor h=halfedge(f, tm); - for(int i=0;i<3;++i) - { - bool no_marked_edge=true; - BOOST_FOREACH(halfedge_descriptor h2, halfedges_around_target(h, tm)) - if ( get(ecm, edge(h2, tm)) ){ - no_marked_edge=false; - break; - } - if (no_marked_edge){ - if ( side_of( get(vpm1, target(h, tm) ) ) == ON_UNBOUNDED_SIDE ) - ccs_to_remove.push_back(cc_id); - cc_not_handled.reset(cc_id); - break; - } - h=next(h, tm); - } - if (!cc_not_handled.any()) break; - } - - if (cc_not_handled.any()) - { - ///\todo handle using barycenters? won't work for coplanar faces - } - //now remove the cc - remove_connected_components(tm, - ccs_to_remove, - bind_property_maps(fid_map, make_property_map(face_cc)), - np_tm); - - return true; -} - -/// \TODO move this to property_map.h? -template -struct Constrained_edge_map -{ - typedef boost::read_write_property_map_tag category; - typedef bool value_type; - typedef bool reference; - typedef typename Set::value_type key_type; - - Constrained_edge_map() - : edge_set(NULL) - {} - - Constrained_edge_map(Set& set) - : edge_set(&set) - {} - - friend bool get(const Constrained_edge_map& map, key_type k) - { - return map.edge_set->count(k)!=0; - } - - friend void put(Constrained_edge_map& map, key_type k, bool b) - { - if (b) - map.edge_set->insert(k); - else - map.edge_set->erase(k); - } -private: - Set* edge_set; -}; - -template -bool -clip_open_impl( TriangleMesh& tm, - TriangleMesh& clipper, - boost::param_not_found, - const NamedParameters1& np_tm, - const NamedParameters2& np_c) -{ - typedef typename boost::graph_traits - ::edge_descriptor edge_descriptor; - boost::unordered_set constrained_edges; - Constrained_edge_map > - cst_map(constrained_edges); - - return clip_open_impl(tm, clipper, - cst_map, - np_tm.edge_is_constrained_map(cst_map), - np_c); -} - -} // end of internal namespace - -#ifndef DOXYGEN_RUNNING - -///\todo clipper const! -/// requires face_index_map, vertex_index_map for np_tm -/// requires face_index_map for np_c -/// if edge_is_constrained_map is not provided in np_tm a default one is -/// provided using boost::unordered_set -template -bool -clip( TriangleMesh& tm, - /*const*/ TriangleMesh& clipper, - bool close, - const NamedParameters1& np_tm, - const NamedParameters2& np_c) -{ - if (close && is_closed(tm)) - return corefine_and_compute_intersection(tm, clipper, tm, np_tm, np_c); - - return internal::clip_open_impl(tm, clipper, - get_param(np_tm, internal_np::edge_is_constrained), np_tm, np_c); -} - -/// \todo document me -template -Oriented_side -clip_to_bbox(const Plane_3& plane, - const Bbox_3& bbox, - TriangleMesh& tm_out, - const NamedParameters& np ) -{ - typedef typename GetGeomTraits::type Geom_traits; - typedef typename Geom_traits::Point_3 Point_3; - typedef typename Geom_traits::Segment_3 Segment_3; - typedef typename GetVertexPointMap::type Vpm; - - Vpm vpm_out = boost::choose_param(get_param(np, internal_np::vertex_point), - get_property_map(boost::vertex_point, tm_out)); - - - cpp11::array corners= {{ - Point_3(bbox.xmin(),bbox.ymin(),bbox.zmin()), - Point_3(bbox.xmin(),bbox.ymax(),bbox.zmin()), - Point_3(bbox.xmax(),bbox.ymax(),bbox.zmin()), - Point_3(bbox.xmax(),bbox.ymin(),bbox.zmin()), - Point_3(bbox.xmin(),bbox.ymin(),bbox.zmax()), - Point_3(bbox.xmin(),bbox.ymax(),bbox.zmax()), - Point_3(bbox.xmax(),bbox.ymax(),bbox.zmax()), - Point_3(bbox.xmax(),bbox.ymin(),bbox.zmax()) - }}; - - cpp11::array orientations = {{ - plane.oriented_side(corners[0]), - plane.oriented_side(corners[1]), - plane.oriented_side(corners[2]), - plane.oriented_side(corners[3]), - plane.oriented_side(corners[4]), - plane.oriented_side(corners[5]), - plane.oriented_side(corners[6]), - plane.oriented_side(corners[7]) - }}; - - std::vector points; - - // look for intersections on edges - cpp11::array edge_indices = {{ // 2 *12 edges - 0,1, 1,2, 2,3, 3,0, // bottom face edges - 4,5, 5,6, 6,7, 7,4, // top face edges - 0,4, 1,5, 2,6, 3,7 - }}; - - for (int i=0; i<12; ++i) - { - int i1=edge_indices[2*i], i2=edge_indices[2*i+1]; - if (orientations[i1]==ON_ORIENTED_BOUNDARY) continue; - if (orientations[i2]==ON_ORIENTED_BOUNDARY) continue; - if (orientations[i1]!=orientations[i2]) - points.push_back( - boost::get( - *intersection(plane, Segment_3(corners[i1], corners[i2]) ) - ) - ); - } - - - Oriented_side last_os = ON_ORIENTED_BOUNDARY; - for (int i=0; i<8; ++i) - if (orientations[i]!=ON_ORIENTED_BOUNDARY) - { - if (last_os==ON_ORIENTED_BOUNDARY) - last_os=orientations[i]; - else - { - if(last_os!=orientations[i]) - { - last_os=ON_ORIENTED_BOUNDARY; - break; - } - } - } - - // the intersection is the full bbox - if (last_os!=ON_ORIENTED_BOUNDARY) - return last_os; - - //add points on negative side and on the plane - for (int i=0; i<8; ++i) - if (orientations[i]!=ON_POSITIVE_SIDE) - points.push_back(corners[i]); - - // take the convex hull of the points on the negative side+intersection points - // overkill... - Polyhedron_3 P; - CGAL::convex_hull_3(points.begin(), points.end(), P); - copy_face_graph(P, tm_out, - Emptyset_iterator(), Emptyset_iterator(), Emptyset_iterator(), - get(vertex_point, P), vpm_out); - return ON_ORIENTED_BOUNDARY; -} - - -// convenience overload -template -bool -clip( TriangleMesh& tm, - /*const*/ TriangleMesh& clipper, - bool close, - const NamedParameters1& np_tm) -{ - return clip(tm, clipper, close, np_tm, parameters::all_default()); -} - -// convenience overload -template -bool -clip( TriangleMesh& tm, - /*const*/ TriangleMesh& clipper, - bool close) -{ - return clip(tm, clipper, close, parameters::all_default()); -} - -// works only with the default point map, for more complex use cases, use -// clip_to_bbox first and the other overload of clip with two meshes -/// \todo document me -template -void clip( TriangleMesh& tm, - const Plane_3& plane, - bool close) -{ - if( boost::begin(faces(tm))==boost::end(faces(tm)) ) return; - CGAL::Bbox_3 bbox = ::CGAL::Polygon_mesh_processing::bbox(tm); - //extend the bbox a bit to avoid border cases - double xd=(bbox.xmax()-bbox.xmin())/100; - double yd=(bbox.ymax()-bbox.ymin())/100; - double zd=(bbox.zmax()-bbox.zmin())/100; - bbox=CGAL::Bbox_3(bbox.xmin()-xd, bbox.ymin()-yd, bbox.zmin()-zd, - bbox.xmax()+xd, bbox.ymax()+yd, bbox.zmax()+zd); - TriangleMesh clipper; - Oriented_side os = clip_to_bbox(plane, bbox, clipper, parameters::all_default()); - - switch(os) - { - case ON_NEGATIVE_SIDE: - return; // nothing to clip, the full mesh is on the negative side - case ON_POSITIVE_SIDE: - clear(tm); // clear the mesh that is fully on the positive side - return; - default: - clip(tm, clipper, close); - } -} - -#endif // !DOXYGEN_RUNNING - -} } //end of namespace CGAL::Polygon_mesh_processing - -#endif // CGAL_POLYGON_MESH_PROCESSING_CLIP_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h index 16986c73b1b..540202f47ba 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h @@ -1640,10 +1640,6 @@ OutputIterator intersecting_meshes(const TriangleMeshRange& range, * @param tm2 second input triangulated surface mesh * @param polyline_output output iterator of polylines. Each polyline will be * given as a vector of points - * @param throw_on_self_intersection if `true`, for each input triangle mesh, - * the set of triangles closed to the intersection of `tm1` and `tm2` will be - * checked for self-intersection and `CGAL::Corefinement::Self_intersection_exception` - * will be thrown if at least one is found. * @param np1 optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below * @param np2 optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below * @@ -1652,6 +1648,11 @@ OutputIterator intersecting_meshes(const TriangleMeshRange& range, * a property map with the points associated to the vertices of `tm1` * (`tm2`). The two property map types must be the same. * \cgalParamEnd + * \cgalParamBegin{throw_on_self_intersection} if `true`, for each input triangle mesh, + * the set of triangles close to the intersection of `tm1` and `tm2` will be + * checked for self-intersection and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` + * will be thrown if at least one is found (`np1` only). + * \cgalParamEnd * \cgalNamedParamsEnd * */ @@ -1664,9 +1665,11 @@ surface_intersection(const TriangleMesh& tm1, const TriangleMesh& tm2, OutputIterator polyline_output, const NamedParameters1& np1, - const NamedParameters2& np2, - const bool throw_on_self_intersection=false) + const NamedParameters2& np2) { + const bool throw_on_self_intersection = + boost::choose_param(get_param(np1, internal_np::throw_on_self_intersection), false); + typedef typename GetVertexPointMap::const_type Vpm; typedef typename GetVertexPointMap +OutputIterator +surface_intersection(const TriangleMesh& tm1, + const TriangleMesh& tm2, + OutputIterator polyline_output, + const NamedParameters1& np1, + const NamedParameters2& np2, + const bool throw_on_self_intersection) +{ + return surface_intersection(tm1, tm2, polyline_output, + np1.throw_on_self_intersection(throw_on_self_intersection), np2); +} + +template +OutputIterator +surface_intersection(const TriangleMesh& tm1, + const TriangleMesh& tm2, + OutputIterator polyline_output, + const bool throw_on_self_intersection) +{ + return surface_intersection(tm1, tm2, polyline_output, + CGAL::Polygon_mesh_processing::parameters::throw_on_self_intersection(throw_on_self_intersection), + CGAL::Polygon_mesh_processing::parameters::all_default()); +} +#endif + namespace experimental { template diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_autorefinement.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_autorefinement.cpp index 155226be9e1..9f603bd22fa 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_autorefinement.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_autorefinement.cpp @@ -12,6 +12,19 @@ typedef CGAL::Surface_mesh Mesh; namespace PMP = CGAL::Polygon_mesh_processing; +template +struct My_visitor : + public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor +{ + void after_subface_creations(TriangleMesh&){++(*i);} + + My_visitor() + : i (new int(0) ) + {} + + boost::shared_ptr i; +}; + void test(const char* fname, std::size_t nb_polylines, std::size_t total_nb_points, std::size_t nb_vertices_after_autorefine, bool all_fixed, std::size_t nb_vertices_after_fix) { @@ -25,6 +38,7 @@ void test(const char* fname, std::size_t nb_polylines, std::size_t total_nb_poin exit(EXIT_FAILURE); } input.close(); + std::size_t nb_vertices_before_autorefine = num_vertices(mesh); // Testing surface_self_intersection() std::vector< std::vector >polylines; @@ -36,8 +50,11 @@ void test(const char* fname, std::size_t nb_polylines, std::size_t total_nb_poin assert(total_nb_points == total_nb_pt); // Testing autorefine() - PMP::experimental::autorefine(mesh); + My_visitor visitor; + PMP::experimental::autorefine(mesh, + PMP::parameters::visitor(visitor)); assert( nb_vertices_after_autorefine==num_vertices(mesh)); + assert( (nb_vertices_before_autorefine!=nb_vertices_after_autorefine)== (*(visitor.i) != 0) ); // Testing autorefine_and_remove_self_intersections() input.open(fname); diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp index 4aee863f3a6..5804b67a88f 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp @@ -10,6 +10,18 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Surface_mesh Surface_mesh; typedef CGAL::Polyhedron_3 Polyhedron_3; +template +struct My_visitor : + public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor +{ + void after_subface_creations(TriangleMesh&){++(*i);} + + My_visitor() + : i (new int(0) ) + {} + + boost::shared_ptr i; +}; void test(const char* f1, const char* f2) { @@ -26,11 +38,16 @@ void test(const char* f1, const char* f2) assert(input); input >> sm2; input.close(); + My_visitor sm_v; - CGAL::Polygon_mesh_processing::corefine(sm1, sm2); + std::size_t nb_v_before = num_vertices(sm1) + num_vertices(sm2); + CGAL::Polygon_mesh_processing::corefine(sm1, sm2, + CGAL::Polygon_mesh_processing::parameters::visitor(sm_v)); + std::size_t nb_v_after = num_vertices(sm1) + num_vertices(sm2); assert(sm1.is_valid()); assert(sm2.is_valid()); + assert((*(sm_v.i) != 0) == (nb_v_before!=nb_v_after)); std::cout << " with Polyhedron_3\n"; Polyhedron_3 P, Q; @@ -41,9 +58,16 @@ void test(const char* f1, const char* f2) input.open(f2); assert(input); input >> Q; + My_visitor sm_p; - CGAL::Polygon_mesh_processing::corefine(P, Q); + nb_v_before = num_vertices(P) + num_vertices(Q); + CGAL::Polygon_mesh_processing::corefine(P, Q, + CGAL::Polygon_mesh_processing::parameters::visitor(sm_p)); + nb_v_after = num_vertices(P) + num_vertices(Q); + assert((*(sm_p.i) != 0) == (nb_v_before!=nb_v_after)); + + assert(*(sm_v.i) == *(sm_p.i)); assert(P.is_valid()); assert(Q.is_valid()); } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_and_constraints.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_and_constraints.cpp index 2cd8a9c2a1a..d79e6f06b10 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_and_constraints.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_and_constraints.cpp @@ -166,24 +166,24 @@ void test_bool_op_no_copy( typedef boost::optional OTM; OTM none; - const CGAL::cpp11::array desired_output = + const CGAL::cpp11::array output = reverse ? CGAL::make_array(OTM(&tm2), OTM(&tm1), none, none) : CGAL::make_array(OTM(&tm1), OTM(&tm2), none, none); - PMP::boolean_operation(tm1, - tm2, - desired_output, - params::edge_is_constrained_map(ecm1), - params::edge_is_constrained_map(ecm2), - CGAL::cpp11::make_tuple(params::edge_is_constrained_map(ecm_out_union), - params::edge_is_constrained_map(ecm_out_inter), - params::no_parameters(params::edge_is_constrained_map(ecm_out_union)), - params::no_parameters(params::edge_is_constrained_map(ecm_out_union)))); + PMP::corefine_and_compute_boolean_operations(tm1, + tm2, + output, + params::edge_is_constrained_map(ecm1), + params::edge_is_constrained_map(ecm2), + CGAL::cpp11::make_tuple(params::edge_is_constrained_map(ecm_out_union), + params::edge_is_constrained_map(ecm_out_inter), + params::no_parameters(params::edge_is_constrained_map(ecm_out_union)), + params::no_parameters(params::edge_is_constrained_map(ecm_out_union)))); - // dump_constrained_edges(*(*desired_output[0]), ecm_out_union, "out_cst_union.cgal"); - // dump_constrained_edges(*(*desired_output[1]), ecm_out_inter, "out_cst_inter.cgal"); + // dump_constrained_edges(*(*output[0]), ecm_out_union, "out_cst_union.cgal"); + // dump_constrained_edges(*(*output[1]), ecm_out_inter, "out_cst_inter.cgal"); - assert( count_constrained_edges(*(*desired_output[0]), ecm_out_union)==838 ); - assert( count_constrained_edges(*(*desired_output[1]), ecm_out_inter)==475 ); + assert( count_constrained_edges(*(*output[0]), ecm_out_union)==838 ); + assert( count_constrained_edges(*(*output[1]), ecm_out_inter)==475 ); } void test_bool_op(Triangle_mesh tm1, Triangle_mesh tm2, bool reverse, const char* outname) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_bool_op.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_bool_op.cpp index 29501f793ea..97e23b20cf2 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_bool_op.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_bool_op.cpp @@ -26,7 +26,7 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef CGAL::Surface_mesh Surface_mesh; namespace PMP = CGAL::Polygon_mesh_processing; -namespace CFR = CGAL::Corefinement; +namespace CFR = PMP::Corefinement; //includes typedefs for Operations on polyhedra typedef CGAL::Polyhedron_3 Polyhedron; @@ -65,15 +65,15 @@ void run_boolean_operations( CGAL::Emptyset_iterator output_it; Facet_id_map P_facet_id_map, Q_facet_id_map; - CGAL::cpp11::array, 4 > desired_output; + CGAL::cpp11::array, 4 > output; - desired_output[Output_builder::P_UNION_Q]=boost::make_optional( &union_ ); - desired_output[Output_builder::P_INTER_Q]=boost::make_optional( &inter ); - desired_output[Output_builder::P_MINUS_Q]=boost::make_optional( &P_minus_Q ); - desired_output[Output_builder::Q_MINUS_P]=boost::make_optional( &Q_minus_P ); + output[Output_builder::P_UNION_Q]=boost::make_optional( &union_ ); + output[Output_builder::P_INTER_Q]=boost::make_optional( &inter ); + output[Output_builder::P_MINUS_Q]=boost::make_optional( &P_minus_Q ); + output[Output_builder::Q_MINUS_P]=boost::make_optional( &Q_minus_P ); Output_builder output_builder(P, Q, - desired_output, + output, Facet_id_pmap(P_facet_id_map), Facet_id_pmap(Q_facet_id_map) ); Split_visitor visitor(output_builder); @@ -169,17 +169,17 @@ void run_boolean_operations( typedef boost::optional OSM; - CGAL::cpp11::array desired_output; + CGAL::cpp11::array output; - desired_output[CFR::UNION]=OSM( &union_ ); - desired_output[CFR::INTER]=OSM( &inter ); - desired_output[CFR::TM1_MINUS_TM2]=OSM( &tm1_minus_tm2 ); - desired_output[CFR::TM2_MINUS_TM1]=OSM( &tm2_minus_tm1 ); + output[CFR::UNION]=OSM( &union_ ); + output[CFR::INTERSECTION]=OSM( &inter ); + output[CFR::TM1_MINUS_TM2]=OSM( &tm1_minus_tm2 ); + output[CFR::TM2_MINUS_TM1]=OSM( &tm2_minus_tm1 ); std::cout << " Vertices before " << tm1.number_of_vertices() << " " << tm2.number_of_vertices() << std::endl; - CGAL::cpp11::array res = PMP::boolean_operation(tm1, tm2, desired_output); + CGAL::cpp11::array res = PMP::corefine_and_compute_boolean_operations(tm1, tm2, output); std::cout << " Vertices after " << tm1.number_of_vertices() << " " << tm2.number_of_vertices() << std::endl; @@ -197,7 +197,7 @@ void run_boolean_operations( else std::cout << " Union is invalid\n"; - if ( res[CFR::INTER] ){ + if ( res[CFR::INTERSECTION] ){ std::cout << " Intersection is a valid operation\n"; assert(inter.is_valid()); #ifdef CGAL_COREFINEMENT_DEBUG @@ -239,7 +239,7 @@ void run_boolean_operations( if ( rc.check ) { if (res[CFR::UNION]!=rc.union_res || - res[CFR::INTER]!=rc.inter_res || + res[CFR::INTERSECTION]!=rc.inter_res || res[CFR::TM1_MINUS_TM2]!=rc.P_minus_Q_res || res[CFR::TM2_MINUS_TM1]!=rc.Q_minus_P_res) { diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp index 9a9d24e4ee7..ea5e22a7795 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -10,56 +10,98 @@ namespace PMP = CGAL::Polygon_mesh_processing; namespace params = PMP::parameters; typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Surface_mesh Triangle_mesh; +typedef CGAL::Surface_mesh Surface_mesh; typedef CGAL::Polyhedron_3 Polyhedron; -typedef Triangle_mesh::Property_map Constrained_edge_map; +template +void test() +{ + // test with a clipper mesh + TriangleMesh tm1, tm2; + std::ifstream input("data-coref/elephant.off"); + input >> tm1; + input.close(); + input.open("data-coref/sphere.off"); + input >> tm2; + input.close(); + + PMP::clip(tm1, tm2, + params::clip_volume(false) + .face_index_map(get(CGAL::dynamic_face_property_t(), tm1)), + params::face_index_map(get(CGAL::dynamic_face_property_t(), tm2)) + ); + assert(!CGAL::is_closed(tm1)); + + CGAL::clear(tm1); + CGAL::clear(tm2); + + input.open("data-coref/elephant.off"); + input >> tm1; + input.close(); + input.open("data-coref/sphere.off"); + input >> tm2; + input.close(); + + PMP::clip(tm1, tm2, params::clip_volume(true) + .face_index_map(get(CGAL::dynamic_face_property_t(), tm1)), + params::face_index_map(get(CGAL::dynamic_face_property_t(), tm2))); + assert(CGAL::is_closed(tm1)); + + // test with a plane + CGAL::clear(tm1); + input.open("data-coref/cube.off"); + input >> tm1; + input.close(); + + K::Plane_3 plane(0, 0, 1, -1); + + PMP::clip(tm1, plane, params::clip_volume(true)); + assert(CGAL::is_closed(tm1)); + CGAL::clear(tm1); + + input.open("data-coref/cube.off"); + input >> tm1; + input.close(); + PMP::clip(tm1, plane, params::clip_volume(false) + .use_compact_clipper(false)); + assert(!CGAL::is_closed(tm1)); + CGAL::clear(tm1); + + input.open("data-coref/cube.off"); + input >> tm1; + input.close(); + PMP::clip(tm1, plane, params::clip_volume(false) + .use_compact_clipper(true)); + assert(CGAL::is_closed(tm1)); + CGAL::clear(tm1); + + input.open("data-coref/cube.off"); + input >> tm1; + input.close(); + PMP::clip(tm1, K::Plane_3(-0.236474, 0.437732, 0.867451, -0.838791), params::clip_volume(true)); + assert(CGAL::is_closed(tm1)); + assert(!CGAL::is_empty(tm1)); + CGAL::clear(tm1); + + input.open("data-coref/cube.off"); + input >> tm1; + input.close(); + PMP::clip(tm1, K::Plane_3(0, 0, 1, 2)); + assert(CGAL::is_empty(tm1)); + CGAL::clear(tm1); + + input.open("data-coref/cube.off"); + input >> tm1; + input.close(); + PMP::clip(tm1, K::Plane_3(0, 0, 1, -2)); + assert(!CGAL::is_empty(tm1)); + CGAL::clear(tm1); +} int main() { - { - // test open clipping with Surface_mesh - Triangle_mesh tm1, tm2; - std::ifstream input("data-coref/elephant.off"); - input >> tm1; - input.close(); - input.open("data-coref/sphere.off"); - input >> tm2; - input.close(); - - Constrained_edge_map ecm1 = - tm1.add_property_map("e:cst", false).first; - - PMP::clip(tm1, tm2, false, params::edge_is_constrained_map(ecm1)); - std::ofstream output("clipped_opened.off"); - output << tm1; - - // test open clipping with Polyhedron - Polyhedron P, Q; - input.open("data-coref/elephant.off"); - input >> P; - input.close(); - input.open("data-coref/sphere.off"); - input >> Q; - - PMP::clip(P, Q, false, - params::face_index_map(get(CGAL::face_external_index, P)). - vertex_index_map(get(CGAL::vertex_external_index, P)), - params::face_index_map(get(CGAL::face_external_index, Q))); - assert(P.size_of_vertices() == tm1.number_of_vertices()); - } - { - Triangle_mesh tm1, tm2; - std::ifstream input("data-coref/elephant.off"); - input >> tm1; - input.close(); - input.open("data-coref/sphere.off"); - input >> tm2; - - PMP::clip(tm1, tm2, true); - std::ofstream output("clipped_closed.off"); - output << tm1; - } + test(); + test(); return 0; } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.cpp index ba3ef64caec..3cfa72e2748 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.cpp @@ -8,7 +8,7 @@ #include "Scene_plane_item.h" #include #include -#include +#include #include "ui_Clip_polyhedron_plugin.h" #include "Viewer.h" @@ -105,7 +105,7 @@ public : plane = NULL; //creates and link the actions actionClipPolyhedra = new QAction("Clip Polyhedra", mw); - actionClipPolyhedra->setProperty("subMenuName","Operations on Polyhedra"); + actionClipPolyhedra->setProperty("subMenuName","Polygon Mesh Processing/Corefinement"); dock_widget = new QDockWidget("Polyhedra Clipping", mw); dock_widget->setVisible(false); // do not show at the beginning ui_widget.setupUi(dock_widget); @@ -139,28 +139,39 @@ public : { Mesh* neg_side = new Mesh(*item->face_graph()); - CGAL::Polygon_mesh_processing::clip(*neg_side, - plane->plane(), - ui_widget.close_checkBox->isChecked()); - Item* new_item = new Item(neg_side); - new_item->setName(QString("%1 on %2").arg(item->name()).arg("negative side")); - new_item->setColor(item->color()); - new_item->setRenderingMode(item->renderingMode()); - new_item->setVisible(item->visible()); - scene->addItem(new_item); - new_item->invalidateOpenGLBuffers(); - // part on the positive side - Mesh* pos_side = new Mesh(*item->face_graph()); - CGAL::Polygon_mesh_processing::clip(*pos_side, - plane->plane().opposite(), - ui_widget.close_checkBox->isChecked()); - new_item = new Item(pos_side); - new_item->setName(QString("%1 on %2").arg(item->name()).arg("positive side")); - new_item->setColor(item->color()); - new_item->setRenderingMode(item->renderingMode()); - new_item->setVisible(item->visible()); - scene->addItem(new_item); - new_item->invalidateOpenGLBuffers(); + try { + CGAL::Polygon_mesh_processing::clip(*neg_side, + plane->plane(), + CGAL::Polygon_mesh_processing::parameters::clip_volume( + ui_widget.close_checkBox->isChecked()). + throw_on_self_intersection(true)); + Item* new_item = new Item(neg_side); + new_item->setName(QString("%1 on %2").arg(item->name()).arg("negative side")); + new_item->setColor(item->color()); + new_item->setRenderingMode(item->renderingMode()); + new_item->setVisible(item->visible()); + scene->addItem(new_item); + new_item->invalidateOpenGLBuffers(); + // part on the positive side + Mesh* pos_side = new Mesh(*item->face_graph()); + CGAL::Polygon_mesh_processing::clip(*pos_side, + plane->plane().opposite(), + CGAL::Polygon_mesh_processing::parameters::clip_volume( + ui_widget.close_checkBox->isChecked()). + throw_on_self_intersection(true)); + + new_item = new Item(pos_side); + new_item->setName(QString("%1 on %2").arg(item->name()).arg("positive side")); + new_item->setColor(item->color()); + new_item->setRenderingMode(item->renderingMode()); + new_item->setVisible(item->visible()); + scene->addItem(new_item); + new_item->invalidateOpenGLBuffers(); + } + catch(CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception) + { + messages->warning(tr("The requested operation is not possible due to the presence of self-intersections in the region handled.")); + } } public Q_SLOTS: void on_plane_destroyed() @@ -230,17 +241,28 @@ public Q_SLOTS: if (ui_widget.clip_radioButton->isChecked()) { - if(sm_item) - { - CGAL::Polygon_mesh_processing::clip(*(sm_item->face_graph()), - plane->plane(), - ui_widget.close_checkBox->isChecked()); + try{ + if(sm_item) + { + CGAL::Polygon_mesh_processing::clip(*(sm_item->face_graph()), + plane->plane(), + CGAL::Polygon_mesh_processing::parameters::clip_volume( + ui_widget.close_checkBox->isChecked()). + throw_on_self_intersection(true)); + } + else + { + CGAL::Polygon_mesh_processing::clip(*(poly_item->face_graph()), + plane->plane(), + CGAL::Polygon_mesh_processing::parameters::clip_volume( + ui_widget.close_checkBox->isChecked()). + throw_on_self_intersection(true)); + + } } - else + catch(CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception) { - CGAL::Polygon_mesh_processing::clip(*(poly_item->face_graph()), - plane->plane(), - ui_widget.close_checkBox->isChecked()); + messages->warning(tr("The requested operation is not possible due to the presence of self-intersections in the region handled.")); } item->invalidateOpenGLBuffers(); viewer->update(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Corefinement_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Corefinement_plugin.cpp index 68c90b62a24..813adb803e6 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Corefinement_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Corefinement_plugin.cpp @@ -12,6 +12,8 @@ using namespace CGAL::Three; namespace PMP = CGAL::Polygon_mesh_processing; +namespace params = PMP::parameters; + class Polyhedron_demo_corefinement_sm_plugin : public QObject, public Polyhedron_demo_plugin_helper @@ -165,11 +167,17 @@ private: } QApplication::setOverrideCursor(Qt::WaitCursor); - PMP::corefine(*item1->face_graph(), *item2->face_graph()); - item1->invalidateOpenGLBuffers(); - item2->invalidateOpenGLBuffers(); - scene->itemChanged(item2); - scene->itemChanged(item1); + try{ + PMP::corefine(*item1->face_graph(), *item2->face_graph(), params::throw_on_self_intersection(true)); + item1->invalidateOpenGLBuffers(); + item2->invalidateOpenGLBuffers(); + scene->itemChanged(item2); + scene->itemChanged(item1); + } + catch(CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception) + { + messages->warning(tr("The requested operation is not possible due to the presence of self-intersections in the neighborhood of the intersection.")); + } // default cursor QApplication::restoreOverrideCursor(); } @@ -193,46 +201,53 @@ private: FaceGraph* new_poly = new FaceGraph(); QString str_op; FaceGraph P, Q; - switch(op) + try{ + switch(op) + { + case CRF_UNION: + P = *first_item->face_graph(), Q = *item->face_graph(); + if (! PMP::corefine_and_compute_union(P, Q, *new_poly, params::throw_on_self_intersection(true)) ) + { + delete new_poly; + messages->warning(tr("The result of the requested operation is not manifold and has not been computed.")); + // default cursor + QApplication::restoreOverrideCursor(); + return; + } + str_op = "Union"; + break; + case CRF_INTER: + P = *first_item->polyhedron(), Q = *item->polyhedron(); + if (! PMP::corefine_and_compute_intersection(P, Q, *new_poly, params::throw_on_self_intersection(true)) ) + { + delete new_poly; + messages->warning(tr("The result of the requested operation is not manifold and has not been computed.")); + // default cursor + QApplication::restoreOverrideCursor(); + return; + } + str_op = "Intersection"; + break; + case CRF_MINUS_OP: + std::swap(first_item, item); + CGAL_FALLTHROUGH; + case CRF_MINUS: + P = *first_item->polyhedron(), Q = *item->polyhedron(); + if (! PMP::corefine_and_compute_difference(P, Q, *new_poly, params::throw_on_self_intersection(true)) ) + { + delete new_poly; + messages->warning(tr("The result of the requested operation is not manifold and has not been computed.")); + // default cursor + QApplication::restoreOverrideCursor(); + return; + } + str_op = "Difference"; + } + } + catch(CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception) { - case CRF_UNION: - P = *first_item->face_graph(), Q = *item->face_graph(); - if (! PMP::corefine_and_compute_union(P, Q, *new_poly) ) - { - delete new_poly; - messages->warning(tr("The result of the requested operation is not manifold and has not been computed.")); - // default cursor - QApplication::restoreOverrideCursor(); - return; - } - str_op = "Union"; - break; - case CRF_INTER: - P = *first_item->polyhedron(), Q = *item->polyhedron(); - if (! PMP::corefine_and_compute_intersection(P, Q, *new_poly) ) - { - delete new_poly; - messages->warning(tr("The result of the requested operation is not manifold and has not been computed.")); - // default cursor - QApplication::restoreOverrideCursor(); - return; - } - str_op = "Intersection"; - break; - case CRF_MINUS_OP: - std::swap(first_item, item); - CGAL_FALLTHROUGH; - case CRF_MINUS: - P = *first_item->polyhedron(), Q = *item->polyhedron(); - if (! PMP::corefine_and_compute_difference(P, Q, *new_poly) ) - { - delete new_poly; - messages->warning(tr("The result of the requested operation is not manifold and has not been computed.")); - // default cursor - QApplication::restoreOverrideCursor(); - return; - } - str_op = "Difference"; + messages->warning(tr("The requested operation is not possible due to the presence of self-intersections in the neighborhood of the intersection.")); + QApplication::restoreOverrideCursor(); } first_item->invalidateOpenGLBuffers(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp index 7bab9f586a7..f2f7ddf66b7 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp @@ -203,7 +203,13 @@ void Polyhedron_demo_repair_polyhedron_plugin::on_actionAutorefine_triggered(Sce qobject_cast(scene->item(index)); if (poly_item) { - CGAL::Polygon_mesh_processing::experimental::autorefine(*poly_item->polyhedron()); + try{ + CGAL::Polygon_mesh_processing::experimental::autorefine(*poly_item->polyhedron()); + } + catch(CGAL::Polygon_mesh_processing::Corefinement::Triple_intersection_exception) + { + messages->warning(tr("The result of the requested operation is not handled (triple intersection).")); + } poly_item->invalidateOpenGLBuffers(); Q_EMIT poly_item->itemChanged(); } @@ -225,11 +231,17 @@ void Polyhedron_demo_repair_polyhedron_plugin::on_actionAutorefineAndRMSelfInter qobject_cast(scene->item(index)); if (poly_item) { - bool solved = - CGAL::Polygon_mesh_processing::experimental:: - autorefine_and_remove_self_intersections(*poly_item->polyhedron()); - if (!solved) - messages->information(tr("Self-intersection could not be removed due to non-manifold edges in the output")); + try{ + bool solved = + CGAL::Polygon_mesh_processing::experimental:: + autorefine_and_remove_self_intersections(*poly_item->polyhedron()); + if (!solved) + messages->information(tr("Self-intersection could not be removed due to non-manifold edges in the output")); + } + catch(CGAL::Polygon_mesh_processing::Corefinement::Triple_intersection_exception) + { + messages->warning(tr("The result of the requested operation is not handled (triple intersection).")); + } poly_item->invalidateOpenGLBuffers(); Q_EMIT poly_item->itemChanged(); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Surface_intersection_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Surface_intersection_plugin.cpp index 9f9402a2030..4b6903fa7eb 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Surface_intersection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Surface_intersection_plugin.cpp @@ -150,7 +150,7 @@ void Polyhedron_demo_intersection_plugin::intersectionSurfaces() std::back_inserter(new_item->polylines), true); } - catch(CGAL::Corefinement::Self_intersection_exception) + catch(CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception) { QMessageBox::warning((QWidget*)NULL, tr("Self-intersections Found"),