mirror of https://github.com/CGAL/cgal
Update from master
This commit is contained in:
commit
9e3345f68f
|
|
@ -101,6 +101,7 @@ CGAL_add_named_parameter(area_threshold_t, area_threshold, area_threshold)
|
|||
CGAL_add_named_parameter(halfedges_keeper_t, halfedges_keeper, halfedges_keeper)
|
||||
CGAL_add_named_parameter(volume_threshold_t, volume_threshold, volume_threshold)
|
||||
CGAL_add_named_parameter(dry_run_t, dry_run, dry_run)
|
||||
CGAL_add_named_parameter(do_not_modify_t, do_not_modify, do_not_modify)
|
||||
|
||||
// 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)
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ void test(const NamedParameters& np)
|
|||
assert(get_parameter(np, CGAL::internal_np::do_lock_mesh).v == 61);
|
||||
assert(get_parameter(np, CGAL::internal_np::halfedges_keeper).v == 62);
|
||||
assert(get_parameter(np, CGAL::internal_np::do_simplify_border).v == 64);
|
||||
assert(get_parameter(np, CGAL::internal_np::do_not_modify).v == 65);
|
||||
assert(get_parameter(np, CGAL::internal_np::maximum_number_of_faces).v == 78910);
|
||||
|
||||
// Named parameters that we use in the package 'Surface Mesh Simplification'
|
||||
|
|
@ -184,6 +185,7 @@ void test(const NamedParameters& np)
|
|||
check_same_type<54>(get_parameter(np, CGAL::internal_np::use_area_smoothing));
|
||||
check_same_type<55>(get_parameter(np, CGAL::internal_np::use_Delaunay_flips));
|
||||
check_same_type<56>(get_parameter(np, CGAL::internal_np::use_safety_constraints));
|
||||
check_same_type<65>(get_parameter(np, CGAL::internal_np::do_not_modify));
|
||||
|
||||
check_same_type<12340>(get_parameter(np, CGAL::internal_np::do_self_intersection_tests));
|
||||
check_same_type<12341>(get_parameter(np, CGAL::internal_np::do_orientation_tests));
|
||||
|
|
@ -353,6 +355,7 @@ int main()
|
|||
.halfedges_keeper(A<62>(62))
|
||||
.use_convex_hull(A<63>(63))
|
||||
.do_simplify_border(A<64>(64))
|
||||
.do_not_modify(A<65>(65))
|
||||
.point_map(A<9000>(9000))
|
||||
.query_point_map(A<9001>(9001))
|
||||
.normal_map(A<9002>(9002))
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <CGAL/Polygon_mesh_processing/triangulate_hole.h>
|
||||
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/Corefinement/Generic_clip_output_builder.h>
|
||||
#include <CGAL/iterator.h>
|
||||
|
||||
#include <CGAL/AABB_triangle_primitive.h>
|
||||
|
|
@ -400,6 +401,97 @@ void split_along_edges(TriangleMesh& tm,
|
|||
CGAL_assertion(is_valid_polygon_mesh(tm));
|
||||
}
|
||||
|
||||
template <class TriangleMesh,
|
||||
class NamedParameters1,
|
||||
class NamedParameters2>
|
||||
void
|
||||
generic_clip_impl(
|
||||
TriangleMesh& tm1,
|
||||
TriangleMesh& tm2,
|
||||
const NamedParameters1& np1,
|
||||
const NamedParameters2& np2)
|
||||
{
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
|
||||
// Vertex point maps
|
||||
//for input meshes
|
||||
typedef typename GetVertexPointMap<TriangleMesh,
|
||||
NamedParameters1>::type Vpm;
|
||||
typedef typename GetVertexPointMap<TriangleMesh,
|
||||
NamedParameters2>::type Vpm2;
|
||||
CGAL_USE_TYPE(Vpm2);
|
||||
CGAL_assertion_code(
|
||||
static const bool same_vpm = (boost::is_same<Vpm,Vpm2>::value); )
|
||||
CGAL_static_assertion(same_vpm);
|
||||
|
||||
Vpm vpm1 = choose_parameter(get_parameter(np1, internal_np::vertex_point),
|
||||
get_property_map(boost::vertex_point, tm1));
|
||||
|
||||
Vpm vpm2 = choose_parameter(get_parameter(np2, internal_np::vertex_point),
|
||||
get_property_map(boost::vertex_point, tm2));
|
||||
|
||||
if (&tm1==&tm2)
|
||||
{
|
||||
// TODO mark all edges
|
||||
return;
|
||||
}
|
||||
|
||||
// handle case of empty meshes (isolated vertices are ignored)
|
||||
if (faces(tm1).empty())
|
||||
return;
|
||||
|
||||
// Edge is-constrained maps
|
||||
//for input meshes
|
||||
typedef typename internal_np::Lookup_named_param_def <
|
||||
internal_np::edge_is_constrained_t,
|
||||
NamedParameters1,
|
||||
Corefinement::No_mark<TriangleMesh>//default
|
||||
> ::type User_ecm1;
|
||||
|
||||
// User and internal edge is-constrained map
|
||||
typedef typename boost::template property_map<TriangleMesh, CGAL::dynamic_edge_property_t<bool> >::type Algo_ecm1;
|
||||
typedef Corefinement::No_mark<TriangleMesh> Ecm2;
|
||||
typedef OR_property_map<Algo_ecm1, User_ecm1> Ecm1;
|
||||
typedef Corefinement::Ecm_bind<TriangleMesh, Ecm1, Ecm2> Ecm_in;
|
||||
|
||||
Algo_ecm1 algo_ecm1 = get(CGAL::dynamic_edge_property_t<bool>(), tm1);
|
||||
Ecm1 ecm1 = Ecm1(algo_ecm1, choose_parameter<User_ecm1>(get_parameter(np1, internal_np::edge_is_constrained)));
|
||||
Ecm2 ecm2;
|
||||
|
||||
// Face index point maps
|
||||
typedef typename CGAL::GetInitializedFaceIndexMap<TriangleMesh, NamedParameters1>::type FaceIndexMap1;
|
||||
FaceIndexMap1 fid_map1 = get_initialized_face_index_map(tm1, np1);
|
||||
|
||||
const bool use_compact_clipper =
|
||||
choose_parameter(get_parameter(np1, internal_np::use_compact_clipper), true);
|
||||
|
||||
|
||||
// User visitor
|
||||
typedef typename internal_np::Lookup_named_param_def <
|
||||
internal_np::graph_visitor_t,
|
||||
NamedParameters1,
|
||||
Corefinement::Default_visitor<TriangleMesh>//default
|
||||
> ::type User_visitor;
|
||||
User_visitor uv(choose_parameter<User_visitor>(get_parameter(np1, internal_np::graph_visitor)));
|
||||
|
||||
// surface intersection algorithm call
|
||||
typedef Corefinement::Generic_clip_output_builder<TriangleMesh,
|
||||
Vpm,
|
||||
Algo_ecm1,
|
||||
FaceIndexMap1,
|
||||
Default> Ob;
|
||||
|
||||
typedef Corefinement::Surface_intersection_visitor_for_corefinement<
|
||||
TriangleMesh, Vpm, Ob, Ecm_in, User_visitor> Algo_visitor;
|
||||
Ecm_in ecm_in(tm1,tm2,ecm1,ecm2);
|
||||
Ob ob(tm1, tm2, vpm1, vpm2, algo_ecm1, fid_map1, use_compact_clipper);
|
||||
|
||||
Corefinement::Intersection_of_triangle_meshes<TriangleMesh, Vpm, Algo_visitor >
|
||||
functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm_in,&tm2));
|
||||
functor(CGAL::Emptyset_iterator(), false, true);
|
||||
}
|
||||
|
||||
} // end of internal namespace
|
||||
|
||||
/**
|
||||
|
|
@ -426,6 +518,7 @@ void split_along_edges(TriangleMesh& tm,
|
|||
*
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
<<<<<<< HEAD
|
||||
* \cgalParamNBegin{vertex_point_map}
|
||||
* \cgalParamDescription{a property map associating points to the vertices of `tm` (resp. `clipper`)}
|
||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<TriangleMesh>::%vertex_descriptor`
|
||||
|
|
@ -473,6 +566,14 @@ void split_along_edges(TriangleMesh& tm,
|
|||
* \cgalParamExtra{This option has an effect only if a surface and not a volume is clipped,
|
||||
* (i.e., if `clip_volume` is `false` or if `tm` is open).}
|
||||
* \cgalParamNEnd
|
||||
* \cgalParamNBegin{do_not_modify}
|
||||
* \cgalParamDescription{(`np_c` only) if `true`, `clipper` will not be modified.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this option is set to `true`, `tm` is no longer required to be without self-intersection.
|
||||
* Setting this option to `true` will automatically set `throw_on_self_intersection` to `false`
|
||||
* and `clip_volume` to `false`.}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* @return `true` if the output surface mesh is manifold.
|
||||
|
|
@ -487,6 +588,14 @@ clip(TriangleMesh& tm,
|
|||
const NamedParameters1& np_tm,
|
||||
const NamedParameters2& np_c)
|
||||
{
|
||||
if (parameters::choose_parameter(parameters::get_parameter(np_c, internal_np::do_not_modify), false))
|
||||
{
|
||||
CGAL_assertion(is_closed(clipper));
|
||||
|
||||
internal::generic_clip_impl(tm, clipper, np_tm, np_c);
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool clip_volume =
|
||||
parameters::choose_parameter(parameters::get_parameter(np_tm, internal_np::clip_volume), false);
|
||||
|
||||
|
|
@ -710,6 +819,14 @@ bool clip(TriangleMesh& tm,
|
|||
* will be thrown if at least one self-intersection is found.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamNBegin{do_not_modify}
|
||||
* \cgalParamDescription{(`np_s` only) if `true`, `splitter` will not be modified.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this option is set to `true`, `tm` is no longer required to be without self-intersection.
|
||||
* Setting this option to `true` will automatically set `throw_on_self_intersection` to `false`
|
||||
* and `clip_volume` to `false`.}
|
||||
* \cgalParamNEnd
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalNamedParamsEnd
|
||||
|
|
@ -742,9 +859,11 @@ void split(TriangleMesh& tm,
|
|||
// create a constrained edge map and corefine input mesh with the splitter,
|
||||
// and mark edges
|
||||
|
||||
const bool do_not_modify_splitter = choose_parameter(get_parameter(np_s, internal_np::do_not_modify), false);
|
||||
|
||||
PMP::corefine(tm, splitter,
|
||||
CGAL::parameters::vertex_point_map(vpm_tm).edge_is_constrained_map(ecm),
|
||||
CGAL::parameters::vertex_point_map(vpm_s));
|
||||
CGAL::parameters::vertex_point_map(vpm_s).do_not_modify(do_not_modify_splitter));
|
||||
|
||||
//split mesh along marked edges
|
||||
internal::split_along_edges(tm, ecm, vpm_tm);
|
||||
|
|
|
|||
|
|
@ -695,6 +695,19 @@ corefine_and_compute_difference( TriangleMesh& tm1,
|
|||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{`np1` only}
|
||||
* \cgalParamNEnd
|
||||
* \cgalParamNBegin{do_not_modify}
|
||||
* \cgalParamDescription{if `true`, the corresponding mesh will not be updated.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this parameter is set to `true` for both meshes nothing will be done.
|
||||
* If this option is set to `true` for one mesh,
|
||||
* the other mesh is no longer required to be without self-intersection.}
|
||||
* \cgalParamNEnd
|
||||
* \cgalParamBegin{do_not_modify}
|
||||
* If this parameter is set to `true` for both meshes nothing will be done.
|
||||
* If this option is set to `true` for one mesh,
|
||||
* the other mesh is no longer required to be without self-intersection.
|
||||
* \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
*/
|
||||
|
|
@ -710,6 +723,19 @@ corefine( TriangleMesh& tm1,
|
|||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
|
||||
TriangleMesh* const_mesh_ptr=nullptr;
|
||||
if (choose_parameter(get_parameter(np1, internal_np::do_not_modify), false))
|
||||
{
|
||||
if (choose_parameter(get_parameter(np2, internal_np::do_not_modify), false))
|
||||
return;
|
||||
const_mesh_ptr=&tm1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (choose_parameter(get_parameter(np2, internal_np::do_not_modify), false))
|
||||
const_mesh_ptr=&tm2;
|
||||
}
|
||||
|
||||
const bool throw_on_self_intersection =
|
||||
choose_parameter(get_parameter(np1, internal_np::throw_on_self_intersection), false);
|
||||
|
||||
|
|
@ -769,7 +795,7 @@ corefine( TriangleMesh& tm1,
|
|||
Ob ob;
|
||||
Ecm ecm(tm1,tm2,ecm1,ecm2);
|
||||
Corefinement::Intersection_of_triangle_meshes<TriangleMesh, Vpm, Algo_visitor>
|
||||
functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm));
|
||||
functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm,const_mesh_ptr));
|
||||
functor(CGAL::Emptyset_iterator(), throw_on_self_intersection, true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,241 @@
|
|||
// Copyright (c) 2020 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||
//
|
||||
//
|
||||
// Author(s) : Sebastien Loriot
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_INTERNAL_GENERIC_CLIP_OUTPUT_BUILDER_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_INTERNAL_GENERIC_CLIP_OUTPUT_BUILDER_H
|
||||
|
||||
#include <CGAL/license/Polygon_mesh_processing/corefinement.h>
|
||||
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/internal/Corefinement/face_graph_utils.h>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||
#include <CGAL/Side_of_triangle_mesh.h>
|
||||
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/Default.h>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
|
||||
namespace CGAL {
|
||||
namespace Polygon_mesh_processing {
|
||||
namespace Corefinement {
|
||||
|
||||
|
||||
namespace PMP=Polygon_mesh_processing;
|
||||
namespace params=PMP::parameters;
|
||||
|
||||
template <class TriangleMesh,
|
||||
class VertexPointMap,
|
||||
class Ecm1,
|
||||
class FaceIdMap1,
|
||||
class Kernel_=Default>
|
||||
class Generic_clip_output_builder
|
||||
{
|
||||
//Default typedefs
|
||||
typedef typename Default::Get<
|
||||
Kernel_,
|
||||
typename Kernel_traits<
|
||||
typename boost::property_traits<VertexPointMap>::value_type
|
||||
>::Kernel >::type Kernel;
|
||||
|
||||
// graph_traits typedefs
|
||||
typedef TriangleMesh TM;
|
||||
typedef boost::graph_traits<TM> GT;
|
||||
typedef typename GT::edge_descriptor edge_descriptor;
|
||||
typedef typename GT::face_descriptor face_descriptor;
|
||||
typedef typename GT::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename GT::vertex_descriptor vertex_descriptor;
|
||||
// Internal typedefs
|
||||
typedef std::size_t Node_id;
|
||||
typedef std::pair<Node_id,Node_id> Node_id_pair;
|
||||
// to maintain a halfedge on each polyline per TriangleMesh + pair<bool,size_t>
|
||||
// with first = "is the key (pair<Node_id,Node_id>) was reversed?" and
|
||||
// second is the number of edges -1 in the polyline
|
||||
typedef std::map< Node_id_pair,
|
||||
std::pair< std::map<TriangleMesh*,
|
||||
halfedge_descriptor>,
|
||||
std::pair<bool,std::size_t> > >
|
||||
An_edge_per_polyline_map;
|
||||
|
||||
typedef boost::unordered_map<vertex_descriptor, Node_id> Node_id_map;
|
||||
typedef boost::unordered_map<edge_descriptor,
|
||||
edge_descriptor> Edge_map;
|
||||
//Data members
|
||||
TriangleMesh &tm1, &tm2;
|
||||
// property maps of input meshes
|
||||
const VertexPointMap vpm1;
|
||||
const VertexPointMap vpm2;
|
||||
Ecm1 ecm1;
|
||||
FaceIdMap1 fids1;
|
||||
bool use_compact_clipper;
|
||||
|
||||
// mapping vertex to node id
|
||||
Node_id_map vertex_to_node_id1;
|
||||
|
||||
// orientation of input surface meshes
|
||||
bool is_tm2_inside_out;
|
||||
// constants
|
||||
const Node_id NID;
|
||||
|
||||
typename An_edge_per_polyline_map::iterator last_polyline;
|
||||
|
||||
Node_id get_node_id(vertex_descriptor v,
|
||||
const Node_id_map& node_ids)
|
||||
{
|
||||
typename Node_id_map::const_iterator it = node_ids.find(v);
|
||||
if (it == node_ids.end())
|
||||
return NID;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Generic_clip_output_builder(TriangleMesh& tm1,
|
||||
TriangleMesh& tm2,
|
||||
const VertexPointMap vpm1,
|
||||
const VertexPointMap vpm2,
|
||||
const Ecm1& ecm1,
|
||||
FaceIdMap1 fids1,
|
||||
bool use_compact_clipper)
|
||||
: tm1(tm1), tm2(tm2)
|
||||
, vpm1(vpm1), vpm2(vpm2)
|
||||
, ecm1(ecm1)
|
||||
, fids1(fids1)
|
||||
, use_compact_clipper(use_compact_clipper)
|
||||
, is_tm2_inside_out( !PMP::is_outward_oriented(tm2, parameters::vertex_point_map(vpm2)) )
|
||||
, NID((std::numeric_limits<Node_id>::max)())
|
||||
{}
|
||||
|
||||
// functions called by the intersection visitor
|
||||
void start_new_polyline(Node_id, Node_id) {}
|
||||
void add_node_to_polyline(Node_id) {}
|
||||
void set_edge_per_polyline(TriangleMesh&, Node_id_pair, halfedge_descriptor){}
|
||||
|
||||
void set_vertex_id(vertex_descriptor v, Node_id node_id, const TriangleMesh& tm)
|
||||
{
|
||||
CGAL_assertion(&tm == &tm1);
|
||||
vertex_to_node_id1.insert( std::make_pair(v, node_id) );
|
||||
}
|
||||
|
||||
template <class Nodes_vector, class Mesh_to_map_node>
|
||||
void operator()(
|
||||
const Nodes_vector& nodes,
|
||||
bool /* input_have_coplanar_faces */,
|
||||
const boost::dynamic_bitset<>& /* is_node_of_degree_one */,
|
||||
const Mesh_to_map_node&)
|
||||
{
|
||||
// The property map must be either writable or well-initialized
|
||||
if( CGAL::internal::Is_writable_property_map<FaceIdMap1>::value &&
|
||||
!BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1)) )
|
||||
{
|
||||
BGL::internal::initialize_face_index_map(fids1, tm1);
|
||||
}
|
||||
CGAL_assertion(BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1)));
|
||||
|
||||
// (1) Assign a patch id to each face indicating in which connected
|
||||
// component limited by intersection edges of the surface they are.
|
||||
std::vector<std::size_t> tm1_patch_ids( num_faces(tm1),NID );
|
||||
|
||||
std::size_t nb_patches_tm1 =
|
||||
PMP::connected_components(tm1,
|
||||
bind_property_maps(fids1,make_property_map(&tm1_patch_ids[0])),
|
||||
params::edge_is_constrained_map(ecm1)
|
||||
.face_index_map(fids1));
|
||||
|
||||
std::vector <std::size_t> tm1_patch_sizes(nb_patches_tm1, 0);
|
||||
for(std::size_t i : tm1_patch_ids)
|
||||
if(i!=NID)
|
||||
++tm1_patch_sizes[i];
|
||||
|
||||
// Use the class Side_of_triangle_mesh to classify each patch
|
||||
boost::dynamic_bitset<> patch_status_not_set_tm1(nb_patches_tm1);
|
||||
patch_status_not_set_tm1.set();
|
||||
|
||||
typedef Side_of_triangle_mesh<TriangleMesh,
|
||||
Kernel,
|
||||
VertexPointMap> Inside_poly_test;
|
||||
|
||||
CGAL::Bounded_side in_tm2 = is_tm2_inside_out
|
||||
? ON_UNBOUNDED_SIDE : ON_BOUNDED_SIDE;
|
||||
|
||||
Inside_poly_test inside_tm2(tm2, vpm2);
|
||||
std::size_t nb_classified=0;
|
||||
std::vector<std::size_t> cc_to_keep;
|
||||
for(face_descriptor f : faces(tm1))
|
||||
{
|
||||
const std::size_t f_id = get(fids1, f);
|
||||
const std::size_t patch_id = tm1_patch_ids[ f_id ];
|
||||
if ( patch_status_not_set_tm1.test( patch_id ) )
|
||||
{
|
||||
patch_status_not_set_tm1.reset( patch_id );
|
||||
halfedge_descriptor h = halfedge(f, tm1);
|
||||
Node_id index_p1 = get_node_id(target(h, tm1), vertex_to_node_id1);
|
||||
std::array<Node_id, 3> fnids = { index_p1, index_p1, index_p1 };
|
||||
if (index_p1 != NID)
|
||||
{
|
||||
h=next(h, tm1);
|
||||
index_p1 = get_node_id(target(h, tm1), vertex_to_node_id1);
|
||||
fnids[1]=index_p1;
|
||||
if (index_p1 != NID)
|
||||
{
|
||||
h=next(h, tm1);
|
||||
index_p1 = get_node_id(target(h, tm1), vertex_to_node_id1);
|
||||
fnids[2]=index_p1;
|
||||
}
|
||||
}
|
||||
if (index_p1 != NID)
|
||||
{
|
||||
typename Nodes_vector::Exact_kernel ek;
|
||||
typedef typename Nodes_vector::Exact_kernel::Point_3 Exact_point_3;
|
||||
Exact_point_3 e_centroid = centroid(nodes.exact_node(fnids[0]),
|
||||
nodes.exact_node(fnids[1]),
|
||||
nodes.exact_node(fnids[2]));
|
||||
|
||||
Bounded_side position = inside_tm2(e_centroid, ek);
|
||||
if ( position==ON_BOUNDARY )
|
||||
{
|
||||
if (use_compact_clipper)
|
||||
{
|
||||
cc_to_keep.push_back(patch_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
if ( position == in_tm2 )
|
||||
{
|
||||
cc_to_keep.push_back(patch_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Bounded_side position = inside_tm2( get(vpm1, target(h, tm1)));
|
||||
CGAL_assertion( position != ON_BOUNDARY);
|
||||
if ( position == in_tm2 )
|
||||
{
|
||||
cc_to_keep.push_back(patch_id);
|
||||
}
|
||||
}
|
||||
if ( ++nb_classified==nb_patches_tm1) break;
|
||||
}
|
||||
}
|
||||
|
||||
PMP::keep_connected_components(tm1, cc_to_keep, bind_property_maps(fids1,make_property_map(&tm1_patch_ids[0])));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} } } // CGAL::Polygon_mesh_processing::Corefinement
|
||||
|
||||
#undef CGAL_COREF_FUNCTION_CALL
|
||||
#endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_GENERIC_CLIP_OUTPUT_BUILDER_H
|
||||
|
|
@ -170,6 +170,7 @@ private:
|
|||
OutputBuilder& output_builder;
|
||||
EdgeMarkMapBind marks_on_edges;
|
||||
bool input_with_coplanar_faces;
|
||||
TriangleMesh* const_mesh_ptr;
|
||||
|
||||
template <class Ecm1, class Ecm2>
|
||||
void call_put(Ecm_bind<TriangleMesh, Ecm1, Ecm2>& ecm,
|
||||
|
|
@ -200,12 +201,13 @@ private:
|
|||
// visitor public functions
|
||||
public:
|
||||
Surface_intersection_visitor_for_corefinement(
|
||||
UserVisitor& uv, OutputBuilder& o, const EdgeMarkMapBind& emm)
|
||||
UserVisitor& uv, OutputBuilder& o, const EdgeMarkMapBind& emm, TriangleMesh* const_mesh_ptr=nullptr)
|
||||
: number_coplanar_vertices(0)
|
||||
, user_visitor(uv)
|
||||
, output_builder(o)
|
||||
, marks_on_edges(emm)
|
||||
, input_with_coplanar_faces(false)
|
||||
, const_mesh_ptr(const_mesh_ptr)
|
||||
{}
|
||||
|
||||
template<class Graph_node>
|
||||
|
|
@ -332,34 +334,39 @@ public:
|
|||
|
||||
//forward to the visitor
|
||||
// user_visitor.new_node_added(node_id, type, h_1, h_2, is_target_coplanar, is_source_coplanar); // NODE_VISITOR_TAG
|
||||
switch(type)
|
||||
if (tm2_ptr!=const_mesh_ptr)
|
||||
{
|
||||
case ON_FACE: //Face intersected by an edge
|
||||
on_face[tm2_ptr][face(h_2,tm2)].push_back(node_id);
|
||||
break;
|
||||
case ON_EDGE: //Edge intersected by an edge
|
||||
switch(type)
|
||||
{
|
||||
on_edge[tm2_ptr][edge(h_2,tm2)].push_back(node_id);
|
||||
// check_node_on_non_manifold_edge(node_id,h_2,tm2);
|
||||
case ON_FACE: //Face intersected by an edge
|
||||
on_face[tm2_ptr][face(h_2,tm2)].push_back(node_id);
|
||||
break;
|
||||
case ON_EDGE: //Edge intersected by an edge
|
||||
{
|
||||
on_edge[tm2_ptr][edge(h_2,tm2)].push_back(node_id);
|
||||
// check_node_on_non_manifold_edge(node_id,h_2,tm2);
|
||||
}
|
||||
break;
|
||||
case ON_VERTEX:
|
||||
{
|
||||
//grab original vertex that is on commom intersection
|
||||
mesh_to_vertices_on_inter[tm2_ptr].insert(std::make_pair(node_id,h_2));
|
||||
Node_id_to_vertex& node_id_to_vertex=mesh_to_node_id_to_vertex[tm2_ptr];
|
||||
if (node_id_to_vertex.size()<=node_id)
|
||||
node_id_to_vertex.resize(node_id+1,Graph_traits::null_vertex());
|
||||
node_id_to_vertex[node_id]=target(h_2,tm2);
|
||||
all_incident_faces_got_a_node_as_vertex(h_2,node_id,*tm2_ptr);
|
||||
// check_node_on_non_manifold_vertex(node_id,h_2,tm2);
|
||||
output_builder.set_vertex_id(target(h_2, tm2), node_id, tm2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case ON_VERTEX:
|
||||
{
|
||||
//grab original vertex that is on commom intersection
|
||||
mesh_to_vertices_on_inter[tm2_ptr].insert(std::make_pair(node_id,h_2));
|
||||
Node_id_to_vertex& node_id_to_vertex=mesh_to_node_id_to_vertex[tm2_ptr];
|
||||
if (node_id_to_vertex.size()<=node_id)
|
||||
node_id_to_vertex.resize(node_id+1,Graph_traits::null_vertex());
|
||||
node_id_to_vertex[node_id]=target(h_2,tm2);
|
||||
all_incident_faces_got_a_node_as_vertex(h_2,node_id,*tm2_ptr);
|
||||
// check_node_on_non_manifold_vertex(node_id,h_2,tm2);
|
||||
output_builder.set_vertex_id(target(h_2, tm2), node_id, tm2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (tm1_ptr==const_mesh_ptr) return;
|
||||
|
||||
CGAL_assertion(!is_target_coplanar || !is_source_coplanar); //coplanar edge are not forwarded
|
||||
|
||||
if ( is_target_coplanar )
|
||||
|
|
@ -624,6 +631,7 @@ public:
|
|||
++it)
|
||||
{
|
||||
TriangleMesh& tm=*it->first;
|
||||
CGAL_assertion(&tm!=const_mesh_ptr);
|
||||
// Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm];
|
||||
|
||||
Node_to_target_of_hedge_map& nodes_to_hedge=it->second;
|
||||
|
|
@ -693,6 +701,7 @@ public:
|
|||
it=on_edge.begin(); it!=on_edge.end(); ++it)
|
||||
{
|
||||
TriangleMesh& tm=*it->first;
|
||||
CGAL_assertion(&tm!=const_mesh_ptr);
|
||||
const VertexPointMap& vpm=vpms[&tm];
|
||||
On_edge_map& on_edge_map=it->second;
|
||||
On_face_map& on_face_map=on_face[&tm];
|
||||
|
|
@ -788,6 +797,7 @@ public:
|
|||
it=on_face.begin(); it!=on_face.end(); ++it)
|
||||
{
|
||||
TriangleMesh& tm=*it->first;
|
||||
CGAL_assertion(&tm!=const_mesh_ptr);
|
||||
const VertexPointMap& vpm=vpms[&tm];
|
||||
On_face_map& on_face_map=it->second;
|
||||
Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm];
|
||||
|
|
|
|||
|
|
@ -75,13 +75,6 @@ public:
|
|||
{
|
||||
typename Traits::Bounding_box bbox = m_helper.get_tree_bbox(tree);
|
||||
|
||||
if( point.x() < bbox.xmin() || point.x() > bbox.xmax()
|
||||
|| point.y() < bbox.ymin() || point.y() > bbox.ymax()
|
||||
|| point.z() < bbox.zmin() || point.z() > bbox.zmax() )
|
||||
{
|
||||
return ON_UNBOUNDED_SIDE;
|
||||
}
|
||||
|
||||
//the direction of the vertical ray depends on the position of the point in the bbox
|
||||
//in order to limit the expected number of nodes visited.
|
||||
Ray query = ray_functor(point, vector_functor(0,0,(2*point.z() < bbox.zmax()+bbox.zmin()?-1:1)));
|
||||
|
|
|
|||
|
|
@ -216,6 +216,60 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
//special case when ray query is from another Kernel K1 is the kernel compatible with the AABB-tree
|
||||
template<typename AABBTraits, class K1, class K2, class Helper>
|
||||
class K2_Ray_3_K1_Triangle_3_traversal_traits
|
||||
{
|
||||
//the status indicates whether the query point is strictly inside the polyhedron, and the number of intersected triangles if yes
|
||||
std::pair<boost::logic::tribool,std::size_t>& m_status;
|
||||
bool m_stop;
|
||||
const AABBTraits& m_aabb_traits;
|
||||
typedef typename AABBTraits::Primitive Primitive;
|
||||
typedef CGAL::AABB_node<AABBTraits> Node;
|
||||
Helper m_helper;
|
||||
CGAL::Cartesian_converter<K1,K2> to_K2;
|
||||
|
||||
public:
|
||||
K2_Ray_3_K1_Triangle_3_traversal_traits(std::pair<boost::logic::tribool,std::size_t>& status,
|
||||
const AABBTraits& aabb_traits,
|
||||
const Helper& h)
|
||||
:m_status(status), m_stop(false), m_aabb_traits(aabb_traits), m_helper(h)
|
||||
{m_status.first=true;}
|
||||
|
||||
bool go_further() const { return !m_stop; }
|
||||
|
||||
template<class Query>
|
||||
void intersection(const Query& query, const Primitive& primitive)
|
||||
{
|
||||
Intersections::internal::r3t3_do_intersect_endpoint_position_visitor visitor;
|
||||
std::pair<bool,Intersections::internal::R3T3_intersection::type> res=
|
||||
Intersections::internal::do_intersect(to_K2(m_helper.get_primitive_datum(primitive, m_aabb_traits)),
|
||||
query, K2(), visitor);
|
||||
|
||||
if (res.first){
|
||||
switch (res.second){
|
||||
case Intersections::internal::R3T3_intersection::CROSS_FACET:
|
||||
++m_status.second;
|
||||
break;
|
||||
case Intersections::internal::R3T3_intersection::ENDPOINT_IN_TRIANGLE:
|
||||
m_status.first=false;
|
||||
m_stop=true;
|
||||
break;
|
||||
default:
|
||||
m_status.first=boost::logic::indeterminate;
|
||||
m_stop=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Query>
|
||||
bool do_intersect(const Query& query, const Node& node) const
|
||||
{
|
||||
return CGAL::do_intersect(query, m_helper.get_node_bbox(node));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}// namespace internal
|
||||
}// namespace CGAL
|
||||
|
||||
|
|
|
|||
|
|
@ -242,6 +242,81 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
template <class K2>
|
||||
Bounded_side operator()(const typename K2::Point_3& point, const K2& k2) const
|
||||
{
|
||||
if(point.x() < box.xmin()
|
||||
|| point.x() > box.xmax()
|
||||
|| point.y() < box.ymin()
|
||||
|| point.y() > box.ymax()
|
||||
|| point.z() < box.zmin()
|
||||
|| point.z() > box.zmax())
|
||||
{
|
||||
return CGAL::ON_UNBOUNDED_SIDE;
|
||||
}
|
||||
|
||||
#ifdef CGAL_HAS_THREADS
|
||||
AABB_tree_* tree_ptr =
|
||||
const_cast<AABB_tree_*>(atomic_tree_ptr.load(std::memory_order_acquire));
|
||||
#endif
|
||||
// Lazily build the tree only when needed
|
||||
if (tree_ptr==nullptr)
|
||||
{
|
||||
#ifdef CGAL_HAS_THREADS
|
||||
CGAL_SCOPED_LOCK(tree_mutex);
|
||||
tree_ptr = const_cast<AABB_tree_*>(atomic_tree_ptr.load(std::memory_order_relaxed));
|
||||
#endif
|
||||
CGAL_assertion(tm_ptr != nullptr && opt_vpm!=boost::none);
|
||||
if (tree_ptr==nullptr)
|
||||
{
|
||||
tree_ptr = new AABB_tree(faces(*tm_ptr).first,
|
||||
faces(*tm_ptr).second,
|
||||
*tm_ptr, *opt_vpm);
|
||||
const_cast<AABB_tree_*>(tree_ptr)->build();
|
||||
#ifdef CGAL_HAS_THREADS
|
||||
atomic_tree_ptr.store(tree_ptr, std::memory_order_release);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
typedef typename Kernel_traits<Point>::Kernel K1;
|
||||
typedef typename AABB_tree::AABB_traits AABB_traits;
|
||||
typedef internal::Default_tree_helper<AABB_tree> Helper;
|
||||
Helper helper;
|
||||
typename AABB_traits::Bounding_box bbox = helper.get_tree_bbox(*tree_ptr);
|
||||
|
||||
static const unsigned int seed = 1340818006;
|
||||
CGAL::Random rg(seed); // seed some value for make it easy to debug
|
||||
Random_points_on_sphere_3<typename K2::Point_3> random_point(1.,rg);
|
||||
|
||||
typename K2::Construct_ray_3 ray = k2.construct_ray_3_object();
|
||||
typename K2::Construct_vector_3 vector = k2.construct_vector_3_object();
|
||||
|
||||
do { //retry with a random ray
|
||||
typename K2::Ray_3 query = ray(point, vector(CGAL::ORIGIN,*random_point++));
|
||||
|
||||
std::pair<boost::logic::tribool,std::size_t>
|
||||
status( boost::logic::tribool(boost::logic::indeterminate), 0);
|
||||
|
||||
internal::K2_Ray_3_K1_Triangle_3_traversal_traits<AABB_traits, K1, K2, Helper>
|
||||
traversal_traits(status, tree_ptr->traits(), helper);
|
||||
|
||||
tree_ptr->traversal(query, traversal_traits);
|
||||
|
||||
if ( !boost::logic::indeterminate(status.first) )
|
||||
{
|
||||
if (status.first)
|
||||
return (status.second&1) == 1 ? ON_BOUNDED_SIDE : ON_UNBOUNDED_SIDE;
|
||||
//otherwise the point is on the facet
|
||||
return ON_BOUNDARY;
|
||||
}
|
||||
} while (true);
|
||||
return ON_BOUNDARY; // should never be reached
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
} // namespace CGAL
|
||||
|
|
|
|||
|
|
@ -71,11 +71,71 @@ void test(const char* f1, const char* f2)
|
|||
assert(P.is_valid());
|
||||
assert(Q.is_valid());
|
||||
}
|
||||
|
||||
void test_no_modifications(const char* f1, const char* f2)
|
||||
{
|
||||
std::cout << "Corefining " << f1
|
||||
<< " and " << f2 << "\n";
|
||||
|
||||
std::cout << " with Surface_mesh\n";
|
||||
Surface_mesh sm1, sm2;
|
||||
std::ifstream input(f1);
|
||||
assert(input);
|
||||
input >> sm1;
|
||||
input.close();
|
||||
input.open(f2);
|
||||
assert(input);
|
||||
input >> sm2;
|
||||
input.close();
|
||||
My_visitor<Surface_mesh> sm_v;
|
||||
|
||||
std::size_t nb_v_before1 = vertices(sm1).size();
|
||||
std::size_t nb_v_before2 = vertices(sm2).size();
|
||||
|
||||
CGAL::Polygon_mesh_processing::corefine(sm1, sm2,
|
||||
CGAL::parameters::visitor(sm_v),
|
||||
CGAL::parameters::do_not_modify(true));
|
||||
|
||||
std::size_t nb_v_after1 = vertices(sm1).size();
|
||||
std::size_t nb_v_after2 = vertices(sm2).size();
|
||||
|
||||
assert(sm1.is_valid());
|
||||
assert(sm2.is_valid());
|
||||
assert(nb_v_after2==nb_v_before2);
|
||||
|
||||
assert((*(sm_v.i) != 0) == (nb_v_before1!=nb_v_after1));
|
||||
|
||||
std::cout << " with Polyhedron_3\n";
|
||||
Polyhedron_3 P, Q;
|
||||
input.open(f1);
|
||||
assert(input);
|
||||
input >> P;
|
||||
input.close();
|
||||
input.open(f2);
|
||||
assert(input);
|
||||
input >> Q;
|
||||
My_visitor<Polyhedron_3> sm_p;
|
||||
|
||||
nb_v_before1 = vertices(P).size();
|
||||
nb_v_before2 = vertices(Q).size();
|
||||
CGAL::Polygon_mesh_processing::corefine(P, Q,
|
||||
CGAL::parameters::visitor(sm_p).do_not_modify(true));
|
||||
nb_v_after1 = vertices(P).size();
|
||||
nb_v_after2 = vertices(Q).size();
|
||||
|
||||
assert(nb_v_after1==nb_v_before1);
|
||||
assert((*(sm_p.i) != 0) == (nb_v_before2!=nb_v_after2));
|
||||
|
||||
assert(P.is_valid());
|
||||
assert(Q.is_valid());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
for(int i=0; i< (argc-1)/2;++i)
|
||||
{
|
||||
test(argv[2*i+1], argv[2*(i+1)]);
|
||||
test(argv[2*(i+1)], argv[2*i+1]);
|
||||
test_no_modifications(argv[2*(i+1)], argv[2*i+1]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue