mirror of https://github.com/CGAL/cgal
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:
commit
d45d1a1070
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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>`
|
||||
|
|
|
|||
|
|
@ -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>`
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
cube-meshed.off
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
mesh_with_border.off
|
||||
|
|
@ -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 ;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
cube.off
|
||||
|
|
@ -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 ;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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() )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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())
|
||||
) ;
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 //
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue