Merge pull request #3111 from sloriot/PMP-coref_preserve_face_attributes

Add a visitor to track the creation of faces in corefinement functions
This commit is contained in:
Laurent Rineau 2018-06-28 16:09:37 +02:00
commit 2d9000efde
40 changed files with 1993 additions and 912 deletions

View File

@ -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)

View File

@ -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) \

View File

@ -67,6 +67,9 @@ CGAL_add_named_parameter(overlap_test_t, overlap_test, do_overlap_test_of_bounde
CGAL_add_named_parameter(preserve_genus_t, preserve_genus, preserve_genus)
CGAL_add_named_parameter(apply_per_connected_component_t, apply_per_connected_component, apply_per_connected_component)
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)

View File

@ -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);
@ -87,8 +90,8 @@ void test(const NamedParameters& np)
assert(get_param(np, CGAL::internal_np::weight_calculator).v == 39);
assert(get_param(np, CGAL::internal_np::preserve_genus).v == 40);
assert(get_param(np, CGAL::internal_np::verbosity_level).v == 41);
assert(get_param(np, CGAL::internal_np::apply_per_connected_component).v == 42);
assert(get_param(np, CGAL::internal_np::projection_functor).v == 43);
assert(get_param(np, CGAL::internal_np::projection_functor).v == 42);
assert(get_param(np, CGAL::internal_np::apply_per_connected_component).v == 46);
// Test types
@ -140,6 +143,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));
@ -154,8 +160,8 @@ void test(const NamedParameters& np)
check_same_type<39>(get_param(np, CGAL::internal_np::weight_calculator));
check_same_type<40>(get_param(np, CGAL::internal_np::preserve_genus));
check_same_type<41>(get_param(np, CGAL::internal_np::verbosity_level));
check_same_type<42>(get_param(np, CGAL::internal_np::apply_per_connected_component));
check_same_type<43>(get_param(np, CGAL::internal_np::projection_functor));
check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor));
check_same_type<46>(get_param(np, CGAL::internal_np::apply_per_connected_component));
}
int main()
@ -206,8 +212,11 @@ int main()
.weight_calculator(A<39>(39))
.preserve_genus(A<40>(40))
.verbosity_level(A<41>(41))
.apply_per_connected_component(A<42>(42))
.projection_functor(A<43>(43))
.projection_functor(A<42>(42))
.throw_on_self_intersection(A<43>(43))
.clip_volume(A<44>(44))
.use_compact_clipper(A<45>(45))
.apply_per_connected_component(A<46>(46))
);
return EXIT_SUCCESS;

View File

