Merge branch 'Surface_mesh_simplification-preserve_constrained_edges_bug_fix-sloriot'

bug-fix: preserve edges marked as non-removable
 See Features/Small_Features/SMS_with_constrained

 Tested in CGAL-4.4-Ic-119 and CGAL-4.4-Ic-120

Conflicts:
	Installation/changes.html
This commit is contained in:
Sébastien Loriot 2014-02-14 08:02:15 +01:00
commit d45d1a1070
27 changed files with 5124 additions and 297 deletions

View File

@ -68,6 +68,8 @@ namespace CGAL {
enum get_cost_policy_params_t { get_cost_policy_params } ;
enum get_placement_policy_t { get_placement_policy } ;
enum get_placement_policy_params_t { get_placement_policy_params } ;
enum edge_is_constrained_t { edge_is_constrained } ;
enum edge_is_constrained_params_t { edge_is_constrained_params } ;
#if BOOST_VERSION >= 105100
template <typename T, typename Tag, typename Base = boost::no_property>
@ -166,6 +168,22 @@ namespace CGAL {
typedef cgal_bgl_named_params<GetPlacementParams, get_placement_policy_params_t, self> Params;
return Params(p, *this);
}
template <typename EdgeIsConstrained>
cgal_bgl_named_params<EdgeIsConstrained, edge_is_constrained_t, self>
edge_is_constrained_map(const EdgeIsConstrained& em) const
{
typedef cgal_bgl_named_params<EdgeIsConstrained, edge_is_constrained_t, self> Params;
return Params(em, *this);
}
template <typename EdgeIsConstrainedParams>
cgal_bgl_named_params<EdgeIsConstrainedParams, edge_is_constrained_params_t, self>
edge_is_constrained_map_params(const EdgeIsConstrainedParams& em) const
{
typedef cgal_bgl_named_params<EdgeIsConstrainedParams, edge_is_constrained_params_t, self> Params;
return Params(em, *this);
}
};
#else
template <typename T, typename Tag, typename Base = boost::no_property>
@ -270,6 +288,22 @@ namespace CGAL {
typedef cgal_bgl_named_params<GetPlacementParams, get_placement_policy_params_t, self> Params;
return Params(p, *this);
}
template <typename EdgeIsConstrained>
cgal_bgl_named_params<EdgeIsConstrained, edge_is_constrained_t, self>
edge_is_constrained_map(const EdgeIsConstrained& em) const
{
typedef cgal_bgl_named_params<EdgeIsConstrained, edge_is_constrained_t, self> Params;
return Params(em, *this);
}
template <typename EdgeIsConstrainedParams>
cgal_bgl_named_params<EdgeIsConstrainedParams, edge_is_constrained_params_t, self>
edge_is_constrained_map_params(const EdgeIsConstrainedParams& em) const
{
typedef cgal_bgl_named_params<EdgeIsConstrainedParams, edge_is_constrained_params_t, self> Params;
return Params(em, *this);
}
};
template <class Tag1, class Tag2, class T1, class Base>
@ -373,6 +407,22 @@ namespace CGAL {
typedef cgal_bgl_named_params<GetPlacementParams, get_placement_policy_params_t> Params;
return Params(p);
}
template <typename EdgeIsConstrained>
cgal_bgl_named_params<EdgeIsConstrained, edge_is_constrained_t>
edge_is_constrained_map(const EdgeIsConstrained& em)
{
typedef cgal_bgl_named_params<EdgeIsConstrained, edge_is_constrained_t> Params;
return Params(em);
}
template <typename EdgeIsConstrainedParams>
cgal_bgl_named_params<EdgeIsConstrainedParams, edge_is_constrained_params_t>
edge_is_constrained_map_params(const EdgeIsConstrainedParams& em)
{
typedef cgal_bgl_named_params<EdgeIsConstrainedParams, edge_is_constrained_params_t> Params;
return Params(em);
}
} //namespace CGAL
#if BOOST_VERSION >= 105100

View File

@ -230,6 +230,12 @@ and <code>src/</code> directories).
<li> Hilbert sort ipelet implements two policies</li>
</ul>
<h3>Triangulated Surface Mesh Simplification</h3>
<ul>
<li>Fix a bug in the way edges can be marked as non-removable by adding
a named-parameter <code>edge_is_constrained_map</code> to the function
<code>edge_collapse</code></li>
</ul>
</div>
<h2 id="release4.3">Release 4.3 </h2>

View File

@ -0,0 +1,38 @@
namespace CGAL {
namespace Surface_mesh_simplification {
/*!
\ingroup PkgSurfaceMeshSimplification
The class `Constrained_placement` is a model for the `GetPlacement` concept
provided the template parameter `BasePlacement` is such a model.
The placement of the vertex resulting from a contraction of an edge adjacent to a constrained edge
is the point of the common vertex. Otherwise the placement is the one computed by `BasePlacement`.
\tparam BasePlacement a model of `GetPlacement`.
\tparam EdgeIsConstrainedMap a model of `boost::ReadablePropertyMap` with `GetPlacement::Profile::edge_descriptor`
as key type and `bool` as value type indicating if an edge is constrained.
\cgalModels `GetPlacement`
*/
template<class BasePlacement, class EdgeIsConstrainedMap>
class Constrained_placement : public BasePlacement
{
public:
/// \name Creation
/// @{
/*!
Constructor
*/
Constrained_placement(
EdgeIsConstrainedMap map=EdgeIsConstrainedMap(),
BasePlacement base= BasePlacement() );
/// @}
}; /* end Surface_mesh_simplification::Midpoint_placement */
} /* end namespace Surface_Mesh_Simplification */
} /* end namespace CGAL */

View File

@ -94,6 +94,20 @@ and whose `value_type` is `bool`.
<B>%Default</B>: the property map obtained by calling `get(edge_is_border,surface)`.
\cgalHeading{edge_is_constrained_map(EdgeIsConstrainedMap ecm)}
Maps each <I>undirected</I> edge in the surface into a Boolean value
which indicates if the edge is constrained.
`EdgeIsConstrainedMap` must be a model
`ReadablePropertyMap` whose `key_type` is
`boost::graph_traits<EdgeCollapsableMesh const>::%edge_descriptor`
and whose `value_type` is `bool`.
\attention If this parameter is provided, `surface` must be a model of the
`EdgeCollapsableMeshWithConstraints` concept.
<B>%Default</B>: A property map always returning `false`, that is no edge is constrained.
\cgalHeading{get_cost(GetCost gc)}
The policy which returns the collapse cost for an edge.

View File

@ -16,9 +16,11 @@ It can have any number of connected components, boundaries
\cgalHeading{Valid Expressions}
The mesh simplification algorithm requires the free function `halfedge_collapse()`.
Let `v0` be the source and `v1` be the target vertices of `v0v1`.
Let `v0v1` an edge of the triangulated surface mesh `ecm` and
`v0` and `v1` being the source and target vertices of that edge.
The mesh simplification algorithm requires the call to the function `halfedge_collapse(v0v1,ecm)`
to be valid and to return the vertex not removed after collapsing
the undirected edge `(v0v1,v1v0)`.
For `e` \f$ \in \{\f$ `v0v1,v1v0` \f$ \}\f$, let `en` and `ep` be the next and previous
edges, that is `en = next_edge(e, mesh)`, `ep = prev_edge(e,mesh)`, and let
@ -30,15 +32,14 @@ Then, after the collapse of `(v0v1,v1v0)` the following holds:
<UL>
<LI>The edge `e` is no longer in `mesh`.
<LI>One of \f$ \{\f$`v0,v1`\f$ \}\f$ is no longer in `mesh` while the other remains.
\cgalFootnote{Even though it would appear that v0 can always be the vertex being removed, there is a case when removing the edge `e` <I>requires</I> `v1` to be removed as well. See figure \ref CollapseFigure5.}
\cgalFootnote{Most of the time v0 is the vertex being removed but in some cases removing the edge e requires v1 to be removed. See Figure \ref CollapseFigure5.}
Let `vgone` be the removed vertex and `vkept` be the remaining vertex.
<LI>If `e` was a border edge, that is `get(is_border, e, mesh) == true`, then `next_edge(ep) == en`, and `prev_edge(en) == ep`.
<LI>If `e` was not a border edge, that is `get(is_border, e, mesh) == false`, then `ep` and `epo` are no longer in `mesh` while `en` and `eno` are kept in `mesh`.
<LI>For all edges `ie` in `in_edges(vgone,mesh)`, `target(ie,mesh) == vkept` and `source(opposite_edge(ie),mesh) == vkept`.
<LI>No other incidence information has changed in `mesh`.
</UL>
</UL>
The function returns vertex `vkept` (which can be either `v0` or `v1`).
\image html general_collapse.png
\image latex general_collapse.png
@ -64,7 +65,7 @@ When the collapsing edge is itself a border, only 1 triangle is
removed. Thus, even if \f$ (ep',epo')\f$ exists, it's not removed.
</b></center>
\anchor CollapseFigure5
\anchor CollapseFigure5
\image html border_collapse4.png
\image latex border_collapse4.png
<center><b>
@ -73,9 +74,8 @@ This figure illustrates the single exceptional case when removing \f$
remains.
</b></center>
\cgalHasModel `CGAL::Polyhedron_3<Traits>` (If it has only triangular faces, and via
<I>External Adaptation</I>, which is described in \cgalCite{cgal:sll-bgl-02}
and this <span class="textsc">Bgl</span> web page: <A HREF="http://www.boost.org/libs/graph/doc/leda_conversion.html"><TT>http://www.boost.org/libs/graph/doc/leda_conversion.html</TT></A>).
\cgalHasModel `CGAL::Polyhedron_3<Traits>` (If it has only triangular faces),
using the specialization \link BGLPolyGT `boost::graph_traits< CGAL::Polyhedron_3<Traits> >` \endlink.
\sa \link BGLPolyGT `boost::graph_traits< CGAL::Polyhedron_3<Traits> >` \endlink
\sa `CGAL::halfedge_graph_traits< CGAL::Polyhedron_3<Traits> >`
@ -88,9 +88,9 @@ public:
/*!
Collapses the undirected edge `(v0v1,v1v0)` replacing it with `v0` or `v1`,
as described in the following paragraph.
as described in the paragraph above.
\pre This function requires `mesh` to be an oriented 2-manifold with or without boundaries. Furthermore, the undirected edge `(v0v1,v1v0)` must satisfy the <I>link condition</I> \cgalCite{degn-tpec-98}, which guarantees that the surface is also 2-manifold after the edge collapse.
\relates EdgeCollapsableMesh
\relates EdgeCollapsableMesh
*/
template<class EdgeCollapsableMesh>
typename boost::graph_traits<EdgeCollapsableMesh>::vertex_descriptor

View File

@ -0,0 +1,61 @@
/*!
\ingroup PkgSurfaceMeshSimplificationConcepts
\cgalConcept
The concept `EdgeCollapsableMeshWithConstraints` describes additional requirements
for the type of triangulated surface meshes that can be passed to the
simplification algorithm.
\cgalRefines `EdgeCollapsableMesh`
\cgalHeading{Valid Expressions}
Let `v0v1` be an edge of the triangulated surface mesh `ecm` and
`v0` and `v1` being the source and target vertices of that edge.
The mesh simplification algorithm requires the call to the function `halfedge_collapse(v0v1,ecm)`
to be valid and to return the vertex not removed after collapsing
the undirected edge `(v0v1,v1v0)`.
For `e` \f$ \in \{\f$ `v0v1,v1v0` \f$ \}\f$, let `en` and `ep` be the next and previous
edges, that is `en = next_edge(e, mesh)`, `ep = prev_edge(e,mesh)`, and let
`eno` and `epo` be their opposite edges, that is
`eno = opposite_edge(en, mesh)` and `epo = opposite_edge(ep,mesh)`.
Then, after the collapse of `(v0v1,v1v0)` the invariants described in the concept `EdgeCollapsableMesh` hold
if `ep` is not constrained. Otherwise, it is `en` that is removed from `ecm`.
\image html collapse_constraints.png
\image latex collapse_constraints.png
\cgalHasModel `CGAL::Polyhedron_3<Traits>` (If it has only triangular faces),
using the specialization \link BGLPolyGT `boost::graph_traits< CGAL::Polyhedron_3<Traits> >` \endlink.
\sa \link BGLPolyGT `boost::graph_traits< CGAL::Polyhedron_3<Traits> >` \endlink
\sa `CGAL::halfedge_graph_traits< CGAL::Polyhedron_3<Traits> >`
*/
class EdgeCollapsableMeshWithConstraints {
public:
}; /* end EdgeCollapsableMeshWithConstraints */
/*!
Collapses the undirected edge `(v0v1,v1v0)` replacing it with `v0` or `v1`,
as described in the paragraph above and guarantees that an halfedge `he`, for which `get(edge_is_constrained_map, he)==true`, is not removed after the collapse.
\tparam EdgeCollapsableMeshWithConstraints a model of `HalfedgeGraph`
\tparam EdgeIsConstrainedMap a model of `ReadablePropertyMap` with the edge descriptor of
`EdgeCollapsableMeshWithConstraints` as key type and a boolean as value type.
It indicates if an edge is constrained or not.
\pre This function requires `mesh` to be an oriented 2-manifold with or without boundaries. Furthermore, the undirected edge `(v0v1,v1v0)` must satisfy the <I>link condition</I> \cgalCite{degn-tpec-98}, which guarantees that the surface is also 2-manifold after the edge collapse.
\pre `get(edge_is_constrained_map, v0v1)==get(edge_is_constrained_map, v1v0)==false`.
\pre `v0` and `v1` are not both incident to a constrained edge.
\relates EdgeCollapsableMeshWithConstraints
*/
template<class EdgeCollapsableMeshWithConstraints,class EdgeIsConstrainedMap>
typename boost::graph_traits<EdgeCollapsableMeshWithConstraints>::vertex_descriptor
halfedge_collapse(typename boost::graph_traits<EdgeCollapsableMeshWithConstraints>::edge_descriptor const& ue,
EdgeCollapsableMeshWithConstraints& mesh,
EdgeIsConstrainedMap edge_is_constrained_map);

View File

@ -132,6 +132,11 @@ Indicates if `v0v1` belongs to a finite face of the mesh (i.e, `v1v0` is not a b
*/
bool right_face_exits() const;
/*!
Returns the surface the edge belongs to.
*/
const ECM& surface() const;
/// @}
}; /* end EdgeProfile */

View File

@ -12,7 +12,6 @@ The placement returned is a `boost::optional` value (i.e., it can
be absent). An absent result indicates that the remaining vertex
must be kept in place, not moved to a new position.
\cgalRefines `DefaultConstructible`
\cgalRefines `CopyConstructible`
\cgalHasModel `CGAL::Surface_mesh_simplification::Midpoint_placement<ECM>`

View File

@ -25,6 +25,7 @@
## Concepts ##
- `EdgeCollapsableMesh`
- `EdgeCollapsableMeshWithConstraints`
- `EdgeProfile`
- `StopPredicate`
- `GetCost`
@ -43,6 +44,7 @@
- `CGAL::Surface_mesh_simplification::Midpoint_placement<ECM>`
- `CGAL::Surface_mesh_simplification::LindstromTurk_cost<ECM>`
- `CGAL::Surface_mesh_simplification::LindstromTurk_placement<ECM>`
- `CGAL::Surface_mesh_simplification::Constrained_placement<Placement,ECM>`
*/

View File

@ -288,12 +288,14 @@ and how to use a visitor object to track the simplification process.
\cgalExample{Surface_mesh_simplification/edge_collapse_enriched_polyhedron.cpp}
\subsection Surface_mesh_simplificationExamplewithedges Example with edges marked as non-removable
\subsection Surface_mesh_simplificationExamplewithedges Example with Edges Marked as Non-Removable
The following example shows how to use the optional named parameter `edge_is_border_map` to prevent
edges from being removed even if they are not really borders.
The following examples show how to use the optional named parameter `edge_is_constrained_map` to prevent
edges from being removed. Edges marked as contrained are guaranteed to be in the final mesh. However,
the vertices of the constrained edges may change and the placement may change the points.
The wrapper `CGAL::Constrained_placement` guarantees that these points are not changed.
\cgalExample{Surface_mesh_simplification/edge_collapse_constrained_polyhedron.cpp}
\cgalExample{Surface_mesh_simplification/edge_collapse_constrained_border_polyhedron.cpp}
*/
} /* namespace CGAL */

View File

@ -1,5 +1,6 @@
/*!
\example Surface_mesh_simplification/edge_collapse_polyhedron.cpp
\example Surface_mesh_simplification/edge_collapse_enriched_polyhedron.cpp
\example Surface_mesh_simplification/edge_collapse_constrained_polyhedron.cpp
\example Surface_mesh_simplification/edge_collapse_constrained_border_polyhedron.cpp
\example Surface_mesh_simplification/edge_collapse_constrain_sharp_edges.cpp
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,157 @@
#include <iostream>
#include <fstream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Surface_mesh_simplification/HalfedgeGraph_Polyhedron_3.h>
#include <CGAL/Surface_mesh_simplification/edge_collapse.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Constrained_placement.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Midpoint_placement.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Count_stop_predicate.h>
#include <CGAL/Unique_hash_map.h>
#include <CGAL/Mesh_3/dihedral_angle_3.h>
#include <CGAL/property_map.h>
#include <cmath>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Polyhedron_3<Kernel> Surface;
typedef boost::graph_traits<Surface const>::edge_descriptor edge_descriptor;
namespace SMS = CGAL::Surface_mesh_simplification ;
typedef Surface::Facet_iterator Facet_iterator;
typedef Surface::Halfedge_handle Halfedge_handle;
typedef Surface::Halfedge_iterator Halfedge_iterator;
//
// BGL property map which indicates whether an edge is marked as non-removable
//
struct Constrained_edge_map : public boost::put_get_helper<bool,Constrained_edge_map>
{
typedef boost::readable_property_map_tag category;
typedef bool value_type;
typedef bool reference;
typedef edge_descriptor key_type;
Constrained_edge_map(const CGAL::Unique_hash_map<key_type,bool>& aConstraints)
: mConstraints(aConstraints) {}
reference operator[](key_type const& e) const { return is_constrained(e); }
bool is_constrained( key_type const& e ) const {
return mConstraints.is_defined(e) ? mConstraints[e] : false ; }
private:
const CGAL::Unique_hash_map<key_type,bool>& mConstraints;
};
int main( int argc, char** argv )
{
CGAL::Unique_hash_map<edge_descriptor,bool> constraint_hmap(false);
Surface surface;
if (argc!=2){
std::cerr<< "Usage: " << argv[0] << " input.off\n";
return 1;
}
std::ifstream is(argv[1]);
if(!is){
std::cerr<< "Filename provided is invalid\n";
return 1;
}
is >> surface ;
Constrained_edge_map constraints_map(constraint_hmap);
SMS::Constrained_placement<SMS::Midpoint_placement<Surface>,
Constrained_edge_map > placement(constraints_map);
// map used to check that constrained_edges and the points of its vertices
// are preserved at the end of the simplification
// Warning: the computation of the diedral angle is only an approximation and can
// be far from the real value and could influence the detection of sharp
// edges after the simplification
std::map<Surface::Halfedge_handle,std::pair<Point_3, Point_3> >constrained_edges;
std::size_t nb_sharp_edges=0;
// detect sharp edges
std::ofstream cst_output("constrained_edges.cgal");
for(Surface::Edge_iterator eb = surface.edges_begin(), ee = surface.edges_end() ; eb != ee ; ++eb )
{
if ( eb->is_border_edge() ){
++nb_sharp_edges;
constraint_hmap[eb]=true;
constraint_hmap[eb->opposite()]=true;
constrained_edges[eb]=std::make_pair( eb->opposite()->vertex()->point(),
eb->vertex()->point() );
}
else{
double angle = CGAL::Mesh_3::dihedral_angle(
eb->opposite()->vertex()->point(),
eb->vertex()->point(),
eb->next()->vertex()->point(),
eb->opposite()->next()->vertex()->point() );
if ( std::abs(angle)<100 ){
++nb_sharp_edges;
constraint_hmap[eb]=true;
constraint_hmap[eb->opposite()]=true;
constrained_edges[eb]=std::make_pair( eb->opposite()->vertex()->point(),
eb->vertex()->point() );
cst_output << "2 " << eb->opposite()->vertex()->point() << " "
<< " " << eb->vertex()->point() << "\n";
}
}
}
cst_output.close();
// Contract the surface as much as possible
SMS::Count_stop_predicate<Surface> stop(0);
int r
= SMS::edge_collapse(surface
,stop
,CGAL::vertex_index_map(boost::get(CGAL::vertex_external_index,surface))
.edge_index_map (boost::get(CGAL::edge_external_index ,surface))
.edge_is_constrained_map(constraints_map)
.get_placement(placement)
);
std::cout << "\nFinished...\n" << r << " edges removed.\n"
<< (surface.size_of_halfedges()/2) << " final edges.\n" ;
std::ofstream os(argc > 2 ? argv[2] : "out.off") ; os << surface ;
std::cout << "Checking sharped edges were preserved...\n";
// check sharp edges were preserved
for(Surface::Edge_iterator eb = surface.edges_begin(), ee = surface.edges_end() ; eb != ee ; ++eb )
{
if ( eb->is_border_edge() ){
--nb_sharp_edges;
assert(
constrained_edges[eb]==std::make_pair( eb->opposite()->vertex()->point(),
eb->vertex()->point() ) );
}
else{
double angle = CGAL::Mesh_3::dihedral_angle(
eb->opposite()->vertex()->point(),
eb->vertex()->point(),
eb->next()->vertex()->point(),
eb->opposite()->next()->vertex()->point() );
if ( std::abs(angle)<100 ){
++nb_sharp_edges;
assert(
constrained_edges[eb]==std::make_pair( eb->opposite()->vertex()->point(),
eb->vertex()->point() ) );
}
}
}
std::cout << "OK\n";
return 0;
}

View File

@ -0,0 +1,118 @@
#include <iostream>
#include <fstream>
#include <map>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// Adaptor for Polyhedron_3
#include <CGAL/Surface_mesh_simplification/HalfedgeGraph_Polyhedron_3.h>
// Simplification function
#include <CGAL/Surface_mesh_simplification/edge_collapse.h>
// Midpoint placement policy
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Midpoint_placement.h>
//Placement wrapper
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Constrained_placement.h>
// Stop-condition policy
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Count_stop_predicate.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Polyhedron_3<Kernel> Surface;
namespace SMS = CGAL::Surface_mesh_simplification ;
//
// BGL property map which indicates whether an edge is marked as non-removable
//
struct Border_is_constrained_edge_map{
typedef boost::graph_traits<Surface>::edge_descriptor key_type;
typedef bool value_type;
typedef value_type reference;
typedef boost::readable_property_map_tag category;
friend bool get(Border_is_constrained_edge_map, key_type edge) {
return edge->is_border_edge();
}
};
//
// Placement class
//
typedef SMS::Constrained_placement<SMS::Midpoint_placement<Surface>,
Border_is_constrained_edge_map > Placement;
int main( int argc, char** argv )
{
Surface surface;
if (argc!=2){
std::cerr<< "Usage: " << argv[0] << " input.off\n";
return 1;
}
std::ifstream is(argv[1]);
if(!is){
std::cerr<< "Filename provided is invalid\n";
return 1;
}
is >> surface ;
// map used to check that constrained_edges and the points of its vertices
// are preserved at the end of the simplification
std::map<Surface::Halfedge_handle,std::pair<Point_3, Point_3> >constrained_edges;
std::size_t nb_border_edges=0;
for (Surface::Halfedge_iterator hit=surface.halfedges_begin(),
hit_end=surface.halfedges_end(); hit!=hit_end;
++hit )
{
if ( hit->is_border() ){
constrained_edges[hit]=std::make_pair( hit->opposite()->vertex()->point(),
hit->vertex()->point() );
++nb_border_edges;
}
}
// Contract the surface as much as possible
SMS::Count_stop_predicate<Surface> stop(0);
// This the actual call to the simplification algorithm.
// The surface and stop conditions are mandatory arguments.
// The index maps are needed because the vertices and edges
// of this surface lack an "id()" field.
int r = SMS::edge_collapse
(surface
,stop
,CGAL::vertex_index_map(boost::get(CGAL::vertex_external_index,surface))
.edge_index_map (boost::get(CGAL::edge_external_index ,surface))
.edge_is_constrained_map(Border_is_constrained_edge_map())
.get_placement(Placement())
);
std::cout << "\nFinished...\n" << r << " edges removed.\n"
<< (surface.size_of_halfedges()/2) << " final edges.\n" ;
std::ofstream os( argc > 2 ? argv[2] : "out.off" ) ; os << surface ;
// now check!
for (Surface::Halfedge_iterator hit=surface.halfedges_begin(),
hit_end=surface.halfedges_end(); hit!=hit_end;
++hit )
{
if (hit->is_border()){
--nb_border_edges;
assert( constrained_edges[hit] ==
std::make_pair( hit->opposite()->vertex()->point(),
hit->vertex()->point() ) );
}
}
assert( nb_border_edges==0 );
return 0 ;
}

View File

@ -1,87 +0,0 @@
#include <iostream>
#include <fstream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
// Adaptor for Polyhedron_3
#include <CGAL/Surface_mesh_simplification/HalfedgeGraph_Polyhedron_3.h>
// Simplification function
#include <CGAL/Surface_mesh_simplification/edge_collapse.h>
// Stop-condition policy
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Count_stop_predicate.h>
// Map used to mark edges as fixed
#include <CGAL/Unique_hash_map.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Surface;
namespace SMS = CGAL::Surface_mesh_simplification ;
//
// BGL property map which indicates whether an edge is border OR is marked as non-removable
//
class Constrains_map : public boost::put_get_helper<bool,Constrains_map>
{
public:
typedef boost::readable_property_map_tag category;
typedef bool value_type;
typedef bool reference;
typedef boost::graph_traits<Surface const>::edge_descriptor key_type;
Constrains_map() : mConstrains(false) {}
reference operator[](key_type const& e) const { return e->is_border() || is_constrained(e) ; }
void set_is_constrained ( key_type const& e, bool is ) { mConstrains[e]=is; }
bool is_constrained( key_type const& e ) const { return mConstrains.is_defined(e) ? mConstrains[e] : false ; }
private:
CGAL::Unique_hash_map<key_type,bool> mConstrains ;
};
int main( int argc, char** argv )
{
Surface surface;
std::ifstream is(argv[1]) ; is >> surface ;
// This is a stop predicate (defines when the algorithm terminates).
// In this example, the simplification stops when the number of undirected edges
// left in the surface drops below the specified number (1000)
SMS::Count_stop_predicate<Surface> stop(10);
Constrains_map constrains_map ;
// This example marks ALL edges as non-removable, but a real world application would mark only selected ones.
for( Surface::Halfedge_iterator eb = surface.halfedges_begin(), ee = surface.halfedges_end() ; eb != ee ; ++ eb )
constrains_map.set_is_constrained(eb,true);
// This the actual call to the simplification algorithm.
// The surface and stop conditions are mandatory arguments.
// The index maps are needed because the vertices and edges
// of this surface lack an "id()" field.
int r = SMS::edge_collapse
(surface
,stop
,CGAL::vertex_index_map(boost::get(CGAL::vertex_external_index,surface))
.edge_index_map (boost::get(CGAL::edge_external_index ,surface))
.edge_is_border_map(constrains_map)
);
std::cout << "\nFinished...\n" << r << " edges removed.\n"
<< (surface.size_of_halfedges()/2) << " final edges.\n" ;
std::ofstream os( argc > 2 ? argv[2] : "out.off" ) ; os << surface ;
return 0 ;
}

View File

@ -80,6 +80,17 @@ bool handle_exists ( Iterator begin, Iterator end, Handle h )
return false ;
}
template <class ECM>
struct No_constrained_edge_map{
typedef typename boost::graph_traits<ECM>::edge_descriptor key_type;
typedef bool value_type;
typedef value_type reference;
typedef boost::readable_property_map_tag category;
friend bool get(No_constrained_edge_map, key_type) {
return false;
}
};
} // namespace Surface_mesh_simplification
template<class N>

View File

@ -35,6 +35,7 @@ template<class ECM_
,class VertexIndexMap_
,class EdgeIndexMap_
,class EdgeIsBorderMap_
,class EdgeIsConstrainedMap_
,class GetCost_
,class GetPlacement_
,class VisitorT_
@ -48,6 +49,7 @@ public:
typedef VertexIndexMap_ VertexIndexMap ;
typedef EdgeIndexMap_ EdgeIndexMap ;
typedef EdgeIsBorderMap_ EdgeIsBorderMap ;
typedef EdgeIsConstrainedMap_ EdgeIsConstrainedMap;
typedef GetCost_ GetCost ;
typedef GetPlacement_ GetPlacement ;
typedef VisitorT_ VisitorT ;
@ -168,16 +170,17 @@ public:
public:
EdgeCollapse( ECM& aSurface
, ShouldStop const& aShouldStop
, VertexIndexMap const& aVertex_index_map
, EdgeIndexMap const& aEdge_index_map
, EdgeIsBorderMap const& aEdge_is_border_map
, GetCost const& aGetCost
, GetPlacement const& aGetPlacement
, VisitorT aVisitor
EdgeCollapse( ECM& aSurface
, ShouldStop const& aShouldStop
, VertexIndexMap const& aVertex_index_map
, EdgeIndexMap const& aEdge_index_map
, EdgeIsBorderMap const& aEdge_is_border_map
, EdgeIsConstrainedMap const& aEdge_is_constrained_map
, GetCost const& aGetCost
, GetPlacement const& aGetPlacement
, VisitorT aVisitor
) ;
int run() ;
private:
@ -208,6 +211,12 @@ private:
bool is_border ( const_edge_descriptor const& aEdge ) const { return Edge_is_border_map[aEdge] ; }
bool is_constrained( const_edge_descriptor const& aEdge ) const { return get(Edge_is_constrained_map,aEdge); }
bool is_constrained( const_vertex_descriptor const& aVertex ) const;
bool is_border_or_constrained( const_edge_descriptor const& aEdge ) const { return Edge_is_border_map[aEdge] ||
Edge_is_constrained_map[aEdge];}
bool is_undirected_edge_a_border ( const_edge_descriptor const& aEdge ) const
{
return is_border(aEdge) || is_border(opposite_edge(aEdge,mSurface)) ;
@ -215,6 +224,8 @@ private:
bool is_border ( const_vertex_descriptor const& aV ) const ;
bool is_border_or_constrained ( const_vertex_descriptor const& aV ) const ;
bool are_shared_triangles_valid( Point const& p0, Point const& p1, Point const& p2, Point const& p3 ) const ;
edge_descriptor find_connection ( const_vertex_descriptor const& v0, const_vertex_descriptor const& v1 ) const ;
@ -251,13 +262,13 @@ private:
std::string vertex_to_string( const_vertex_descriptor const& v ) const
{
Point const& p = get_point(v);
return boost::str( boost::format("[V%1%:%2%]") % v->id() % xyz_to_string(p) ) ;
return boost::str( boost::format("[V%1%:%2%]") % get(Vertex_index_map,v) % xyz_to_string(p) ) ;
}
std::string edge_to_string ( const_edge_descriptor const& aEdge ) const
{
const_vertex_descriptor p,q ; boost::tie(p,q) = get_vertices(aEdge);
return boost::str( boost::format("{E%1% %2%->%3%}%4%") % aEdge->id() % vertex_to_string(p) % vertex_to_string(q) % ( is_border(aEdge) ? " (BORDER)" : ( is_border(aEdge->opposite()) ? " (~BORDER)": "" ) ) ) ;
return boost::str( boost::format("{E%1% %2%->%3%}%4%") % get(Edge_index_map,aEdge) % vertex_to_string(p) % vertex_to_string(q) % ( is_border(aEdge) ? " (BORDER)" : ( is_border(aEdge->opposite()) ? " (~BORDER)": "" ) ) ) ;
}
Cost_type get_cost ( Profile const& aProfile ) const
@ -321,18 +332,55 @@ private:
}
return rEdge ;
}
/// Functions to ensure the backward compatibility before addition of the constrained edge map
template<class AEdgeIsConstrainedMap>
vertex_descriptor
halfedge_collapse_bk_compatibility(
edge_descriptor const& pq, AEdgeIsConstrainedMap aEdge_is_constrained_map)
{
return halfedge_collapse(pq, mSurface, aEdge_is_constrained_map);
}
template<class ECM>
vertex_descriptor
halfedge_collapse_bk_compatibility(
edge_descriptor const& pq, No_constrained_edge_map<ECM> )
{
return halfedge_collapse(pq, mSurface);
}
///
/// We wrap this test to avoid penalizing runtime when no constraints are present
template<class AEdgeIsConstrainedMap>
bool
is_edge_adjacent_to_a_constrained_edge(
Profile const& aProfile, AEdgeIsConstrainedMap)
{
return is_constrained(aProfile.v0()) && is_constrained(aProfile.v1());
}
template<class ECM>
bool
is_edge_adjacent_to_a_constrained_edge(
edge_descriptor const&, No_constrained_edge_map<ECM> )
{
return false;
}
///
private:
ECM& mSurface ;
ShouldStop const& Should_stop ;
VertexIndexMap const& Vertex_index_map ;
EdgeIndexMap const& Edge_index_map ;
EdgeIsBorderMap const& Edge_is_border_map ;
GetCost const& Get_cost ;
GetPlacement const& Get_placement ;
VisitorT Visitor ;
ShouldStop const& Should_stop ;
VertexIndexMap const& Vertex_index_map ;
EdgeIndexMap const& Edge_index_map ;
EdgeIsBorderMap const& Edge_is_border_map;
EdgeIsConstrainedMap const& Edge_is_constrained_map;
GetCost const& Get_cost ;
GetPlacement const& Get_placement ;
VisitorT Visitor ;
private:

View File

@ -24,15 +24,16 @@ namespace CGAL {
namespace Surface_mesh_simplification
{
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::EdgeCollapse( ECM& aSurface
, ShouldStop const& aShould_stop
, VertexIndexMap const& aVertex_index_map
, EdgeIndexMap const& aEdge_index_map
, EdgeIsBorderMap const& aEdge_is_border_map
, GetCost const& aGet_cost
, GetPlacement const& aGet_placement
, VisitorT aVisitor
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::EdgeCollapse( ECM& aSurface
, ShouldStop const& aShould_stop
, VertexIndexMap const& aVertex_index_map
, EdgeIndexMap const& aEdge_index_map
, EdgeIsBorderMap const& aEdge_is_border_map
, EdgeIsConstrainedMap const& aEdge_is_constrained_map
, GetCost const& aGet_cost
, GetPlacement const& aGet_placement
, VisitorT aVisitor
)
:
mSurface (aSurface)
@ -40,6 +41,7 @@ EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::EdgeCollapse( ECM& aS
,Vertex_index_map (aVertex_index_map)
,Edge_index_map (aEdge_index_map)
,Edge_is_border_map (aEdge_is_border_map)
,Edge_is_constrained_map (aEdge_is_constrained_map)
,Get_cost (aGet_cost)
,Get_placement (aGet_placement)
,Visitor (aVisitor)
@ -63,8 +65,8 @@ EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::EdgeCollapse( ECM& aS
#endif
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
int EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::run()
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
int EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::run()
{
CGAL_SURF_SIMPL_TEST_assertion( mSurface.is_valid() && mSurface.is_pure_triangle() ) ;
@ -85,8 +87,8 @@ int EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::run()
return r ;
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collect()
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Collect()
{
CGAL_ECMS_TRACE(0,"Collecting edges...");
@ -114,10 +116,12 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collect()
std::set<edge_descriptor> zero_length_edges;
undirected_edge_iterator eb, ee ;
for ( boost::tie(eb,ee) = undirected_edges(mSurface); eb!=ee; ++eb )
for ( boost::tie(eb,ee) = undirected_edges(mSurface); eb!=ee; ++eb, id+=2 )
{
edge_descriptor lEdge = *eb ;
if ( is_constrained(lEdge) ) continue;//no not insert constrainted edges
CGAL_assertion( get_directed_edge_id(lEdge) == id ) ;
CGAL_assertion( get_directed_edge_id(opposite_edge(lEdge,mSurface)) == id+1 ) ;
@ -141,8 +145,6 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collect()
CGAL_ECMS_TRACE(2,edge_to_string(lEdge));
id += 2 ;
}
CGAL_SURF_SIMPL_TEST_assertion ( lInserted + lNotInserted == mInitialEdgeCount ) ;
@ -156,20 +158,48 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collect()
// edges of length 0 removed no longer need to be treated
if ( lProfile.left_face_exists() )
zero_length_edges.erase( primary_edge(lProfile.vL_v0()) );
if ( lProfile.right_face_exists() )
zero_length_edges.erase( primary_edge(lProfile.vR_v1()) );
{
edge_descriptor lEdge_to_remove = is_constrained(lProfile.vL_v0()) ?
primary_edge(lProfile.v1_vL()) :
primary_edge(lProfile.vL_v0()) ;
zero_length_edges.erase( lEdge_to_remove );
Edge_data& lData = get_data(lEdge_to_remove) ;
if ( lData.is_in_PQ() ){
CGAL_ECMS_TRACE(2,"Removing E" << get(Edge_index_map,lEdge_to_remove) << " from PQ" );
remove_from_PQ(lEdge_to_remove,lData);
}
--mCurrentEdgeCount;
}
if ( lProfile.right_face_exists() )
{
edge_descriptor lEdge_to_remove = is_constrained(lProfile.vR_v1()) ?
primary_edge(lProfile.v0_vR()) :
primary_edge(lProfile.vR_v1()) ;
zero_length_edges.erase( lEdge_to_remove );
Edge_data& lData = get_data(lEdge_to_remove) ;
if ( lData.is_in_PQ() ){
CGAL_ECMS_TRACE(2,"Removing E" << get(Edge_index_map,lEdge_to_remove) << " from PQ" );
remove_from_PQ(lEdge_to_remove,lData);
}
--mCurrentEdgeCount;
}
--mCurrentEdgeCount;
//the placement is trivial, it's always the point itself
Placement_type lPlacement = lProfile.p0();
Collapse(lProfile ,lPlacement);
vertex_descriptor rResult
= halfedge_collapse_bk_compatibility(lProfile.v0_v1(), Edge_is_constrained_map);
put(vertex_point,mSurface,rResult,*lPlacement);
Visitor.OnCollapsed(lProfile,rResult);
}
CGAL_ECMS_TRACE(0,"Initial edge count: " << mInitialEdgeCount ) ;
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Loop()
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Loop()
{
CGAL_ECMS_TRACE(0,"Collapsing edges...") ;
@ -180,18 +210,23 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Loop()
// Pops and processes each edge from the PQ
//
optional<edge_descriptor> lEdge ;
#ifdef CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTING
int i_rm=0;
#endif
while ( (lEdge = pop_from_PQ()) )
{
CGAL_SURF_SIMPL_TEST_assertion ( lLoop_watchdog ++ < mInitialEdgeCount ) ;
CGAL_ECMS_TRACE(1,"Poped " << edge_to_string(*lEdge) ) ;
CGAL_assertion( !is_constrained(*lEdge) );
Profile const& lProfile = create_profile(*lEdge);
Cost_type lCost = get_data(*lEdge).cost();
Visitor.OnSelected(lProfile,lCost,mInitialEdgeCount,mCurrentEdgeCount);
if ( lCost )
{
if ( Should_stop(*lCost,lProfile,mInitialEdgeCount,mCurrentEdgeCount) )
@ -212,7 +247,22 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Loop()
Placement_type lPlacement = get_placement(lProfile);
if ( Is_collapse_geometrically_valid(lProfile,lPlacement) )
{
#ifdef CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTING
std::cout << "step " << i_rm << " " << source(*lEdge,mSurface)->point() << " " << target(*lEdge,mSurface)->point() << "\n";
#endif
Collapse(lProfile,lPlacement);
#ifdef CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTING
std::stringstream sstr;
sstr << "debug/P-";
if (i_rm<10) sstr << "0";
if (i_rm<100) sstr << "0";
sstr << i_rm << ".off";
std::ofstream out(sstr.str().c_str());
out << mSurface;
++i_rm;
#endif
}
}
else
{
@ -230,8 +280,8 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Loop()
}
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::is_border( const_vertex_descriptor const& aV ) const
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::is_border( const_vertex_descriptor const& aV ) const
{
bool rR = false ;
@ -249,22 +299,44 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::is_border( const_vertex_descriptor
return rR ;
}
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::is_border_or_constrained( const_vertex_descriptor const& aV ) const
{
const_in_edge_iterator eb, ee ;
for ( boost::tie(eb,ee) = in_edges(aV,mSurface) ; eb != ee ; ++ eb )
{
const_edge_descriptor lEdge = *eb ;
if ( is_undirected_edge_a_border(lEdge) || is_constrained(lEdge) )
return true;
}
return false;
}
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::is_constrained( const_vertex_descriptor const& aV ) const
{
const_in_edge_iterator eb, ee ;
for ( boost::tie(eb,ee) = in_edges(aV,mSurface) ; eb != ee ; ++ eb )
if ( is_constrained(*eb) ) return true;
return false;
}
// Some edges are NOT collapsable: doing so would break the topological consistency of the mesh.
// This function returns true if a edge 'p->q' can be collapsed.
//
// An edge p->q can be collapsed iff it satisfies the "link condition"
// (as described in the "Mesh Optimization" article of Hoppe et al (1993))
//
// The link conidition is as follows: for every vertex 'k' adjacent to both 'p and 'q',
// The link condition is as follows: for every vertex 'k' adjacent to both 'p and 'q',
// "p,k,q" is a facet of the mesh.
//
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_topologically_valid( Profile const& aProfile )
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Is_collapse_topologically_valid( Profile const& aProfile )
{
bool rR = true ;
CGAL_ECMS_TRACE(3,"Testing topological collapsabilty of p_q=V" << aProfile.v0()->id() << "(%" << aProfile.v0()->vertex_degree() << ")"
<< "->V" << aProfile.v1()->id() << "(%" << aProfile.v1()->vertex_degree() << ")"
CGAL_ECMS_TRACE(3,"Testing topological collapsabilty of p_q=V" << get(Vertex_index_map,aProfile.v0()) << "(%" << aProfile.v0()->vertex_degree() << ")"
<< "->V" << get(Vertex_index_map,aProfile.v1()) << "(%" << aProfile.v1()->vertex_degree() << ")"
);
CGAL_ECMS_TRACE(4, "is p_q border:" << aProfile.is_v0_v1_a_border() );
@ -274,13 +346,13 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_topologically_valid( Pr
out_edge_iterator eb2, ee2 ;
CGAL_ECMS_TRACE(4," t=V"
<< ( aProfile.left_face_exists() ? aProfile.vL()->id() : -1 )
<< ( aProfile.left_face_exists() ? get(Vertex_index_map,aProfile.vL()) : -1 )
<< "(%"
<< ( aProfile.left_face_exists() ? aProfile.vL()->vertex_degree() : 0 )
<< ")"
);
CGAL_ECMS_TRACE(4," b=V"
<< ( aProfile.right_face_exists() ? aProfile.vR()->id() : -1 )
<< ( aProfile.right_face_exists() ? get(Vertex_index_map,aProfile.vR()) : -1 )
<< "(%"
<< ( aProfile.right_face_exists() ? aProfile.vR()->vertex_degree() :0 )
<< ")"
@ -343,13 +415,13 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_topologically_valid( Pr
if ( !lIsFace )
{
CGAL_ECMS_TRACE(3," k=V" << k->id() << " IS NOT in a face with p-q. NON-COLLAPSABLE edge." ) ;
CGAL_ECMS_TRACE(3," k=V" << get(Vertex_index_map,k) << " IS NOT in a face with p-q. NON-COLLAPSABLE edge." ) ;
rR = false ;
break ;
}
else
{
CGAL_ECMS_TRACE(4," k=V" << k->id() << " is in a face with p-q") ;
CGAL_ECMS_TRACE(4," k=V" << get(Vertex_index_map,k) << " is in a face with p-q") ;
}
}
}
@ -358,6 +430,10 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_topologically_valid( Pr
if ( rR )
{
/// ensure two constrained edges cannot get merged
if ( is_edge_adjacent_to_a_constrained_edge(
aProfile, Edge_is_constrained_map) ) return false ;
if ( aProfile.is_v0_v1_a_border() )
{
if ( Is_open_triangle(aProfile.v0_v1()) )
@ -399,8 +475,8 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_topologically_valid( Pr
return rR ;
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_tetrahedron( edge_descriptor const& h1 )
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Is_tetrahedron( edge_descriptor const& h1 )
{
//
// Code copied from Polyhedron_3::is_tetrahedron()
@ -451,8 +527,8 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_tetrahedron( edge_descriptor con
return true;
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_open_triangle( edge_descriptor const& h1 )
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Is_open_triangle( edge_descriptor const& h1 )
{
bool rR = false ;
@ -463,7 +539,7 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_open_triangle( edge_descriptor c
if ( next_edge(h3,mSurface) == h1 )
{
// Now check if it is open
CGAL_ECMS_TRACE(4," p-q is a border edge... checking E" << h2->id() << " and E" << h3->id() ) ;
CGAL_ECMS_TRACE(4," p-q is a border edge... checking E" << get(Edge_index_map,h2) << " and E" << get(Edge_index_map,h3) ) ;
rR = is_border(h2) && is_border(h3);
@ -482,8 +558,8 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_open_triangle( edge_descriptor c
// respective areas is no greater than a max value and the internal
// dihedral angle formed by their supporting planes is no greater than
// a given threshold
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::are_shared_triangles_valid( Point const& p0, Point const& p1, Point const& p2, Point const& p3 ) const
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::are_shared_triangles_valid( Point const& p0, Point const& p1, Point const& p2, Point const& p3 ) const
{
bool rR = false ;
@ -533,9 +609,9 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::are_shared_triangles_valid( Point c
}
// Returns the directed halfedge connecting v0 to v1, if exists.
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
typename EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::edge_descriptor
EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::find_connection ( const_vertex_descriptor const& v0, const_vertex_descriptor const& v1 ) const
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
typename EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::edge_descriptor
EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::find_connection ( const_vertex_descriptor const& v0, const_vertex_descriptor const& v1 ) const
{
out_edge_iterator eb, ee ;
for ( boost::tie(eb,ee) = out_edges(v0,mSurface) ; eb != ee ; ++ eb )
@ -549,10 +625,10 @@ EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::find_connection ( const_vertex_descripto
}
// Given the edge 'e' around the link for the collapsinge edge "v0-v1", finds the vertex that makes a triangle adjacent to 'e' but exterior to the link (i.e not containing v0 nor v1)
// If 'e' is a mnull handle OR 'e' is a border edge, there is no such triangle and a null handle is returned.
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
typename EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::vertex_descriptor
EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::find_exterior_link_triangle_3rd_vertex ( const_edge_descriptor const& e, const_vertex_descriptor const& v0, const_vertex_descriptor const& v1 ) const
// If 'e' is a null handle OR 'e' is a border edge, there is no such triangle and a null handle is returned.
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
typename EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::vertex_descriptor
EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::find_exterior_link_triangle_3rd_vertex ( const_edge_descriptor const& e, const_vertex_descriptor const& v0, const_vertex_descriptor const& v1 ) const
{
vertex_descriptor r ;
@ -581,15 +657,15 @@ EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::find_exterior_link_triangle_3rd_vertex (
}
// A collase is geometrically valid if, in the resulting local mesh no two adjacent triangles form an internal dihedral angle
// A collapse is geometrically valid if, in the resulting local mesh no two adjacent triangles form an internal dihedral angle
// greater than a fixed threshold (i.e. triangles do not "fold" into each other)
//
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_geometrically_valid( Profile const& aProfile, Placement_type k0)
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
bool EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Is_collapse_geometrically_valid( Profile const& aProfile, Placement_type k0)
{
bool rR = true ;
CGAL_ECMS_TRACE(3,"Testing geometrical collapsabilty of v0-v1=E" << aProfile.v0_v1()->id() );
CGAL_ECMS_TRACE(3,"Testing geometrical collapsabilty of v0-v1=E" << get(Edge_index_map,aProfile.v0_v1()) );
if ( k0 )
{
//
@ -610,7 +686,7 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_geometrically_valid( Pr
vertex_descriptor k2 = * l ;
vertex_descriptor k3 = *nx ;
CGAL_ECMS_TRACE(4," Screening link vertices k1=V" << k1->id() << " k2=V" << k2->id() << " k3=V" << k3->id() ) ;
CGAL_ECMS_TRACE(4," Screening link vertices k1=V" << get(Vertex_index_map,k1) << " k2=V" << get(Vertex_index_map,k2) << " k3=V" << get(Vertex_index_map,k3) ) ;
edge_descriptor e12 = find_connection(k1,k2);
edge_descriptor e23 = k3 != k1 ? find_connection(k2,k3) : edge_descriptor() ;
@ -622,7 +698,7 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_geometrically_valid( Pr
if ( !are_shared_triangles_valid( *k0, get_point(k1), get_point(k2), get_point(k3) ) )
{
CGAL_ECMS_TRACE(3," Triangles VX-V" << k1->id() << "-V" << k2->id() << " and VX-V" << k3->id() << " are not geometrically valid. Collapse rejected");
CGAL_ECMS_TRACE(3," Triangles VX-V" << get(Vertex_index_map,k1) << "-V" << get(Vertex_index_map,k2) << " and VX-V" << get(Vertex_index_map,k3) << " are not geometrically valid. Collapse rejected");
rR = false ;
}
}
@ -635,11 +711,11 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_geometrically_valid( Pr
// There is indeed a triangle shared along e12
if ( handle_assigned(k4) )
{
CGAL_ECMS_TRACE(4," Found exterior link triangle shared along E" << e12->id() << " with third vertex: V" << k4->id() ) ;
CGAL_ECMS_TRACE(4," Found exterior link triangle shared along E" << get(Edge_index_map,e12) << " with third vertex: V" << get(Vertex_index_map,k4) ) ;
if ( !are_shared_triangles_valid( get_point(k1), get_point(k4), get_point(k2), *k0 ) )
{
CGAL_ECMS_TRACE(3," Triangles V" << k1->id() << "-V" << k4->id() << " and V" << k2->id() << "-VX are not geometrically valid. Collapse rejected");
CGAL_ECMS_TRACE(3," Triangles V" << get(Vertex_index_map,k1) << "-V" << get(Vertex_index_map,k4) << " and V" << get(Vertex_index_map,k2) << "-VX are not geometrically valid. Collapse rejected");
rR = false ;
}
}
@ -653,11 +729,11 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_geometrically_valid( Pr
// There is indeed a triangle shared along e12
if ( handle_assigned(k5) )
{
CGAL_ECMS_TRACE(4," Found exterior link triangle shared along E" << e23->id() << " with third vertex: V" << k5->id() ) ;
CGAL_ECMS_TRACE(4," Found exterior link triangle shared along E" << get(Edge_index_map,e23) << " with third vertex: V" << get(Vertex_index_map,k5) ) ;
if ( !are_shared_triangles_valid( get_point(k2), get_point(k5), get_point(k3), *k0 ) )
{
CGAL_ECMS_TRACE(3," Triangles V" << k2->id() << "-V" << k5->id() << " and V" << k3->id() << "-VX are not geometrically valid. Collapse rejected");
CGAL_ECMS_TRACE(3," Triangles V" << get(Vertex_index_map,k2) << "-V" << get(Vertex_index_map,k5) << " and V" << get(Vertex_index_map,k3) << "-VX are not geometrically valid. Collapse rejected");
rR = false ;
}
}
@ -669,8 +745,8 @@ bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_geometrically_valid( Pr
return rR ;
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collapse( Profile const& aProfile, Placement_type aPlacement )
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Collapse( Profile const& aProfile, Placement_type aPlacement )
{
CGAL_ECMS_TRACE(1,"S" << mStep << ". Collapsig " << edge_to_string(aProfile.v0_v1()) ) ;
@ -691,15 +767,17 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collapse( Profile const& aProfile,
if ( aProfile.left_face_exists() )
{
edge_descriptor lV0VL = primary_edge(aProfile.vL_v0());
if ( is_constrained(lV0VL) ) //make sure a constrained edge will not disappear
lV0VL=primary_edge(aProfile.v1_vL());
CGAL_ECMS_TRACE(3,"V0VL E" << lV0VL->id()
<< "(V" << lV0VL->vertex()->id() << "->V" << lV0VL->opposite()->vertex()->id() << ")"
CGAL_ECMS_TRACE(3,"V0VL E" << get(Edge_index_map,lV0VL)
<< "(V" << get(Vertex_index_map,lV0VL->vertex()) << "->V" << get(Vertex_index_map,lV0VL->opposite()->vertex()) << ")"
) ;
Edge_data& lData = get_data(lV0VL) ;
if ( lData.is_in_PQ() )
{
CGAL_ECMS_TRACE(2,"Removing E" << lV0VL->id() << " from PQ" ) ;
CGAL_ECMS_TRACE(2,"Removing E" << get(Edge_index_map,lV0VL) << " from PQ" ) ;
remove_from_PQ(lV0VL,lData) ;
}
@ -710,22 +788,24 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collapse( Profile const& aProfile,
if ( aProfile.right_face_exists() )
{
edge_descriptor lVRV1 = primary_edge(aProfile.vR_v1());
CGAL_ECMS_TRACE(3,"V1VRE" << lVRV1->id()
<< "(V" << lVRV1->vertex()->id() << "->V" << lVRV1->opposite()->vertex()->id() << ")"
if ( is_constrained(lVRV1) ) //make sure a constrained edge will not disappear
lVRV1=primary_edge(aProfile.v0_vR());
CGAL_ECMS_TRACE(3,"V1VRE" << get(Edge_index_map,lVRV1)
<< "(V" << get(Vertex_index_map,lVRV1->vertex()) << "->V" << get(Vertex_index_map,lVRV1->opposite()->vertex()) << ")"
) ;
Edge_data& lData = get_data(lVRV1) ;
if ( lData.is_in_PQ() )
{
CGAL_ECMS_TRACE(2,"Removing E" << lVRV1->id() << " from PQ") ;
CGAL_ECMS_TRACE(2,"Removing E" << get(Edge_index_map,lVRV1) << " from PQ") ;
remove_from_PQ(lVRV1,lData) ;
}
-- mCurrentEdgeCount ;
CGAL_SURF_SIMPL_TEST_assertion_code( -- lResultingEdgeCount ) ;
}
CGAL_ECMS_TRACE(1,"Removing:\n v0v1: E" << aProfile.v0_v1()->id() << "(V" << aProfile.v0()->id() << "->V" << aProfile.v1()->id() << ")" );
CGAL_ECMS_TRACE(1,"Removing:\n v0v1: E" << get(Edge_index_map,aProfile.v0_v1()) << "(V" << get(Vertex_index_map,aProfile.v0()) << "->V" << get(Vertex_index_map,aProfile.v1()) << ")" );
// Perform the actuall collapse.
@ -734,7 +814,7 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collapse( Profile const& aProfile,
// (PT and QB are removed if they are not null).
// All other edges must be kept.
// All directed edges incident to vertex removed are relink to the vertex kept.
rResult = halfedge_collapse(aProfile.v0_v1(),mSurface);
rResult = halfedge_collapse_bk_compatibility(aProfile.v0_v1(), Edge_is_constrained_map);
CGAL_SURF_SIMPL_TEST_assertion_code( -- lResultingEdgeCount ) ;
@ -746,7 +826,7 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collapse( Profile const& aProfile,
CGAL_SURF_SIMPL_TEST_assertion( mSurface.is_valid() && mSurface.is_pure_triangle() ) ;
CGAL_ECMS_TRACE(1,"V" << rResult->id() << " kept." ) ;
CGAL_ECMS_TRACE(1,"V" << get(Vertex_index_map,rResult) << " kept." ) ;
#ifdef CGAL_SURFACE_SIMPLIFICATION_ENABLE_TRACE
out_edge_iterator eb1, ee1 ;
@ -767,8 +847,8 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collapse( Profile const& aProfile,
CGAL_ECMS_DEBUG_CODE ( ++mStep ; )
}
template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Update_neighbors( vertex_descriptor const& aKeptV )
template<class M,class SP, class VIM,class EIM,class EBM,class ECTM, class CF,class PF,class V>
void EdgeCollapse<M,SP,VIM,EIM,EBM,ECTM,CF,PF,V>::Update_neighbors( vertex_descriptor const& aKeptV )
{
CGAL_ECMS_TRACE(3,"Updating cost of neighboring edges..." ) ;
@ -795,7 +875,7 @@ void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Update_neighbors( vertex_descriptor
edge_descriptor lEdge2 = primary_edge(*eb2) ;
Edge_data& lData2 = get_data(lEdge2);
CGAL_ECMS_TRACE(4,"Inedge around V" << lAdj_k->id() << edge_to_string(lEdge2) ) ;
CGAL_ECMS_TRACE(4,"Inedge around V" << get(Vertex_index_map,lAdj_k) << edge_to_string(lEdge2) ) ;
// Only those edges still in the PQ _and_ not already collected are updated.
if ( lData2.is_in_PQ() && lToUpdate.find(lEdge2) == lToUpdate.end() )

View File

@ -0,0 +1,80 @@
// Copyright (c) 2014 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$
//
// Author(s) : Sebastien Loriot <sebastien.loriot@cgal.org>
//
#ifndef CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_CONSTRAINED_PLACEMENT_H
#define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_CONSTRAINED_PLACEMENT_H
#include <CGAL/Surface_mesh_simplification/Detail/Common.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_profile.h>
namespace CGAL {
namespace Surface_mesh_simplification
{
template<class BasePlacement, class EdgeIsConstrainedMap>
class Constrained_placement : public BasePlacement
{
public:
typedef typename BasePlacement::Profile Profile;
typedef typename BasePlacement::Point Point;
typedef optional<Point> result_type ;
EdgeIsConstrainedMap Edge_is_constrained_map;
public:
Constrained_placement(
EdgeIsConstrainedMap map=EdgeIsConstrainedMap(),
BasePlacement base=BasePlacement() )
: BasePlacement(base)
, Edge_is_constrained_map(map)
{}
result_type operator()( Profile const& aProfile ) const
{
typedef typename Profile::ECM ECM;
typedef typename boost::graph_traits<ECM> GraphTraits;
typedef typename GraphTraits::in_edge_iterator in_edge_iterator;
in_edge_iterator eb, ee ;
for ( boost::tie(eb,ee) = in_edges(aProfile.v0(),aProfile.surface()) ;
eb != ee ; ++ eb )
{
if( get(Edge_is_constrained_map, *eb) )
return get(vertex_point,
aProfile.surface(),
aProfile.v0());
}
for ( boost::tie(eb,ee) = in_edges(aProfile.v1(),aProfile.surface()) ;
eb != ee ; ++ eb )
{
if( get(Edge_is_constrained_map, *eb) )
return get(vertex_point,
aProfile.surface(),
aProfile.v1());
}
return static_cast<const BasePlacement*>(this)->operator()(aProfile);
}
};
} // namespace Surface_mesh_simplification
} //namespace CGAL
#endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_CONSTRAINED_PLACEMENT_H

View File

@ -23,6 +23,7 @@
#include <CGAL/boost/graph/named_function_params.h>
#include <CGAL/Surface_mesh_simplification/Detail/Edge_collapse.h>
#include <CGAL/Surface_mesh_simplification/Detail/Common.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/LindstromTurk.h>
namespace CGAL {
@ -35,23 +36,25 @@ template<class ECM
,class VertexIndexMap
,class EdgeIndexMap
,class EdgeIsBorderMap
,class EdgeIsConstrainedMap
,class GetCost
,class GetPlacement
,class Visitor
>
int edge_collapse ( ECM& aSurface
, ShouldStop const& aShould_stop
int edge_collapse ( ECM& aSurface
, ShouldStop const& aShould_stop
// optional mesh information policies
, VertexIndexMap const& aVertex_index_map // defaults to get(vertex_index,aSurface)
, EdgeIndexMap const& aEdge_index_map // defaults to get(edge_index,aSurface)
, EdgeIsBorderMap const& aEdge_is_border_map // defaults to get(edge_is_border,aSurface)
, VertexIndexMap const& aVertex_index_map // defaults to get(vertex_index,aSurface)
, EdgeIndexMap const& aEdge_index_map // defaults to get(edge_index,aSurface)
, EdgeIsBorderMap const& aEdge_is_border_map // defaults to get(edge_is_border,aSurface)
, EdgeIsConstrainedMap const& aEdge_is_constrained_map // defaults to No_constrained_edge_map<ECM>()
// optional strategy policies - defaults to LindstomTurk
, GetCost const& aGet_cost
, GetPlacement const& aGet_placement
, GetCost const& aGet_cost
, GetPlacement const& aGet_placement
, Visitor aVisitor
, Visitor aVisitor
)
{
typedef EdgeCollapse< ECM
@ -59,6 +62,7 @@ int edge_collapse ( ECM& aSurface
, VertexIndexMap
, EdgeIndexMap
, EdgeIsBorderMap
, EdgeIsConstrainedMap
, GetCost
, GetPlacement
, Visitor
@ -70,6 +74,7 @@ int edge_collapse ( ECM& aSurface
, aVertex_index_map
, aEdge_index_map
, aEdge_is_border_map
, aEdge_is_constrained_map
, aGet_cost
, aGet_placement
, aVisitor
@ -110,10 +115,11 @@ int edge_collapse ( ECM& aSurface
,choose_const_pmap(get_param(aParams,boost::vertex_index),aSurface,boost::vertex_index)
,choose_const_pmap(get_param(aParams,boost::edge_index),aSurface,boost::edge_index)
,choose_const_pmap(get_param(aParams,edge_is_border),aSurface,edge_is_border)
,choose_param (get_param(aParams,edge_is_constrained),No_constrained_edge_map<ECM>())
,choose_param (get_param(aParams,get_cost_policy), LindstromTurk_cost<ECM>())
,choose_param (get_param(aParams,get_placement_policy), LindstromTurk_placement<ECM>())
,choose_param (get_param(aParams,vis), Dummy_visitor())
) ;
);
}

View File

@ -28,93 +28,162 @@ namespace CGAL {
namespace Surface_mesh_simplification
{
/*
Function responsible for contracting an edge while respecting constrained edges
Notations used in the following function:
Top=TopFace
Btm=BottomFace
t
/ \
/ \
/ Top \
p -------- q
\ Btm /
\ /
\ /
b
Prerequisites:
If Top exists, amongst p-t and t-q only one is constrained
If Btm exists, amongst p-b and q-b only one is constrained
p-q is not constrained
*/
template<class Gt, class I, CGAL_HDS_PARAM_, class A, class EdgeIsConstrainedMap>
typename boost::graph_traits< Polyhedron_3<Gt,I,HDS,A> >::vertex_descriptor
halfedge_collapse( typename boost::graph_traits< Polyhedron_3<Gt,I,HDS,A> >::edge_descriptor const& pq
, Polyhedron_3<Gt,I,HDS,A>& aSurface
, EdgeIsConstrainedMap Edge_is_constrained_map
)
{
CGAL_assertion( !get(Edge_is_constrained_map,pq) );
typedef Polyhedron_3<Gt,I,HDS,A> Surface ;
typedef typename boost::graph_traits<Surface>::vertex_descriptor vertex_descriptor ;
typedef typename boost::graph_traits<Surface>::edge_descriptor edge_descriptor ;
edge_descriptor qp = opposite_edge(pq,aSurface);
edge_descriptor pt = opposite_edge(prev_edge(pq,aSurface),aSurface);
edge_descriptor qb = opposite_edge(prev_edge(qp,aSurface),aSurface);
edge_descriptor tq = pq->next()->opposite();
edge_descriptor bp = qp->next()->opposite();
bool lTopFaceExists = !pq->is_border() ;
bool lBottomFaceExists = !qp->is_border() ;
CGAL_precondition( !lTopFaceExists || (lTopFaceExists && ( pt->vertex()->vertex_degree() > 2 ) ) ) ;
CGAL_precondition( !lBottomFaceExists || (lBottomFaceExists && ( qb->vertex()->vertex_degree() > 2 ) ) ) ;
vertex_descriptor q = pq->vertex();
vertex_descriptor p = pq->opposite()->vertex();
CGAL_ECMS_TRACE(3, "Collapsing p-q E" << pq->id() << " (V" << p->id() << "->V" << q->id() << ")" ) ;
//used to collect edges to remove from the surface
edge_descriptor edges_to_erase[2];
edge_descriptor* edges_to_erase_ptr=edges_to_erase;
// If the top facet exists, we need to choose one out of the two edges which one disappears:
// p-t if it is not constrained and t-q otherwise
if ( lTopFaceExists )
{
CGAL_precondition( !pt->opposite()->is_border() ) ; // p-q-t is a face of the mesh
if ( !get(Edge_is_constrained_map,pt) )
{
CGAL_ECMS_TRACE(3, "Removing p-t E" << pt->id() << " (V" << p->id() << "->V" << pt->vertex()->id()) ;
*edges_to_erase_ptr++=pt;
}
else
{
CGAL_ECMS_TRACE(3, "Removing t-q E" << pt->id() << " (V" << pt->vertex()->id() << "->V" << q->id() ) ;
CGAL_assertion( !get(Edge_is_constrained_map,tq) );
*edges_to_erase_ptr++=tq;
}
}
// If the bottom facet exists, we need to choose one out of the two edges which one disappears:
// q-b if it is not constrained and b-p otherwise
if ( lBottomFaceExists )
{
if ( !get(Edge_is_constrained_map,qb) )
{
CGAL_ECMS_TRACE(3, "Removing q-b E" << qb->id() << " (V" << q->id() << "->V" << qb->vertex()->id() ) ;
*edges_to_erase_ptr++=qb;
}
else{
CGAL_ECMS_TRACE(3, "Removing b-p E" << qb->id() << " (V" << qb->vertex()->id() << "->V" << p->id() ) ;
CGAL_assertion( !get(Edge_is_constrained_map,bp) );
*edges_to_erase_ptr++=bp;
}
}
if (lTopFaceExists && lBottomFaceExists)
{
if ( edges_to_erase[0]->facet()==edges_to_erase[1]->facet()
&& !edges_to_erase[0]->is_border() )
{
// the vertex is of valence 3 and we simply need to remove the vertex
// and its indicent edges
bool lP_Erased=false;
edge_descriptor edge =
edges_to_erase[0]->next()==edges_to_erase[1]?
edges_to_erase[0]:edges_to_erase[1];
if (edge->vertex()==p)
lP_Erased=true;
aSurface.erase_center_vertex(edge);
return lP_Erased? q : p;
}
else
{
if (!edges_to_erase[0]->is_border())
aSurface.join_facet(edges_to_erase[0]);
else
aSurface.erase_facet(edges_to_erase[0]->opposite());
if (!edges_to_erase[1]->is_border())
aSurface.join_facet(edges_to_erase[1]);
else
aSurface.erase_facet(edges_to_erase[1]->opposite());
aSurface.join_vertex(pq);
return q;
}
}
else
{
if (lTopFaceExists)
{
if (!edges_to_erase[0]->is_border()){
aSurface.join_facet(edges_to_erase[0]);
aSurface.join_vertex(pq);
return q;
}
bool lQ_Erased=pq->next()->opposite()->is_border();
aSurface.erase_facet(edges_to_erase[0]->opposite());
return lQ_Erased?p:q;
}
CGAL_assertion(lBottomFaceExists);
if (!edges_to_erase[0]->is_border()){
aSurface.join_facet(edges_to_erase[0]);
aSurface.join_vertex(qp);
return p;
}
bool lP_Erased=qp->next()->opposite()->is_border();
aSurface.erase_facet(edges_to_erase[0]->opposite());
return lP_Erased?q:p;
};
}
template<class Gt, class I, CGAL_HDS_PARAM_, class A>
typename boost::graph_traits< Polyhedron_3<Gt,I,HDS,A> >::vertex_descriptor
halfedge_collapse( typename boost::graph_traits< Polyhedron_3<Gt,I,HDS,A> >::edge_descriptor const& pq
, Polyhedron_3<Gt,I,HDS,A>& aSurface
)
)
{
typedef Polyhedron_3<Gt,I,HDS,A> Surface ;
typedef typename boost::graph_traits<Surface>::vertex_descriptor vertex_descriptor ;
typedef typename boost::graph_traits<Surface>::edge_descriptor edge_descriptor ;
edge_descriptor qp = opposite_edge(pq,aSurface);
edge_descriptor pt = opposite_edge(prev_edge(pq,aSurface),aSurface);
edge_descriptor qb = opposite_edge(prev_edge(qp,aSurface),aSurface);
bool lTopFaceExists = !pq->is_border() ;
bool lBottomFaceExists = !qp->is_border() ;
bool lTopLeftFaceExists = lTopFaceExists && !pt->is_border() ;
bool lBottomRightFaceExists = lBottomFaceExists && !qb->is_border() ;
CGAL_precondition( !lTopFaceExists || (lTopFaceExists && ( pt->vertex()->vertex_degree() > 2 ) ) ) ;
CGAL_precondition( !lBottomFaceExists || (lBottomFaceExists && ( qb->vertex()->vertex_degree() > 2 ) ) ) ;
vertex_descriptor q = pq->vertex();
vertex_descriptor p = pq->opposite()->vertex();
CGAL_ECMS_TRACE(3, "Collapsing p-q E" << pq->id() << " (V" << p->id() << "->V" << q->id() << ")" ) ;
bool lP_Erased = false, lQ_Erased = false ;
if ( lTopFaceExists )
{
CGAL_precondition( !pt->opposite()->is_border() ) ; // p-q-t is a face of the mesh
if ( lTopLeftFaceExists )
{
CGAL_ECMS_TRACE(3, "Removing p-t E" << pt->id() << " (V" << p->id() << "->V" << pt->vertex()->id() << ") by joining top-left face" ) ;
aSurface.join_facet (pt);
}
else
{
CGAL_ECMS_TRACE(3, "Removing p-t E" << pt->id() << " (V" << p->id() << "->V" << pt->vertex()->id() << ") by erasing top face" ) ;
aSurface.erase_facet(pt->opposite());
if ( !lBottomFaceExists )
{
CGAL_ECMS_TRACE(3, "Bottom face doesn't exist so vertex P already removed" ) ;
lP_Erased = true ;
}
}
}
if ( lBottomFaceExists )
{
CGAL_precondition( !qb->opposite()->is_border() ) ; // p-q-b is a face of the mesh
if ( lBottomRightFaceExists )
{
CGAL_ECMS_TRACE(3, "Removing q-b E" << qb->id() << " (V" << q->id() << "->V" << qb->vertex()->id() << ") by joining bottom-right face" ) ;
aSurface.join_facet (qb);
}
else
{
CGAL_ECMS_TRACE(3, "Removing q-b E" << qb->id() << " (V" << q->id() << "->V" << qb->vertex()->id() << ") by erasing bottom face" ) ;
aSurface.erase_facet(qb->opposite());
if ( !lTopFaceExists )
{
CGAL_ECMS_TRACE(3, "Top face doesn't exist so vertex Q already removed" ) ;
lQ_Erased = true ;
}
}
}
CGAL_assertion( !lP_Erased || !lQ_Erased ) ;
if ( !lP_Erased && !lQ_Erased )
{
CGAL_ECMS_TRACE(3, "Removing vertex P by joining pQ" ) ;
aSurface.join_vertex(pq);
lP_Erased = true ;
}
return lP_Erased ? q : p ;
}
return halfedge_collapse( pq,
aSurface,
No_constrained_edge_map<Polyhedron_3<Gt,I,HDS,A> >() );
}
} // namespace Surface_mesh_simplification
@ -124,4 +193,4 @@ halfedge_collapse( typename boost::graph_traits< Polyhedron_3<Gt,I,HDS,A> >::edg
#endif // CGAL_SURFACE_MESH_SIMPLIFICATION_COLLAPSE_TRIANGULATION_EDGE_POLYHEDRON_3_H
// EOF //

View File

@ -36,23 +36,25 @@ int main( int argc, char** argv )
Surface surface;
is >> surface ;
std::size_t initial_count = (surface.size_of_halfedges()/2);
std::cout << "Initial count " << initial_count << " edges.\n" ;
// Contract the surface as much as possible
SMS::Count_stop_predicate<Surface> stop(0);
int r = SMS::edge_collapse
(surface
,stop
,CGAL::vertex_index_map(boost::get(CGAL::vertex_external_index,surface))
.edge_index_map (boost::get(CGAL::edge_external_index ,surface))
);
std::cout << "\nFinished...\n" << r << " edges removed.\n"
<< (surface.size_of_halfedges()/2) << " final edges.\n" ;
assert( initial_count == (surface.size_of_halfedges()/2) + r );
// std::ofstream os( argc > 2 ? argv[2] : "out.off" ) ; os << surface ;
return 0 ;
}