@ -68,6 +68,17 @@ Release date: September 2018
- 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()`

View File

@ -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<typename K::Intersect_3(Plane,Line)>::type
res = typename K::Intersect_3()(plane,line);
CGAL_assertion(res!=boost::none);
const Point* e_pt = boost::get<Point>(&(*res));
CGAL_assertion(e_pt!=NULL);
return *e_pt;
}
};
template <typename K>

View File

@ -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);
/// @}
};

View File

@ -332,6 +332,39 @@ of a mesh independently.\n
\b Type : `bool` \n
\b Default value is `false`
\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
*/

View File

@ -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
*/

View File

@ -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

View File

@ -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
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -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")

View File

@ -0,0 +1,73 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> 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<boost::optional<Mesh*>, 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<bool, 4> 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;
}

View File

@ -0,0 +1,103 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <boost/container/flat_map.hpp>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
namespace PMP = CGAL::Polygon_mesh_processing;
struct Visitor :
public PMP::Corefinement::Default_visitor<Mesh>
{
typedef Mesh::Face_index face_descriptor;
boost::container::flat_map<const Mesh*, Mesh::Property_map<Mesh::Face_index, int> > 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<Mesh::Face_index, int>
mesh1_id = mesh1.add_property_map<Mesh::Face_index, int>("f:id", -1).first,
mesh2_id = mesh2.add_property_map<Mesh::Face_index, int>("f:id", -1).first,
out_id = out.add_property_map<Mesh::Face_index, int>("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;
}

View File

@ -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 <CGAL/license/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/AABB_triangle_primitive.h>
namespace CGAL{
namespace Polygon_mesh_processing {
namespace internal
{
template <class TriangleMesh,
class NamedParameters1,
class NamedParameters2>
bool
clip_open_impl( TriangleMesh& tm,
TriangleMesh& clipper,
const NamedParameters1& np_tm,
const NamedParameters2& np_c)
{
typedef typename GetVertexPointMap<TriangleMesh,
NamedParameters1>::type Vpm;
typedef typename GetGeomTraits<TriangleMesh, NamedParameters2>::type GeomTraits;
typedef boost::graph_traits<TriangleMesh> 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<typename GeomTraits::Triangle_3> Clipper_triangles;
typedef typename Clipper_triangles::iterator Tr_iterator;
typedef CGAL::AABB_triangle_primitive<GeomTraits, Tr_iterator> Primitive;
typedef CGAL::AABB_traits<GeomTraits, Primitive> AABB_triangle_traits;
typedef CGAL::AABB_tree<AABB_triangle_traits> 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<TriangleMesh, GeomTraits, Vpm, Clipper_tree> side_of(clipper_tree);
// Second corefine the meshes
typedef CGAL::dynamic_edge_property_t<bool> Ecm_tag;
typedef typename boost::property_map<TriangleMesh, Ecm_tag>::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<TriangleMesh,
NamedParameters1>::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<std::size_t> Vid_tag;
typedef typename boost::property_map<TriangleMesh, Vid_tag>::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<std::size_t> 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 <std::size_t> 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 <class Geom_traits, class Plane_3, class Point_3>
int
inter_pt_index(int i, int j,
const Plane_3& plane,
std::vector<Point_3>& points,
std::map<std::pair<int,int>, int>& id_map)
{
std::pair<std::map<std::pair<int,int>, int>::iterator, bool> res =
id_map.insert(std::make_pair(make_sorted_pair(i,j),
static_cast<int> (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 <class Plane_3,
class TriangleMesh,
class NamedParameters>
Oriented_side
clip_to_bbox(const Plane_3& plane,
const Bbox_3& bbox,
TriangleMesh& tm_out,
const NamedParameters& np )
{
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Geom_traits;
typedef typename Geom_traits::Point_3 Point_3;
typedef typename GetVertexPointMap<TriangleMesh,
NamedParameters>::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<Point_3> 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<CGAL::Oriented_side,8> 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<int, 24> 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<std::pair<int,int>, int> id_map;
std::vector< std::vector<int> > output_faces(6);
bool all_in = true;
bool all_out = true;
std::set<int> 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<Geom_traits>(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<Geom_traits>(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<TriangleMesh> 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<int, vertex_descriptor> 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<int,int>, 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<int>& 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<halfedge_descriptor> 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<typename std::map< std::pair<int,int>,
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 <class TriangleMesh,
class NamedParameters1,
class NamedParameters2>
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 <class TriangleMesh, class NamedParameters>
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<std::size_t>(), tm)),
parameters::face_index_map(get(CGAL::dynamic_face_property_t<std::size_t>(), clipper)));
}
template <class TriangleMesh, class NamedParameters>
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 <class TriangleMesh,
class NamedParameters>
bool clip( TriangleMesh& tm,
#ifdef DOXYGEN_RUNNING
const Plane_3& plane,
#else
const typename GetGeomTraits<TriangleMesh, NamedParameters>::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<TriangleMesh, CGAL::face_index_t>());
}
/// \cond SKIP_IN_MANUAL
// convenience overloads
template <class TriangleMesh>
bool clip( TriangleMesh& tm,
const typename GetGeomTraits<TriangleMesh>::type::Plane_3& plane)
{
return clip(tm, plane, parameters::all_default());
}
// convenience overload
template <class TriangleMesh,
class NamedParameters1>
bool
clip( TriangleMesh& tm,
TriangleMesh& clipper,
const NamedParameters1& np_tm)
{
return clip(tm, clipper, np_tm, parameters::all_default());
}
// convenience overload
template <class TriangleMesh>
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

View File

@ -34,6 +34,13 @@
#include <CGAL/iterator.h>
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 <class TriangleMesh>
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<std::size_t> 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<i>(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<TriangleMesh> \
> ::type Ecm_out_##I; \
Ecm_out_##I ecm_out_##I = \
boost::choose_param( get_param(cpp11::get<I>(nps_out), internal_np::edge_is_constrained), \
boost::choose_param( boost::get_param(cpp11::get<I>(nps_out), internal_np::edge_is_constrained), \
Corefinement::No_mark<TriangleMesh>() );
/**
\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 <class TriangleMesh,
class NamedParameters1,
class NamedParameters2,
@ -255,17 +349,19 @@ template <class TriangleMesh,
class NamedParametersOut2,
class NamedParametersOut3>
cpp11::array<bool,4>
boolean_operation( TriangleMesh& tm1,
TriangleMesh& tm2,
const cpp11::array< boost::optional<TriangleMesh*>,4>& desired_output,
const NamedParameters1& np1,
const NamedParameters2& np2,
const cpp11::tuple<NamedParametersOut0,
NamedParametersOut1,
NamedParametersOut2,
NamedParametersOut3>& nps_out,
const bool throw_on_self_intersection = false )
corefine_and_compute_boolean_operations(
TriangleMesh& tm1,
TriangleMesh& tm2,
const cpp11::array< boost::optional<TriangleMesh*>,4>& output,
const NamedParameters1& np1,
const NamedParameters2& np2,
const cpp11::tuple<NamedParametersOut0,
NamedParametersOut1,
NamedParametersOut2,
NamedParametersOut3>& 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<Vpm,Vpm2>::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<Vpm*, 4> output_vpms;
std::vector<Vpm> 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<Vpm>::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<Point_3, NamedParametersOut0, TriangleMesh>,
Corefinement::TweakedGetVertexPointMap<Point_3, NamedParametersOut1, TriangleMesh>,
Corefinement::TweakedGetVertexPointMap<Point_3, NamedParametersOut2, TriangleMesh>,
Corefinement::TweakedGetVertexPointMap<Point_3, NamedParametersOut3, TriangleMesh>
> 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<Point_3>(cpp11::get<0>(nps_out), output[0],
typename cpp11::tuple_element<0, Vpm_out_tuple_helper>::type::Use_default_tag()),
Corefinement::get_vpm<Point_3>(cpp11::get<1>(nps_out), output[1],
typename cpp11::tuple_element<1, Vpm_out_tuple_helper>::type::Use_default_tag()),
Corefinement::get_vpm<Point_3>(cpp11::get<2>(nps_out), output[2],
typename cpp11::tuple_element<2, Vpm_out_tuple_helper>::type::Use_default_tag()),
Corefinement::get_vpm<Point_3>(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<Corefinement::UNION>(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<Corefinement::INTERSECTION>(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<Fid_map,Fid_map2>::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<TriangleMesh>//default
> ::type User_visitor;
User_visitor uv( boost::choose_param( boost::get_param(np1, internal_np::graph_visitor),
Corefinement::Default_visitor<TriangleMesh>() ) );
// surface intersection algorithm call
typedef Corefinement::Default_node_visitor<TriangleMesh> Dnv;
typedef Corefinement::Default_face_visitor<TriangleMesh> Dfv;
typedef Corefinement::Face_graph_output_builder<TriangleMesh,
Vpm,
Vpm_out_tuple,
Fid_map,
Default,
Ecm_in,
Edge_mark_map_tuple > Ob;
Edge_mark_map_tuple,
User_visitor> Ob;
typedef Corefinement::Visitor<TriangleMesh,Vpm,Ob,Ecm_in> 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<TriangleMesh,Vpm,Visitor >
functor(tm1, tm2, vpm1, vpm2, Visitor(dnv,dfv,ob,ecm_in));
Corefinement::Intersection_of_triangle_meshes<TriangleMesh, Vpm, Algo_visitor >
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 <class TriangleMesh>
cpp11::array<bool,4>
boolean_operation( TriangleMesh& tm1,
TriangleMesh& tm2,
const cpp11::array< boost::optional<TriangleMesh*>,4>& desired_output,
const bool throw_on_self_intersection = false )
corefine_and_compute_boolean_operations(
TriangleMesh& tm1,
TriangleMesh& tm2,
const cpp11::array< boost::optional<TriangleMesh*>,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 <class TriangleMesh, class NamedParameters1>
cpp11::array<bool,4>
corefine_and_compute_boolean_operations(
TriangleMesh& tm1,
TriangleMesh& tm2,
const cpp11::array< boost::optional<TriangleMesh*>,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 <class TriangleMesh, class NamedParameters1, class NamedParameters2>
cpp11::array<bool,4>
corefine_and_compute_boolean_operations(
TriangleMesh& tm1,
TriangleMesh& tm2,
const cpp11::array< boost::optional<TriangleMesh*>,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<TriangleMesh*>,4> desired_output;
desired_output[Corefinement::UNION]=&tm_out;
cpp11::array< boost::optional<TriangleMesh*>,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<TriangleMesh*>,4> desired_output;
desired_output[Corefinement::INTER]=&tm_out;
cpp11::array< boost::optional<TriangleMesh*>,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<TriangleMesh*>,4> desired_output;
desired_output[TM1_MINUS_TM2]=&tm_out;
using namespace CGAL::Polygon_mesh_processing::Corefinement;
cpp11::array< boost::optional<TriangleMesh*>,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<TriangleMesh,
NamedParameters1>::type Vpm;
@ -625,10 +794,10 @@ corefine_and_compute_difference( TriangleMesh& tm1,
static const bool same_vpm = (boost::is_same<Vpm,Vpm2>::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<TriangleMesh>//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<TriangleMesh>() );
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<TriangleMesh>() );
typedef Corefinement::Ecm_bind<TriangleMesh, Ecm1, Ecm2> 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<TriangleMesh>//default
> ::type User_visitor;
User_visitor uv( boost::choose_param( boost::get_param(np1, internal_np::graph_visitor),
Corefinement::Default_visitor<TriangleMesh>() ) );
// surface intersection algorithm call
typedef Corefinement::Default_node_visitor<TriangleMesh> Dnv;
typedef Corefinement::Default_face_visitor<TriangleMesh> Dfv;
typedef Corefinement::No_extra_output_from_corefinement<TriangleMesh> Ob;
typedef Corefinement::Visitor<TriangleMesh,Vpm,Ob,Ecm> 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<TriangleMesh,Vpm,Visitor >
functor(tm1, tm2, vpm1, vpm2, Visitor(dnv,dfv,ob,ecm));
Corefinement::Intersection_of_triangle_meshes<TriangleMesh, Vpm, Algo_visitor>
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<TriangleMesh,
NamedParameters>::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<TriangleMesh>() );
// User visitor
typedef typename boost::lookup_named_param_def <
internal_np::graph_visitor_t,
NamedParameters,
Corefinement::Default_visitor<TriangleMesh>//default
> ::type User_visitor;
User_visitor uv( boost::choose_param( boost::get_param(np, internal_np::graph_visitor),
Corefinement::Default_visitor<TriangleMesh>() ) );
// surface intersection algorithm call
typedef Corefinement::Default_node_visitor<TriangleMesh> Dnv;
typedef Corefinement::Default_face_visitor<TriangleMesh> Dfv;
typedef Corefinement::No_extra_output_from_corefinement<TriangleMesh> Ob;
typedef Default D;
typedef Corefinement::Visitor<TriangleMesh,Vpm,Ob,Ecm,D,D,true> 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<TriangleMesh,Vpm,Visitor >
functor(tm, vpm, Visitor(dnv,dfv,ob,ecm) );
Corefinement::Intersection_of_triangle_meshes<TriangleMesh, Vpm, Algo_visitor>
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<TriangleMesh,
NamedParameters>::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<TriangleMesh,
NamedParameters>::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<TriangleMesh>//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<TriangleMesh>() );
// User visitor
typedef typename boost::lookup_named_param_def <
internal_np::graph_visitor_t,
NamedParameters,
Corefinement::Default_visitor<TriangleMesh>//default
> ::type User_visitor;
User_visitor uv( boost::choose_param( boost::get_param(np, internal_np::graph_visitor),
Corefinement::Default_visitor<TriangleMesh>() ) );
// surface intersection algorithm call
typedef Corefinement::Default_node_visitor<TriangleMesh> Dnv;
typedef Corefinement::Default_face_visitor<TriangleMesh> Dfv;
typedef Corefinement::Output_builder_for_autorefinement<TriangleMesh,
Vpm,
Fid_map,
Ecm,
Default > Ob;
typedef Default D;
typedef Corefinement::Visitor<TriangleMesh,Vpm,Ob,Ecm,D,D,true> 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<TriangleMesh,Vpm,Visitor >
functor(tm, vpm, Visitor(dnv,dfv,ob,ecm) );
Corefinement::Intersection_of_triangle_meshes<TriangleMesh, Vpm, Algo_visitor>
functor(tm, vpm, Algo_visitor(uv,ob,ecm) );
functor(CGAL::Emptyset_iterator(), true);
@ -941,22 +1132,60 @@ template <class TriangleMesh, class NamedParameters1>
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 <class TriangleMesh>
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 <class TriangleMesh,
class NamedParameters1,
class NamedParameters2>
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 <class TriangleMesh, class NamedParameters1>
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 <class TriangleMesh>
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 {

View File

@ -36,29 +36,44 @@
#include <boost/dynamic_bitset.hpp>
#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 TriangleMesh,
class VertexPointMap,
class VpmOutTuple,
class FaceIdMap,
class Kernel_=Default,
class EdgeMarkMapBind_=Default,
class EdgeMarkMapTuple_=Default >
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<TriangleMesh>,
No_mark<TriangleMesh>,
No_mark<TriangleMesh> > >::type EdgeMarkMapTuple;
typedef typename Default::Get<
UserVisitor_, Default_visitor<TriangleMesh> >::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<VertexPointMap*, 4 >& output_vpms;
const VpmOutTuple& output_vpms;
EdgeMarkMapTuple& out_edge_mark_maps;
UserVisitor& user_visitor;
// output meshes
const cpp11::array<boost::optional<TriangleMesh*>, 4>& desired_output;
const cpp11::array<boost::optional<TriangleMesh*>, 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<class EdgeMarkMap>
void mark_edges(const EdgeMarkMap& edge_mark_map,
const std::vector<edge_descriptor>& edges)
const std::vector<edge_descriptor>& edges)
{
BOOST_FOREACH(edge_descriptor ed, edges)
put(edge_mark_map, ed, true);
}
void mark_edges(const No_mark<TriangleMesh>&,
const std::vector<edge_descriptor>&)
const std::vector<edge_descriptor>&)
{} //nothing to do
template<class EdgeMarkMapTuple>
@ -336,17 +354,19 @@ public:
const FaceIdMap& fids1,
const FaceIdMap& fids2,
EdgeMarkMapBind& marks_on_input_edges,
const cpp11::array<VertexPointMap*, 4>& output_vpms,
const VpmOutTuple& output_vpms,
EdgeMarkMapTuple& out_edge_mark_maps,
UserVisitor& user_visitor,
const cpp11::array<
boost::optional<TriangleMesh*>, 4 >& desired_output)
boost::optional<TriangleMesh*>, 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<Boolean_operation> out_of_place_operations;
Boolean_operation inplace_operation_tm1=NONE,
inplace_operation_tm2=NONE;
std::vector<Boolean_operation_type> 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<Boolean_operation>(i);
Boolean_operation_type operation=enum_cast<Boolean_operation_type>(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<edge_descriptor> 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<BO_type>(output_vpms), \
marks_on_input_edges.ecm1, \
marks_on_input_edges.ecm2, \
cpp11::get<BO_type>(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<BO_type>(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<BO_type>(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<TriangleMesh>(
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<TriangleMesh>( \
tm1, marks_on_input_edges.ecm1, \
cpp11::get<BO_type>(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<BO_type>(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<BO_type>(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

View File

@ -29,6 +29,7 @@
#include <CGAL/property_map.h>
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

View File

@ -40,6 +40,7 @@
#include <boost/dynamic_bitset.hpp>
namespace CGAL {
namespace Polygon_mesh_processing {
namespace Corefinement {
namespace PMP=Polygon_mesh_processing;
@ -1012,7 +1013,7 @@ public:
};
} } // CGAL::Corefinement
} } } // CGAL::Corefinement
#include <CGAL/enable_warnings.h>

View File

@ -36,46 +36,11 @@
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
namespace CGAL{
namespace Polygon_mesh_processing {
namespace Corefinement{
// TODO option to ignore internal edges for patches of coplanar faces
template <class TriangleMesh>
struct Default_node_visitor{
typedef boost::graph_traits<TriangleMesh> 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 <class TriangleMesh>
struct Default_face_visitor{
typedef boost::graph_traits<TriangleMesh> 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 <class G, class Ecm1, class Ecm2=Ecm1>
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<EdgeMarkMapBind_,
Ecm_bind<TriangleMesh, No_mark<TriangleMesh> > >::type EdgeMarkMapBind;
typedef typename Default::Get<OutputBuilder_,
No_extra_output_from_corefinement<TriangleMesh> >::type OutputBuilder;
typedef typename Default::Get<
NewNodeVisitor_, Default_node_visitor<TriangleMesh> >::type NewNodeVisitor;
typedef typename Default::Get<
NewFaceVisitor_, Default_face_visitor<TriangleMesh> >::type NewFaceVisitor;
UserVisitor_, Default_visitor<TriangleMesh> >::type UserVisitor;
// config flags
public:
@ -211,8 +173,7 @@ private:
std::map< Node_id,std::set<Node_id> > 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<TriangleMesh*>(&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<TriangleMesh*>(&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 <CGAL/enable_warnings.h>

View File

@ -28,11 +28,14 @@
#include <CGAL/Polygon_mesh_processing/orientation.h>
#include <CGAL/property_map.h>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/mpl/if.hpp>
#include <fstream>
#include <sstream>
#include <set>
namespace CGAL {
namespace Polygon_mesh_processing {
namespace Corefinement {
template <typename G>
@ -152,13 +155,115 @@ void copy_edge_mark(G&,
No_mark<G>&)
{} // nothing to do
// Parts to get default property maps for output meshes based on the value type
// of input vertex point maps.
template <typename Point_3, typename vertex_descriptor>
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 <class Point_3, class NamedParameter, class PolygonMesh>
struct TweakedGetVertexPointMap
{
typedef typename GetVertexPointMap<PolygonMesh,
NamedParameter>::type Default_map;
typedef typename boost::is_same<Point_3,
typename boost::property_traits<Default_map>::value_type>::type Use_default_tag;
typedef typename boost::mpl::if_<
Use_default_tag,
Default_map,
Dummy_default_vertex_point_map<Point_3,
typename boost::graph_traits<PolygonMesh>::vertex_descriptor >
>::type type;
};
template <class PT, class NP, class PM>
boost::optional< typename TweakedGetVertexPointMap<PT, NP, PM>::type >
get_vpm(const NP& np, boost::optional<PM*> 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 <class PT, class NP, class PM>
boost::optional< typename TweakedGetVertexPointMap<PT, NP, PM>::type >
get_vpm(const NP&, boost::optional<PM*> opm, boost::false_type)
{
if (boost::none == opm) return boost::none;
return typename TweakedGetVertexPointMap<PT, NP, PM>::type();
}
//
template <class TriangleMesh>
struct Default_visitor{
typedef boost::graph_traits<TriangleMesh> 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<TriangleMesh>::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<TriangleMesh> 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 <class PolygonMesh>
@ -484,6 +590,7 @@ template <class PolygonMesh,
class EdgeMap,
class VertexMap,
class VertexPointMap,
class VertexPointMapOut,
class IntersectionEdgeMap>
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<typename boost::graph_traits<PolygonMesh>
::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<TriangleMesh>::edge_descriptor,
typename boost::graph_traits<TriangleMesh>::edge_descriptor
>& tm_to_output_edges)
>& tm_to_output_edges,
UserVisitor& user_visitor)
{
typedef boost::graph_traits<TriangleMesh> GT;
typedef typename GT::halfedge_descriptor halfedge_descriptor;
@ -781,7 +891,9 @@ void append_patches_to_triangle_mesh(
{
cpp11::array<halfedge_descriptor,3> 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<TriangleMesh>::edge_descriptor>&
output_shared_edges)
output_shared_edges,
UserVisitor& user_visitor)
{
typedef boost::graph_traits<TriangleMesh> 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<false>(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<false>(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 TriangleMesh,
@ -1149,7 +1268,8 @@ template <class TriangleMesh,
class VertexPointMap,
class EdgeMarkMapIn1,
class EdgeMarkMapIn2,
class EdgeMarkMapOut>
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<TriangleMesh> 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<false>(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 TriangleMesh,
@ -1303,7 +1426,8 @@ template <class TriangleMesh,
class VertexPointMap,
class EdgeMarkMapIn1,
class EdgeMarkMapIn2,
class EdgeMarkMapOut1>
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<TriangleMesh>::edge_descriptor,
typename boost::graph_traits<TriangleMesh>::edge_descriptor
>& tm2_edge_to_tm1_edge)
>& tm2_edge_to_tm1_edge,
UserVisitor& user_visitor)
{
typedef boost::unordered_map<
typename boost::graph_traits<TriangleMesh>::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<false>(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 TriangleMesh,
@ -1404,7 +1531,8 @@ template <class TriangleMesh,
class VertexPointMap,
class EdgeMarkMapIn1,
class EdgeMarkMapIn2,
class EdgeMarkMapOut1>
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<TriangleMesh> 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

View File

@ -31,6 +31,7 @@
#include <CGAL/property_map.h>
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

View File

@ -34,6 +34,7 @@
#include <set>
namespace CGAL {
namespace Polygon_mesh_processing {
namespace Corefinement {
template<class TriangleMesh, class EdgeToFaces>
@ -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

View File

@ -41,6 +41,7 @@
#include <boost/dynamic_bitset.hpp>
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

View File

@ -30,6 +30,7 @@
#include <CGAL/Kernel_traits.h>
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

View File

@ -33,6 +33,7 @@
#include <bitset>
namespace CGAL{
namespace Polygon_mesh_processing {
namespace Corefinement{
template <class TriangleMesh, class VertexPointMap>
@ -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

View File

@ -29,6 +29,7 @@
#include <CGAL/use.h>
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

View File

@ -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 <CGAL/license/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/convex_hull_3.h>
namespace CGAL{
namespace Polygon_mesh_processing {
namespace internal
{
template <class TriangleMesh,
class Ecm,
class NamedParameters1,
class NamedParameters2>
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<TriangleMesh,
NamedParameters1>::type Vpm;
typedef typename GetFaceIndexMap<TriangleMesh,
NamedParameters1>::type Fid_map;
typedef typename GetVertexIndexMap<TriangleMesh,
NamedParameters1>::type Vid_map;
typedef boost::graph_traits<TriangleMesh> GT;
typedef typename GetGeomTraits<TriangleMesh, NamedParameters2>::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<std::size_t> 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 <std::size_t> ccs_to_remove;
/// \todo clipper has been modified, this is not robust if inexact constructions are used
CGAL::Side_of_triangle_mesh<TriangleMesh, GeomTraits, Vpm>
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 <class Set>
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<Set>& map, key_type k)
{
return map.edge_set->count(k)!=0;
}
friend void put(Constrained_edge_map<Set>& map, key_type k, bool b)
{
if (b)
map.edge_set->insert(k);
else
map.edge_set->erase(k);
}
private:
Set* edge_set;
};
template <class TriangleMesh,
class NamedParameters1,
class NamedParameters2>
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<TriangleMesh>
::edge_descriptor edge_descriptor;
boost::unordered_set<edge_descriptor> constrained_edges;
Constrained_edge_map<boost::unordered_set<edge_descriptor> >
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<edge_descriptor>
template <class TriangleMesh,
class NamedParameters1,
class NamedParameters2>
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 <class Plane_3,
class TriangleMesh,
class NamedParameters>
Oriented_side
clip_to_bbox(const Plane_3& plane,
const Bbox_3& bbox,
TriangleMesh& tm_out,
const NamedParameters& np )
{
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Geom_traits;
typedef typename Geom_traits::Point_3 Point_3;
typedef typename Geom_traits::Segment_3 Segment_3;
typedef typename GetVertexPointMap<TriangleMesh,
NamedParameters>::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<Point_3,8> 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<CGAL::Oriented_side,8> 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<Point_3> points;
// look for intersections on edges
cpp11::array<int,24> 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<Point_3>(
*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<Geom_traits> 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 <class TriangleMesh,
class NamedParameters1>
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 <class TriangleMesh>
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 <class TriangleMesh,
class Plane_3>
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

View File

@ -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<TriangleMesh,
NamedParameters1>::const_type Vpm;
typedef typename GetVertexPointMap<TriangleMesh,
@ -1747,16 +1750,57 @@ template <class OutputIterator,
OutputIterator
surface_intersection(const TriangleMesh& tm1,
const TriangleMesh& tm2,
OutputIterator polyline_output,
const bool throw_on_self_intersection=false)
OutputIterator polyline_output)
{
return surface_intersection(tm1, tm2, polyline_output,
CGAL::Polygon_mesh_processing::parameters::all_default(),
CGAL::Polygon_mesh_processing::parameters::all_default(),
throw_on_self_intersection
);
CGAL::Polygon_mesh_processing::parameters::all_default());
}
template <class OutputIterator,
class TriangleMesh,
class NamedParameters1>
OutputIterator
surface_intersection(const TriangleMesh& tm1,
const TriangleMesh& tm2,
OutputIterator polyline_output,
const NamedParameters1& np)
{
return surface_intersection(tm1, tm2, polyline_output, np,
CGAL::Polygon_mesh_processing::parameters::all_default());
}
#ifndef CGAL_NO_DEPRECATED_CODE
template <class OutputIterator,
class TriangleMesh,
class NamedParameters1,
class NamedParameters2 >
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 <class OutputIterator,
class TriangleMesh >
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 <class OutputIterator,
class TriangleMesh >

View File

@ -12,6 +12,19 @@ typedef CGAL::Surface_mesh<K::Point_3> Mesh;
namespace PMP = CGAL::Polygon_mesh_processing;
template <class TriangleMesh>
struct My_visitor :
public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor<TriangleMesh>
{
void after_subface_creations(TriangleMesh&){++(*i);}
My_visitor()
: i (new int(0) )
{}
boost::shared_ptr<int> 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<K::Point_3> >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<Mesh> 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);

View File

@ -10,6 +10,18 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Surface_mesh;
typedef CGAL::Polyhedron_3<K> Polyhedron_3;
template <class TriangleMesh>
struct My_visitor :
public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor<TriangleMesh>
{
void after_subface_creations(TriangleMesh&){++(*i);}
My_visitor()
: i (new int(0) )
{}
boost::shared_ptr<int> 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<Surface_mesh> 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<Polyhedron_3> 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());
}

View File

@ -166,24 +166,24 @@ void test_bool_op_no_copy(
typedef boost::optional<Triangle_mesh*> OTM;
OTM none;
const CGAL::cpp11::array<OTM,4> desired_output =
const CGAL::cpp11::array<OTM,4> 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)

View File

@ -26,7 +26,7 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> 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<Kernel> 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<boost::optional<Polyhedron*>, 4 > desired_output;
CGAL::cpp11::array<boost::optional<Polyhedron*>, 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<Surface_mesh*> OSM;
CGAL::cpp11::array<OSM,4> desired_output;
CGAL::cpp11::array<OSM,4> 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<bool,4> res = PMP::boolean_operation(tm1, tm2, desired_output);
CGAL::cpp11::array<bool,4> 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)
{

View File

@ -1,5 +1,5 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/internal/clip.h>
#include <CGAL/Polygon_mesh_processing/clip.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polyhedron_3.h>
@ -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<K::Point_3> Triangle_mesh;
typedef CGAL::Surface_mesh<K::Point_3> Surface_mesh;
typedef CGAL::Polyhedron_3<K> Polyhedron;
typedef Triangle_mesh::Property_map<Triangle_mesh::Edge_index,bool> Constrained_edge_map;
template <class TriangleMesh>
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<std::size_t>(), tm1)),
params::face_index_map(get(CGAL::dynamic_face_property_t<std::size_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<std::size_t>(), tm1)),
params::face_index_map(get(CGAL::dynamic_face_property_t<std::size_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<Triangle_mesh::Edge_index,bool>("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<Surface_mesh>();
test<Polyhedron>();
return 0;
}

View File

@ -8,7 +8,7 @@
#include "Scene_plane_item.h"
#include <CGAL/Three/Viewer_interface.h>
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <CGAL/Polygon_mesh_processing/internal/clip.h>
#include <CGAL/Polygon_mesh_processing/clip.h>
#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();

View File

@ -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();

View File

@ -203,7 +203,13 @@ void Polyhedron_demo_repair_polyhedron_plugin::on_actionAutorefine_triggered(Sce
qobject_cast<Item*>(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<Item*>(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();
}

View File

@ -148,9 +148,9 @@ void Polyhedron_demo_intersection_plugin::intersectionSurfaces()
PMP::surface_intersection(*itemA->polyhedron(),
*itemB->polyhedron(),
std::back_inserter(new_item->polylines),
true);
PMP::parameters::throw_on_self_intersection(true));
}
catch(CGAL::Corefinement::Self_intersection_exception)
catch(CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception)
{
QMessageBox::warning((QWidget*)NULL,
tr("Self-intersections Found"),